Minimal AI agent harness — wrap any LLM with tools and a streaming CLI.
中文一句话:极小的 AI Agent 执行环境(harness),给任意 LLM 配上工具、对话循环、流式事件与 CLI 会话。约 1,100 行、零魔法、一个依赖,是 1998x-stack harness 家族的最小基线。
tiny-harness is a ~1,100-line Python package that turns an LLM into an AI agent — giving it tools, a conversation loop, streaming events, and a CLI session. One dependency. One import.
pip install tiny-harnessfrom tiny_harness import Agent, Prompt, Config
import os
agent = Agent(
prompt=Prompt("You are a helpful coding assistant."),
config=Config(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
workspace=".",
)
)
# files skill loaded by default in CLI; explicit in Python API
agent.load_skill("files")
result = await agent.run("Create hello.py") # agent writes the file# CLI loads files skill automatically
tiny-harness "Create hello.py"- Agent loop — while-loop state machine with safety valves (max iterations, error budget, loop detection)
- Tool system — pluggable tools with JSON Schema validation, automatic error→result conversion, LLM self-correction
- Streaming — real-time SSE streaming from Anthropic and OpenAI-compatible APIs
- Multi-provider — Anthropic (native) and OpenAI/DeepSeek (compatible) providers
- Skills — packaged bundles of tools + prompt instructions, loaded with
agent.load_skill("files") - CLI — session REPL with streaming output, one-shot mode for scripts. Files skill loaded by default.
- Rich TUI — optional
--tuimode with panels, markdown rendering, color-coded messages (pip install tiny-harness[tui]) - Persistence — JSONL session history with
/saveand/historycommands - Filesystem guard — workspace boundary enforcement, path traversal protection
- 1,125 lines — readable top-to-bottom, CleanRL-inspired code style
中文要点:三行 pip 安装即可用;CLI 一行运行单次任务,或进入交互式会话;
files技能默认加载。
pip install tiny-harness# Set your API key
export ANTHROPIC_API_KEY="sk-ant-..."
# Run a single prompt
tiny-harness "Create a hello.py file that prints 'Hello, world!'"tiny-harness --model claude-sonnet-4-20250514 --skills files> Create hello.py
[Iter 1/25 | Tokens: 1.2K]
Let me create that file.
⚡ write_file path=hello.py content=print(...) (Created, 22B)
[Iter 2/25 | Tokens: 1.4K]
Done! Created hello.py.
> Add a shebang line for python3
[Iter 1/25 | Tokens: 2.1K]
...
Session commands: /exit, /help, /tools, /clear.
pip install tiny-harness[tui]
tiny-harness --tui --model claude-sonnet-4-20250514 --skills files┌─ tiny-harness ─────────────────────────────────────────┐
│ claude-sonnet-4 │ Iter 3/25 │ 4.2K tokens │ 12s │
├─────────────────────────────────────────────────────────┤
│ │
│ You: Create a hello.py file │
│ │
│ Let me create that file for you. │
│ ⚡ write_file path=hello.py content=print(...) │
│ Created hello.py (1 line, 22B) │
│ Done! The file is ready. │
│ │
├─────────────────────────────────────────────────────────┤
│ > _ │
└─────────────────────────────────────────────────────────┘
Rich-powered terminal UI with color-coded messages, tool call indicators, status bar with iteration/token/time tracking.
export DEEPSEEK_API_KEY="sk-..."
tiny-harness "Say hi" --model deepseek-chat --provider deepseek --api-base-url https://api.deepseek.com/v1中文要点:
Agent.run(prompt)内嵌 MessageManager(对话数组)+ AgentLoop(while 状态机),LLMProvider 负责流式、ToolExecutor 负责「校验→守卫→执行→格式化」。核心循环:prompt → LLM → 工具调用 → 执行 → 结果 → LLM → …→ 最终答案。
User Prompt
│
▼
Agent.run(prompt)
├─ MessageManager — conversation array (system + user + assistant + tool results)
└─ AgentLoop — while-loop state machine
├─ LLMProvider — AnthropicProvider / OpenAIProvider (HTTP + SSE)
└─ ToolExecutor — validate → guard → execute → format → result
├─ ToolRegistry — {name: (ToolDef, handler)}
└─ FilesystemGuard — path resolution + boundary enforcement
Core loop: prompt → LLM → tool calls → execute → results → LLM → ... → final answer
tiny_harness/
├── __init__.py # Public API: Agent, Prompt, Config, ToolDef
├── _core.py # Agent class — orchestrator, session management
├── _loop.py # AgentLoop — state machine, error budget, loop detection
├── _llm.py # LLMProvider ABC + AnthropicProvider + OpenAIProvider
├── _messages.py # MessageManager — conversation, token counting
├── _tools.py # ToolRegistry + ToolExecutor + schema validator
├── _config.py # AgentConfig, Prompt dataclasses
├── _events.py # StreamEvent types + EventBus
├── _guard.py # FilesystemGuard — workspace boundaries
├── cli.py # CLI entry point
├── tools/files.py # File tool handlers (read, write, list, find, delete, mkdir, move)
└── skills/files.py # register(agent) → file tools + prompt instructions
from tiny_harness import Agent, Prompt, Config
agent = Agent(
prompt=Prompt("You are a helpful assistant. Use tools when needed."),
config=Config(
model="claude-sonnet-4-20250514",
api_key=os.environ["ANTHROPIC_API_KEY"],
workspace=".", # root dir for file operations
provider="anthropic", # "anthropic" | "openai" | "deepseek"
max_iterations=25, # safety cap on loop iterations
)
)# Single prompt — returns final answer
result = await agent.run("Create a hello.py file")
# Streaming — yields events as they happen
async for event in agent.run_stream("Create hello.py"):
if event.type == "text_delta":
print(event.content, end="", flush=True)
elif event.type == "tool_start":
print(f"\n ⚡ {event.tool_name}")# Register a custom tool
from tiny_harness import ToolDef
agent.tools.register_from_def(
ToolDef(
name="weather",
description="Get current weather for a city.",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
),
handler=lambda city: f"Sunny, 22°C in {city}",
)
# Load built-in skills
agent.load_skill("files") # file system accessagent.on("tool_start", lambda e: print(f"Calling {e.tool_name}"))
agent.on("tool_end", lambda e: print(f"Done ({e.duration_ms}ms)"))
agent.on("error", lambda e: print(f"Error: {e.message}"))| Field | Default | Description |
|---|---|---|
model |
required | Model identifier |
api_key |
required | Provider API key |
workspace |
required | Root directory for file operations |
provider |
"anthropic" |
"anthropic", "openai", "deepseek" |
api_base_url |
provider default | Custom API endpoint |
max_iterations |
25 |
Max loop iterations |
max_errors |
10 |
Total tool error budget |
max_consecutive_errors |
3 |
Consecutive error budget |
timeout_ms |
30_000 |
Tool execution timeout |
max_tool_result_chars |
50_000 |
Truncate large results |
config = Config(model="claude-sonnet-4-20250514", api_key="...", workspace=".")config = Config(model="gpt-4o", api_key="...", workspace=".", provider="openai")config = Config(
model="deepseek-chat", api_key="...", workspace=".",
provider="deepseek", api_base_url="https://api.deepseek.com/v1",
)tiny-harness 是 1998x-stack 循序渐进 harness 家族的最小基线——先做到可读、零魔法、零冗余,再演进出更完整的能力:
| 成员 | 定位 | 与 tiny-harness 的关系 |
|---|---|---|
| tiny-harness | 最小执行环境:loop + tools + streaming CLI | --- 本仓库(基线) |
mid-harness |
hooks / MCP / 渐进式 skills | 在 hook 之上扩展,不破坏单文件可读性 |
effective-harness |
零配置 CLI wrapper 实现 Anthropic twelve-factor agent |
面向生产约定,tiny 的思路给到上一层 |
mega-harness |
更完整的全家桶(规划/脚手架) | 集 loop + 调度 + 扩展包 |
agent-loop |
跨会话的多 Agent 编排(Initializer / Executor) | 从「单会话执行」升级到「跨会话编排」 |
ralph-loop |
插件式确定性自治循环(文件系统即记忆) | 从「工具封装」到「闭环自治」 |
loop-runner |
Generator–Evaluator 编码验证循环引擎 | 把「写码→验证」固化为循环 |
特点:从 tiny 的小而美到 mid/effective 的工程化,再到 agent-loop/ralph-loop 的自治化——每一层都继承上一层的可读性与诚实度。
This project follows CleanRL philosophy:
- Single-file readability — each module is focused and <300 lines
- Functions over classes — classes only when bundling state + behavior
- Dataclasses for data — no ORMs, no heavy frameworks
- Explicit data flow — no global state, no service locators
- Zero magic — read the source, understand it completely
Full design documentation in docs/:
- thoughts.md — first principles of AI agent harness design
- tools/ — deep dive on tool system design (7 documents)
- agent-loop/ — loop mechanics, state machine
- cli.md — CLI design, streaming format
- skills.md — skill system architecture
- code-style.md — coding conventions
- adr/ — architectural decision records
git clone https://github.com/1998x-stack/tiny-harness.git
cd tiny-harness
pip install -e ".[dev]"
# Run tests (excludes integration tests that need API keys)
pytest tests/ --ignore=tests/test_integration.py
# Run integration tests (requires ANTHROPIC_API_KEY)
ANTHROPIC_API_KEY="sk-ant-..." pytest tests/test_integration.py -v -m integrationMIT