fix: main.py — Практическое задание №3: Память и подтверждение действий

This commit is contained in:
2026-07-02 08:20:12 +00:00
parent 02e0579a8c
commit c2ba581de2
+53 -48
View File
@@ -1,15 +1,19 @@
import os import os
import uuid
import asyncio import asyncio
from typing import Optional, Dict, Any
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.messages import HumanMessage from langchain_core.messages import HumanMessage
from deepagents import create_deep_agent from langchain.tools import tool
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
from langgraph.checkpoint.memory import MemorySaver from langgraph.checkpoint.memory import MemorySaver
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
from deepagents import create_deep_agent as create_agent
from rich.console import Console from rich.console import Console
# Конфигурация LLM через OpenRouter # Инициализация консоли rich
console = Console()
# Инициализация LLM через OpenRouter
llm = ChatOpenAI( llm = ChatOpenAI(
model="openai/gpt-oss-20b:free", model="openai/gpt-oss-20b:free",
base_url="https://openrouter.ai/api/v1", base_url="https://openrouter.ai/api/v1",
@@ -17,7 +21,7 @@ llm = ChatOpenAI(
temperature=0.0, temperature=0.0,
) )
# Backend для выполнения инструментов (необязательно, но удобно) # Backend для deepagents (необязательно, но удобно)
backend = CompositeBackend([ backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"), LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(), FilesystemBackend(),
@@ -25,74 +29,75 @@ backend = CompositeBackend([
# Пример простого инструмента # Пример простого инструмента
@tool @tool
def get_price(params: dict) -> str: def get_price(city: str, date: str) -> str:
""" """Возвращает цену в указанном городе и дате."""
Получить цену товара в указанном городе и дате. return f"Цена в {city} на {date} составляет $100"
"""
city = params.get("city", "неизвестный город") # Память разговора
date = params.get("date", "неизвестная дата") memory = MemorySaver()
return f"Цена в {city} на {date} составляет 100$"
# Создание агента с памятью и паузой перед инструментом # Создание агента с памятью и паузой перед инструментом
memory = MemorySaver() agent = create_agent(
agent = create_deep_agent(
model=llm, model=llm,
tools=[get_price], tools=[get_price],
backend=backend, backend=backend,
system_prompt="You are a helpful agent that asks for confirmation before calling tools.", system_prompt="You are a helpful agent.",
checkpointer=memory, checkpointer=memory,
interrupt_before=["tools"], interrupt_before=["tools"],
) )
console = Console() # Конфигурация разговора
config: Dict[str, Any] = {"configurable": {"thread_id": "conversation-1"}}
async def ask_and_run(user_input, config): async def ask_and_run(user_input: Optional[Dict[str, Any]], config: Dict[str, Any]) -> None:
""" """
Запускает потоковое выполнение агента, обрабатывает паузы и подтверждения. Запускает потоковое взаимодействие с агентом.
Если агент останавливается перед вызовом инструмента, запрашивает подтверждение у пользователя.
""" """
async for chunk in agent.stream(user_input, config=config, stream_mode=["messages", "updates"]): async for chunk_type, chunk_data in agent.stream(
chunk_type, chunk_data = chunk user_input,
config=config,
# Потоковый вывод токенов stream_mode=["messages", "updates"],
):
# Вывод токенов ответа
if chunk_type == "messages": if chunk_type == "messages":
console.print(chunk_data, end="", style="cyan") content = chunk_data.get("content", "")
continue console.print(content, end="")
# Вывод вызовов инструментов # Вывод информации о вызове инструмента
if chunk_type == "updates": elif chunk_type == "updates":
console.print(chunk_data, style="magenta") console.print(chunk_data)
continue
# Обнаружение паузы перед инструментом # Обнаружение паузы перед инструментом
if "__interrupt__" in chunk_data and agent.get_state(config).next == ("tools",): if "__interrupt__" in chunk_data and agent.get_state(config).next == ("tools",):
state = agent.get_state(config) state = agent.get_state(config)
# Последнее сообщение должно содержать вызов инструмента # Последнее сообщение содержит вызов инструмента
last_msg = state.values["messages"][-1] tool_call = state.values["messages"][-1].tool_calls[0]
tool_call = last_msg.tool_calls[0] console.print("\n")
tool_name = tool_call["name"] console.print(f"{tool_call['name']}({tool_call['args']})")
tool_args = tool_call["args"] console.print("Агент хочет вызвать утилиту")
console.print(f"\nАгент хочет вызвать утилиту {tool_name}({tool_args})")
answer = input("Разрешить? (Y/n): ") answer = input("Разрешить? (Y/n): ")
if answer.lower().strip() == "y": if answer.lower().strip() == "y":
# Возобновляем выполнение с того места, где остановились
await ask_and_run(None, config) await ask_and_run(None, config)
return
else: else:
console.print("Отменено", style="red") console.print("Отменено")
break return
async def main(): def main() -> None:
thread_id = f"session-{uuid.uuid4()}" console.print("\n--- --- ---\n")
config = {"configurable": {"thread_id": thread_id}}
console.print("Введите 'exit' для выхода.", style="bold green")
while True: while True:
user_input = input("\nВы: ") user_input = input("\nВы: ")
if user_input.lower() == "exit": if user_input.lower().strip() == "exit":
break break
await ask_and_run( # Запускаем асинхронную функцию
{"messages": [HumanMessage(content=user_input)]}, asyncio.run(
config, ask_and_run(
{"messages": [{"role": "human", "content": user_input}]},
config,
)
) )
console.print("\n--- --- ---\n")
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) main()