Files
task-6a1d75dbfd30e81cf3126b09/main.py
T
2026-06-04 16:02:55 +00:00

153 lines
5.3 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 asyncio
from typing import TypedDict, Annotated, List
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
# --- LLM configuration (OpenRouter) ---
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,
)
# --- Backend for deepagents ---
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# --- Tavily search tool ---
@tool
def tavily_search(query: str) -> str:
"""Search the web using Tavily and return a short summary of the top result."""
from tavily import TavilyClient
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
results = client.search(query, max_results=1)
if not results:
return "No relevant information found."
# Return the first result's content (title + snippet)
first = results[0]
return f"{first.title}\n{first.snippet}"
# --- State definition for LangGraph (used inside a tool) ---
class BriefState(TypedDict):
topic: str
outline: List[str] | None
step_index: int
notes: List[str]
final_brief: str | None
# --- LangGraph graph implementation ---
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
# Helper to format outline as a numbered list
def format_outline(outline: List[str]) -> str:
return "\n".join(f"{i+1}. {p}" for i, p in enumerate(outline))
# Node: generate outline
async def outline_node(state: BriefState) -> BriefState:
prompt = f"Create a concise 45 point outline for a research brief on the topic: {state['topic']}"
response = await llm.ainvoke([HumanMessage(content=prompt)])
outline_text = response.content.strip()
# Assume the LLM returns a numbered list; split into lines
outline = [line.strip() for line in outline_text.splitlines() if line.strip()]
state['outline'] = outline
state['step_index'] = 0
state['notes'] = []
return state
# Node: research one step
async def research_step_node(state: BriefState) -> BriefState:
current_point = state['outline'][state['step_index']]
# Use Tavily to get info
search_query = f"{current_point}"
search_result = tavily_search(search_query)
# Summarize the result with LLM
prompt = f"Summarize the following information in 58 sentences, focusing on the key points relevant to the research brief: {search_result}"
summary = await llm.ainvoke([HumanMessage(content=prompt)])
state['notes'].append(f"**{current_point}**\n{summary.content.strip()}")
state['step_index'] += 1
return state
# Node: synthesize final brief
async def synthesize_node(state: BriefState) -> BriefState:
notes_text = "\n\n".join(state['notes'])
prompt = f"Using the following notes, write a cohesive ½–1 page research brief. Include headings for each section.\n\n{notes_text}"
brief = await llm.ainvoke([HumanMessage(content=prompt)])
state['final_brief'] = brief.content.strip()
return state
# 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)
# Define transitions
graph.set_entry_point("outline")
graph.add_conditional_edges(
"outline",
lambda _: "research_step",
)
def research_cond(state: BriefState):
return "research_step" if state['step_index'] < len(state['outline']) else "synthesize"
graph.add_conditional_edges("research_step", research_cond)
graph.add_edge("synthesize", END)
# Compile the graph into a tool
from langgraph.prebuilt import create_react_agent
# The graph will be used as a tool inside deepagents
@tool
async def run_brief(topic: str) -> str:
"""Generate a research brief for the given topic."""
# Initialize state
state: BriefState = {
"topic": topic,
"outline": None,
"step_index": 0,
"notes": [],
"final_brief": None,
}
# Run the graph
async for partial_state in graph.astream(state):
pass # we just wait for completion
# After completion, return the brief
return state['final_brief']
# --- DeepAgent setup ---
agent = create_deep_agent(
model=llm,
tools=[tavily_search, run_brief],
backend=backend,
system_prompt="You are a research assistant that creates concise research briefs. Use the provided tools to gather information and synthesize a brief.",
)
# --- Demo execution ---
async def main():
default_topic = "Как студенту безопасно подключать MCP к LangChain"
result = await agent.ainvoke(
{"messages": [HumanMessage(content=f"Create a research brief on: {default_topic}")]},
{"configurable": {"thread_id": "session-1"}},
)
print("\n--- Outline ---")
# The outline is part of the first tool call; extract it
for msg in result["messages"]:
if msg.type == "tool":
if "outline" in msg.content.lower():
print(msg.content)
print("\n--- Final Brief ---")
print(result["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())