Back to Blog
Axon Tutorial2026-06-19

03. 让 Agent 更聪明:System Prompt 的分层设计

拆解 System Prompt 的分层设计:反模式接种、风险评估、工具偏好和工作流约束如何让 Agent 更稳定。

AI Agent

03. 让 Agent 更聪明:System Prompt 的分层设计

从零到一实现一个 AI Agent 框架 · 第三篇


1. 为什么 System Prompt 需要设计?

先看一个最天真的 System Prompt:

你是一个 AI 助手,可以调用工具。

效果怎么样?模型会:

  • 试图用 cat 读文件,而不是已有的 read_file 工具
  • 修一个 bug 顺便改了三处无关代码
  • 为"可能存在 null"的地方加 try-catch(实际上永远不会 null)
  • 回答长得像论文摘要

问题的本质:LLM 的训练数据让它有很强的"默认行为"。它见过海量的 shell 脚本,所以倾向于用 bash 而不是专用工具。它在代码库训练语料里看过无数防御性编程,所以觉得加 try-catch 是"好习惯"。

不给规则,它就回退到训练数据中的默认模式。


2. 第一招:反模式接种

与其说"要怎么做",不如先说明 不要怎么做

Axon 参考了 Claude Code 的 System Prompt 设计,引入了三条反模式规则:

规则一:不要扩大范围

- 用户:修一下这个函数里的 bug
- AI:好,我修了 bug,顺便重构了周围的模块、加了注释、优化了命名……

修 bug 就修 bug。不要顺手重构、加注释、或者"顺带优化"。你的任务边界由用户的问题定义,不是由你发现的"可以改进的地方"定义。

用户:"这个计算不对"
你的回答应该是修复计算,不是重构整个文件。

规则二:不要防御性编程

// ❌ 不要
function parseUserId(input: string): number {
  try {
    return parseInt(input);
  } catch {
    return 0; // 这个永远不会触发,parseInt 不抛异常
  }
}

// ✅ 要
function parseUserId(input: string): number {
  return parseInt(input);
}

不为不可能发生的场景加 try-catch、null 检查或兜底逻辑。如果某个值理论上一定存在、某个操作理论上一定成功,就不要为假设的失败写代码。

规则三:不要过早抽象

一次用法 → 保持内联
两次用法 → 可以观望
三次及以上 → 考虑抽象

"Three similar lines > premature abstraction."


3. 第二招:爆炸半径框架

反模式规则再多也列不完。更好的方法:给模型一个 风险评估框架,让它自己判断。

爆炸半径 = 可逆性 × 影响范围

等级特征例子
🟢 低风险可逆,仅影响当前上下文读文件、查数据、写分析
🟡 中风险可逆但影响共享环境创建文件、npm install、git commit
🔴 高风险不可逆或影响大范围git push、rm -rf、修改生产配置

高风险操作 = 不可逆 + 影响共享环境

规则很简单:

  • 🟢 低风险:直接做
  • 🟡 中风险:做完告知用户
  • 🔴 高风险:先说风险和替代方案,等用户确认

这比列一个"禁止做的事情"清单要实用得多。清单永远列不完,但框架可以推广到任何场景。


4. 第三招:工具偏好映射表

模型在训练数据中看到最多的文件操作方式是 shell 命令(catsedecho >)。但你的 Agent 有专用工具。需要明确告诉模型"用哪个",否则它会回退到知识中最高频的模式。

- 读文件 → cat
+ 读文件 → read_file(不要用 cat)

- 改文件 → sed
+ 改文件 → edit_file(不要用 sed)

- 写新文件 → tee / heredoc
+ 写新文件 → write_file(不要用 tee)

- 搜索 → grep
+ 搜索 → search_files / list_files

- 长时间任务 → 阻塞等待
+ 长时间任务 → background_run + check_background

这不是教条——这些专用工具在框架层面做了错误处理、上下文管理、权限检查。用 shell 绕过它们等于白装了这些功能。


5. 如何组织到一起:7 层递进结构

把这些内容按从抽象到具体的顺序组织:

内容作用
1️⃣ Identity你是谁,定位是什么建立角色认知
2️⃣ System核心行为法则(反模式 + 爆炸半径)设定行为边界
3️⃣ Doing Tasks任务管理系统(DAG 模型)拆解复杂目标
4️⃣ Actions工具偏好映射表选择正确工具
5️⃣ Using Tools各工具的用途和限制具体操作指引
6️⃣ Tone输出风格要求统一表达方式
7️⃣ Output Efficiency输出精简要求减少废话

这个顺序的逻辑:先定义身份,再划边界,然后教工作流,最后规范输出。

前两层的 token 消耗是固定的,后几层可以动态按需加载。


6. 代码实现:在 Axon 中落地

Axon 的 System Prompt 拼装在 agent.ts 中,核心逻辑很简单:

// 将所有部分拼装成最终的 system prompt
const systemPrompt = [
  BASE_SYSTEM_PROMPT,           // 1-3 层:身份 + 行为法则 + 任务管理
  permissionsSection,           // 权限与安全(补充第 2 层)
  memoryContext,                // 记忆系统
  memoryPromptSection,          // 结构化记忆指引
  agentsContext,                // 项目上下文(AGENTS.md)
  skillsSection,                // Skill 列表
  toolsSummarySection,          // 工具摘要
  teamsSection,                 // 协作信息
].filter(Boolean).join('\n\n');

关键点:

  • BASE_SYSTEM_PROMPT 是硬编码的核心,包含 7 层的第 1-3 层 + 工具偏好映射表
  • 动态部分(记忆、Skills、工具列表)在运行期拼装,放在末尾利用近因效应
  • 权限部分单独成段,因为涉及敏感操作需要突出显示

7. 效果对比

修改前 VS 修改后,同一个场景的典型表现:

场景:用户说"帮我看看这个函数的 bug"

修改前:

"好的,我读了文件,发现函数 calculateTotal 有几个问题:首先它没有做 null 检查,其次变量命名不够清晰,另外周围的模块也可以优化一下,我一起改了……"

🔴 扩大了范围、做了防御性判断、还顺手重构。

修改后:

"问题在第 42 行:total 被除 100 而不是 1000。"(然后只修改那一行)

🟢 精确、克制、只解决问题。


总结

手法解决什么问题Token 成本
反模式接种模型默认行为不符合预期~150 tokens
爆炸半径框架穷举禁止清单不可扩展~100 tokens
工具偏好映射表模型倾向于用 shell 而非专用工具~80 tokens
7 层递进结构组织方式影响模型遵循程度0(重新组织而已)

成本不到 400 tokens,买来的是模型行为质的提升。

下一篇:权限与安全系统——别让 Agent 乱跑。