160 lines
6.6 KiB
Markdown
160 lines
6.6 KiB
Markdown
# focus — 纯 Rust 实现的 LLM Agent 框架
|
||
|
||
> 参考 [pi](https://github.com/earendil-works/pi)(TypeScript 原版)与
|
||
> [pi_agent_rust](https://github.com/Dicklesworthstone/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 职责单一、边界清晰、可独立测试。依赖严格单向向下,禁止循环。
|
||
|
||
```text
|
||
focus/
|
||
├── crates/
|
||
│ ├── focus-json/ # 极简 JSON 解析器 / 序列化器(零外部依赖)
|
||
│ ├── focus-core/ # ★ 核心层:领域类型 + Agent 循环 + Tool / StreamProvider trait
|
||
│ ├── 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),组装所有模块
|
||
├── docs/architecture/ # 架构解读文档(三份精读笔记)
|
||
└── AGENTS.md # 工程规范(依赖白名单、代码/测试/提交规范)
|
||
```
|
||
|
||
### 分层依赖图
|
||
|
||
```text
|
||
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-tools ← focus-core
|
||
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)执行——遵守依赖图约束
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 构建与测试
|
||
|
||
```bash
|
||
# 构建整个 workspace
|
||
cargo build --workspace
|
||
|
||
# 运行全部测试(177 个用例,全绿)
|
||
cargo test --workspace
|
||
|
||
# 代码质量检查
|
||
cargo fmt --all --check
|
||
cargo clippy --workspace -- -D warnings
|
||
```
|
||
|
||
### 运行 TUI
|
||
|
||
```bash
|
||
cargo run -p focus-tui
|
||
```
|
||
|
||
> 或直接运行构建产物 `target/debug/focus`。
|
||
|
||
首次启动后先配置 provider:
|
||
|
||
1. 在 TUI 中输入 `/config` 打开配置表单(或直接编辑 `~/.focus/config.json`)
|
||
2. 填写 `apiKey`、选择 provider 与模型
|
||
3. 回到输入框即可开始对话
|
||
|
||
## ⚙️ 配置
|
||
|
||
配置文件位于 `<data>/config.json`,`<data>` 默认为 `~/.focus`
|
||
(Windows 为 `%USERPROFILE%\.focus`),可用环境变量 `FOCUS_DATA_DIR` 覆盖。
|
||
|
||
```json
|
||
{
|
||
"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 追加写,树结构) |
|
||
|
||
## ⌨️ TUI 使用
|
||
|
||
### 命令
|
||
|
||
| 命令 | 功能 |
|
||
|---|---|
|
||
| `/new` | 新建会话 |
|
||
| `/sessions` | 查看 / 切换历史会话 |
|
||
| `/config` | 编辑配置(provider / 模型 / 密钥) |
|
||
| `/compact` | 手动触发上下文压缩 |
|
||
| `/help` | 显示帮助 |
|
||
|
||
### 键盘
|
||
|
||
| 按键 | 功能 |
|
||
|---|---|
|
||
| `Enter` | 发送消息 |
|
||
| `Shift+Enter` | 换行 |
|
||
| `Esc` | 运行中中止任务;空闲时退出 |
|
||
| `Ctrl+C` | 退出 |
|
||
| `Ctrl+D` | 继续(追问一轮) |
|
||
| `Tab` | 展开 / 折叠最近的思考或工具块 |
|
||
| `Ctrl+U` / `PageUp` / `PageDown` | 滚动消息区(输入为空时 `↑` `↓` 也可滚动) |
|
||
|
||
## 🧪 测试
|
||
|
||
- 所有测试位于各 crate 的 `crates/<name>/tests/`,禁止在 `src/` 内联测试
|
||
- 核心层(`focus-core`)测试**零网络依赖**,用 mock provider 驱动
|
||
- provider 测试用**录制 / 回放**的 mock transport,不打真实 API(真实 API 测试用 `#[ignore]` 标记)
|
||
- 当前共 **177 个测试用例**,`cargo test --workspace` 全绿
|
||
|
||
## 📚 文档
|
||
|
||
- [`AGENTS.md`](./AGENTS.md) — 工程规范:依赖白名单、双语注释规则、代码 / 测试 / Git 提交规范
|
||
- [`docs/architecture/00-pi-architecture-overview.md`](./docs/architecture/00-pi-architecture-overview.md) — 参考项目架构全景总览
|
||
- [`docs/architecture/01-types-and-protocol.md`](./docs/architecture/01-types-and-protocol.md) — 类型契约逐行精读
|
||
- [`docs/architecture/02-agent-loop.md`](./docs/architecture/02-agent-loop.md) — 核心循环逐行精读
|
||
|
||
## 📄 许可证
|
||
|
||
[MIT](./Cargo.toml)
|