zcbot/DESIGN.md

1141 lines
38 KiB
Markdown

# 个人任务 Agent 设计方案 v2
> 一个面向"写汇报 PPT、写科研申报书、写代码"三类任务的轻量级 agent 框架。
> 完全自实现,综合 nanobot / CoreCoder / better-claw / smolagents 的优点,模型自由,**为模型持续升级做了演化性设计**。
> **v2 更新要点(2026-05)**:
> 1. 模型策略基于 DeepSeek V4 实际能力(2026-04-24 发布)重新设计
> 2. Skill 系统对齐 Anthropic 开放标准(2025-12 发布,行业已成共识)
> 3. 工具范式改为 Hybrid:JSON tool call + run_python 混合
> 4. 新增第 6 章「演化性设计」—— 让 agent 跟着模型升级而升级
> 5. 新增 Eval Suite 框架,作为模型升级的决策依据
---
## 1. 设计目标与边界
### 1.1 要做什么
构建一个本地运行的任务型 agent,能稳定完成三类工作:
| 任务模式 | 输入示例 | 输出 | 主要能力 |
|---------|---------|------|---------|
| **PPT 模式** | "把这份会议纪要做成 5 页汇报 PPT" | `.pptx` 文件 | 大纲提炼、版式设计、图表生成 |
| **科研申报模式** | "写一份国自然青年基金的立项依据" | `.docx` 文件(分章节) | 长文写作、文献检索、格式套模板 |
| **编码模式** | "修这个文件的 bug" / "实现这个函数" | 修改后的代码 | 文件编辑、shell 执行、迭代验证 |
### 1.2 明确不做什么
- ❌ 子 agent / subagent
- ❌ IM 渠道(Telegram / WeChat / Discord 等)
- ❌ 多用户系统
- ❌ Web UI(初期 CLI 即可)
- ❌ 自定义 RAG / 向量检索
- ❌ Anthropic 锁定(必须模型自由)
### 1.3 关键约束
- **模型自由**:支持 DeepSeek V4 / Kimi / Qwen / GPT / Claude 等任意 OpenAI-compatible API
- **代码可控**:总代码量 1100-1500 行,自己能完全读懂
- **任务持久化**:任意时刻关机,下次能恢复
- **长任务稳定**:单任务可跑数小时不崩
- **演化性**:模型升级时 agent 能力随之升级,不需要大改架构
---
## 2. 各家方案借鉴清单
| 借鉴自 | 借鉴的设计 | 为什么抄 |
|--------|-----------|---------|
| **CoreCoder** | 主 agent loop 简洁实现 | ~150 行写完核心,可读性极高 |
| **CoreCoder** | Edit 工具的"唯一匹配"约束 | 防止 LLM 改错地方,业界最佳实践 |
| **CoreCoder** | 三层 context 压缩(简化版,V4 时代不太用) | 兜底方案 |
| **Anthropic Agent Skills** | **SKILL.md + 渐进披露 标准** | 行业标准,2025-12 开放,跨平台兼容 |
| **nanobot** | Workspace + 任务隔离的目录结构 | 多任务并行不互相污染 |
| **nanobot** | 双层记忆(core + extended) | core 注入 prompt,extended 按需读 |
| **better-claw** | Mid-query 轮转 + carryover | 长任务工程兜底 |
| **better-claw** | 任务状态持久化(state.json) | 支持中断恢复 |
| **smolagents** | LiteLLM 做模型层 | 一行切换 25+ provider |
| **smolagents** | `@tool` 装饰器风格 | 工具定义最简洁 |
| **smolagents** | **CodeAgent 思路(部分采用)** | 通过 `run_python` 工具实现 hybrid 范式 |
| **CodeAct 论文** | 代码作为 action 的范式 | 在数据/计算/批处理任务上比 JSON 强 20% |
---
## 3. 整体架构
### 3.1 分层结构
```
┌──────────────────────────────────────────────┐
│ 入口层 │
│ - CLI(初期):interactive REPL │
│ - 后期可加:Streamlit / Gradio Web UI │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ 任务路由层 │
│ - /ppt → PPT 模式 + ppt skill │
│ - /doc → 科研写作模式 + proposal skill │
│ - /code → 编码模式 + coding skill │
│ - 默认 → 通用模式 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Agent 核心 │
│ - Loop:ReAct 循环(LLM ↔ Tool) │
│ - Capability Manager:模型能力探测与适配 │
│ - Context Manager:三层压缩(必要时) │
│ - Session:对话历史持久化 │
│ - Memory:双层记忆系统 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ 工具层(Hybrid 范式) │
│ 通用工具(JSON tool call): │
│ - read / write / edit / glob / grep / shell │
│ - web_search / web_fetch │
│ - load_skill │
│ - run_python(沙盒执行,Hybrid 关键) │
│ │
│ Skill 提供的工具(标准格式): │
│ - skills/<name>/scripts/*.py │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ LLM 层(LiteLLM + Model Profile) │
│ - 默认:DeepSeek V4-Flash │
│ - 升级:DeepSeek V4-Pro / Claude Opus 4.7 │
│ - Profile 化配置,新模型 5 分钟接入 │
└──────────────────────────────────────────────┘
```
### 3.2 目录结构
```
your_agent/
├── core/
│ ├── __init__.py
│ ├── loop.py # 主 agent loop ~150 行
│ ├── llm.py # LiteLLM 封装 ~120 行
│ ├── capabilities.py # 模型能力探测与适配 ~100 行 ⭐新增
│ ├── context.py # 三层 context 压缩 ~150 行
│ ├── session.py # 会话持久化 ~100 行
│ └── memory.py # 双层记忆 ~80 行
├── tools/
│ ├── __init__.py
│ ├── base.py # Tool 基类 ~50 行
│ ├── fs.py # read/write/edit/glob/grep ~250 行
│ ├── shell.py # bash 执行 + 安全检查 ~80 行
│ ├── web.py # web_search + fetch ~80 行
│ ├── run_python.py # 沙盒 Python 执行器 ~100 行 ⭐新增
│ └── skill_tool.py # load_skill 工具 ~60 行
├── skills/ # ⭐ 标准 Anthropic Agent Skills 格式
│ ├── ppt/
│ │ ├── SKILL.md # 主指引(短)
│ │ ├── references/ # 详细资料(按需读)
│ │ ├── scripts/ # 可执行脚本
│ │ └── assets/ # 模板等资源
│ ├── proposal/
│ │ ├── SKILL.md
│ │ ├── references/
│ │ ├── scripts/
│ │ └── assets/
│ └── coding/
│ ├── SKILL.md
│ └── references/
├── prompts/ # ⭐ 版本化系统提示词
│ ├── system/
│ │ ├── general_v1.md
│ │ └── general_active.md → general_v1.md
│ └── modes/
│ ├── ppt.md
│ ├── proposal.md
│ └── coding.md
├── config/
│ ├── agent.yaml # 主配置
│ └── models/ # ⭐ 模型档案库
│ ├── _template.yaml
│ ├── deepseek_v4.yaml
│ ├── claude_4_7.yaml
│ └── gpt_5.yaml
├── evals/ # ⭐ 评估任务集
│ ├── coding/
│ ├── ppt/
│ ├── proposal/
│ └── runner.py # eval 执行器
├── workspace/ # 用户数据(gitignore)
│ ├── tasks/
│ ├── memory/
│ │ ├── core.md
│ │ └── extended/
│ └── logs/
├── cli.py # CLI 入口 ~150 行
├── main.py # 装配 + 启动 ~50 行
├── requirements.txt
└── README.md
```
**总代码量预估:1300-1600 行 Python**(比 v1 多 ~200 行,因为加了 capabilities 和 run_python)
---
## 4. 核心组件设计
### 4.1 Agent Loop(`core/loop.py`)
```python
class AgentLoop:
def __init__(self, llm, tools, capabilities, context_manager, session,
max_iterations=None):
self.llm = llm
self.tools = tools
self.caps = capabilities # ⭐ 模型能力档案
self.ctx = context_manager
self.session = session
# 迭代次数从 capabilities 读取,不同模型不同
self.max_iterations = max_iterations or self.caps.max_iterations
def run(self, user_message: str) -> str:
self.session.append({"role": "user", "content": user_message})
for iteration in range(self.max_iterations):
messages = self.ctx.check_and_compress(self.session.messages)
response = self.llm.chat(
messages=messages,
tools=[t.schema for t in self.tools.values()],
# ⭐ 高级特性按 capabilities 启用
parallel_tool_calls=self.caps.parallel_tools,
reasoning_effort=self.caps.default_reasoning_effort
)
msg = response.choices[0].message
self.session.append(msg)
if not msg.tool_calls:
return msg.content
# ⭐ 并行 vs 串行执行根据能力决定
if self.caps.parallel_tools and len(msg.tool_calls) > 1:
results = self._execute_tools_parallel(msg.tool_calls)
else:
results = self._execute_tools_serial(msg.tool_calls)
for tool_call, result in results:
self.session.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
return "[已达到最大迭代次数]"
```
### 4.2 Model Profile + Capabilities(`core/capabilities.py`)⭐ 新增
**核心思想**:每个模型有自己的能力档案,agent 行为根据档案动态调整。新模型出来,加一个 yaml 即可。
```python
@dataclass
class ModelCapabilities:
"""模型能力档案"""
model_id: str
# 基础能力
max_context: int # 最大上下文(tokens)
reliable_context: int # 实测可靠上下文
max_output: int # 最大输出
# Tool calling
parallel_tools: bool # 是否支持并行 tool call
tool_calling_quality: str # "excellent" / "good" / "fair"
# 思考模式
thinking_mode: bool # 是否支持思考模式
reasoning_effort_levels: list # ["low","medium","high","max"]
default_reasoning_effort: str
# 推理与代码
code_quality: str # CodeAct 范式适配度
enable_run_python: bool # 是否启用 run_python 工具
# 工程参数
max_iterations: int # 最大迭代次数
optimal_temperature: float
# 特殊功能
prompt_caching: bool # Anthropic 特有
extended_thinking: bool # Claude 4.x 特有
@classmethod
def from_yaml(cls, path: Path) -> "ModelCapabilities":
"""从 config/models/*.yaml 加载"""
...
@classmethod
def detect(cls, model_id: str) -> "ModelCapabilities":
"""根据 model_id 自动找到对应档案
deepseek-v4-flash → config/models/deepseek_v4.yaml (variant=flash)
claude-opus-4-7 → config/models/claude_4_7.yaml
"""
...
```
**模型档案示例** (`config/models/deepseek_v4.yaml`):
```yaml
family: deepseek_v4
variants:
flash:
model_id: deepseek/deepseek-v4-flash
max_context: 1048576
reliable_context: 262144
max_output: 384000
parallel_tools: true
tool_calling_quality: good
thinking_mode: true
reasoning_effort_levels: [non_thinking, thinking]
default_reasoning_effort: non_thinking
code_quality: good
enable_run_python: true
max_iterations: 50
optimal_temperature: 0.3
prompt_caching: false
extended_thinking: false
pro:
model_id: deepseek/deepseek-v4-pro
max_context: 1048576
reliable_context: 524288
max_output: 384000
parallel_tools: true
tool_calling_quality: excellent
thinking_mode: true
reasoning_effort_levels: [low, medium, high, max]
default_reasoning_effort: medium
code_quality: excellent
enable_run_python: true
max_iterations: 100
optimal_temperature: 0.2
prompt_caching: false
extended_thinking: false
```
**Claude 4.7 档案** (`config/models/claude_4_7.yaml`):
```yaml
family: claude_4_7
variants:
opus:
model_id: anthropic/claude-opus-4-7
max_context: 200000
reliable_context: 200000
max_output: 8192
parallel_tools: true
tool_calling_quality: excellent
thinking_mode: true
reasoning_effort_levels: [low, medium, high]
default_reasoning_effort: medium
code_quality: excellent
enable_run_python: true
max_iterations: 100
optimal_temperature: 0.2
prompt_caching: true # Claude 特有
extended_thinking: true
```
**新模型怎么加(以未来 V5 为例)**:
```bash
# 1. 复制模板
cp config/models/_template.yaml config/models/deepseek_v5.yaml
# 2. 填能力(从模型发布博客 + 跑一次 capability probe)
vim config/models/deepseek_v5.yaml
# 3. 跑 eval suite 验证
python evals/runner.py --model deepseek-v5-pro
# 4. 切换默认模型
vim config/agent.yaml # default_model: deepseek-v5-pro
```
整个流程不需要改任何 agent 核心代码。
### 4.3 LLM 封装(`core/llm.py`)
```python
class LLM:
def __init__(self, capabilities: ModelCapabilities, api_key: str, base_url: str = None):
self.caps = capabilities
self.api_key = api_key
self.base_url = base_url
self.token_counter = TokenCounter()
def chat(self, messages, tools=None, parallel_tool_calls=None,
reasoning_effort=None, max_retries=3):
# 用 capabilities 自动填充默认值
kwargs = {
"model": self.caps.model_id,
"messages": messages,
"tools": tools,
"temperature": self.caps.optimal_temperature,
"api_key": self.api_key,
"base_url": self.base_url,
}
# 按能力启用
if self.caps.parallel_tools and parallel_tool_calls is not False:
kwargs["parallel_tool_calls"] = True
if self.caps.thinking_mode and reasoning_effort:
kwargs["reasoning_effort"] = reasoning_effort
if self.caps.prompt_caching:
kwargs["extra_headers"] = {"anthropic-beta": "prompt-caching-2024-07-31"}
for attempt in range(max_retries):
try:
response = litellm.completion(**kwargs)
self.token_counter.add(response.usage, self.caps.model_id)
return response
except (RateLimitError, APIConnectionError):
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
```
### 4.4 工具系统(Hybrid 范式)
#### 4.4.1 通用工具(JSON tool call)
文件操作工具的核心仍然是 **Edit 工具的"唯一匹配"约束**(借鉴 CoreCoder):
```python
class EditTool(Tool):
name = "edit"
description = "Replace a unique string in a file with another string."
def execute(self, path: str, old_str: str, new_str: str) -> str:
content = Path(path).read_text()
count = content.count(old_str)
if count == 0:
return f"[Error] old_str not found in {path}"
if count > 1:
return f"[Error] old_str appears {count} times, must be unique"
Path(path).write_text(content.replace(old_str, new_str))
return self._make_diff(content, ...)
```
#### 4.4.2 `run_python` 工具 ⭐ 新增
**Hybrid 范式的关键**:agent 主要用 JSON tool call,但需要时可以写代码作为 action:
```python
class RunPythonTool(Tool):
name = "run_python"
description = """Execute Python code in a sandboxed environment.
Use for:
- Data analysis, statistics, calculations
- Batch file operations (process many files)
- Document generation (PPT, Word, charts)
- Tasks where code is more natural than tool composition
Available libraries: pandas, numpy, matplotlib, python-pptx, python-docx,
arxiv, requests, pypdf, pdfplumber.
Working directory is the current task's tasks/<task_id>/.
Files created here are automatically available to the user.
"""
def execute(self, code: str, timeout: int = 60) -> str:
# 阶段 1(本地用):subprocess + venv + 工作目录限制
# 阶段 2(更安全):Docker container
# 阶段 3(公开服务):E2B / Modal
with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f:
f.write(code)
script_path = f.name
try:
result = subprocess.run(
[sys.executable, script_path],
cwd=self.task_dir, # 限制工作目录
capture_output=True,
timeout=timeout,
env=self._safe_env() # 过滤敏感环境变量
)
return self._format_result(result)
finally:
os.unlink(script_path)
```
**为什么这是关键设计**:
- **JSON tool call 处理离散操作**(读文件、跑命令、查文献)
- **Code execution 处理连续逻辑**(算数据、批处理、生成文档)
- 模型自己决定什么时候用什么,不是你硬编码
#### 4.4.3 工具粒度原则 ⭐ 新增
工具切分按"原子操作"原则,不做高级封装:
```python
# ❌ 反模式:工具做太多,模型用不灵活
class GenerateProposalTool:
def execute(self, topic):
# 内部硬编码 8 章节流程
# ✅ 正模式:原子操作,组合策略给模型
class WriteSectionTool: # 写一节
class CompileDocxTool: # 合并成 docx
class SearchPapersTool: # 查文献
class FormatBibtexTool: # BibTeX 格式化
```
**理由**:模型变强后会有更好的组合策略。封装太死接收不到模型升级的红利。
### 4.5 Skill 系统(对齐 Anthropic 开放标准)⭐ 重大调整
#### 4.5.1 标准目录结构
每个 skill 是一个目录,包含:
```
skills/proposal/
├── SKILL.md # 主指引(短,~3000 tokens 内)
├── references/ # 详细资料(按需加载)
│ ├── nsfc_format.md
│ ├── citation_style.md
│ └── section_examples.md
├── scripts/ # 可执行脚本(可作为工具)
│ ├── search_papers.py
│ ├── format_bibtex.py
│ └── compile_docx.py
└── assets/ # 模板、字体等
└── templates/
├── nsfc_youth.docx
├── nsfc_general.docx
└── nsfc_key.docx
```
#### 4.5.2 SKILL.md 标准格式
```markdown
---
name: proposal
description: 撰写科研申报书(国自然/省基金/横向项目)。当用户需要写课题申请、立项依据、项目书时使用。
---
# 科研申报书
## 资源
- `references/nsfc_format.md`:国自然格式细节
- `references/citation_style.md`:引文规范
- `references/section_examples.md`:各章节范例
- `scripts/search_papers.py`:可执行,文献检索
- `scripts/compile_docx.py`:可执行,合并章节为 docx
- `assets/templates/`:不同基金类型的 docx 模板
## 原则
- 文献必须真实(用 search_papers,绝不编造)
- 分章节写,不一次性生成全文
- 先与用户对齐课题信息卡片
## 工作目录
所有产出在 `tasks/<task_id>/`:
- `project.md` - 课题信息卡片
- `sections/<section_name>.md` - 各章节草稿
- `proposal.docx` - 最终输出
## 字数参考
立项依据 5000-8000 字,研究内容 3000-5000 字。
具体格式参见 references/nsfc_format.md。
```
注意:**不再写"Step 1/2/3"流程**,只写资源、原则、目标。让模型自己规划。
#### 4.5.3 Progressive Disclosure(渐进披露)的三层加载
按 Anthropic 标准:
| 层 | 时机 | 内容 | Token 占用 |
|---|------|------|----------|
| **Discovery** | Agent 启动 | 仅 `name + description`,所有 skill 都加载 | 几百 tokens |
| **Activation** | 任务匹配某个 skill | 完整 SKILL.md 主体 | 1000-5000 tokens |
| **Execution** | SKILL.md 引用某个 reference 时 | 单个 reference 文件 | 视情况 |
具体实现:
```python
# 启动时:Discovery
def build_initial_system_prompt(skills) -> str:
skill_descriptions = []
for name, skill in skills.items():
meta = parse_frontmatter(skill["SKILL.md"])
skill_descriptions.append(f"- {name}: {meta['description']}")
return f"""
You are a task agent. Available skills:
{chr(10).join(skill_descriptions)}
Use `load_skill(name)` to load full instructions when relevant.
"""
# Agent 调用 load_skill 后:Activation
class LoadSkillTool(Tool):
def execute(self, name: str) -> str:
return (skills_dir / name / "SKILL.md").read_text()
# Agent 在 SKILL.md 里看到 references/xxx.md,主动调 read_file:Execution
# 这一层不需要专门工具,就是普通 read 工具
```
#### 4.5.4 Skill 设计原则(基于 Anthropic 官方 + 行业经验)
1. **Description 是关键** —— 决定模型能否触发,要明确具体
2. **SKILL.md 主体不超过 5000 tokens / 500 行** —— 超过就拆 references/
3. **写 WHY+WHAT,不写 HOW** —— 描述目标和资源,不写步骤
4. **代码即工具又是文档** —— scripts/ 里的脚本可以执行,也可以读到 context 当文档
5. **保持 Skill 数 ≤ 20**,工具数 ≤ 10 同时可见(超过后准确率下降)
### 4.6 Context 管理(简化版)
V4 时代 long context 性能好了很多,**大部分任务不再需要复杂压缩**。但保留三层兜底:
```python
class ContextManager:
def __init__(self, capabilities, llm):
# ⭐ 阈值从 capabilities 读取,不同模型不同
self.max_tokens = capabilities.reliable_context
self.soft = self.max_tokens * 0.6 # V4 长 context 强,提高阈值
self.force = self.max_tokens * 0.85
self.collapse = self.max_tokens * 0.95
self.llm = llm
def check_and_compress(self, messages):
tokens = count_tokens(messages)
if tokens < self.soft:
return messages
if tokens < self.force:
return self._snip_old_tool_results(messages)
if tokens < self.collapse:
return self._microcompact(messages)
return self._collapse(messages, llm=self.llm)
```
**实测预期**:DeepSeek V4-Pro 在 256K 内基本不触发任何压缩,写一份完整申报书(7-8 万 token)用 V4-Flash 也只触发到 soft 层。
### 4.7 Session 与 Memory
(沿用 v1 设计,无重大变化)
---
## 5. 模型路由策略(基于 V4 实际能力)⭐ 重大调整
### 5.1 默认配置:V4-Flash 当主力
```yaml
# config/agent.yaml
default_model: deepseek_v4.flash
# 模式覆盖
by_mode:
general: deepseek_v4.flash
coding: deepseek_v4.flash # SWE-Bench 80.6,Flash 已够用
coding_hard: deepseek_v4.pro # 复杂 bug、架构设计
ppt: deepseek_v4.flash # PPT 生成不需要顶级模型
proposal_draft: deepseek_v4.flash
proposal_final:
profile: deepseek_v4.pro
reasoning_effort: max # 终稿用最强模式
# 工具用模型(便宜)
utility:
summarize: deepseek_v4.flash
title: deepseek_v4.flash
# 紧急升级路径(V4 不行时手动切)
fallback:
- claude_4_7.opus # 国基终稿如果质量不够,临时切 Claude
```
### 5.2 成本预估
| 任务 | V4-Flash | V4-Pro-Max | Claude Opus 4.7 |
|-----|---------|-----------|------------------|
| 修一个 bug(~10 轮) | $0.01 | $0.05 | $0.30 |
| 5 页汇报 PPT | $0.05 | $0.20 | $1.50 |
| 一份完整申报书(2-3 小时) | $0.30 | $1.50 | $10-15 |
**结论**:99% 任务 V4-Flash 已够用,关键终稿可升级 Pro,Claude 仅作 fallback。
---
## 6. 演化性设计 ⭐ 新增章节
> **核心问题**:模型每 3-6 个月迭代一次,agent 怎么不被甩在后面?
### 6.1 设计哲学
**Less Scaffolding, More Trust**(少脚手架,多信任)
老 agent 框架(LangChain 早期、AutoGPT)失败的核心原因:**给 LLM 太多脚手架,模型升级后这些脚手架成了枷锁**。
参考反例:
- 强制 ReAct 三段式输出 —— GPT-4 出来后这种格式反而降智
- PydanticOutputParser 死磕格式 —— Structured Output 内置后成了多此一举
- Prompt 里详细教"应该怎么思考" —— 强模型不需要被教
**正确做法**:把 LLM 当一个**会持续变强的同事**对待,告诉它目标,不告诉它步骤。
### 6.2 七条具体原则
#### 原则 1:Prompt 用 WHY+WHAT,不用 HOW
```
❌ HOW 型:
"修 bug 时:
1. 先用 read 工具读文件
2. 再用 grep 找相关位置
3. 然后用 edit 工具替换
4. 最后跑测试..."
✅ WHY+WHAT 型:
"目标:修复用户报告的 bug,做最小可逆修改。
工具:read, edit, grep, run, ...
原则:验证后再改、最小变更、有测试就跑。"
```
#### 原则 2:Skill 用渐进披露,不写完整流程
直接对齐 Anthropic 开放标准。Discovery 层只放 description,模型理解能力越强触发越准 —— 你不用回头给老 description 加 trigger 词。
#### 原则 3:工具按原子操作切分,不做高级封装
详见 4.4.3。粒度太粗,模型升级后没有施展空间。
#### 原则 4:Model Profile 化,不硬编码
详见 4.2。所有模型相关参数都在 yaml 里,新模型 5 分钟接入。
#### 原则 5:Capability Probing(启动时探测)
```python
def probe_capabilities(llm) -> dict:
"""启动时跑几个小测试,验证 yaml 里声称的能力"""
results = {}
# 测试 1:并行 tool call 是否真的工作
response = llm.chat([...], tools=[...test_tools])
results["parallel_tools_actual"] = len(response.tool_calls) > 1
# 测试 2:thinking mode 输出是否符合预期
response = llm.chat([...], reasoning_effort="medium")
results["thinking_works"] = hasattr(response.choices[0].message, "reasoning_content")
# 测试 3:long context recall(简化版 needle in haystack)
needle = f"The secret code is {random_code()}."
haystack = make_long_context(needle, target_tokens=100_000)
response = llm.chat([{"role": "user", "content": haystack + "\nWhat is the secret code?"}])
results["long_context_100k"] = random_code() in response.choices[0].message.content
return results
```
发现实际能力跟 yaml 不符 → 警告并自动调整。
#### 原则 6:版本化 Prompt,支持 A/B 切换
```
prompts/system/
├── coding_v1.md # 老模型用的详细版
├── coding_v2.md # 新模型用的精简版
└── coding_active.md → coding_v2.md
```
模型升级时:
1. 写一个新版本 prompt(更精简、更信任模型)
2. 在 eval suite 上对比 v1 vs v2
3. 数据说话,切换 active 软链接
#### 原则 7:Eval Suite —— 模型升级的决策基础
**最关键的一条**。没有 eval,你升级模型只能"凭感觉"。
详见下一节。
### 6.3 Eval Suite 框架
#### 6.3.1 目录结构
```
evals/
├── coding/
│ ├── fix_import_bug/
│ │ ├── input/ # 输入文件
│ │ │ └── main.py
│ │ ├── prompt.txt # 给 agent 的指令
│ │ ├── expected/ # 期望输出
│ │ │ └── main.py
│ │ └── rubric.yaml # 评分标准
│ ├── implement_function/
│ └── refactor/
├── ppt/
│ ├── meeting_to_slides/
│ │ ├── input/
│ │ │ └── notes.md
│ │ ├── prompt.txt
│ │ └── rubric.yaml # 主观评分(LLM-as-judge)
│ └── ...
├── proposal/
│ ├── write_intro_section/
│ ├── search_and_cite/
│ └── ...
└── runner.py # 执行器
```
#### 6.3.2 Rubric 示例
**客观评分**(coding 任务):
```yaml
# evals/coding/fix_import_bug/rubric.yaml
type: deterministic
checks:
- type: file_diff
path: main.py
expected_path: expected/main.py
- type: run_command
command: python -c "import main"
expect_exit_code: 0
- type: run_tests
command: pytest tests/
```
**主观评分**(ppt/proposal 任务):
```yaml
# evals/ppt/meeting_to_slides/rubric.yaml
type: llm_judge
judge_model: claude-opus-4-7 # 用强模型当裁判
criteria:
- "幻灯片数是否符合要求(5 页)"
- "每页 bullet 是否 ≤ 5 条"
- "信息密度是否合理"
- "是否有图表(如果数据 ≥ 3 个点)"
score_threshold: 7 # 满分 10
```
#### 6.3.3 Runner
```python
# evals/runner.py
def run_eval_suite(model_id: str, suite: str = "all"):
results = []
for case_dir in find_cases(suite):
# 起一个干净的 agent 实例
agent = build_agent(model=model_id, workspace=tmp_workspace())
# 跑测试
prompt = (case_dir / "prompt.txt").read_text()
result = agent.run(prompt)
# 评分
rubric = load_rubric(case_dir / "rubric.yaml")
score = grade(rubric, agent.workspace, result)
results.append({
"case": case_dir.name,
"score": score,
"tokens": agent.token_counter.total,
"cost": agent.token_counter.cost_usd,
"duration": agent.duration_seconds
})
return summarize(results)
if __name__ == "__main__":
# 模型升级时,跑这个
print(run_eval_suite("deepseek-v4-flash"))
print(run_eval_suite("deepseek-v4-pro"))
# 对比看哪个性价比最高
```
#### 6.3.4 Eval 的真实用途
每次模型升级,你能用数据回答这些问题:
> Q1:V5-Flash 出来了,值得升级吗?
> A:跑 eval suite,对比 V4-Flash vs V5-Flash 的 score 和 cost。
> Q2:Claude Opus 5.0 出来了,要不要换主力?
> A:跑 eval。如果 score 提升 < 10% 但 cost 涨 10x,继续用 DeepSeek。
> Q3:某个 prompt 改了之后,效果是好是坏?
> A:跑 eval。
**没有 eval suite,你的"升级"全靠想象。**
### 6.4 实操:模型升级 checklist
未来 V5、Opus 5、GPT-6 出来时,按这个流程:
```markdown
## 模型升级 Checklist
- [ ] 1. 写新模型档案 yaml (5 分钟,从 _template 起)
- [ ] 2. 跑 capability probe 验证 yaml(10 分钟)
- [ ] 3. 跑完整 eval suite 测试新模型(30 分钟,看任务量)
- [ ] 4. 对比 score / cost / latency,判断是否升级
- [ ] 5. 如果升级:
- [ ] 在 config 里调整 default_model
- [ ] 检查现有 prompt 是否可以精简(强模型不需要那么多脚手架)
- [ ] 跑 eval 回归一遍
- [ ] 6. 部分模式按需升级(比如只把 proposal_final 升级到新 Pro)
```
整个流程**不需要改 agent 核心代码**。
---
## 7. 关键工程细节
### 7.1 任务状态(`tasks/<task_id>/state.json`)
```json
{
"task_id": "proposal_20260102_1430",
"mode": "proposal",
"description": "国自然青年基金 - LLM agent 在医疗问诊",
"status": "active",
"model_used": "deepseek-v4-pro",
"reasoning_effort": "max",
"created_at": 1735800000,
"tokens_used": {"prompt": 145000, "completion": 38000},
"cost_usd": 0.42
}
```
### 7.2 中断恢复 / 成本控制 / 安全约束
(沿用 v1 设计,无重大变化)
---
## 8. 实施路线图
### Phase 1:最小可用骨架(2 天)
- [x] `core/llm.py` + Model Profile 雏形
- [x] `core/loop.py` - 主循环
- [x] `core/session.py`
- [x] `tools/base.py` + `tools/fs.py` + `tools/shell.py`
- [x] `cli.py` - 基础 REPL
- [x] `config/agent.yaml` + `config/models/deepseek_v4.yaml`
**验收**:`python cli.py chat` 能让 V4-Flash 修一个简单 Python bug。
### Phase 2:Skill 系统(标准格式)+ 三个 skill(2 天)
- [x] `tools/skill_tool.py`(LoadSkill)
- [x] 三个 skill 目录,对齐 Anthropic 格式
- [x] 任务模式路由
**验收**:三种模式都能进入,渐进披露正常工作。
### Phase 3:Hybrid 范式(1-2 天)
- [x] `tools/run_python.py` - subprocess 沙盒版
- [x] PPT/Word 通过 run_python 生成(不再做高级 API 封装)
- [x] PDF / 文献检索脚本到 skills/proposal/scripts/
**验收**:能产出 .pptx 和 .docx,文献检索真实。
### Phase 4:演化性能力(1-2 天)⭐ 新增
- [x] `core/capabilities.py` - Model Profile 加载
- [x] Capability Probing 启动检测
- [x] 版本化 prompts/ 目录结构
- [x] 配置热重载
**验收**:能切换 V4-Flash 和 V4-Pro 不用改代码,只改 config。
### Phase 5:Eval Suite(2 天)⭐ 新增
- [x] `evals/runner.py`
- [x] 每种任务 3-5 个测试 case
- [x] LLM-as-judge 评分
- [x] 报告输出(score / cost / latency)
**验收**:`python evals/runner.py --model deepseek-v4-flash` 能跑完所有任务并出报告。
### Phase 6:长任务工程化(2-3 天)
- [x] `core/context.py` - 三层压缩(兜底用)
- [x] `core/memory.py` - 双层记忆
- [x] 任务恢复机制
**验收**:写完整一份申报书不崩,中断后能恢复。
### Phase 7:打磨(持续)
- 双层记忆系统完善
- 更多 skill
- Web UI(可选)
- Docker 沙盒(替代 subprocess)
- 更多模型档案(Claude / GPT / Kimi 等)
---
## 9. 技术栈清单
```
# requirements.txt(核心)
# LLM
litellm>=1.50.0
tiktoken>=0.7.0
# 文档(给 run_python 用)
python-pptx>=0.6.21
python-docx>=1.1.0
pypdf>=3.17.0
pdfplumber>=0.10.0
matplotlib>=3.8.0
pandas>=2.0.0
# 文献
arxiv>=2.1.0
requests>=2.31.0
# CLI / 配置
click>=8.1.0
rich>=13.7.0
pydantic>=2.5.0
pyyaml>=6.0
python-frontmatter>=1.0.0
# Eval
deepdiff>=6.0 # 文件 diff 比对
# 开发
pytest>=7.4.0
ruff>=0.1.0
```
---
## 10. 风险与权衡
### 10.1 已知风险
| 风险 | 缓解 |
|-----|------|
| run_python 沙盒安全(subprocess 不够强) | 限制工作目录 + 环境变量过滤;后期升级 Docker |
| V4 在某些复杂任务上仍不如 Claude | Eval suite 帮判断;fallback 机制 |
| Skill description 不够好 → 触发不准 | 用 V4-Pro 优化 description;eval 测触发率 |
| Long context 退化 | Capability probe 探测 reliable_context;不要依赖宣称值 |
| Prompt 改了一次就不敢动 | 版本化 + eval 让改动有数据支撑 |
### 10.2 取舍说明
**为什么改用 Anthropic Skill 标准而不是自创**:
- 行业标准已成,跨平台兼容
- 直接拿到 Anthropic skills repo 的现成资源
- 未来想换底层 SDK 不用改 skill
**为什么用 Hybrid 范式而不是纯 CodeAgent**:
- DeepSeek V4 在 JSON tool calling 已足够稳定
- 沙盒成本更低(只在需要时执行代码)
- 可以兼容 thinking 模式(纯 CodeAgent 跟 thinking 不太合)
**为什么花精力做 Eval Suite**:
- 没有 eval,模型升级决策只能凭感觉
- 一次性投入,长期复用
- 跑 eval 的成本(~$10)远低于因为没 eval 选错模型的成本
**为什么不做 subagent**:
- 用户明确不需要
- 加了之后状态管理复杂度爆炸
- 单 agent + skill 已能覆盖 95% 场景
---
## 11. 与 v1 方案的差异
| 维度 | v1 | v2 |
|-----|----|----|
| 默认模型 | deepseek-chat (V3.2) | deepseek-v4-flash |
| Context 阈值 | 65K, 0.5/0.7/0.85 | 256K, 0.6/0.85/0.95 |
| 工具范式 | 纯 JSON tool call | **Hybrid:JSON + run_python** |
| Skill 格式 | 自创 | **Anthropic 开放标准** |
| Skill 描述风格 | "Step 1/2/3" 流程 | **WHY+WHAT 风格** |
| 模型配置 | 散在 config 里 | **Model Profile 化** |
| 升级机制 | 无 | **Capability Probing + Eval Suite** |
| Prompt 管理 | 散在代码里 | **版本化 + active 软链接** |
| 工具粒度 | 部分高级封装(如 make_pptx) | **原子化(用 run_python 调 python-pptx)** |
| 代码量 | 1100-1300 行 | 1300-1600 行 |
---
## 12. 下一步
确认方案后,从 Phase 1 开始落地:
1. 起项目骨架
2.`core/llm.py` + `core/capabilities.py`
3.`config/models/deepseek_v4.yaml`
4. 跑通最小 REPL
5. 用 V4-Flash 跑一个简单任务
Phase 1 预计 2 天完成,跑通后能立即用。
---
## 附录 A:Anthropic Skill 标准参考
官方资源:
- [Agent Skills 文档](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview)
- [Anthropic skills 仓库](https://github.com/anthropics/skills)(开源,可直接抄)
- 现成可用的 skill:pdf-processing、xlsx、docx、pptx、claude-api 等
行业落地:
- Claude Code: `~/.claude/skills/`
- OpenAI Codex CLI: `.agents/skills/`
- Google Gemini CLI: `.gemini/skills/`
- GitHub Copilot: 同日跟进
- **格式完全统一**,只是路径不同
## 附录 B:DeepSeek V4 关键事实(2026-04-24)
模型:
- V4-Pro:1.6T 总 / 49B 激活,1M context
- V4-Flash:284B 总 / 13B 激活,1M context
- 三种推理模式:non-thinking / thinking / thinking-max
Agent 能力(V4-Pro-Max):
- SWE-Bench Verified: 80.6%(对标 Claude Opus 4.6 的 80.8%)
- Terminal-Bench 2.0: 67.9%(超过 Claude 4.6 的 64.3%)
- MCPAtlas: 73.6%(对标 Claude 4.6 的 73.8%)
价格:
- 输入约 $0.145 / M tokens(Claude Opus 的 1/7)
- 输出约 $1.74 / M tokens(Claude Opus 的 1/6)
迁移:
- `deepseek-chat` / `deepseek-reasoner` 在 2026-07-24 后下线
- 必须迁到 `deepseek-v4-flash` / `deepseek-v4-pro`
## 附录 C:演化性设计的灵感来源
- **Anthropic "Equipping agents for the real world"** —— Skill 渐进披露的设计哲学
- **CodeAct 论文(Wang et al. 2024)** —— Code as action 范式
- **LangChain 早期教训** —— 过度脚手架的反例
- **Karpathy 的 nanoGPT 哲学** —— "可读性优先于功能完备"
---
*Last updated: 2026-05-02*
*v2 changes: DeepSeek V4 / Anthropic Skill 标准 / Hybrid 范式 / 演化性设计*