Add main.py

This commit is contained in:
+77 -93
View File
@@ -1,109 +1,96 @@
"""
# main.py
# LangGraph agent with reflection and rewrite loop
# Author: ChatGPT
# Requirements: langgraph, langchain-openai, deepagents
import os
import asyncio
from typing import TypedDict
from typing import TypedDict, Dict, Any
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, LocalShellBackend
# LLM setup
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 = CompositeBackend(
default=LocalShellBackend(root_dir="./workspace", virtual_mode=True, inherit_env=True),
routes={},
)
agent = create_deep_agent(
model=llm,
tools=[],
backend=backend,
system_prompt="You are a helpful assistant.",
)
# 1. State definition
class ReflectState(TypedDict):
question: str
draft: str
critique: str
verdict: str
verdict: str # "ok" | "needs_revision"
round: int
max_rounds: int
async def draft_answer(state: ReflectState) -> ReflectState:
prompt = f"Write a concise answer (510 sentences) to the following question:\n\n{state['question']}"
response = await agent.ainvoke({"messages": ["Human: " + prompt]}, {"configurable": {"thread_id": "draft"}})
draft = response["messages"][-1].content
state["draft"] = draft
return state
# 2. LLM instance
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.2)
async def reflect(state: ReflectState) -> ReflectState:
# 3. Nodes
def draft_answer(state: ReflectState) -> Dict[str, Any]:
prompt = (
"You are a critical reviewer.\n"
"Evaluate the following draft answer for completeness, specificity, and lack of filler.\n"
"Provide a verdict: 'ok' if the answer is satisfactory, otherwise 'needs_revision'.\n"
"If revision is needed, give 23 concrete points for improvement.\n"
"Respond in JSON with keys 'verdict' and 'critique'.\n"
f"Draft: {state['draft']}"
"Write a concise answer (510 sentences) to the following question:\n"
f"Question: {state['question']}\n"
"Answer:"
)
response = await agent.ainvoke({"messages": ["Human: " + prompt]}, {"configurable": {"thread_id": "reflect"}})
import json
try:
data = json.loads(response["messages"][-1].content)
verdict = data.get("verdict", "needs_revision")
critique = data.get("critique", "")
except Exception:
verdict = "needs_revision"
critique = "Could not parse critique."
response = llm.invoke(prompt)
state["draft"] = response.content.strip()
return {"draft": state["draft"]}
def reflect(state: ReflectState) -> Dict[str, Any]:
prompt = (
"You are a critical reviewer of the draft answer.\n"
"Evaluate the draft for completeness, specificity, and lack of filler.\n"
"Provide a verdict: 'ok' if the answer is satisfactory, otherwise 'needs_revision'.\n"
"If revision is needed, give 23 concise points for improvement.\n"
f"Draft: {state['draft']}\n"
"Verdict and critique:"
)
response = llm.invoke(prompt)
text = response.content.strip()
lines = text.splitlines()
verdict_line = lines[0].lower().strip()
verdict = "ok" if "ok" in verdict_line else "needs_revision"
critique = "\n".join(lines[1:]).strip()
state["verdict"] = verdict
state["critique"] = critique
return state
return {"verdict": verdict, "critique": critique}
async def rewrite(state: ReflectState) -> ReflectState:
def rewrite(state: ReflectState) -> Dict[str, Any]:
prompt = (
"You are revising the following draft answer based on the critique.\n"
"Make the answer clearer, more specific, and remove any filler.\n"
"Do not add new information beyond what is already in the draft.\n"
f"Draft: {state['draft']}\n"
f"Critique: {state['critique']}"
"Rewrite the draft answer incorporating the following critique points.\n"
"Keep the answer concise (510 sentences).\n"
f"Critique: {state['critique']}\n"
f"Original Draft: {state['draft']}\n"
"Revised Answer:"
)
response = await agent.ainvoke({"messages": ["Human: " + prompt]}, {"configurable": {"thread_id": "rewrite"}})
new_draft = response["messages"][-1].content
state["draft"] = new_draft
response = llm.invoke(prompt)
state["draft"] = response.content.strip()
state["round"] += 1
return {"draft": state["draft"], "round": state["round"]}
# 4. Graph construction
builder = StateGraph(ReflectState)
builder.add_node("draft_answer", draft_answer)
builder.add_node("reflect", reflect)
builder.add_node("rewrite", rewrite)
# Edges
builder.set_entry_point("draft_answer")
builder.add_edge("draft_answer", "reflect")
builder.add_conditional_edges(
"reflect",
lambda x: x["verdict"],
{
"ok": END,
"needs_revision": "rewrite"
}
)
builder.add_edge("rewrite", "reflect")
# Max rounds guard
@builder.before_node("rewrite")
def check_rounds(state: ReflectState) -> ReflectState:
if state["round"] >= state["max_rounds"]:
state["verdict"] = "ok"
return state
graph = builder.compile()
def build_graph() -> StateGraph[ReflectState]:
graph = StateGraph(ReflectState)
graph.add_node("draft_answer", draft_answer)
graph.add_node("reflect", reflect)
graph.add_node("rewrite", rewrite)
graph.add_edge(START, "draft_answer")
graph.add_edge("draft_answer", "reflect")
graph.add_conditional_edges(
"reflect",
lambda state: state["verdict"],
{"ok": END, "needs_revision": "rewrite"},
)
graph.add_conditional_edges(
"rewrite",
lambda state: "rewrite" if state["round"] < state["max_rounds"] else END,
{"rewrite": "reflect", END: END},
)
return graph
async def main():
# 5. Demo execution
if __name__ == "__main__":
question = "Объясни студенту разницу между tool и resource в MCP"
initial_state: ReflectState = {
"question": question,
@@ -111,15 +98,12 @@ async def main():
"critique": "",
"verdict": "",
"round": 0,
"max_rounds": 2,
"max_rounds": 2
}
graph = build_graph()
result = await graph.ainvoke(initial_state)
print("\n--- Final Answer ---")
result = graph.invoke(initial_state)
print("\n--- Final Draft ---\n")
print(result["draft"])
print("\n--- Final Critique ---")
print("\n--- Critique ---\n")
print(result["critique"])
if __name__ == "__main__":
asyncio.run(main())
"""
print("\n--- Verdict ---\n")
print(result["verdict"])