add: main.py
This commit is contained in:
@@ -0,0 +1,138 @@
|
|||||||
|
import os
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
from typing import TypedDict, Annotated
|
||||||
|
|
||||||
|
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
|
||||||
|
from langgraph.graph import StateGraph, START, END
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
|
||||||
|
# ---------- LLM ----------
|
||||||
|
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 ----------
|
||||||
|
backend = CompositeBackend([
|
||||||
|
LocalShellBackend(workspace_dir="./workspace"),
|
||||||
|
FilesystemBackend(),
|
||||||
|
])
|
||||||
|
|
||||||
|
# ---------- Test tool (unreliable) ----------
|
||||||
|
@tool
|
||||||
|
def unreliable_tool(query: str) -> str:
|
||||||
|
"""Tool that succeeds 70% of the time, otherwise raises ValueError."""
|
||||||
|
if random.random() < 0.3:
|
||||||
|
raise ValueError("Simulated tool failure")
|
||||||
|
return f"Result for '{query}'"
|
||||||
|
|
||||||
|
# ---------- Agent ----------
|
||||||
|
agent = create_deep_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=[unreliable_tool],
|
||||||
|
backend=backend,
|
||||||
|
system_prompt="You are a helpful agent that can execute tasks and self‑check the result.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------- State definition ----------
|
||||||
|
class AgentState(TypedDict):
|
||||||
|
task: str
|
||||||
|
result: str
|
||||||
|
attempts: int
|
||||||
|
status: str # pending | success | failed | max_attempts
|
||||||
|
error: str | None
|
||||||
|
max_attempts: int
|
||||||
|
|
||||||
|
# ---------- Nodes ----------
|
||||||
|
async def execute_task(state: AgentState) -> AgentState:
|
||||||
|
task = state["task"]
|
||||||
|
try:
|
||||||
|
# Use the agent to run the task via the tool
|
||||||
|
response = await agent.ainvoke(
|
||||||
|
{"messages": [HumanMessage(content=task)]},
|
||||||
|
{"configurable": {"thread_id": "session-1"}},
|
||||||
|
)
|
||||||
|
result = response["messages"][-1].content
|
||||||
|
state["result"] = result
|
||||||
|
state["error"] = None
|
||||||
|
except Exception as e:
|
||||||
|
state["result"] = ""
|
||||||
|
state["error"] = str(e)
|
||||||
|
return state
|
||||||
|
|
||||||
|
async def verify_result(state: AgentState) -> AgentState:
|
||||||
|
# Ask LLM to judge the result
|
||||||
|
prompt = (
|
||||||
|
f"Task: {state['task']}\n"
|
||||||
|
f"Result: {state['result']}\n"
|
||||||
|
f"Error: {state['error']}\n"
|
||||||
|
"Is the result correct? Respond with only 'success' or 'failed'."
|
||||||
|
)
|
||||||
|
judge = await llm.ainvoke([HumanMessage(content=prompt)])
|
||||||
|
verdict = judge.content.strip().lower()
|
||||||
|
if verdict == "success":
|
||||||
|
state["status"] = "success"
|
||||||
|
else:
|
||||||
|
state["status"] = "failed"
|
||||||
|
return state
|
||||||
|
|
||||||
|
async def handle_error(state: AgentState) -> AgentState:
|
||||||
|
state["attempts"] += 1
|
||||||
|
if state["attempts"] >= state["max_attempts"]:
|
||||||
|
state["status"] = "max_attempts"
|
||||||
|
else:
|
||||||
|
state["status"] = "pending"
|
||||||
|
return state
|
||||||
|
|
||||||
|
# ---------- Graph ----------
|
||||||
|
graph = StateGraph(AgentState)
|
||||||
|
graph.add_node("execute_task", execute_task)
|
||||||
|
graph.add_node("verify_result", verify_result)
|
||||||
|
graph.add_node("handle_error", handle_error)
|
||||||
|
|
||||||
|
graph.set_entry_point("execute_task")
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"execute_task",
|
||||||
|
lambda x: "verify_result",
|
||||||
|
)
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"verify_result",
|
||||||
|
lambda x: "handle_error" if x["status"] == "failed" else "END",
|
||||||
|
)
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"handle_error",
|
||||||
|
lambda x: "execute_task" if x["status"] == "pending" else "END",
|
||||||
|
)
|
||||||
|
|
||||||
|
flow = graph.compile()
|
||||||
|
|
||||||
|
# ---------- Runner ----------
|
||||||
|
async def run_task(task: str, max_attempts: int = 5):
|
||||||
|
initial_state: AgentState = {
|
||||||
|
"task": task,
|
||||||
|
"result": "",
|
||||||
|
"attempts": 0,
|
||||||
|
"status": "pending",
|
||||||
|
"error": None,
|
||||||
|
"max_attempts": max_attempts,
|
||||||
|
}
|
||||||
|
async for event in flow.astream(initial_state):
|
||||||
|
if event.get("type") == "state":
|
||||||
|
state = event["data"]
|
||||||
|
print(f"Попытка {state['attempts'] + 1}: status={state['status']}")
|
||||||
|
if state["error"]:
|
||||||
|
print(f" Error: {state['error']}")
|
||||||
|
if state["result"]:
|
||||||
|
print(f" Result: {state['result']}")
|
||||||
|
final_state = event["data"]
|
||||||
|
print("\nИтог:", final_state["status"], "за", final_state["attempts"] + 1, "попытки")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(run_task("Вычисли 2+2", max_attempts=5))
|
||||||
Reference in New Issue
Block a user