add main.py
This commit is contained in:
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# main.py
|
||||||
|
# LangGraph planning agent example
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import TypedDict, List, Dict, Any
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph, END
|
||||||
|
from langgraph.prebuilt import create_chat_agent
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from langchain_core.messages import HumanMessage, AIMessage
|
||||||
|
|
||||||
|
# ---------- 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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------- State ----------
|
||||||
|
class PlanningState(TypedDict):
|
||||||
|
task: str
|
||||||
|
plan: List[str] | None
|
||||||
|
current_step: int
|
||||||
|
results: List[str]
|
||||||
|
|
||||||
|
# ---------- Planning node ----------
|
||||||
|
planning_prompt = (
|
||||||
|
"You are a helpful assistant. Given the following task, break it into 3-6 concrete steps. "
|
||||||
|
"Return the steps as a numbered list or JSON array. The steps should be short, actionable, and in Russian."
|
||||||
|
)
|
||||||
|
|
||||||
|
async def planning(state: PlanningState) -> PlanningState:
|
||||||
|
task = state["task"]
|
||||||
|
messages = [HumanMessage(content=f"{planning_prompt}\nTask: {task}")]
|
||||||
|
response = await llm.ainvoke(messages)
|
||||||
|
text = response.content.strip()
|
||||||
|
|
||||||
|
# Try to parse JSON first
|
||||||
|
plan: List[str] | None = None
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
data = json.loads(text)
|
||||||
|
if isinstance(data, list):
|
||||||
|
plan = [str(item).strip() for item in data]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If JSON parsing failed, try to extract numbered list
|
||||||
|
if plan is None:
|
||||||
|
import re
|
||||||
|
lines = re.findall(r"\d+\.\s*(.+)", text)
|
||||||
|
if lines:
|
||||||
|
plan = [line.strip() for line in lines]
|
||||||
|
|
||||||
|
if plan is None:
|
||||||
|
raise ValueError("Could not parse plan from LLM response")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"task": task,
|
||||||
|
"plan": plan,
|
||||||
|
"current_step": 0,
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- Execution node ----------
|
||||||
|
async def execution(state: PlanningState) -> PlanningState:
|
||||||
|
plan = state["plan"]
|
||||||
|
idx = state["current_step"]
|
||||||
|
if plan is None or idx >= len(plan):
|
||||||
|
return state
|
||||||
|
|
||||||
|
step = plan[idx]
|
||||||
|
# Execute the step: ask LLM to produce result for this step
|
||||||
|
messages = [HumanMessage(content=f"Task: {state['task']}\nStep {idx+1}: {step}\nProvide the result for this step.")]
|
||||||
|
response = await llm.ainvoke(messages)
|
||||||
|
result = response.content.strip()
|
||||||
|
|
||||||
|
new_results = state["results"].copy()
|
||||||
|
new_results.append(result)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"task": state["task"],
|
||||||
|
"plan": plan,
|
||||||
|
"current_step": idx + 1,
|
||||||
|
"results": new_results,
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- Should continue ----------
|
||||||
|
async def should_continue(state: PlanningState) -> str:
|
||||||
|
if state["current_step"] >= len(state["plan"]):
|
||||||
|
return "finish"
|
||||||
|
return "execute"
|
||||||
|
|
||||||
|
# ---------- Build graph ----------
|
||||||
|
builder = StateGraph(PlanningState)
|
||||||
|
builder.add_node("planning", planning)
|
||||||
|
builder.add_node("execution", execution)
|
||||||
|
builder.add_conditional_edges(
|
||||||
|
"planning",
|
||||||
|
lambda _: "execute",
|
||||||
|
)
|
||||||
|
builder.add_conditional_edges(
|
||||||
|
"execution",
|
||||||
|
should_continue,
|
||||||
|
{
|
||||||
|
"execute": "execution",
|
||||||
|
"finish": END,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
builder.set_entry_point("planning")
|
||||||
|
graph = builder.compile()
|
||||||
|
|
||||||
|
# ---------- Run example ----------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
task = "Сравни Python и JavaScript"
|
||||||
|
initial_state: PlanningState = {
|
||||||
|
"task": task,
|
||||||
|
"plan": None,
|
||||||
|
"current_step": 0,
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
result = graph.invoke(initial_state)
|
||||||
|
|
||||||
|
print(f"\nЗадача: {task}\n")
|
||||||
|
print("План:")
|
||||||
|
for i, step in enumerate(result["plan"], 1):
|
||||||
|
print(f"{i}. {step}")
|
||||||
|
print("\n[Шаги]")
|
||||||
|
for i, res in enumerate(result["results"], 1):
|
||||||
|
print(f"[Шаг {i}] {res}\n")
|
||||||
|
print("Итог: " + "\n".join(result["results"]))
|
||||||
Reference in New Issue
Block a user