Files
task-6a1d75dbfd30e81cf3126b09/main.py
T
2026-06-27 13:47:34 +00:00

163 lines
5.0 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.
import os
import json
import asyncio
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# LLM configuration OpenRouter via langchain_openai
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="openai/gpt-oss-20b:free",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.0,
)
# Tavily search tool
from langchain_tavily import TavilySearchResults
tavily = TavilySearchResults(tavily_api_key=os.getenv("TAVILY_API_KEY"))
# LangGraph imports
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated, List
from langgraph.graph.message import add_messages
# Define state
class BriefState(TypedDict):
topic: str
outline: List[str] | None
step_index: int
notes: List[str]
final_brief: str | None
# Outline node generate 45 bullet points
async def outline_node(state: BriefState) -> BriefState:
prompt = (
f"Given the research topic: {state['topic']}\n"
"Provide a concise outline with 45 bullet points. Return the outline as a JSON array of strings."
)
response = await llm.ainvoke(prompt)
try:
outline = json.loads(response)
if not isinstance(outline, list):
raise ValueError
except Exception:
# Fallback: split lines
outline = [line.strip('- • ') for line in response.splitlines() if line.strip()]
return {
"topic": state['topic'],
"outline": outline,
"step_index": 0,
"notes": [],
"final_brief": None,
}
# Research step node one web search per step
async def research_step_node(state: BriefState) -> BriefState:
step = state['outline'][state['step_index']]
query = f"{state['topic']} {step}"
search_results = tavily.run(query)
prompt = (
f"Using the following search results, write a concise note (58 sentences) about the step: {step}.\n"
f"Search results:\n{search_results}\n"
"Note:")
note = await llm.ainvoke(prompt)
notes = state['notes'] + [note]
step_index = state['step_index'] + 1
return {
"topic": state['topic'],
"outline": state['outline'],
"step_index": step_index,
"notes": notes,
"final_brief": None,
}
# Synthesize node produce final brief
async def synthesize_node(state: BriefState) -> BriefState:
notes_text = "\n\n".join(state['notes'])
prompt = (
f"Based on the following notes, write a cohesive research brief about the topic: {state['topic']}\n"
"Include headings for each point and keep the brief ½–1 page long.\n"
f"Notes:\n{notes_text}\n"
"Final brief:")
final = await llm.ainvoke(prompt)
return {
"topic": state['topic'],
"outline": state['outline'],
"step_index": state['step_index'],
"notes": state['notes'],
"final_brief": final,
}
# Build the graph
graph = StateGraph(BriefState)
graph.add_node("outline", outline_node)
graph.add_node("research_step", research_step_node)
graph.add_node("synthesize", synthesize_node)
# Conditional edge after research_step
def condition(state: BriefState):
if state['step_index'] < len(state['outline']):
return "research_step"
return "synthesize"
graph.add_edge(START, "outline")
graph.add_edge("outline", "research_step")
graph.add_conditional_edges("research_step", condition)
graph.add_edge("synthesize", END)
# Compile the graph into a function
compiled_graph = graph.compile()
# Function to run the whole brief generation
async def run_brief(topic: str) -> str:
init_state: BriefState = {
"topic": topic,
"outline": None,
"step_index": 0,
"notes": [],
"final_brief": None,
}
final_state = await compiled_graph.ainvoke(init_state)
return final_state["final_brief"]
# DeepAgents integration
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
from langchain.tools import tool
from langchain_core.messages import HumanMessage
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
@tool
async def generate_brief(topic: str) -> str:
"""Generate a research brief for the given topic."""
return await run_brief(topic)
agent = create_deep_agent(
model=llm,
tools=[generate_brief],
backend=backend,
system_prompt="You are a research assistant. Use the provided tools to generate briefs.",
)
async def main():
default_topic = "Как студенту безопасно подключать MCP к LangChain"
response = await agent.ainvoke(
{"messages": [HumanMessage(content=f"Generate brief on '{default_topic}'")],
"configurable": {"thread_id": "session-1"}},
)
# The tool output will be in the last message
print("\n=== Research Brief ===\n")
print(response["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())