from dataclasses import dataclass, field
from typing import Any
from qitos import Action, AgentModule, Decision, StateSchema, ToolRegistry
from qitos.kit import CodingToolSet, REACT_SYSTEM_PROMPT, ReActTextParser, format_action, render_prompt
@dataclass
class ExplorerState(StateSchema):
scratchpad: list[str] = field(default_factory=list)
EXPLORER_PROMPT = """\
You are a code explorer agent. Investigate directories and report findings.
Output contract (strict):
Thought: <reasoning>
Action: <tool_name>(arg=value )
or
Final Answer: <summary>
"""
class ExplorerAgent(AgentModule[ExplorerState, dict[str, Any], Action]):
def __init__(self, llm, workspace_root):
registry = ToolRegistry()
registry.include(CodingToolSet(
workspace_root=workspace_root,
include_notebook=False,
enable_lsp=False,
enable_tasks=False,
enable_web=False,
expose_modern_names=False,
))
super().__init__(tool_registry=registry, llm=llm, model_parser=ReActTextParser())
def init_state(self, task, **kwargs):
return ExplorerState(task=task, max_steps=5)
def build_system_prompt(self, state):
return render_prompt(EXPLORER_PROMPT, {"tool_schema": self.tool_registry.get_tool_descriptions()})
def prepare(self, state):
lines = [f"Task: {state.task}", f"Step: {state.current_step}/{state.max_steps}"]
if state.scratchpad:
lines.extend(state.scratchpad[-8:])
return "\n".join(lines)
def reduce(self, state, observation, decision):
action_results = observation.get("action_results", []) if isinstance(observation, dict) else []
if decision.rationale:
state.scratchpad.append(f"Thought: {decision.rationale}")
if decision.actions:
state.scratchpad.append(f"Action: {format_action(decision.actions[0])}")
if action_results:
state.scratchpad.append(f"Observation: {action_results[0]}")
state.scratchpad = state.scratchpad[-20:]
return state