feat: solution for 6a1864fa8a94f887e50d46f0

This commit is contained in:
@@ -0,0 +1,116 @@
from typing import TypedDict, Any
import random
# LLM setup use placeholder values if no specific provider is mentioned
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
llm = ChatOpenAI(
model="openai/gpt-oss-20b",
base_url='https://platform.brojs.ru/jrnl-bh/api/inference/v1',
api_key=SecretStr("jrnl_30283ab953615cbb6846ff9940a1eedce0b76d7b2f59a2394f29e74643e6a90d"),
temperature=0.2,
)
# ---------- State ----------
class AgentState(TypedDict):
task: str
result: str
attempts: int
status: str # pending | success | failed | max_attempts
error: str | None
max_attempts: int
# ---------- Tool ----------
def unreliable_tool(task: str) -> str:
"""Simulate a tool that fails ~30% of the time."""
if random.random() < 0.3:
raise ValueError("Tool failure")
# Very simple evaluation: just return the task string for demo
return f"Result of '{task}'"
# ---------- Nodes ----------
def execute_task(state: AgentState) -> AgentState:
try:
result = unreliable_tool(state["task"])
state.update(result=result, error=None)
except Exception as e:
state.update(result="", error=str(e))
state.update(status="pending")
return state
def verify_result(state: AgentState) -> AgentState:
if state["error"]:
# If tool failed, skip verification
state.update(status="failed")
return state
prompt = f"Task result: {state['result']}. Is this correct? Respond with 'success' or 'failed'."
verdict_obj = llm.invoke(prompt)
# Depending on the LLM implementation, the response may be a string or an object with `content`
if hasattr(verdict_obj, "content"):
verdict = verdict_obj.content.strip().lower()
else:
verdict = str(verdict_obj).strip().lower()
if "success" in verdict:
state.update(status="success")
else:
state.update(status="failed")
return state
def handle_error(state: AgentState) -> AgentState:
state["attempts"] += 1
if state["attempts"] >= state["max_attempts"]:
state.update(status="max_attempts")
else:
state.update(status="pending")
return state
# ---------- Graph ----------
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
builder = StateGraph(AgentState)
builder.add_node("execute_task", execute_task)
builder.add_node("verify_result", verify_result)
builder.add_node("handle_error", handle_error)
builder.set_entry_point("execute_task")
builder.add_edge("execute_task", "verify_result")
def _next(state: AgentState) -> str:
status = state["status"]
if status == "success":
return END
if status == "failed" and state["attempts"] < state["max_attempts"]:
return "handle_error"
return END
builder.add_conditional_edges("verify_result", _next)
builder.add_edge("handle_error", "execute_task")
graph = builder.compile(checkpointer=InMemorySaver())
# ---------- CLI ----------
def main():
task = input("Задача: ").strip()
if not task:
print("Нет задачи")
return
initial_state: AgentState = {
"task": task,
"result": "",
"attempts": 0,
"status": "pending",
"error": None,
"max_attempts": 5,
}
state = graph.invoke(initial_state)
attempts = state["attempts"] + (1 if state["status"] != "failed" else 0)
print(f"\nИтог: {state['status']} за {attempts} попытки(й)")
if state["result"]:
print(f"Результат: {state['result']}")
if state["error"]:
print(f"Ошибка: {state['error']}")
if __name__ == "__main__":
main()