Agent
The agent loop is picobot's core — a ReAct (Reason + Act) cycle that alternates between LLM reasoning and tool execution.
How It Works¶
Each call to process_direct() runs this cycle:
- Build system prompt from workspace templates + KB context + TODO + skills
- Send messages to the LLM via LiteLLM
- If tool calls returned, execute them, feed results back to the LLM, go to step 2
- If text returned, that's the response — save session and exit
- If budget exceeded, compress history and continue
The loop runs until the LLM produces a final text response, hits the tool iteration limit (maxToolIterations, default 20), or exhausts the token budget.
Sessions¶
Each invocation creates an episodic session by default. Sessions persist as JSONL files in ~/.picobot/sessions/.
Start fresh (default):
picobot agent -m "hello"
Resume the most recent session:
picobot agent --session latest
Resume a specific session:
picobot agent --session abc123
Session IDs are channel:uuid internally (e.g., cli:a1b2c3d4-20240222-144530). The cli: prefix is added automatically if you pass a bare ID.
History Management¶
Sessions can grow large. Two mechanisms keep them bounded:
maxHistoryMessages(default 50): When saving, only the most recent N messages are retained.- History trimming: Keeps the system message + first user/assistant exchange (for coherence) + the most recent messages that fit.
Streaming¶
picobot agent --stream
Tokens appear as the LLM generates them. Combine with --show-tools to see tool calls inline:
picobot agent --stream --show-tools
In streaming mode, tool call arguments are reconstructed from deltas as they arrive. You'll see each tool call start and complete in real time.
Token Budget¶
The tokenBudget config (default 128,000) controls when history compression kicks in. The budget system:
- Estimates tokens using chars/4 approximation (fast, no tokenizer dependency)
- Triggers at 80% of budget — when estimated tokens exceed this threshold
- Compresses by keeping system message + first 2 user/assistant exchanges + as many recent messages as fit in 75% of budget
- Truncates tool results to 8,000 characters to prevent oversized responses from consuming the context window
This means the agent can run indefinitely without hitting context limits. Early context is preserved for coherence, and the most recent exchanges are always available.
Tuning the Budget¶
{
"agents": {
"defaults": {
"tokenBudget": 128000,
"maxHistoryMessages": 50
}
}
}
- Lower budget (e.g., 32000): More aggressive compression, lower cost per session
- Higher budget (e.g., 200000): More context retained, better for complex multi-step work
- Lower maxHistoryMessages: Sessions save faster, less disk usage
Error Recovery¶
The loop handles three categories of failure:
Transient LLM Errors¶
Rate limits, timeouts, and connection errors are retried once with a 2-second backoff. If the retry also fails, the error propagates.
Detected patterns: rate_limit, timeout, connection, 429, 503, overloaded.
Consecutive Errors¶
If the LLM fails 3 times in a row (across turns, not retries), the loop stops. This prevents infinite loops when the model is consistently failing.
Stuck Detection¶
If the agent calls the same tool with identical arguments twice in a row, it's stuck. The loop injects a system message:
"You appear stuck — you just called the same tool with the same arguments. Try a different approach."
This nudges the LLM to try something different rather than repeating the same action.
Structured Tracing¶
The Tracer class records per-turn metrics:
- Turn number, timing (start/end/duration)
- Token counts (prompt, completion, total)
- Cost estimate (from LiteLLM)
- Tool calls with individual timing
- Final outcome
Traces are stored as JSONL in ~/.picobot/data/traces/traces.jsonl. Use --logs to see runtime log output during a session.
CLI Reference¶
| Flag | Description |
|---|---|
-m, --message TEXT |
Send a single message (non-interactive) |
-s, --session ID |
Session ID to resume (latest for most recent) |
--stream / --no-stream |
Stream LLM tokens live |
--show-tools / --no-show-tools |
Show tool calls inline |
--markdown / --no-markdown |
Render markdown in output |
--logs / --no-logs |
Show runtime logs |
--max-tokens INT |
Override max tokens per LLM response |
--json |
Use JSON-over-stdio protocol (see JSON Channel) |