# Prompt 设计：Agent 的系统提示词怎么写

作者：Hex4C59 · 发布于 2026-03-23

原文：https://hex4c59.cc/agent/prompt-design-agent/


在前面几篇文章里，代码示例里都有一个 `system` 字段，每次都用几行占位文字一笔带过。这篇文章要把这件事正面讲清楚。

system prompt 是应用给模型的系统指令，用来说明角色、工具规则和处理边界。即使工具调用已经跑通，模型仍可能不知道何时搜索、失败后重试几次，或什么时候该停止。

本文面向会写简单代码、了解工具调用的读者，逐项解释这些要求怎样写进提示词。提示词只是影响行为的一部分；问题也可能来自工具、状态或模型，不能把所有失败都归因于措辞。

## 先给结论

1. **Agent 的 system prompt 和普通聊天 prompt 是两件不同的事。** 聊天 prompt 主要定义风格和人格；Agent prompt 需要定义角色、能力边界、工具使用规则、推理格式和失败处理策略。
2. **Prompt 是 Agent 决策的隐性框架。** 模型在每一步推理时都在这个框架内做选择。框架设计得清不清晰，直接决定了 Agent 在边界情况下的行为质量。
3. **工具描述是 prompt 的一部分，而且往往比 system prompt 本身更重要。** 很多开发者在 system prompt 上花了大量精力，却把工具描述写得一塌糊涂。
4. **好的 prompt 不是越长越好。** 过长的 prompt 会分散模型注意力，把真正重要的指令淹没在冗余内容里。每一行都应该有存在的理由。
5. **Prompt 需要像代码一样维护。** 随着 Agent 功能的扩展和失败案例的积累，prompt 需要持续更新，而不是写完一次就不动了。

## Agent Prompt 的结构

一个完整的 Agent system prompt 通常包含五个部分，每个部分解决不同的问题：

