focus — 纯 Rust 实现的 LLM Agent 框架
参考 pi(TypeScript 原版)与
pi_agent_rust(Rust 移植)实现的
分层、极简依赖的 LLM agent 框架。核心目标:以最小的依赖面实现一个分层清晰、
可独立测试的 agent 框架,重点学习 pi 的核心设计——agent 循环、消息类型、工具抽象、流式协议。
✨ 核心特性
- 双层 agent 循环:外层 follow-up、内层 steering + 工具执行,支持 sequential / parallel 两种工具执行模式
- 流式输出:SSE 增量解析,流式 assistant 消息随 delta 原地更新;
Esc 随时中止
- 多 provider 支持:Anthropic(Messages API)+ OpenAI(Responses API 与 Chat Completions 双协议),统一翻译为
StreamEvent 事件流,错误编码进事件而非中断循环
- 内置工具:
read / write / edit(pi 风格精确替换)/ shell,跨平台(Windows + Linux)
- 会话管理:树形会话结构(
id + parentId)+ JSONL 追加写,支持分支与会话切换
- 上下文压缩:token 估算 + 压缩方案(切点 + 摘要指令),自动执行滚动总结
- 交互式 TUI:多行输入、Markdown 渲染、实时 transcript、折叠的思考/工具块、跨平台滚轮滚动
- 类型安全:自研
focus-json 手写 JSON 编解码,不依赖 serde / serde_json
- 依赖极简:运行时依赖仅 tokio + rustls 生态(另为 TUI 特批 ratatui + crossterm)
🏗️ 架构设计
细粒度 7-crate workspace 拆分,每个 crate 职责单一、边界清晰、可独立测试。依赖严格单向向下,禁止循环。
focus/
├── Cargo.toml # workspace 清单
├── README.md # 本文件
├── AGENTS.md # 工程规范(依赖白名单、代码/测试/提交规范)
├── LICENSE # MIT
├── docs/architecture/ # 架构解读文档(三份精读笔记)
└── crates/
├── focus-json/ # 极简 JSON 解析器 / 序列化器(零外部依赖)
├── focus-core/ # ★ 核心层:领域类型 + Agent 循环 + Tool / StreamProvider trait(纯同步,无 tokio)
├── focus-transport/ # 传输层:HTTPS 客户端(tokio + rustls)、HTTP/1.1、SSE 解析
├── focus-providers/ # Provider 实现(Anthropic / OpenAI)
├── focus-tools/ # 文件工具(read / write / edit / shell)
├── focus-harness/ # 会话持久化 + 上下文压缩 + 系统提示模板
└── focus-tui/ # 终端交互层(ratatui + crossterm),组装所有模块
分层依赖图
focus-json ← 零外部依赖,纯 std
focus-core ← focus-json(纯同步逻辑,不依赖 tokio)
focus-transport ← focus-json, tokio, rustls, tokio-rustls, rustls-native-certs
focus-providers ← focus-core, focus-transport, focus-json, tokio
focus-tools ← focus-core, focus-json
focus-harness ← focus-core, focus-json
focus-tui ← focus-core, focus-providers, focus-tools, focus-harness, focus-json, ratatui, crossterm
核心设计要点
- I/O 边界注入:
focus-core 是纯逻辑层,不碰网络 / 文件系统;provider 与工具都通过 trait 注入
content / details 分离:工具结果的 content(模型可见)与 details(UI / 日志可见)分开
- 错误编码而非抛出:
StreamProvider 绝不用 Err 中断循环,错误编码进 StreamEvent::Error
- 压缩只出方案:harness 产出「压缩方案」,真正的摘要 LLM 调用由上层(TUI)执行——遵守依赖图约束
🚀 快速开始
构建与测试
# 构建整个 workspace
cargo build --workspace
# 运行全部测试(181 个用例,全绿)
cargo test --workspace
# 代码质量检查
cargo fmt --all --check
cargo clippy --workspace -- -D warnings
运行 TUI
cargo run -p focus-tui
或直接运行构建产物 target/debug/focus。
首次启动后先配置 provider:
- 在 TUI 中输入
/config 打开配置表单(或直接编辑 ~/.focus/config.json)
- 填写
apiKey、选择 provider 与模型
- 回到输入框即可开始对话
⚙️ 配置
配置文件位于 <data>/config.json,<data> 默认为 ~/.focus
(Windows 为 %USERPROFILE%\.focus),可用环境变量 FOCUS_DATA_DIR 覆盖。
{
"provider": "anthropic",
"openaiProtocol": "responses",
"baseUrl": "https://api.anthropic.com",
"apiKey": "sk-...",
"model": "claude-sonnet-4-5",
"contextWindow": 200000
}
| 字段 |
说明 |
provider |
anthropic 或 openai |
openaiProtocol |
OpenAI 协议:responses(默认)或 chatCompletions |
baseUrl |
API 基址(可选,缺省用默认端点;具体端点路径由 provider 自行拼接) |
apiKey |
API 密钥 |
model |
模型 id |
contextWindow |
上下文窗口(可选,缺省按已知模型表自动查询,未知模型兜底 128k) |
数据目录
| 内容 |
位置 |
| 配置 |
<data>/config.json |
| 会话 |
<data>/sessions/<session-id>.jsonl(JSONL 追加写,树结构) |
环境变量
| 变量 |
说明 |
FOCUS_DATA_DIR |
覆盖数据根目录(默认 ~/.focus) |
FOCUS_SHELL |
覆盖 shell 工具使用的 shell 程序(Windows 默认 PowerShell、可回退 cmd;Linux 默认 bash) |
FOCUS_DEBUG_FILE |
设置后,providers 把每个原始网络 chunk 追加到该文件(排查协议不兼容,如推理字段名差异) |
⌨️ TUI 使用
命令
| 命令 |
功能 |
/new |
新建会话 |
/sessions |
查看 / 切换历史会话 |
/config |
编辑配置(provider / 模型 / 密钥) |
/compact |
手动触发上下文压缩 |
/help |
显示帮助 |
键盘
| 按键 |
功能 |
Enter |
发送消息 |
Shift+Enter |
换行 |
Esc |
运行中中止任务;空闲时退出 |
Ctrl+C |
退出 |
Ctrl+D |
继续(追问一轮) |
Tab |
展开 / 折叠最近的思考或工具块(两级:先预览,再全量) |
| 鼠标滚轮 |
逐行滚动(Linux / Windows 均支持;上滚暂停自动跟随) |
Ctrl+U / PageUp / PageDown |
滚动消息区(输入为空时 ↑ ↓ 也可滚动) |
🧪 测试
- 所有测试位于各 crate 的
crates/<name>/tests/,禁止在 src/ 内联测试
- 核心层(
focus-core)测试零网络依赖,用 mock provider 驱动
- provider 测试用录制 / 回放的 mock transport,不打真实 API(真实 API 测试用
#[ignore] 标记)
- 当前共 181 个测试用例,
cargo test --workspace 全绿
📚 文档
📄 许可证
MIT