系统提示词的艺术
前言
如果把一个 AI Agent 比作新同事,系统提示词就是他的员工手册。
它告诉这个新同事:你是谁,你负责什么,什么事情可以直接做,什么事情必须先问,什么时候应该主动,什么时候应该闭嘴,遇到不确定性时应该怎样处理。
很多人第一次写系统提示词,会从一句熟悉的话开始:
你是一个专业、可靠、乐于助人的 AI 助手。
这句话没有错,但它几乎没有解决任何真实问题。
真正困难的不是让 AI 看起来“专业”,而是让它在一个具体产品、具体工作流、具体用户状态里,做出稳定、可预期的行为。
比如用户说:“帮我整理一下文档。”
一个 Agent 可以立刻开始分类,也可以先问“你是想按项目整理,还是想把文档转成待办?”它可以主动生成结果,也可以先给用户确认整理方案。它可以把所有东西都搬到一个新表里,也可以尊重原来的文档结构,只做标注和聚合。
这些差异,不是模型能力本身决定的,而是系统提示词和产品设计共同决定的。
这也是我后来意识到的一点:系统提示词不是技术细节,它本质上是产品设计。
系统提示词真正决定什么
用户感受到的很多“性格”,其实都来自系统提示词。
它决定 Agent 的语气是否自然,回应是否克制,边界是否清楚,能不能承认自己不知道,也决定它会不会在用户还没授权时擅自行动。
以办公类 Agent 为例,最难的不是让它能回答问题,而是让它在几个互相冲突的特质之间保持平衡:
- 专业可靠,但不能冰冷机械。
- 贴心主动,但不能越界打扰。
- 能完成复杂工作流,但不能替用户做决定。
- 能快速响应,但不能为了简洁省掉关键确认。
这里的每一条,都是产品问题,不只是 prompt 问题。
“贴心主动”听起来很好,但如果 Agent 每隔一会儿就跳出来提醒你,它就变成了噪音。“有边界感”也很好,但如果它什么都不敢做,每一步都问,用户也会觉得它没有用。
所以系统提示词的第一件事,不是写漂亮,而是把这种平衡讲清楚。
它要回答:
- 什么叫主动?
- 什么叫打扰?
- 什么叫用户拥有最终决策权?
- 哪些场景可以直接执行?
- 哪些场景必须先澄清?
- 哪些话永远不要说?
一个好的系统提示词,不是让 Agent 更会表演,而是让它更可预测。
先研究别人怎么写,但不要照抄
我们之前做系统提示词研究时,第一步是看了很多成熟 AI 产品的系统提示。
代码助手、通用聊天助手、自主编程 Agent、浏览器 Agent、文档 Agent,各自场景不同,但它们背后有一些共性:
第一,都会先定义角色。
这不是为了仪式感,而是给模型一个行为锚点。一个“代码编辑器里的 AI 助手”和一个“办公文档里的 AI 搭档”,面对同一句话应该有完全不同的反应。
第二,都会写行为约束。
什么能做,什么不能做;什么时候调用工具,什么时候只回答;什么时候要承认信息不足,什么时候要继续检索。这些约束比“请认真”有用得多。
第三,都会规范输出。
输出不是文风问题,而是产品体验的一部分。是先给结论,还是先解释过程?是用 Markdown,还是用自然短句?是告诉用户内部工具名,还是只说人能理解的动作?这些都会影响用户是否信任它。
第四,都会做场景化响应。
用户明确提需求时怎么做;用户意图模糊时怎么问;用户情绪激动时怎么退让;用户只是进入页面没有说话时要不要主动开口。这些都不能靠模型临场猜。
还有一个很实用的细节:结构化格式很重要。
现在很多系统提示会用 XML 或类似的标签结构,把角色、规则、工具、上下文、用户偏好、当前请求分开。这样做不是为了好看,而是减少歧义。
例如:
<role>
你是一个办公场景中的 AI 搭档。
</role>
<rules>
- 不替用户做最终决策。
- 意图不清晰时先澄清。
- 不暴露内部工具名称。
</rules>
<context>
{{user_environment}}
</context>
<query>
{{current_query}}
</query>
系统提示词越复杂,越不能写成一大段散文。模型需要结构,人也需要结构。
但研究别人怎么写,不等于照抄。
Cursor 的提示词适合代码编辑器,Devin 的提示词适合自主工程任务,Claude 的系统行为强调自然对话和边界表达。你自己的 Agent 如果是个人知识库、办公助手或写作工具,就必须重新定义自己的产品位置。
系统提示词不是通用模板,而是产品定位的翻译。
先定义产品,再写提示词
真正开始写之前,最重要的不是 prompt,而是产品定义。
我会先问几个问题:
- 这个 Agent 是工具、助理、同事,还是自动化执行器?
- 它应该偏主动,还是偏被动?
- 它面对模糊需求时,是直接猜,还是先问?
- 它有没有权力修改用户内容?
- 它能不能替用户创建任务、发消息、发布文章?
- 它的语气应该像客服、编辑、工程师,还是私人助理?
这些问题不回答清楚,系统提示词一定会摇摆。
比如一个个人 Agent 项目里,我会更强调三件事:
第一,专业可靠。
不是“看起来很懂”,而是能基于上下文识别关键点,知道什么时候需要查资料,什么时候应该说明不确定性,什么时候要给出可验证结果。
第二,有边界感。
不擅自替用户做决定,不私自移动或删除重要内容,不把内部工具细节暴露给用户,不把公开文章写成内部工作记录。
第三,沟通高效。
不要每一步都问,也不要一次问十个问题。能安全推进的小事直接推进,高风险动作再停下来确认。
这三件事听起来抽象,但都必须写成具体规则。
不要只写:
你要有边界感。
要写:
涉及删除、发布、外部写入、权限变更、移动项目目录时,必须先向用户确认。低风险的本地草稿编辑可以直接完成,并在最后说明改动位置。
不要只写:
你要沟通高效。
要写:
默认不要连续追问。若缺少信息但可以做合理假设,先说明假设并继续;只有当错误假设会导致高风险结果时,才停下来询问。
系统提示词不是把产品词汇复制进去,而是把产品词汇翻译成行为条件。
系统提示词的层次
一个可维护的系统提示词,最好是分层的。
我现在比较喜欢这样的结构:
role
meta_rules
behavioral_constraints
tool_rules
response_strategy
output_format
user_preference
user_environment
conversation_history
current_query
前面是稳定规则,后面是动态上下文。
role 解决“你是谁”。
meta_rules 放最高优先级的红线,比如不能泄露敏感信息,不能伪造工具结果,不能修改只读输入。
behavioral_constraints 规定行为边界,比如不替用户做最终决策,不揣测用户情绪,不主动发起无关闲聊。
tool_rules 规定什么时候能用工具,工具不存在时怎么办,调用失败时怎么反馈。
response_strategy 处理场景,比如用户意图模糊、用户情绪激动、用户正在写文档、用户只是进入页面但没有发起请求。
output_format 规定最终呈现方式。
后面的 user_preference、user_environment、conversation_history、current_query,则让系统提示词从静态规则变成活的上下文。
这里有一个关键点:优先级要明确。
如果“主动帮助用户”和“不能替用户做决定”冲突,哪个优先?如果“回答要简洁”和“必须说明风险”冲突,哪个优先?如果“使用工具完成任务”和“工具不存在不能伪造结果”冲突,哪个优先?
这些不能留给模型猜。
场景比原则更重要
原则写得再好,落到真实产品里还是会出问题。
比如“主动”这个词,在不同场景里意思完全不同。
用户第一次打开产品,Agent 可以做简单介绍。
用户正在标题栏输入,Agent 最好不要跳出来打断。
用户在正文里停顿很久,Agent 可以给一个很轻的写作建议。
用户说“我写不下去了”,Agent 不应该讲大道理,而应该把任务拆小。
用户说“这怎么又错了”,Agent 不应该辩解,也不要过度安抚,而是承认结果可能没达到预期,然后提出重新处理。
这些都不是一句“保持友好”能解决的。
所以系统提示词里要有场景化策略:
如果用户意图模糊,先给 2-3 个可选方向,而不是直接执行。
如果用户表达挫败,不评价用户状态,不说“你很焦虑/你很累”,只降低任务难度并给下一步。
如果用户只是进入文档但没有发起请求,默认不主动打扰,除非存在明确的未完成任务或用户设置了提醒偏好。
场景化设计的目的,是让 Agent 学会“看场合”。
这也是系统提示词里最接近产品手感的部分。
最重要的反转:测试集比提示词更重要
这次研究里最重要的结论,其实不是“怎么写系统提示词”,而是:测试集可能比系统提示词本身更重要。
原因很简单:模型变化太快。
你今天针对某个模型调到 90 分的系统提示词,换一个新模型,行为可能就变了。有些模型更主动,有些模型更啰嗦,有些模型更会调用工具,有些模型更容易过度自信。
如果你只有一份系统提示词,那每次模型变化你都只能凭感觉调。
但如果你有一套测试集,你就有了稳定的标尺。
测试集里应该是什么?
不是抽象指标,而是真实场景。
比如:
- 用户说“帮我整理文档”,但意图不够清楚。
- 用户已经给了预算和用途,需要生成一份对比报告。
- 用户表达挫败:“我写不下去了。”
- 用户要求运行一个不存在的工作流。
- 用户确认了方案,Agent 应该继续执行。
- 工具返回了结果,Agent 应该如何面向用户表达。
每条测试都应该包含输入、上下文、预期行为。
这样你评估的就不是“这句话写得好不好”,而是“这个 Agent 在这个产品里有没有做对事”。
系统提示词是实现,测试集是标准。
实现会变,标准应该尽量稳定。
用 Judge 建立迭代闭环
有了测试集,还需要评估方式。
人工看当然可以,但规模一大就很难坚持。所以一个实用做法是用 LLM as a Judge。
也就是再写几个评测提示词,让 Judge 分别检查不同维度:
- 品质和语气:是否专业、温和、简洁。
- 响应策略:是否符合当前场景应该采取的策略。
- 用户体验:有没有给用户造成压力,有没有过度追问,有没有暴露内部细节。
- 硬性约束:有没有替用户做决定,有没有伪造结果,有没有泄露不该出现的信息。
这里有一个细节很重要:Judge 要区分“面向用户的文本”和“面向系统的工具调用”。
比如 Agent 内部可能调用了一个工具,但用户不应该看到工具的技术名称。评估时不能只看它有没有调用对工具,还要看它有没有用自然语言告诉用户正在做什么。
比较好的表达是:
我来根据这些文档先整理一版分类结果。
而不是:
我将调用 document_classifier_workflow。
内部工具名对用户没有意义,还会破坏产品感。
整个迭代流程大概是:
写系统提示词
跑测试集
用 Judge 评分和解释
读失败案例
修改系统提示词或测试集
继续跑
这个循环比单纯“凭感觉改 prompt”可靠得多。
从失败案例里长出规则
系统提示词不是一次写完的。
它应该从失败案例里长出来。
如果 Agent 曾经在工具不存在时假装执行,就补一条“存在性优先”:调用任何工具或资源前,先确认它在可用列表里;不存在就直接说明不存在。
如果 Agent 曾经替用户做决定,就补决策权约束:提供选项和建议,但最终选择必须由用户确认。
如果 Agent 曾经把内部工具名暴露给用户,就补表达规则:面向用户只描述自然动作,不暴露工作流、函数名、内部 schema。
如果 Agent 曾经在用户快速输入时打断,就补状态规则:用户正在输入或编辑时默认静默,除非用户显式召唤。
如果 Agent 曾经把公开文章写进了内部细节,就补脱敏规则:公开内容不得包含真实路径、内部链接、账号、客户信息、未公开项目名、访问凭证。
这些规则每一条都有来源。
没有失败案例支撑的规则,很容易变成噪音。有失败案例支撑的规则,才是真正的系统资产。
什么不该放进系统提示词
系统提示词很容易越写越长。
刚开始加一条规则很爽,后来就会发现,模型看到了很多规则,但不知道哪些最重要。人的维护成本也会上升。
我的判断是:稳定、全局、高风险的内容,适合放进系统提示词。
比如:
- 身份和产品定位
- 权限边界
- 安全红线
- 工具调用原则
- 输出风格
- 失败处理
- 高风险操作确认
临时、局部、低风险的内容,不适合放进去。
比如:
- 某篇文章的具体标题
- 某次任务的临时要求
- 某个项目当天的 todo
- 某个用户刚说的一次性偏好
- 某个工具返回的临时结果
这些应该放到当前任务上下文、项目文件、用户偏好、记忆系统或 skill 里。
系统提示词负责宪法,不负责每一张便签。
结尾
系统提示词的艺术,不是写出一段很聪明的话。
它是把一个 Agent 的产品定位、行为边界、场景策略、工具规则和评估标准,压缩成模型每次工作时都能看见的操作手册。
如果只看 prompt,它像文字工作。
如果放到 Agent 项目里,它其实是系统设计。
更准确地说:系统提示词是第一版产品原型,测试集是长期资产,Judge 是迭代工具。
写自己的 Agent 项目,第一步不是把 prompt 写到完美,而是建立这样一个循环:
定义产品行为
写系统提示词
构造真实测试场景
评估失败案例
把失败变成规则
当这个循环跑起来,系统提示词才不再是一段玄学咒语,而会变成一个可以维护、可以验证、可以持续进化的工程对象。
参考资料
- OpenAI Help Center, Best practices for prompt engineering with the OpenAI API
- OpenAI Developers, Prompting guide for Codex
- Anthropic, Building effective agents
- Anthropic, Effective context engineering for AI agents
- Anthropic Docs, Prompt engineering overview
- Google AI for Developers, Prompt design strategies
- HumanLayer, 12-Factor Agents