Files
python-mcp/tools/agent_loop.py
AuroraCrimsonRose e471f9bc54 Added many tools
2026-06-03 06:01:06 -05:00

178 lines
4.1 KiB
Python

from __future__ import annotations
from typing import Any
from core.tools.registry import registry
from core.events import bus
class AgentLoop:
"""
Minimal deterministic agent loop.
Orchestrates tools like:
- search
- intelligent_search
- crawler
- research
Can later be upgraded with LLM-based planning.
"""
def __init__(self, ctx):
self.ctx = ctx
self.max_steps = 5
# =========================
# ENTRY POINT
# =========================
def run(self, goal: str) -> dict[str, Any]:
state = {
"goal": goal,
"steps": [],
"memory": [],
"final": None
}
bus.log(
"AGENT",
"agent_loop_start",
"INFO",
{"goal": goal}
)
for step in range(self.max_steps):
action = self._decide(state)
if action["type"] == "stop":
state["final"] = action.get("result")
break
result = self._execute(action, state)
state["steps"].append(action)
state["memory"].append(result)
# simple convergence heuristic
if self._is_satisfied(state):
state["final"] = result
break
if not state["final"]:
state["final"] = state["memory"][-1] if state["memory"] else {}
return state
# =========================
# DECISION LOGIC (RULE-BASED FOR NOW)
# =========================
def _decide(self, state: dict[str, Any]) -> dict[str, Any]:
"""
Very simple heuristic planner.
Later upgrade point: replace with LLM planner.
"""
goal = state["goal"]
memory = state["memory"]
if not memory:
return {
"type": "tool",
"tool": "research",
"input": {
"query": goal,
"depth": 1
}
}
last = memory[-1]
# if research returned sources, refine or stop
if isinstance(last, dict) and "sources" in last:
if len(last["sources"]) >= 2:
return {
"type": "stop",
"result": last
}
# if weak results, deepen search
return {
"type": "tool",
"tool": "research",
"input": {
"query": goal,
"depth": 2,
"max_sources": 5
}
}
return {
"type": "stop",
"result": last
}
# =========================
# TOOL EXECUTION
# =========================
def _execute(self, action: dict[str, Any], state: dict[str, Any]) -> Any:
tool_name = action.get("tool")
tool_input = action.get("input", {})
bus.log(
"AGENT",
"agent_tool_call",
"INFO",
{
"tool": tool_name,
"input": tool_input
}
)
if not tool_name:
return {"error": "No tool specified"}
try:
result = registry.run(
tool_name,
tool_input,
self.ctx
)
return result
except Exception as e:
return {
"error": str(e),
"tool": tool_name
}
# =========================
# STOPPING CONDITION
# =========================
def _is_satisfied(self, state: dict[str, Any]) -> bool:
"""
Simple satisfaction heuristic.
Later upgrade: LLM-based evaluation.
"""
memory = state["memory"]
if not memory:
return False
last = memory[-1]
if isinstance(last, dict):
# if research returned structured sources, assume OK
if "sources" in last and len(last["sources"]) > 0:
return True
if "error" in last:
return True
return False