![Agent Prompt 结构图](https://hex4c59.cc/agent/prompt-design-agent/prompt_design_agent_structure.svg)

*图 1：一个可维护的 Agent system prompt，通常由角色定义、工具使用规则、推理格式、约束与边界，以及动态上下文注入五个部分构成。*

前四个部分相对稳定，构成 prompt 的骨架；最后一个部分是动态的，每轮推理前根据当前状态更新注入。

## 角色定义：不只是“你是一个助手”

角色定义是 prompt 的第一句话，也是最容易写废的地方。

**常见的错误写法：**

```
你是一个有用的 AI 助手，帮助用户完成各种任务。
```

这句话几乎没有提供任何有效信息。“有用”是废话，“各种任务”没有范围，模型从中得不到任何关于自己应该如何行动的指引。

**更好的写法应该回答三个问题：**

1. 你在这个系统里扮演什么角色
2. 你的主要职责是什么
3. 你的能力边界在哪里

```python
# 代码调试 Agent 的角色定义
ROLE = """
你是一个代码调试 Agent。你的职责是：分析用户描述的代码问题，
通过读取代码文件、执行测试、搜索相关文档，定位 bug 的根本原因，
并给出可验证的修复方案。

你只处理代码相关的问题。如果用户的请求与代码调试无关，
礼貌地说明你的专注范围，不要尝试回答超出范围的问题。
"""

# 研究助手 Agent 的角色定义
ROLE = """
你是一个研究助手 Agent。给定一个研究问题，你负责：
系统性地搜集相关资料、整理关键信息、识别不同来源之间的矛盾，
最终输出一份有数据支撑的结构化分析报告。

你只陈述能从来源中找到证据的结论。当信息不足时，
明确说明"现有资料不足以得出结论"，不要推测或补充训练知识。
"""
```

这两段角色定义都说明了职责、可用动作和边界。代码调试者需要读取代码并验证修复，研究助手则需要对照来源判断结论。重要的是要求具体、前后一致；不能仅凭放在角色定义还是约束段，就判断模型会更好地遵守。

## 工具使用规则：最被忽视的关键部分

[Tool Use](https://hex4c59.cc/agent/tool-use-core-of-agent/) 那篇讲过，工具描述的质量直接影响模型的调用决策。但除了工具描述本身，system prompt 里还需要明确告诉模型**工具调用的整体策略**。

### 什么时候该调用工具

如果不说明触发条件，模型可能在信息已经足够时继续调用，也可能在需要文件内容或实时资料时直接猜测。规则应把“还缺什么信息”与“调用哪个工具能补上”对应起来。

```python
TOOL_RULES = """
工具调用原则：

需要调用工具的情况：
- 任务需要实时或动态信息（当前价格、最新文档、文件内容）
- 需要执行计算或代码验证
- 需要读写外部系统（数据库、文件、API）

不需要调用工具的情况：
- 问题可以直接用已有信息回答
- 用户只是在澄清上下文或确认理解
- 工具上一步已经返回了足够的信息

判断标准：如果我在不调用工具的情况下能给出准确、完整的答案，
就直接回答，不要为了"显得认真"而调用不必要的工具。
"""
```

### 工具失败时怎么办

这是很多 prompt 完全没有覆盖的地方，导致工具失败时模型行为完全不可预测。

```python
TOOL_FAILURE = """
工具调用失败的处理策略：

1. 工具返回空结果：
   - 分析是否是查询参数问题，调整参数重试（最多 2 次）
   - 如果仍然为空，在回答里说明"未找到相关信息"，不要推测

2. 工具返回错误：
   - 记录错误信息，判断是否可以换用其他工具达到同样目的
   - 如果无法绕过，明确告知用户工具不可用，不要继续执行

3. 工具返回了结果但结果可疑（与已知信息矛盾）：
   - 不要直接使用可疑结果
   - 尝试用其他工具验证，或在回答里标注"该信息需要进一步确认"

核心原则：工具失败不等于任务失败。要尝试恢复，
但不要在错误信息的基础上继续推进。
"""
```

### 过度调用的防止

模型有时会在信息已经足够时继续调用工具，这既浪费 token 也增加延迟。

```python
STOP_CRITERIA = """
停止调用工具的时机：

当以下条件满足时，直接整合已有信息给出答案，不再调用工具：
- 已经收集到回答问题所需的全部信息
- 新的工具调用不会带来增量信息
- 已经达到合理的信息饱和点（继续搜索只会找到重复内容）

不要为了"更全面"而无限调用工具。80% 的情况下，
3-5 次有针对性的工具调用已经足够完成任务。
"""
```

## 推理格式：让思考过程可见

[ReAct](https://hex4c59.cc/agent/react-paradigm/) 那篇展示了如何记录当前状态、动作理由和工具结果。这里约定的是应用能检查的简短动作说明，帮助开发者核对调用是否针对任务；它不代表模型内部思考的完整记录。

### 结构化推理格式

```python
REASONING_FORMAT = """
每次决策前，按以下格式组织思考：

【当前状态】：简述目前已知的信息和完成进度
【下一步判断】：基于当前状态，判断下一步应该做什么
【行动】：如果需要调用工具，说明调用哪个工具和原因；
         如果信息已足够，说明准备给出最终答案

这个格式不需要在最终回答里显示给用户，
但在执行过程中要始终保持这样的思考结构。
"""
```

是否展示动作说明由应用决定。使用原生 function calling（结构化函数调用）时，只有工具定义中自行声明了 `reasoning` 参数，模型才会按该接口填写；它不是所有工具调用自动具备的字段。使用文本版 ReAct 时，可以用 Thought 标签记录动作理由，再与实际调用核对。

### 输出格式

输出格式和推理格式是两回事。输出格式规定了最终回答的结构：

```python
OUTPUT_FORMAT = """
最终答案的格式要求：

- 直接回答问题，不要重复用户的问题
- 如果答案来自工具调用，说明信息来源
- 如果有多个步骤或要点，用清晰的结构组织（列表、小标题）
- 如果任务未能完成，说明原因和已经完成的部分
- 长度与任务复杂度匹配：简单问题给简洁答案，复杂分析给完整报告

不要在回答末尾加"如有其他问题请随时告知"之类的套话。
"""
```

## 约束与边界：防御性设计

约束部分说明不允许的行为，以及遇到这些情况应怎样处理。提示词可以引导模型遵守，但不能代替执行模块的权限检查、参数校验或批准流程。

常见需要约束的行为：

**幻觉引用**：模型在工具没有返回相关信息时，从训练知识里补充内容，但表现得像是从工具里得到的一样。

```python
"只使用工具返回的信息作为答案依据。如果工具没有返回某条信息，"
"不要从其他来源补充，即使你认为这条信息是正确的。"
```

**任务范围蔓延**：用户在对话中带偏了方向，Agent 跟着偏移，忘记了原始任务。

```python
"始终保持对原始任务目标的追踪。如果对话偏离了原始目标，"
"在完成偏离部分之前，先确认用户是否希望改变任务方向。"
```

**过度自信**：在信息不足的情况下仍然给出确定性的结论。

```python
"当现有信息不足以得出确定结论时，明确说明不确定性，"
"而不是用模糊的语言掩盖不确定性。"
"'根据目前的信息，X 的可能性更大，但需要进一步确认' 比 'X 是正确的' 更诚实。"
```

**危险操作的确认缺失**：对有副作用的工具调用（删除文件、发送邮件、修改数据库）没有二次确认。

```python
"以下操作在执行前必须向用户确认：删除或覆盖文件、"
"发送任何形式的外部通信、修改生产数据库、调用付费 API。"
"即使用户在任务描述里已经提到了这些操作，执行前也要再次确认。"
```

## 动态上下文注入

前四个部分构成了 prompt 的静态骨架，最后一个部分是动态的：每轮推理前，把当前任务状态、用户信息、执行进度等注入到 prompt 里。

[上下文与记忆](https://hex4c59.cc/agent/context-memory-state-management/) 那篇里详细讲过任务状态对象的设计，这里重点说注入的格式和位置。

### 注入位置

动态上下文有两个可选的注入位置：

```python
def build_system_prompt(static_prompt: str, task_state: TaskState) -> str:
    # 方式一：追加在 system prompt 末尾
    return static_prompt + "\n\n---\n" + task_state.to_context_string()

def build_messages(static_prompt: str, task_state: TaskState,
                   history: list, user_input: str) -> list:
    # 方式二：作为独立的 user 消息注入
    return [
        {"role": "system", "content": static_prompt},
        {"role": "user", "content": task_state.to_context_string()},
        {"role": "assistant", "content": "已了解当前任务状态，继续执行。"},
        *history[-10:],
        {"role": "user", "content": user_input}
    ]
```

两种写法都是在调用前重新构造本轮输入。第一种把状态与静态规则拼成一条系统消息；第二种把状态放在独立消息里，便于区分任务数据与长期规则。无论采用哪一种，都要控制总长度，并确保状态确实是最新的。

选择应看所用接口、指令层级和评测结果，不能仅用固定 token 数决定位置。示例中的“已了解”消息也是应用添加的文本，不代表模型已经验证了状态。

### 注入格式

任务状态的注入格式要简洁、结构化，避免冗长的自然语言描述：

```python
def to_context_string(self) -> str:
    return f"""
## 当前任务状态

**目标**：{self.goal}
**当前步骤**：{self.current_focus}
**已完成**：{len(self.completed_subtasks)} 个步骤
**关键发现**：
{chr(10).join(f"- {f}" for f in self.key_findings[-5:])}
**已确定的决策**：
{chr(10).join(f"- {d}" for d in self.decisions_made[-3:])}
""".strip()
```

注意 `key_findings[-5:]` 和 `decisions_made[-3:]`：示例只保留最近几条来控制长度。但“较新”不等于“更重要”，原始目标、关键约束和仍有效的早期结论也应按当前任务需要保留；其他历史再按需检索。

## 完整示例：一个代码调试 Agent 的 Prompt

把上面所有部分组合起来，看一个完整的例子：

```python
SYSTEM_PROMPT = """
你是一个代码调试 Agent。你的职责是：分析用户描述的代码问题，
通过读取代码文件、执行测试、搜索错误文档，定位 bug 的根本原因，
并给出可验证的修复方案。

你只处理代码相关的问题。对于与代码调试无关的请求，
礼貌说明专注范围，不要尝试回答。

---

## 工具使用原则

需要调用工具时：需要查看文件内容、运行代码验证、搜索错误文档。

不需要调用工具时：问题是通用的语言/框架知识，不依赖具体代码内容。

工具失败处理：
- 读取文件失败 → 请用户确认路径是否正确
- 执行代码报错 → 分析错误信息，这通常就是 bug 的线索
- 搜索无结果 → 换关键词重试一次，仍无结果则说明

---

## 推理要求

每次决策前在内部明确：当前已知什么 → 还缺少什么 → 下一步做什么。

不要在没有读取代码文件的情况下猜测 bug 原因。
不要在没有执行验证的情况下声称修复方案一定有效。

---

## 约束

- 只引用从工具中获取的代码内容，不要补充假设的代码片段
- 修改文件前必须向用户展示修改内容并确认
- 如果 bug 原因不明确，说明"目前无法确定根本原因"，
  给出排查方向，而不是给出不确定的猜测

---

## 输出格式

调试报告结构：
1. 问题定位：bug 在哪里，是什么
2. 根本原因：为什么会出现这个 bug
3. 修复方案：具体的代码修改（附修改前后对比）
4. 验证方式：如何确认修复有效

如果无法完成调试，说明：已完成的分析、遇到的阻碍、建议的下一步。
"""
```

这个示例把角色、调用条件、失败策略、约束和输出格式放到一起。它们分别告诉模型要做什么、缺信息时怎样查、失败后怎样处理、哪些动作需批准，以及如何交付结果。提示词中的要求仍需通过实际执行和评测确认，不能把“写了规则”当成“已经遵守”。

前面的调用次数、比例和后面的版本评测分数都只用于展示提示词或记录格式，不是实测结论。应按任务设置上限，并记录实际测量。

## 常见的 Prompt 错误

### 错误一：用“请”和“尽量”代替明确指令

```python
# 错误：模糊的期望
"请尽量在调用工具之前先思考是否真的需要。"

# 正确：明确的规则
"只有在以下情况才调用工具：[具体条件]。其他情况直接回答。"
```

关键不在是否用了“请”，而在有没有可判断的条件。“尽量少调用”没有说明何时能调用、何时必须停止；明确条件后，执行程序和评测也才有依据检查。

### 错误二：规则之间互相矛盾

```python
# 矛盾：同时要求快和全
"尽量减少工具调用次数，提高响应速度。"
"确保信息全面，不要遗漏任何重要细节。"
```

当这两条规则冲突时（快速 vs 全面），模型会随机选择，行为不可预测。需要明确优先级：

```python
"在保证核心问题有答案的前提下，控制工具调用在 5 次以内。
如果核心问题需要更多工具调用，优先完成核心问题，
再决定是否补充周边信息。"
```

### 错误三：Prompt 越写越长，关键规则被淹没

随着失败案例积累，prompt 可能加入许多只针对特定情境的补丁。重复或矛盾的规则会让当前要求更难辨认，但不能据此断言每加一条规则，遵守率就按固定幅度下降。

解决方法不是无限堆砌，而是定期重构：把功能相同的规则合并，把只在特定场景才需要的规则移到动态注入里，保持 prompt 骨架的精简。

### 错误四：忘记测试边界行为

写完 prompt 之后，大多数人只测试“正常路径”——用户提了一个清晰的问题，Agent 顺利完成了。但真正决定 prompt 质量的，是边界情况下的行为：

- 用户给了一个超出 Agent 能力范围的任务，怎么办？
- 工具连续失败三次，还要继续吗？
- 用户中途改变了任务目标，Agent 怎么响应？

这些边界情况应该明确写进测试用例（参考[评测篇](https://hex4c59.cc/agent/agent-evaluation/)），而不只是靠直觉判断。

## Prompt 版本管理

把 prompt 像代码一样管理，而不是散落在各个文件里的字符串。

```python
# prompts/debug_agent/v3.py

METADATA = {
    "version": "3.2.1",
    "last_updated": "2026-03-23",
    "changelog": [
        "3.2.1: 在工具失败处理里加了'不要继续在错误信息基础上执行'",
        "3.2.0: 重构输出格式，加入修改前后对比要求",
        "3.1.0: 加入范围约束，处理非代码相关请求",
    ],
    "eval_scores": {
        "task_completion": 0.83,
        "tool_precision":  0.91,
        "stability":       0.87,
    }
}

SYSTEM_PROMPT = """..."""
```

每次修改 prompt 都记录 changelog 和对应的评测分数。这样有两个好处：当某次修改导致指标下降时，可以快速定位是哪个版本引入了问题；当团队成员看到当前 prompt 时，能理解每条规则背后的来历，而不是把它当成不可动的黑盒。

## 总结

Agent 的 system prompt 不是一段介绍文字，而是一套决策框架。它定义了 Agent 在每个推理时刻的行为边界：什么时候调工具、怎么处理失败、如何组织思考、哪些事情不能做。

一个好的 Agent prompt 应该：

- 角色定义清晰，包含能力边界，而不只是笼统的“有用的助手”
- 工具调用规则明确，覆盖触发条件、失败处理和停止时机
- 记录可检查的动作理由，并与实际请求和结果核对
- 写清常见失败的处理规则，并由执行层检查权限与参数
- 动态上下文注入精简，只放当前决策需要的信息

写完不是终点。每次评测发现新的失败模式，都应该问一个问题：这个失败是因为 prompt 没有覆盖这种情况吗？如果是，就更新 prompt，记录变更，重新跑评测。

Prompt 和工具描述、代码逻辑一样，是需要持续维护的工程产物。

---

*下一篇：Reflection——Agent 如何在执行完成后审视自己的输出，发现并纠正错误*

