Skip to main content
Every QitOS agent follows one execution path: AgentModule + Engine + Trace. You define the strategy; the Engine owns the loop. This guide walks through that path using the minimal coding agent from examples/quickstart/minimal_agent.py.

Prerequisites

  • Python 3.10+
  • An API key for any OpenAI-compatible endpoint
1

Install qitos and set your model config

pip install "qitos[models]"
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://api.siliconflow.cn/v1/"
export QITOS_MODEL="Qwen/Qwen3-8B"
2

Define state around a coding task

Every minimal QitOS coding agent still starts from a typed StateSchema:
from dataclasses import dataclass, field

from qitos import StateSchema


@dataclass
class MinimalCodingState(StateSchema):
    scratchpad: list[str] = field(default_factory=list)
    target_file: str = "buggy_module.py"
    test_command: str = 'python -c "import buggy_module; assert buggy_module.add(20, 22) == 42"'
The key mindset is that the task, scratchpad, and verification contract all live in state, not in hidden runtime glue.
3

Attach a real model and a real coding toolset

The minimal example is intentionally model-backed. It uses an OpenAI-compatible model plus the canonical coding tool preset:
from typing import Any

from qitos import Action, AgentModule, Decision
from qitos.kit import REACT_SYSTEM_PROMPT, ReActTextParser, format_action, render_prompt
from qitos.kit.toolset import coding_tools
from qitos.models import OpenAICompatibleModel


class MinimalCodingAgent(AgentModule[MinimalCodingState, dict[str, Any], Action]):
    def __init__(self, llm: OpenAICompatibleModel, workspace_root: str) -> None:
        super().__init__(
            toolset=[coding_tools(workspace_root=workspace_root, shell_timeout=20, include_notebook=False)],
            llm=llm,
            model_parser=ReActTextParser(),
        )

    def init_state(self, task: str, **kwargs: Any) -> MinimalCodingState:
        return MinimalCodingState(
            task=task,
            max_steps=int(kwargs.get("max_steps", 8)),
            target_file=str(kwargs.get("target_file", "buggy_module.py")),
            test_command=str(kwargs.get("test_command")),
        )

    def build_system_prompt(self, state: MinimalCodingState) -> str | None:
        _ = state
        return render_prompt(
            REACT_SYSTEM_PROMPT,
            {"tool_schema": self.tool_registry.get_tool_descriptions()},
        )
4

Reduce trajectory back into state

reduce() is where QitOS turns tool results into agent progress:
    def reduce(
        self,
        state: MinimalCodingState,
        observation: dict[str, Any],
        decision: Decision[Action],
    ) -> MinimalCodingState:
        action_results = observation.get("action_results", [])
        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:
            first = action_results[0]
            state.scratchpad.append(f"Observation: {first}")
            if isinstance(first, dict) and int(first.get("returncode", 1)) == 0:
                state.final_result = "Patch applied and verification passed."
        return state
You must return state from reduce. Forgetting to return it is still the most common mistake.
5

Seed a workspace and run the agent

The minimal example creates one tiny buggy file, then asks the agent to fix it:
from pathlib import Path

WORKSPACE = Path("./playground/minimal_coding_agent")
TARGET_FILE = "buggy_module.py"
TASK = "Fix the bug in buggy_module.py and make the verification command pass."


def seed_workspace() -> None:
    WORKSPACE.mkdir(parents=True, exist_ok=True)
    target = WORKSPACE / TARGET_FILE
    if not target.exists():
        target.write_text("def add(a, b):\n    return a - b\n", encoding="utf-8")


def main() -> None:
    seed_workspace()
    agent = MinimalCodingAgent(llm=build_model(), workspace_root=str(WORKSPACE))
    result = agent.run(
        task=TASK,
        workspace=str(WORKSPACE),
        max_steps=8,
        target_file=TARGET_FILE,
        return_state=True,
    )
    print("final_result:", result.state.final_result)
    print("stop_reason:", result.state.stop_reason)
6

Inspect the run with qita

qita board --logdir ./runs
The board starts on http://127.0.0.1:8765. Use it to inspect the patching trajectory, replay the run, and export the trace as HTML.

Complete example

The full minimal coding-agent example lives here: You can also run the packaged version directly:
qit demo minimal

Common mistakes

MistakeSymptomFix
Forget return state in reduceStateValidationError or None stateAlways end reduce with return state
Skip provider configOPENAI_API_KEY / QITOS_API_KEY error before the run startsExport the API key and base URL before running the demo
Never run verificationThe agent edits code but finishes without proofKeep a concrete test_command in state and treat passing it as success
Wrong prompt-parser pairingParse errors every stepMatch your system prompt format to ReActTextParser or another explicit parser
Toolset too broad for the taskThe run wanders through unnecessary toolsStart with the minimal coding preset and add tools only when the task really needs them

Next steps

Tutorial track

Continue with the four-lesson research path: ReAct, PlanAct, Claude Code-style, and code security audit.

Agent patterns

Compare the high-level tradeoffs between QitOS pattern families before choosing one.

Observability

Inspect every run with qita board, replay, and export.

Kit reference

Look up the parsers, planners, toolsets, and helpers used in the lessons.