feat: solution for task-003

This commit is contained in:
+29 -41
View File
@@ -29,7 +29,7 @@ memory = MemorySaver()
agent = create_agent( agent = create_agent(
model=llm, model=llm,
tools=tools, tools=tools,
system_prompt="Ты полезный помощкиник. Если пользователь спрашивает о погоде, используй инструмент get_price.", system_prompt="Ты полезный помощмущник. Если пользователь спрашивает о погоде, используй инструмент get_price.",
checkpointer=memory, checkpointer=memory,
interrupt_before=['tools'], interrupt_before=['tools'],
) )
@@ -39,39 +39,32 @@ config = {"configurable": {"thread_id": "chat-session-123"}}
def ask_and_run(user_input, config): def ask_and_run(user_input, config):
"""Основная функция обработки сообщений и управления циклом подтверждения.""" """Основная функция обработки сообщений и управления циклом подтверждения."""
# Если user_input is None, мы просто продолжаем выполнение (возобновление после паузы) # Если user_input is None, мы просто продолжаем выполнение (возобновление после паузы)
input_payload = {"messages": [{"role": "human", "content": user_input}]} if user_input else None # В LangGraph для возобновления через stream передается None или пустой список сообщений
stream_input = user_input if user_input is not None else []
# stream_mode=['messages', 'updates'] позволяет обрабатывать и токены, и изменения состояния
stream = agent.stream(input_payload, config=config, stream_mode=['messages', 'updates'])
# Используем stream_mode=['messages', 'updates'] согласно заданию
try: try:
for chunk in stream: for chunk in agent.stream(stream_input, config=config, stream_mode=['messages', 'updates']):
chunk_type, chunk_data = chunk
state = agent.get_state(config) state = agent.get_state(config)
chunk_type, chunk_data = chunk
if chunk_type == 'messages': if chunk_type == 'messages':
# Потоковый вывод текста (токены) # Потоковый вывод токенов (сообщений)
# В новых версиях LangGraph/LangChain чанк сообщения может быть объектом BaseMessage message, metadata = chunk_data
content = "" if hasattr(message, "content") and message.content:
if hasattr(chunk_data, 'content'): print(message.append if hasattr(message, 'append') else message.content, end="", flush=True)
content = chunk_data.content
elif isinstance(chunk_data, str):
content = chunk_data
if content:
console.print(content, end="", flush=True)
elif chunk_type == 'updates': elif chunk_type == 'updates':
# Здесь можно обрабатывать обновления узлов, если нужно # Вывод информации об обновлениях (например, вызовы инструментов)
pass pass
# Проверка на прерывание (интеррупт перед инструментом) # Проверка на прерывание перед вызовом инструмента
# В LangGraph при interrupt_before ключ '__interrupt__' появляется в данных чанка
if '__interrupt__' in chunk_data and state.next == ('tools',): if '__interrupt__' in chunk_data and state.next == ('tools',):
console.print("\n" + "---" * 10) console.print("\n" + "---" * 15)
# Извлекаем информацию о том, какой инструмент вызывается # Извлекаем информацию о том, какой инструмент хочет вызвать агент
last_message = state.values['messages'][-1] last_message = state.values['messages'][-1]
if hasattr(last_message, 'tool_calls') and last_message.tool_calls: if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
tool_call = last_message.tool_calls[0] tool_call = last_message.tool_calls[0]
@@ -81,39 +74,34 @@ def ask_and_run(user_input, config):
answer = input("Разрешить? (Y/n): ") answer = input("Разрешить? (Y/n): ")
if answer.lower().strip() == 'y': if answer.lower().strip() == 'y':
# Рекурсивный вызов с None для продолжения выполнения # Рекурсивный вызов для продолжения без нового сообщения пользователя
ask_and_run(None, config) ask_and_run(None, config)
else: else:
console.print("Отменено") console.print("Отменено")
return # Выход из текущей итерации функции return # Выходим из текущей итерации стрима
# Печать переноса строки после завершения стрима (если не было прерывания) if user_input: # Печать переноса строки только если был новый ввод
if user_input is not None: print()
console.print()
except Exception as e: except Exception as e:
# Обработка ошибок или остановки потока # Если произошла ошибка в процессе стрима (например, при рекурсии), пробрасываем её выше
pass raise e
def main(): if __name__ == "__main__":
console.print("[bold blue]Чат запущен. Напишите 'exit' для выхода.[/bold blue]") console.print("[bold blue]Чат запущен. Напишите 'exit' для выхода.[/bold blue]")
while True: while True:
try: try:
user_text = input("\nВы: ") user_text = input("\nВы: ")
if user_text.lower().strip() == 'exit': if user_text.lower().strip() == 'exit':
console.print("До свидания!")
break break
# Запуск агента с новым сообщением # Формируем входные данные для агента
ask_and_run( input_payload = {"messages": [{"role": "human", "content": user_text}]}
{"messages": [{"role": "human", "content": user_text}]},
config, ask_and_run(input_payload, config)
)
except EOFError: except KeyboardInterrupt:
break break
except Exception as e: except Exception as e:
console.print(f"Ошибка: {e}") console.print(f"[bold red]Ошибка: {e}[/bold red]")
if __name__ == "__main__":
main()