llm-agent-backend/app/workflows.py
dimitrievgs d27156d152 init
2025-09-12 00:26:19 +03:00

148 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Определяет логику рабочих процессов агента,
используя LangGraph для управления состояниями
и переходами между узлами обработки.
"""
from typing import Dict, Any, Optional
from langgraph.graph import StateGraph, END
from graph_history_manager import GraphHistoryManager
from models import AgentState
from nodes import parse_command_node, execute_command_node, call_llm_node, generate_images_node, analyze_image_node, get_meet_subtitles_node, get_teams_subtitles_node, summarize_history_node, handle_error_node, help_node
# --- LangGraph: Построение графа ---
graph_history_manager = GraphHistoryManager()
workflow = StateGraph(AgentState)
# Добавляем узлы
workflow.add_node("parse_command", parse_command_node)
workflow.add_node("execute_command",
execute_command_node) # Это по сути роутер
workflow.add_node("call_llm", call_llm_node)
workflow.add_node("generate_images", generate_images_node)
workflow.add_node("analyze_image", analyze_image_node)
workflow.add_node("get_meet_subtitles", get_meet_subtitles_node)
workflow.add_node("get_teams_subtitles", get_teams_subtitles_node)
workflow.add_node("summarize_history", summarize_history_node)
workflow.add_node("handle_error", handle_error_node)
workflow.add_node("help", help_node)
# Устанавливаем точку входа
workflow.set_entry_point("parse_command")
# Определяем маршрутизацию после parse_command
def route_command(state: AgentState) -> str:
"""Маршрутизирует выполнение на основе распознанной команды."""
if state.error:
return "handle_error" # Если была ошибка парсинга команды
elif state.command == "imagine":
return "generate_images"
elif state.command == "analyze":
return "analyze_image"
elif state.command == "subtitles_meet":
return "get_meet_subtitles"
elif state.command == "subtitles_teams":
return "get_teams_subtitles"
elif state.command == "summarize":
return "summarize_history"
elif state.command == "help":
return "help"
elif state.command == "chat":
return "call_llm"
elif state.command == "error": # Команда была, но не известная
return "handle_error"
else:
# Fallback на LLM, если команда не распознана или пуста (хотя parse_command должен был бы ее поймать)
return "call_llm"
workflow.add_conditional_edges(
"parse_command", # Откуда
route_command, # Функция, определяющая куда идти
{
"generate_images": "generate_images",
"analyze_image": "analyze_image",
"get_meet_subtitles": "get_meet_subtitles",
"get_teams_subtitles": "get_teams_subtitles",
"summarize_history": "summarize_history",
"help": "help",
"call_llm": "call_llm",
"handle_error":
"handle_error", # Для ошибок, найденных в parse_command
},
)
# Все узлы операций (кроме ошибок) ведут к END после завершения
workflow.add_edge("call_llm", END)
workflow.add_edge("generate_images", END)
workflow.add_edge("analyze_image", END)
workflow.add_edge("get_meet_subtitles", END)
workflow.add_edge("get_teams_subtitles", END)
workflow.add_edge("summarize_history", END)
workflow.add_edge("help", END)
workflow.add_edge("handle_error",
END) # Ошибка - это тоже конечный пункт для текущего запроса
# Компилируем граф
app = workflow.compile()
def run_agent(user_input: str,
existing_graph_id: Optional[str] = None,
parent_node_id: Optional[str] = None) -> Dict[str, Any]:
"""
Запускает агента с заданным пользовательским вводом.
Может продолжить существующий граф по graph_id.
Возвращает обновленное состояние и ID графа.
"""
initial_state = AgentState(input=user_input, parent_node_id=parent_node_id)
# Если есть существующий ID графа, загружаем его историю
if existing_graph_id:
existing_graph_data = graph_history_manager.get_graph(
existing_graph_id)
if existing_graph_data:
print(f"Продолжаю существующий граф {existing_graph_id}")
initial_state.chat_history = existing_graph_data.get(
"messages", [])
initial_state.graph_nodes = existing_graph_data.get(
"graph_nodes", [])
initial_state.graph_edges = existing_graph_data.get(
"graph_edges", [])
initial_state.parent_node_id = parent_node_id or existing_graph_data.get(
"current_node_id")
# Запускаем граф
result = app.invoke(initial_state)
final_state = AgentState(**result)
# Сохраняем обновленное состояние графа
graph_data_to_save = {
"id": existing_graph_id,
"messages": final_state.chat_history,
"graph_nodes": final_state.graph_nodes,
"graph_edges": final_state.graph_edges,
"current_node_id": final_state.current_node_id
}
new_graph_id = graph_history_manager.save_graph(graph_data_to_save)
# Формируем ответ для фронтенда
response_data = {
"graph_id": new_graph_id,
"messages": final_state.chat_history,
"llm_response": final_state.llm_response,
"image_urls": final_state.image_urls,
"analysis_result": final_state.analysis_result,
"subtitles": final_state.subtitles,
"summarized_history_text": final_state.summarized_history_text,
"error": final_state.error,
"graph_visualization_data": {
"nodes": final_state.graph_nodes,
"edges": final_state.graph_edges,
"current_node_id": final_state.current_node_id
}
}
return response_data