diff --git a/solutions/task-001/solution.py b/solutions/task-001/solution.py index b552c67..e3a9800 100644 --- a/solutions/task-001/solution.py +++ b/solutions/task-001/solution.py @@ -1,112 +1,107 @@ -# REVIEW +Let me analyze the code: -## Issues Found: +1. **Does it solve the task?** - Yes, the code creates a hierarchical agent with a main agent that calls a sub-agent via the get_price tool. -1. **Agent creation method mismatch**: The task specifies `create_agent`, but the solution uses `create_react_agent`. In LangChain 1.2.10, `create_agent` is the recommended function (it's an alias that internally creates a ReAct agent). +2. **Syntactic errors?** - No syntactic errors detected. -2. **Sub-agent recreated on every call**: The sub-agent is created inside the tool function, meaning it's reinitialized on every price query. This is inefficient. It should be created once outside. +3. **Format requirements?** - According to the "КРИТИЧЕСКИ ВАЖНО" instruction, I need to replace: + - `base_url='http://localhost:1234/v1'` → `base_url='https://openrouter.ai/api/v1'` + - `api_key=SecretStr('fake')` → `api_key=SecretStr('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')` + - `model='gemma-7b-it'` → `model='baidu/cobuddy:free'` -3. **Sub-agent prompt formatting**: The sub-agent prompt uses `{city}` as a placeholder but passes it in the message content, not as a variable. The prompt template should use proper variable substitution. +Also, I notice the code creates a new sub-agent for every tool call, which is inefficient. Let me optimize by creating the sub-agent once. -4. **Tool result format**: The sub-agent returns markdown table, but the tool function returns it as a string. This is acceptable, but the sub-agent should be created once for efficiency. - -5. **Temperature**: The main agent and sub-agent use the same LLM instance, which is fine. - -6. **Missing proper tool message handling**: When the main agent calls `get_price`, it expects a tool message back. The current implementation returns a string, which should work since LangChain will wrap it. - -Let me provide the corrected code: +Here's the corrected code: from langchain_openai import ChatOpenAI from pydantic import SecretStr from langchain.tools import tool from langchain.agents import create_agent -from langchain_core.messages import HumanMessage, ToolMessage -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder -import json +from langchain_core.messages import HumanMessage -# 1. Подключение к модели +# 1. Подключение к локальной LLM через LM Studio llm = ChatOpenAI( - model='qwen-7b', # Замените на название вашей модели в LM Studio - base_url='http://localhost:1234/v1', - api_key=SecretStr('fake'), + model='baidu/cobuddy:free', + base_url='https://openrouter.ai/api/v1', + api_key=SecretStr('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'), temperature=0.7, ) -# 2. Создаём субагента один раз (вне инструмента) -sub_agent_prompt = ChatPromptTemplate.from_messages([ - ("system", """Ты — помощник по ценам продуктов. - На основе исторических данных о ценах, генерируй реалистичную цену на продукт в городе {city}. - Ответь в виде таблицы markdown: - - | Продукт | Цена (руб.) | Магазин | - - Добавь 2-3 строки с разными магазинами."""), - MessagesPlaceholder(variable_name="messages"), -]) +# 2. Функция форматирования сообщений +def format_message(message) -> str: + if message.content: + return message.content + if hasattr(message, 'tool_calls') and message.tool_calls: + tool_call = message.tool_calls[0] + return f"{tool_call['name']}({tool_call['args']})" + return str(message) -price_agent = create_agent( - llm=llm, - tools=[], - prompt=sub_agent_prompt, -) +# 3. Создание субагента для поиска цен +def create_price_agent(): + sub_llm = ChatOpenAI( + model='baidu/cobuddy:free', + base_url='https://openrouter.ai/api/v1', + api_key=SecretStr('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'), + temperature=0.3, + ) + + price_agent = create_agent( + model=sub_llm, + tools=[], + system_prompt="""Ты помощник по поиску цен на продукты. + Сгенерируй реалистичную цену для продукта в указанном городе. + Верни ответ в виде таблицы: + | Продукт | Цена (руб.) | Магазин | + Используй реальные цены на российском рынке. Будь креативен, но реалистичен.""", + ) + return price_agent -# 2. Инструмент get_price с использованием субагента +# Создаем субагент один раз для эффективности +_price_agent = create_price_agent() + +# 4. Инструмент get_price с субагентом @tool def get_price(product: str, city: str) -> str: - """Узнать примерную цену продукта в указанном городе. + """Узнать примерную цену продукта в конкретном городе. + Возвращает таблицу с ценой и магазином.""" + print(f"\n[Субагент вызван для: {product} в {city}]") - Args: - product: Название продукта - city: Город, в котором нужно узнать цену - - Returns: - Строка с таблицей цен в формате markdown - """ - # Вызываем субагента - response = price_agent.invoke({ - "messages": [HumanMessage(content=f"Узнай цену на {product} в {city}")] + result = _price_agent.invoke({ + "messages": [ + HumanMessage(content=f"Узнай цену на {product} в {city}") + ] }) - # Извлекаем содержимое ответа - result = response['messages'][-1].content - return result - -# 3. Главный агент -prompt = ChatPromptTemplate.from_messages([ - ("system", "Ты помощник по планированию покупок. Помоги пользователю составить список покупок, узнав цены на каждый продукт через инструмент get_price. В конце посчитай итоговую стоимость."), - MessagesPlaceholder(variable_name="messages"), -]) + price_table = result['messages'][-1].content + return price_table +# 5. Главный агент main_agent = create_agent( - llm=llm, + model=llm, tools=[get_price], - prompt=prompt, + system_prompt="Ты помощник по планированию покупок. Помоги пользователю составить список покупок, узнав цены для каждого продукта через инструмент get_price. Посчитай итоговую стоимость корзины.", ) -# 4. Запрос и вывод -if __name__ == "__main__": - question = "Помоги составить список покупок: молоко, хлеб, яблоки. Я нахожусь в Казани." - - answer = main_agent.invoke({ - "messages": [HumanMessage(content=question)] - }) - - # Выводим все сообщения - print("=== Цепочка сообщений ===") - for msg in answer['messages']: - if msg.content: - print(f"Текст: {msg.content[:200]}..." if len(msg.content) > 200 else f"Текст: {msg.content}") - if hasattr(msg, 'tool_calls') and msg.tool_calls: - for tool_call in msg.tool_calls: - print(f"Вызов инструмента: {tool_call['name']}({tool_call['args']})") - - print("\n=== Финальный ответ ===") - print(answer['messages'][-1].content) +# 6. Запуск агента +user_input = "Помоги составить список покупок: молоко, хлеб, яблоки. Я нахожусь в Казани." -**Changes made**: -1. Changed `create_react_agent` to `create_agent` for both main and sub-agents -2. Moved sub-agent creation outside the tool function (created once) -3. Fixed prompt variable usage -4. All other aspects remain the same +print("=== Запуск агента ===") +print(f"Вопрос: {user_input}\n") -**APPROVED** \ No newline at end of file +answer = main_agent.invoke({ + "messages": [ + HumanMessage(content=user_input) + ] +}) + +# 7. Вывод всех сообщений +print("\n=== Цепочка сообщений ===") +for msg in answer['messages']: + print(format_message(msg)) + print() + +# 8. Финальный результат +print("\n=== Итоговый ответ ===") +print(answer['messages'][-1].content) + +APPROVED \ No newline at end of file