- a typed state
- a real LLM harness
- a real system prompt
- a real parser
- real tools
- a real
reduce()loop - real
qitatraces
examples/patterns/react.py, but the lesson is written so you do not need to reverse-engineer that file to understand why it works.
What you are building
The task is tiny:- open
buggy_module.py - fix
add(a, b)so it returnsa + b - run a verification command
The design for this lesson
| Design branch | Choice in this lesson | Why this is the right first choice |
|---|---|---|
| Task shape | One-file bug fix with one verification command | Easy to verify, easy to trace |
| State | scratchpad, target_file, test_command | Just enough to influence the next step |
| Model harness | OpenAICompatibleModel returning text | Simple and portable across providers |
| Prompt contract | REACT_SYSTEM_PROMPT | Explicit one-tool-per-turn text protocol |
| Parser | ReActTextParser | Direct match for Thought: / Action: output |
| Tools | Compact CodingToolSet inside a manual ToolRegistry | Learn tool design before presets |
| Memory | None | The run is too short to justify separate memory |
| History | Default Engine history behavior | Do not introduce context control before you need it |
| Traceability | qita board | Learn to inspect the kernel from day one |
Why we start with the text ReAct harness
The first lesson uses:messages -> text model output -> ReAct parser -> Decision -> tool execution
We do not start with native tool calling, XML, JSON, or model-specific harnesses because those add coupling before you understand the core loop.
The system prompt is a contract, not decoration
The lesson uses the canonical ReAct prompt:ReActTextParser is not doing magic. It expects exactly this style of output.
The first durable QitOS lesson is:
- prompt format and parser choice are one design decision
- if you change one, you usually need to change the other
The full model harness for this lesson
The example builds the model like this:- it works with OpenAI-compatible endpoints
- it keeps the response in plain text
- it stays compatible with the prompt-injection tool schema path used by
REACT_SYSTEM_PROMPT - it keeps the lesson portable across research labs and local gateways
Design the state around the next step
The state is intentionally small:Why these fields?
scratchpadstores the compressed trajectory that the next model step can usetarget_filekeeps the agent grounded in one artifacttest_commandturns “done” into an executable success condition
Expose a minimal tool surface
The example uses a manual registry so you can see exactly what is being exposed:This is important.
CodingToolSet is a bundle, but you still control its surface.For lesson 1, the right tool surface is just enough to:- inspect files
- edit files
- run the verification command
Bind the prompt to the parser
The agent constructor pairs the prompt contract and the parser:Read that as one sentence:“This agent asks the model to speak ReAct text, and the Engine parses that text with the ReAct parser.”In QitOS, this pairing is the harness.Later lessons will change prompts and protocols. For now, keep this pair fixed.
Prepare only the context the next step needs
prepare() curates the current step’s input:prepare() is not a state dump. It is a prompt-ready view of state.Use reduce to define what the agent remembers
ReAct learns inside Three lessons are hidden in this one function:
reduce():- not every observation belongs in future context
- state is where you keep the compressed working memory
final_resultis a clean, explicit success signal
Notice what we are not using yet
We do not use:
decide()overrides- explicit planning
- memory adapters
- custom history implementations
- context compaction
- model-specific protocol overrides
Run the example and inspect the kernel with qita
Run it:Then inspect it:In
qita, check:- the exact prompt text sent to the model
- whether the parser produced clean
ThoughtandActionfields - whether the tool output made the verification condition obvious
- whether
final_resultis set at the first true success condition
Why there is no separate memory or compaction yet
For this lesson, the right memory choice is “none.” Why:- the run is short
- the useful context is already visible in
scratchpad - adding retrieval or compaction here would blur the architecture before you understand it
Full example
The full runnable lesson lives at:What lesson 2 changes
Lesson 2 keeps the same model harness and the same execution parser, but introduces a new idea: planning should become explicit state and explicit control flow, not a longer hidden thought.Next lesson: PlanAct
Add a planner, a cursor, and a
decide() gate without changing the core runtime.Related reference: kit
Review
ReActTextParser, prompt templates, and coding tool surfaces used in this lesson.