Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

reACT-Agent

A minimal ReAct agent (Reason → Act → Observe) that calls Groq's chat API. No framework, no tool-calling schemas, no SDK — the whole project is a small set of plain modules, and every piece of wiring lives in one function: build_agent().

The whole project in one function

Everything you need to understand the agent is in build_agent() at src/main.py:25. It does exactly four things:

def build_agent() -> ReActAgent:
    tools = ToolRegistry()                # 1. a registry of capabilities
    tools.register(CalculatorTool())
    tools.register(EchoTool())

    actions = ActionRegistry()            # 2. a registry of self-actions
    actions.register(ReflectAction())
    actions.register(SaveMemoryAction())
    actions.register(AskUserAction())

    config = ReActConfig(max_llm_calls=10, max_tool_calls=8, debug=False)

    return ReActAgent(llm_call=call_groq, tool_registry=tools,
                      action_registry=actions, config=config)
  • Inputs: nothing. Everything the agent needs is constructed inline.
  • Output: a ready-to-use ReActAgent whose only public entry point is await agent.run(query: str) -> str.
  • To change the agent, you change this one function — swap call_groq for any async (system_prompt, user_turn) -> str callable, register a new tool or action, or tweak the config.

What makes it different

  • The agent only decides; it never executes. _run_loop in src/react/agent.py delegates every call to ToolExecutor/ActionExecutor.
  • Tools vs. Actions. A tool reaches into the outside world (CalculatorTool, EchoTool); an action operates on the agent itself and receives AgentState (reflect, save_memory, ask_user).
  • Errors are data, not exceptions. Every failure becomes a success: False result rendered as an observation, so the model can recover — an unknown tool is a graceful retry, not a crash.
  • A parser that never raises. src/react/parser.py tolerates fenced JSON, preamble/postamble text, and trailing commas, and feeds parse errors back to the model with a bounded retry.
  • Loop detection that nudges before it kills. The same (kind, name, sorted-args) call seen repeatedly is first fed back as a hint to stop, then hard-stops the run.
  • Human-in-the-loop. The ask_user action pauses the run, reads a reply from the terminal, and feeds it back as the next observation.
  • LLM-agnostic core. Any async (system, user) -> str callable works, and the test suite drives the full loop with a ScriptedLLM — no network.

How to run

# one-shot
.\venv\Scripts\python.exe src\main.py "What is 12 * (7 + 3)?"

# interactive session
.\venv\Scripts\python.exe src\main.py

You need a Groq API key in the GROQ_API_KEY (or API_KEY) environment variable (or in a .env file). The key is never hardcoded in the source.

Project layout

src/
  main.py                 entrypoint; build_agent() and the CLI loop
  api/
    request.py            Groq client (stdlib urllib only)
  react/
    agent.py              the ReAct loop — decides, never executes
    parser.py             never-raises JSON -> Decision parser
    models.py             Decision/Result dataclasses (all have success: bool)
    state.py              AgentState: steps, budgets, scratch, session turns
    config.py             every tunable (budgets, retry, loop detection)
    exceptions.py         typed errors for recoverable vs fatal failures
    prompt/               system prompt + user-turn rendering
    runtime/              loop_detector, retry
    tool/                 BaseTool, registry, executor, builtins
    action/               BaseAction, registry, executor, builtins
  tests/                  unit tests (scripted LLM, no API calls)

Configuration

See src/react/config.py. Highlights:

Field Default Purpose
max_llm_calls 15 hard budget on LLM calls per turn
max_tool_calls 10 hard budget on tool calls per turn
retry_limit 2 retries per failed step (parse or execute)
enable_loop_detection True stop repeating identical calls
loop_window / loop_repeat_threshold 4 / 3 repeat signature window & cutoff
loop_nudge_limit 2 hints to the model before hard-stop

Tests

.\venv\Scripts\python.exe -m pytest

The suite runs fully offline: the agent loop is driven by a ScriptedLLM that returns canned JSON responses, so no API key or network is needed.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages