init
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# Bantam: tiny, powerful, DIY AI agent
|
||||
|
||||
## About
|
||||
|
||||
Bantam is a minimalist, dependency-free AI agent specification and implementation (under 300 SLOC of Python (~250 in the reference implementation)). It provides an agentic loop capable of autonomous tool execution, shell interaction, real-time response streaming, Fibonacci backoff network resilience, and subagent delegation using any OpenAI-compatible completions API.
|
||||
|
||||
The entire philosophy of Bantam is built upon two principles:
|
||||
|
||||
1. The structure must be as simple as possible for anyone to be able to reimplement the agent from a plain algorithm description.
|
||||
2. The agent only needs to provide two tools: a tool to call shell commands and a tool to call itself. In theory, this should be sufficient to give LLMs the ability to handle tasks of any complexity.
|
||||
|
||||
Because of the second principle, Bantam itself was named after Victorinox Bantam Alox, a small and lightweight Swiss army knife with only two tools.
|
||||
|
||||
## Usage
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.7+ (no external dependencies required)
|
||||
- An OpenAI-compatible API endpoint (or OpenAI API key)
|
||||
|
||||
### Running Bantam
|
||||
|
||||
1. Configure `model.cfg` with your API settings:
|
||||
```ini
|
||||
endpoint=https://api.openai.com/v1
|
||||
model=gpt-4o
|
||||
temperature=0.7
|
||||
api_key=your_api_key_here
|
||||
stream=true
|
||||
```
|
||||
|
||||
2. Interactive mode:
|
||||
```bash
|
||||
python3 bantam.py
|
||||
```
|
||||
|
||||
In interactive mode, prompts can span multiple lines: press **Ctrl+J** to insert a real line break (the cursor moves to the next line), then **Enter** to submit the whole multi-line prompt. This works when `readline` is available; without it, prompts are single-line.
|
||||
|
||||
Sessions are saved under `~/.bantam/sessions/` and can be managed with these commands:
|
||||
- `/save` — save the entire conversation to a new session file (auto-id like `20260808-190038`) and generate its summary
|
||||
- `/list` — list saved sessions (newest first) with their ids, timestamps, message counts and summaries
|
||||
- `/load <id>` — load a saved session (exact id or unique prefix) and continue from there
|
||||
- `/compact` — summarize the conversation with the LLM and compact the context down to just the system message plus the summary
|
||||
- `/help` — show all supported commands
|
||||
- `/clear` — reset the conversation to just the system prompt
|
||||
- `/quit` — exit
|
||||
|
||||
The current conversation is also **auto-saved** to `~/.bantam/sessions/autosave.json` after every turn, on `/clear`, `/load`, `/compact`, and on exit — so you can always `/load autosave` to resume where you left off.
|
||||
|
||||
3. File input mode:
|
||||
```bash
|
||||
python3 bantam.py prompt.txt
|
||||
```
|
||||
|
||||
## Rules of Bantam (The Algorithm)
|
||||
|
||||
Using these rules, everyone can build their own copy of Bantam from scratch in little time in any language that supports file access, shell and HTTP(S) calls.
|
||||
|
||||
### High-Level Overview
|
||||
|
||||
1. **Initialization**: Read `system.txt` and `model.cfg`. Prepare an array of messages starting with the system prompt `{"role": "system", "content": system_prompt}`.
|
||||
2. **Input Processing**: Take user prompt (via command-line file parameter or interactive stdin), append `{"role": "user", "content": prompt}`, and invoke `AL(cfg, messages)`.
|
||||
3. **Agentic Loop (`AL`)**:
|
||||
- Send `messages` and tool definitions to the OpenAI-compatible `/chat/completions` API endpoint with custom `User-Agent` headers.
|
||||
- On network or HTTP failure, retry using Fibonacci backoff delays (`1s, 1s, 2s, 3s, 5s`).
|
||||
- If `stream=true`, parse SSE data chunks (`data: {...}`) in real-time to stream reasoning content (`reasoning_content`) and response text directly to stdout, bracketing the reasoning block with `--- reasoning start ---` / `--- reasoning end ---` markers.
|
||||
- Reconstruct the assistant message. If `tool_calls` exist, trace the call (`[tool call: name(args)]`), validate JSON arguments, execute the requested tool (`shell_exec` or `run_subagent`), trace the result (`[tool result: name]`), append the tool response `{"role": "tool", "tool_call_id": id, "content": result}`, and repeat the loop.
|
||||
- If no tool calls remain or `max_al_iterations` is reached, return the updated messages list.
|
||||
|
||||
---
|
||||
|
||||
### Main program
|
||||
|
||||
1. Read system prompt from `system.txt` (default if missing).
|
||||
2. Read model parameters from `model.cfg` (`key=value` format).
|
||||
3. Prepare a new message list with the system prompt (`role: "system"`).
|
||||
4. Read the first command-line parameter. If non-empty, read user prompt from the specified file, append to `messages` (`role: "user"`), run `AL(cfg, messages)`, and exit.
|
||||
5. Read user prompt from standard input (with `readline` line editing and history in `~/.bantam_history`; **Ctrl+J** inserts a real newline into the line being edited). If equal to `/quit` or EOF, exit. If equal to `/clear`, reset `messages` to step 3 and return to step 5. If equal to `/save`, write the whole `messages` array to `~/.bantam/sessions/<id>.json` (with an auto-generated summary) and return to step 5. If equal to `/list`, print saved sessions and their summaries and return to step 5. If starting with `/load`, replace `messages` with the saved session's messages (by exact id or unique prefix) and return to step 5. If equal to `/compact`, ask the LLM to summarize the conversation, replace `messages` with `[system, summary-user-message]`, and return to step 5. If equal to `/help`, print the command list and return to step 5. After every user turn and on exit, auto-save `messages` to `~/.bantam/sessions/autosave.json`.
|
||||
6. Append user prompt to `messages` (`role: "user"`), run `AL(cfg, messages)`, and go to step 5.
|
||||
|
||||
### Agentic loop (`AL(cfg, messages)`) function
|
||||
|
||||
1. Call OpenAI-compatible Completions API (`POST {endpoint}/chat/completions`) using parameters from `cfg` (`model`, `temperature`, optional `api_key` bearer header).
|
||||
- Set custom `User-Agent` header (`Mozilla/5.0 (compatible; Bantam/1.0)`) to avoid gateway 403 blocks.
|
||||
- Retry network/HTTP errors with Fibonacci backoff delays (`1s, 1s, 2s, 3s, 5s`).
|
||||
- If `stream=true`, parse SSE stream (`data: {...}`) for real-time reasoning and text output, bracketing reasoning with `--- reasoning start ---` / `--- reasoning end ---` markers.
|
||||
2. Append the assistant's response message object to `messages`. If non-streaming and response has reasoning tokens (`reasoning_content` or `reasoning`), output them wrapped in `--- reasoning start ---` / `--- reasoning end ---` markers.
|
||||
3. If there are pending `tool_calls` in the assistant response:
|
||||
- For each tool call, output a trace log (`[tool call: name(args)]`).
|
||||
- Validate JSON arguments. If invalid, format a tool-error response so the LLM can self-correct.
|
||||
- Execute tool action (`shell_exec` or `run_subagent`).
|
||||
- Output a trace log of the result (`[tool result: name]`).
|
||||
- Append tool result message (`role: "tool"`, `tool_call_id`, `content`: result string) to `messages`.
|
||||
- Loop back to step 1.
|
||||
4. If no pending tool calls (or if `max_al_iterations` is reached), stop and return `messages`.
|
||||
|
||||
### Model configuration parameters
|
||||
|
||||
- `endpoint` (base OpenAI-compatible API URL, default `https://api.openai.com/v1`)
|
||||
- `model` (model name, e.g. `gpt-4o`)
|
||||
- `temperature` (model temperature, default 0.7)
|
||||
- `api_key` (API key / Bearer token, optional; fall back to `OPENAI_API_KEY` env var)
|
||||
- `stream` (stream response tokens in real-time, default `true`)
|
||||
- `color` (ANSI coloring: `auto` (TTY-detected, default), `always`, or `never`; also disabled by `NO_COLOR`/`BANTAM_NO_COLOR` env vars)
|
||||
- `timeout` (HTTP timeout in seconds for LLM API calls, default 60)
|
||||
- `shell_timeout` (timeout in seconds for `shell_exec` commands, default 120)
|
||||
- `max_al_iterations` (max tool-call loop iterations per `AL()` invocation, default 1000)
|
||||
|
||||
When enabled, the interactive console uses a subtle ANSI palette: the pending-request status `...requesting...` is darkened bold, reasoning markers are cyan, reasoning text is dim, `[tool call: ...]` traces are yellow, `[tool result: ...]` headers are green, tool result bodies are dim (red for tool errors/unknown tools), and errors/network retries are red. Tool result payloads fed back to the LLM are never colored.
|
||||
|
||||
### Tool call definitions
|
||||
|
||||
#### `run_subagent` tool
|
||||
|
||||
- Parameters: `prompt` (string)
|
||||
- Return value: string
|
||||
- Action: run `AL(cfg, [{"role": "system", "content": system_prompt + "\n\nImportant: this is a child agent"}, {"role": "user", "content": prompt}])` and return the text content of the last `assistant`-role message.
|
||||
|
||||
#### `shell_exec` tool
|
||||
|
||||
- Parameters: `command` (string)
|
||||
- Return value: string
|
||||
- Action: run shell command specified in `command` subject to `shell_timeout` (default 120s) and return `output + '\n\nexit: ' + exit_code` string.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why no MCP support?
|
||||
|
||||
If you need MCP server tools, there's nothing a simple shell wrapper cannot solve in this case.
|
||||
|
||||
### Why no sandboxing?
|
||||
|
||||
Same philosophy as the Pi agent: there's nothing a simple chroot environment cannot solve in case sandboxing is really necessary.
|
||||
|
||||
### Any advanced authentication schemes or header injection?
|
||||
|
||||
You can pair Bantam with the [Dynagate](https://code.luxferre.top/luxferre/dynagate) LLM gateway to achieve all that.
|
||||
|
||||
## Credits
|
||||
|
||||
Created by Luxferre in 2026, released into the public domain with no warranties.
|
||||
Reference in New Issue
Block a user