Повторный экзамен: Исследовательский бриф (план → шаги → сводка): solution.py
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
|||||||
|
import os
|
||||||
|
from typing import TypedDict, List, Optional, Dict, Any
|
||||||
|
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from langchain_tavily import TavilySearchResults
|
||||||
|
from langgraph.graph import StateGraph, START, END
|
||||||
|
from langgraph.prebuilt import ToolNode
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# ---------- 1. Состояние ----------
|
||||||
|
class BriefState(TypedDict):
|
||||||
|
topic: str
|
||||||
|
outline: List[str] | None
|
||||||
|
step_index: int
|
||||||
|
notes: List[str]
|
||||||
|
final_brief: str | None
|
||||||
|
|
||||||
|
# ---------- 2. Инструменты ----------
|
||||||
|
search_tool = TavilySearchResults()
|
||||||
|
|
||||||
|
# ---------- 3. Узлы ----------
|
||||||
|
def outline_node(state: BriefState) -> Dict[str, Any]:
|
||||||
|
"""Создаёт план из 4–5 пунктов по теме."""
|
||||||
|
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||||
|
prompt = (
|
||||||
|
f"Составь план исследования по теме: {state['topic']}. "
|
||||||
|
"План должен содержать 4–5 пунктов, каждый пункт – заголовок без описания."
|
||||||
|
)
|
||||||
|
plan = llm.invoke(prompt).content.strip()
|
||||||
|
outline = [line.strip("- ").strip() for line in plan.splitlines() if line.strip()]
|
||||||
|
return {"outline": outline, "step_index": 0, "notes": []}
|
||||||
|
|
||||||
|
def research_step_node(state: BriefState) -> Dict[str, Any]:
|
||||||
|
"""Для текущего пункта плана делает поиск и формирует заметку."""
|
||||||
|
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||||
|
current_index = state["step_index"]
|
||||||
|
if state["outline"] is None or current_index >= len(state["outline"]):
|
||||||
|
return {"step_index": current_index, "notes": state["notes"]}
|
||||||
|
|
||||||
|
topic = state["outline"][current_index]
|
||||||
|
# Поиск
|
||||||
|
search_query = f"{state['topic']} - {topic}"
|
||||||
|
results = search_tool.invoke({"query": search_query})
|
||||||
|
# Сводка
|
||||||
|
summary_prompt = (
|
||||||
|
f"Сделай краткую заметку (5–8 предложений) по теме '{topic}'. "
|
||||||
|
f"Используй найденные источники: {results}"
|
||||||
|
)
|
||||||
|
note = llm.invoke(summary_prompt).content.strip()
|
||||||
|
notes = state["notes"] + [f"**{topic}**\n{note}"]
|
||||||
|
return {"step_index": current_index + 1, "notes": notes}
|
||||||
|
|
||||||
|
def synthesize_node(state: BriefState) -> Dict[str, Any]:
|
||||||
|
"""Объединяет все заметки в связный бриф."""
|
||||||
|
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||||
|
combined = "\n\n".join(state["notes"])
|
||||||
|
prompt = (
|
||||||
|
f"Собери из следующих заметок связный исследовательский бриф (½–1 страница):\n{combined}"
|
||||||
|
)
|
||||||
|
brief = llm.invoke(prompt).content.strip()
|
||||||
|
return {"final_brief": brief}
|
||||||
|
|
||||||
|
# ---------- 4. Граф ----------
|
||||||
|
def build_graph() -> StateGraph[BriefState]:
|
||||||
|
graph = StateGraph(BriefState)
|
||||||
|
|
||||||
|
# Добавляем узлы
|
||||||
|
graph.add_node("outline", outline_node)
|
||||||
|
graph.add_node("research_step", research_step_node)
|
||||||
|
graph.add_node("synthesize", synthesize_node)
|
||||||
|
|
||||||
|
# Переходы
|
||||||
|
graph.set_entry_point("outline")
|
||||||
|
graph.add_edge("outline", "research_step")
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"research_step",
|
||||||
|
lambda state: "synthesize" if state["step_index"] >= len(state["outline"] or []) else "research_step",
|
||||||
|
{
|
||||||
|
"synthesize": "synthesize",
|
||||||
|
"research_step": "research_step",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
graph.add_edge("synthesize", END)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
# ---------- 5. Запуск ----------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
topic = input("Введите тему исследования: ").strip()
|
||||||
|
initial_state: BriefState = {
|
||||||
|
"topic": topic,
|
||||||
|
"outline": None,
|
||||||
|
"step_index": 0,
|
||||||
|
"notes": [],
|
||||||
|
"final_brief": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
graph = build_graph()
|
||||||
|
chain = graph.compile()
|
||||||
|
|
||||||
|
result = chain.invoke(initial_state)
|
||||||
|
print("\n=== Итоговый исследовательский бриф ===\n")
|
||||||
|
print(result["final_brief"])
|
||||||
Reference in New Issue
Block a user