213 lines
9.8 KiB
Markdown
213 lines
9.8 KiB
Markdown
# 设计文档
|
|
|
|
> 一个本地运行的个人任务 agent。覆盖三类工作:写汇报 PPT、写科研申报书、写代码。
|
|
> 模型自由(LiteLLM 接 OpenAI-compatible),代码可控(目标 1500-2000 行 Python,自己读得懂)。
|
|
|
|
---
|
|
|
|
## 1. 边界
|
|
|
|
### 做什么
|
|
- **PPT**:文本 / 会议纪要 → `.pptx`(用 `python-pptx`)
|
|
- **科研申报**:课题信息 → 分章节 `.docx`(用 `python-docx`)
|
|
- **编码**:文件编辑、shell 执行、迭代验证
|
|
|
|
### 不做什么
|
|
- 子 agent / IM 渠道 / 多用户 / Web UI(初期 CLI 即可)/ 自定义 RAG / 锁定 Anthropic
|
|
- **Eval Suite**:个人工具用 dogfooding 判断模型升级,造作 case 没区分度
|
|
|
|
### 关键约束
|
|
- 模型自由:LiteLLM 接 OpenAI-compatible 任意 provider(默认 DeepSeek V4)
|
|
- 任务持久化:任意时刻关机,下次能恢复
|
|
- 演化性:模型升级时 agent 跟着升级,不需要大改架构
|
|
|
|
---
|
|
|
|
## 2. 架构
|
|
|
|
### 目录树(实际)
|
|
|
|
```
|
|
zcbot/
|
|
├── core/
|
|
│ ├── capabilities.py # ModelCapabilities,从 yaml 加载
|
|
│ ├── llm.py # LiteLLM 封装,按 capabilities 自动启用 features
|
|
│ ├── loop.py # ReAct 主循环
|
|
│ ├── probe.py # 真实探测对账 yaml 声称的能力
|
|
│ ├── session.py # 消息列表 + meta + 落盘 messages.json
|
|
│ ├── skills.py # SkillRegistry (Anthropic 渐进披露格式)
|
|
│ └── task.py # TaskState (mode/desc/status/tokens/timestamps)
|
|
├── tools/
|
|
│ ├── base.py # Tool 基类 + _resolve 路径
|
|
│ ├── fs.py # read / write / edit (唯一匹配) / glob / grep
|
|
│ ├── shell.py # subprocess + 黑名单
|
|
│ ├── run_python.py # tmp .py + subprocess,过滤敏感 env
|
|
│ └── skill_tool.py # load_skill
|
|
├── skills/
|
|
│ ├── coding/ # SKILL.md
|
|
│ ├── ppt/ # SKILL.md + references/ + scripts/ + assets/
|
|
│ └── proposal/ # SKILL.md
|
|
├── prompts/system/
|
|
│ └── general_v1.md
|
|
├── config/
|
|
│ ├── agent.yaml
|
|
│ └── models/
|
|
│ └── deepseek_v4.yaml # flash + pro 两档
|
|
├── workspace/
|
|
│ └── tasks/<task_id>/
|
|
│ ├── state.json # TaskState
|
|
│ └── messages.json # Session
|
|
├── main.py # 装配 (build_agent)
|
|
└── cli.py # CLI: chat / tasks / probe
|
|
```
|
|
|
|
### 启动时拼装顺序
|
|
1. 读 `config/agent.yaml` 拿 default_model
|
|
2. `ModelCapabilities.load("deepseek_v4.flash", config/models/)` 拿能力档案
|
|
3. `LLM(caps)` 构造,从 env 读 API key
|
|
4. 解析 task_dir(新建 or resume)
|
|
5. 拼 system prompt:`prompts/system/general_v1.md` + `SkillRegistry.discovery_block()`(skill 列表)+ 当前 cwd
|
|
6. 装配工具集(fs / shell / load_skill / run_python)
|
|
7. 写初始 `state.json` + `messages.json`,启动 REPL
|
|
|
|
---
|
|
|
|
## 3. 核心组件
|
|
|
|
### 3.1 主循环(`core/loop.py`)
|
|
ReAct 风格:LLM → 若有 tool_calls 就执行 → 把结果塞回消息列表 → 再调 LLM。无 tool_call 即返回。
|
|
- 工具结果对模型截断到 16K 字符,对用户预览 400 字符
|
|
- 用 `console.status("thinking...")` 转圈点,所有日志走 `rich`
|
|
- `max_iterations` 从 capabilities 读,不同模型不同
|
|
|
|
### 3.2 Model Profile(`core/capabilities.py` + `config/models/*.yaml`)
|
|
**核心思想**:每个模型一份 yaml 档案,agent 行为按档案动态调整。新模型 5 分钟接入,不改代码。
|
|
|
|
`ModelCapabilities` 字段:max/reliable_context、max_output、parallel_tools、tool_calling_quality、thinking_mode、reasoning_effort_levels、code_quality、enable_run_python、max_iterations、optimal_temperature、prompt_caching、extended_thinking、api_base、api_key_env。
|
|
|
|
`LLM.chat` 按 capabilities 自动启 `parallel_tool_calls` / `reasoning_effort` / Anthropic prompt-caching header。
|
|
|
|
### 3.3 Capability Probing(`core/probe.py` + `cli.py probe`)
|
|
yaml 是手填的,可能错。`probe` 用真实 LLM 调用对账:
|
|
- `basic_chat`:连通性
|
|
- `parallel_tools`:给两个独立工具,看 single response 是否 ≥2 个 tool_calls
|
|
- `thinking_mode`:对 declared=True 的模型试 reasoning_effort,看 API 是否接受 + 是否产 reasoning_content
|
|
- `long_context`:needle-in-haystack 简化版(opt-in,默认关)
|
|
|
|
不修改 yaml,只输出 rich Table 报告。退出码 0/2/3 区分 ok / mismatch / error。**显式触发,不进启动路径**(每次启动跑会烧 API)。
|
|
|
|
### 3.4 工具系统(Hybrid 范式)
|
|
|
|
**两类工具并存**:
|
|
- **JSON tool call**(`tools/`):read / write / edit / glob / grep / shell / run_python / load_skill —— 处理离散操作
|
|
- **Code execution**(`run_python`):tmp `.py` + subprocess + 工作目录限制 + 敏感 env 过滤(`*API_KEY *TOKEN *SECRET *PASSWORD *PRIVATE_KEY`)—— 处理批处理 / 算数据 / 生成文档
|
|
|
|
**关键设计**:
|
|
- `edit` 用 **唯一匹配**约束(CoreCoder 风格):old_str 必须只出现一次,否则报错。防 LLM 改错地方。
|
|
- 工具按**原子操作**切分,不做高级封装。`make_pptx()` ❌,`run_python(code)` 调 `python-pptx` ✅。粒度太粗会接收不到模型升级红利。
|
|
|
|
### 3.5 Skill 系统(Anthropic 渐进披露标准)
|
|
对齐 Anthropic 2025-12 开放标准,跨平台兼容(Claude Code / Codex CLI / Gemini CLI 都用)。
|
|
|
|
**三层加载**:
|
|
| 层 | 时机 | 内容 | Token |
|
|
|---|------|------|------|
|
|
| Discovery | agent 启动 | 仅 `name + description`,所有 skill 都读 | 几百 |
|
|
| Activation | `load_skill(name)` | 完整 SKILL.md | 1000-5000 |
|
|
| Execution | SKILL.md 指 `references/xxx` | 单个 reference 文件 | 视情况 |
|
|
|
|
**Skill 设计原则**:写 WHY+WHAT,不写 Step 1/2/3。让模型自己规划。description 要明确具体——决定模型能否触发。
|
|
|
|
### 3.6 Session 与 Task
|
|
|
|
**Session**(`core/session.py`)= 消息列表 + meta + 落 `messages.json`。
|
|
|
|
**Task**(`core/task.py`)= Session 的上层概念,含 mode / description / status (active/completed/abandoned) / model / reasoning_effort / cwd / created_at / updated_at / tokens_prompt / tokens_completion。落 `state.json`。
|
|
|
|
存储:`workspace/tasks/<task_id>/{state.json, messages.json}`。每轮 `agent.run` 后调 `sync_task_tokens` 把 LLM 累计 tokens 写回。
|
|
|
|
CLI:`chat --mode coding --desc "..."`;REPL `/status /done /abandon /desc`;`tasks [--status active|completed|abandoned]` 列任务。
|
|
|
|
---
|
|
|
|
## 4. 模型路由
|
|
|
|
### 默认配置(`config/agent.yaml`)
|
|
```yaml
|
|
default_model: deepseek_v4.flash
|
|
```
|
|
|
|
设计上的分模式路由(后续要做)思路:
|
|
| 模式 | 模型 | 理由 |
|
|
|-----|-----|------|
|
|
| 通用 / 编码 / PPT / 提案初稿 | flash | flash SWE-Bench 80.6,够用 |
|
|
| 复杂 bug / 提案终稿 | pro + reasoning_effort=max | 关键产出值得花 |
|
|
| fallback | claude_4_7.opus | V4 不行时手动切 |
|
|
|
|
### 成本量级
|
|
| 任务 | flash | pro-max | Claude Opus 4.7 |
|
|
|-----|------|--------|------|
|
|
| 修一个 bug(~10 轮) | $0.01 | $0.05 | $0.30 |
|
|
| 5 页汇报 PPT | $0.05 | $0.20 | $1.50 |
|
|
| 完整申报书 | $0.30 | $1.50 | $10-15 |
|
|
|
|
99% 任务 flash 够用,关键终稿升级 Pro。
|
|
|
|
---
|
|
|
|
## 5. 设计哲学
|
|
|
|
### 核心原则:Less Scaffolding, More Trust
|
|
老 agent 框架(早期 LangChain、AutoGPT)失败的核心:给 LLM 太多脚手架,模型升级后这些脚手架成枷锁。
|
|
|
|
**正确做法**:把 LLM 当一个**会持续变强的同事**对待,告诉它目标,不告诉它步骤。
|
|
|
|
### 七条具体原则
|
|
1. **Prompt 用 WHY+WHAT,不用 HOW** —— 详细教"应该怎么思考"会降智强模型
|
|
2. **Skill 渐进披露,不写完整流程** —— 对齐 Anthropic 标准
|
|
3. **工具按原子操作切分,不做高级封装** —— 留出组合空间给模型
|
|
4. **Model Profile 化,不硬编码** —— 新模型 5 分钟接入
|
|
5. **Capability Probing** —— yaml 是手填的,跑探测对账实际行为
|
|
6. **版本化 Prompt** —— `prompts/system/active.md` 软链接(尚未做,等真要切版本时再做)
|
|
7. **eval 评估** —— 设计阶段曾认为是关键,落地后判断:个人工具 dogfooding 更有效;**已删**
|
|
|
|
### 借鉴自(简版)
|
|
| 来源 | 借鉴 |
|
|
|-----|------|
|
|
| CoreCoder | 主循环简洁实现 + Edit 唯一匹配约束 |
|
|
| Anthropic Agent Skills | SKILL.md + 渐进披露标准 |
|
|
| nanobot | Workspace + 任务隔离 |
|
|
| smolagents | LiteLLM 做模型层 + CodeAct 范式启发 run_python |
|
|
|
|
---
|
|
|
|
## 6. 风险与取舍
|
|
|
|
### 已知风险
|
|
| 风险 | 缓解 |
|
|
|-----|------|
|
|
| run_python subprocess 沙盒不够强 | 限制工作目录 + 敏感 env 过滤;后续可升级 Docker |
|
|
| V4 在某些复杂任务不如 Claude | dogfooding 判断,fallback 手动切 |
|
|
| Skill description 不够好 → 触发不准 | 用 Pro 优化 description,实战观察 |
|
|
| Long context 退化 | `probe --long-context` 探测可靠 ceiling,不依赖宣称值 |
|
|
| `Session.save()` 不原子,异常会留 0 字节文件 | 后续改 tmp + rename(已记 PROGRESS) |
|
|
|
|
### 取舍说明
|
|
**为什么用 Hybrid 范式而不是纯 CodeAgent**:V4 JSON tool call 已稳定;沙盒成本只在需要时付;兼容 thinking 模式。
|
|
|
|
**为什么用 Anthropic Skill 标准而不是自创**:行业标准已成,跨 SDK 兼容;直接拿 Anthropic 现成 skills repo。
|
|
|
|
**为什么不做 subagent**:状态管理复杂度爆炸;单 agent + skill 已覆盖 95% 场景。
|
|
|
|
**为什么不做 Eval Suite**:DESIGN 旧版按团队/产品场景设计;个人单用户场景里,跑两个真实任务的 dogfooding 比造作 case 信号更强,probe 已覆盖健康检查。
|
|
|
|
---
|
|
|
|
## 附录:DeepSeek V4 关键事实(2026-04-24)
|
|
|
|
- **V4-Pro**:1.6T 总 / 49B 激活,1M context,SWE-Bench 80.6 / Terminal-Bench 67.9 / MCPAtlas 73.6
|
|
- **V4-Flash**:284B 总 / 13B 激活,1M context
|
|
- 三种推理模式:non-thinking / thinking / thinking-max
|
|
- 价格:输入 ~$0.145/M,输出 ~$1.74/M(约 Claude Opus 1/6 ~ 1/7)
|
|
- `deepseek-chat` / `deepseek-reasoner` 在 2026-07-24 后下线 → 必须迁 `deepseek-v4-flash` / `deepseek-v4-pro`
|