Skip to content

Implementation of example storage and example selector classes [draft]#428

Open
3LayerPerceptron wants to merge 49 commits intodeeppavlov:devfrom
RLKRo:feat/few-shot-examples-llm
Open

Implementation of example storage and example selector classes [draft]#428
3LayerPerceptron wants to merge 49 commits intodeeppavlov:devfrom
RLKRo:feat/few-shot-examples-llm

Conversation

@3LayerPerceptron
Copy link

Description

Please describe here what changes are made and why.

Checklist

  • I have performed a self-review of the changes

List here tasks to complete in order to mark this PR as ready for review.

To Consider

  • Add tests (if functionality is changed)
  • Update API reference / tutorials / guides
  • Update CONTRIBUTING.md (if devel workflow is changed)
  • Update .ignore files, scripts (such as lint), distribution manifest (if files are added/deleted)
  • Search for references to changed entities in the codebase

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears this PR is a release PR (change its base from master if that is not the case).

Here's a release checklist:

  • Update package version
  • Update poetry.lock
  • Change PR merge option
  • Update template repo
  • Search for objects to be deprecated
  • Test parts not covered with pytest:
    • web_api tutorials
    • Test integrations with external services (telegram; stats)

@3LayerPerceptron
Copy link
Author

Также, во время написания у меня возникли вопросы к архитектуре решения.

Основная проблема архитектуры заключается в том, что в langchain ExampleSelector является холдером примеров. Наша обертка тоже пытается быть холдером примеров.

Из этого следуют описанные ниже проблемы:

Проблема владения примерами
Если мы действительно хотим оставить владение примерами на стороне обертки, то мы столкнемся с тем, что примеры в селекторе и обертке не общаются между собой.
Также будет происходить дублирование информации, т.к. и обертка и селектор владеют копиями одних и тех же примеров.
Это связано с тем, что логика выбора примеров реализована в селекторе и ему необходимо знать из чего выбирать.

Возможный фикс
Добавить в нашу обертку метод add_example и конструктор, которые будут синхронизировать примеры.
Также нужно будет передавать примеры в метод select_examples, чтобы избежать дубликации.

**Возникают вопросы. **
А для чего нам дополнительный уровень абстракции в виде обертки?
Если селектору надо передавать примеры, так может просто логику селектора реализовать в качестве метода в обертку?

Допустим, мы решили, что холдером может быть только один из классов.

Если холдер - это селектор из langchain, то зачем нам обертка, почему просто не использовать переопределенный селектор.

Если холдер - это обертка, то зачем мы вообще наследуемся от langchain базы?
Выходит так, что мы не используем в этом случае один из методов селектора, а другой используем только потому, что наследуемся от этой базы.

Copy link
Member

@RLKRo RLKRo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А для чего нам дополнительный уровень абстракции в виде обертки?

It's desirable to provide both simple and advanced ways of adding examples.

ExamplePrompt(
    examples=[
        {"input": "3+4", "output": "7"},
    ]
)

-- this is simple and easy to use in scripts.

example_selector = SemanticSimilarityExampleSelector(vector_store=Chroma(...))

example_selector.add_examples(...)

ExamplePrompt(
    examples=example_selector
)

-- difficult to setup but more customizable.

Another layer of abstraction would allow the same API for the both options.

@3LayerPerceptron
Copy link
Author

All coding is done and tests have been added.
Moving to the writing proper docstrings and guide page.

@RLKRo RLKRo changed the base branch from master to dev May 16, 2025 18:00
@3LayerPerceptron
Copy link
Author

I run poe lint and refactored code accordingly.
The most recent commit also removes prompt specification in integration tests, since changes in Prompt related classes broke it and they are still ongoing to my understanding.

prompt = Prompt.model_validate(element)
prompt_langchain_message = await message_to_langchain(await prompt.message(ctx), ctx, source="human")

prompt_messages = await element.to_langchain_messages(ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Element is of type Any. It should be converted to Prompt first.

from chatsky.core import BaseResponse, AnyResponse, MessageInitTypes, Message, Context
from chatsky.llm._langchain_imports import HumanMessage, AIMessage, SystemMessage
from langchain_core.example_selectors.base import BaseExampleSelector
from chatsky.llm.example_selector import to_langchain_context
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imports should be grouped:
https://peps.python.org/pep-0008/#imports

Uses Langchain's example selectors and prompt templates for few-shot learning.
"""
template: Optional[str] = Field(None)
examples: Optional[BaseExampleSelector] = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Union[StaticExampleSelector|BaseExampleSelector] to allow initializing the class as

FewShotExamplePrompt(examples=[
  {"input": "", "output": ""},
])



@pytest.fixture(name="ctx")
def ctx() -> Context:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between this fixture and book_context?
They return almost the same thing.

Comment on lines 62 to 65
# --------------------
# Tests for BasePrompt
# --------------------
class TestBasePrompt:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this comment.

Comment on lines 191 to 194
assert isinstance(result[0], SystemMessage)
assert "Query: 2 + 2" in result[0].content[0]["text"]
assert "Answer: 4" in result[0].content[0]["text"]
assert "Answer the following:" in result[0].content[0]["text"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to compare result to the actual result.
e.g.

assert result == [SystemMessage("...")]

prefix=self.prefix,
suffix=self.suffix,
)
text = prompt_template.format(input=user_input, output="")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should support including user_input in this prompt.
User request is already added to context history in get_langchain_context.

assert called_args["vars"]["input"] == ""

@pytest.mark.asyncio
async def test_message_to_langchain_error(ctx, monkeypatch):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this test?

await prompt.to_langchain_messages(ctx)

@pytest.mark.asyncio
async def test_template_without_prefix_suffix(self, ctx, monkeypatch):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the point of this test.
Empty strings are allowed in langchain's FewShotPromptTemplate and our FewShotExamplePrompt does not do anything special with suffix or prefix.

It would make more sense to pass None as prefix and suffix instead of empty strings, since our model allows None but FewShotPromptTemplate accepts only strings.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of tests but a lot of them seem unnecessary.

While some more important tests concerning UX are missing.
E.g. test what happens when prefix is None and template is not or vice versa (there should be a warning or exception if prefix is used without template).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants