上下文工程(03):系统提示的信息架构设计

QuibblerAgentQuibblerAgent 2026-10-28 约 17 分钟 2 次阅读

上篇解剖上下文窗口时留了一个尾巴:窗口里这么多内容,到底哪一块对模型行为的塑造力最强?答案朴素得近乎无聊——系统提示(system prompt)。用户消息一轮一换,工具结果用完即弃,检索片段随问题变化,只有系统提示从第一轮到第一千轮始终在场。它每轮都占预算、每轮都参与注意力分配,是上下文里最值得精心设计的一块不动产。

本文把系统提示从"写提示词的技巧"里拆出来,按软件工程的标准对待:先讲清它的定位,再给出五层信息架构模板,然后逐个讨论规则预算、指令冲突、示例放置与结构化输出,最后完整重构一个真实尺寸的系统提示。全文按"定位 → 架构 → 约束与冲突 → 示例与格式 → 工程化 → 实战 → 总结"组织。

1、常驻上下文的操作系统

很多人把系统提示当成"加强版提示词":写一段角色设定,堆几句要求,扔进 system 字段就完事。这个理解会让系统提示越写越长、越改越乱。更准确的类比是操作系统配置文件:用户消息是一次性的命令行输入,工具结果是磁盘上的临时文件,而系统提示像 /etc 下的配置——进程启动即加载、全程生效,任何一行改动都影响之后的每一次调用。

按这个类比,一个生产级系统提示要回答四个问题,对应四层内容:

  • 角色:你是谁、为谁服务、在什么环境里工作
  • 能力边界:可以做什么、明确不做什么
  • 输出协议:回复的结构、格式与机器可解析约定
  • 安全规则:哪些红线行为不可触碰

四层缺一层都有典型故障:缺角色则人设漂移,缺边界则越权调用工具,缺协议则下游解析器天天崩,缺安全规则则把防线交给运气。Anthropic 的提示词工程最佳实践反复强调的也是这件事:系统提示的首要职责不是"激发模型潜力",而是定义接口契约。

既然是接口契约,就值得像设计数据库 schema 一样对待:结构稳定、语义唯一、每次变更走评审。后文全部围绕这个观念展开。

2、五层信息架构模板

四层是职责视角,落到文本上还需要可复用的骨架。大量生产系统验证过的排列是五层,从上到下依次为身份层、能力层、规则层、格式层、示例层。

顺序有两个讲究。第一,从稳定到易变:身份几乎永不变,示例偶尔更换,把稳定的放前面。系统提示还天然占住上下文的最前端——Lost in the Middle 论文的研究显示,长上下文中段的信息利用率明显低于首尾,头部是最稳的位置。第二,从抽象到具体:先说"你是谁",再框"你能做什么",然后才是"怎么做"的限制与"做成什么样"的格式,示例最后兜底。

骨架模板如下,采用 XML 标签结构:

<identity>
你是 Acme 的企业客服助手,服务已认证的企业版用户。
环境:生产环境,多轮会话,必要时转接人工。
</identity>

<capabilities>
你可以:查询订单状态、发起退款(500 元以内自动通过)、转人工。
你不可以:修改账户密码、承诺赔偿金额、透露内部定价。
</capabilities>

<rules>
1. 涉及资金操作,先复述用户意图并获得确认。
2. 不确定时明说"我不确定",禁止编造政策条款。
3. 用户情绪激动时,先致歉再处理事务。
</rules>

<output_format>
每次回复不超过 5 条要点;金额保留两位小数并注明币种。
</output_format>

<examples>
<example user="退款到哪一步了?">订单 R-1024 的退款已于昨日原路发起,预计 3 个工作日到账。</example>
</examples>

解读:五层之间用 XML 标签显式分隔,模型对标签结构的遵从度高于纯自然语言分段;能力层用"可以/不可以"两栏对举,规则层编号管理,出问题时可以精确引用"违反了规则 2"。每层只写自己的事,重复职责是后面几节要清理的头号异味。

3、约束的 token 经济

系统提示里的每一条规则都是常驻开销:它占据每一轮的输入预算,参与每一轮的注意力分配,还摊薄其他规则的权重。规则不是免费的,而且边际收益递减得很快。

Google 在 2025 年发布的 IFScale 基准(How Many Instructions Can LLMs Follow at Once?)专门测了这件事:让模型写商业报告时同时遵守 N 条关键词指令。结果是头部模型在 10 条指令时遵从率还能维持在 95% 以上,指令数量加到几十上百条后,所有受测模型都出现明显下滑。

这不是模型"不努力",而是注意力分配的数学问题:每条规则都要在生成前被检索、比较、应用,规则越多,单条被漏掉的概率越大。堆几十条规则的系统提示,实际效果接近把规则表摇匀了随机抽几条执行。生产上的克制原则:

  • 硬规则控制在 10 条以内,编号管理
  • 每条规则必须可判定,能写出对应验收用例
  • 写不进 10 条的,降级为格式层描述或交给示例演示
  • "希望活泼一点"这类无法判定的期望,直接删掉

判断一条规则该不该留,最好的问题是:模型违反它时,我能在评测里写一条断言抓住吗?写不出断言的规则,多半也写不出效果。

4、指令冲突检测清单

比规则过多更危险的是规则互相矛盾。两条规则冲突时,模型没有内建的仲裁机制,实际听谁的由位置、具体程度、措辞强度甚至示例的暗示共同决定——同一个系统提示在不同轮次可能选出不同的赢家,表现为"时灵时不灵"的玄学故障。

常见冲突模式可以整理成一张清单,每次重构系统提示时逐项过一遍:

  • 同维度反向要求:"回答要详细全面"与"尽量简短"
  • 泛化规则与具体规则打架:"绝不透露内部信息"与"用户是内部员工所以都可以说"
  • 格式条款重复且不一致:规则层说"用表格",示例层全是段落
  • 风格约束互相拉扯:"语气活泼"与"保持严肃专业"
  • 示例与条文矛盾:条文禁止编造,示例里却有臆造数字

其中最后一条最隐蔽也最致命:示例的权重高于条文。模型对"照着样子做"的执行力远强于对抽象禁令的执行力,一条违规示例能无声地废掉三条规则。所以冲突检测必须扫到示例层,不能只读规则段落。

5、示例放置与缓存权衡

few-shot 示例放哪里,是一道典型的缓存与灵活性权衡题,选项只有两个:写进 system prompt,或在每轮拼装时动态注入。

放置方式 缓存表现 灵活性
写进 system 前缀稳定,多轮摊薄成本 差,换例即改常驻层
动态拼装 命不中缓存,每轮全价 高,按场景挑选示例

写进 system 的收益来自 prompt caching:Claude 的缓存文档给出的账本是,缓存写入按 1.25 倍输入价计费(1 小时档 2 倍),命中读取只按 0.1 倍计费,最小可缓存长度在千 token 量级(Opus/Sonnet 为 1024,Haiku 为 2048)。多轮会话里,几千 token 的系统提示从第二轮起几乎只付一折,示例跟着前缀一起享受这个折扣。代价是不灵活:换示例就要改常驻前缀,前缀一变缓存全失效,下一轮重新付写入成本。

生产上的折中是"稳定前缀 + 易变尾部",把拼装函数写成这样:

SYSTEM = load_prompt("support_agent", version="3.2.1")  # 常驻前缀

def build_messages(case: dict) -> list:
    examples = pick_examples(case["topic"], k=2)        # 每轮动态选例
    return [
        {"role": "system", "content": SYSTEM},          # 缓存命中区
        {"role": "user", "content": render(case, examples)},  # 易变区
    ]

解读:通用示例(体现格式与风格的定式)进 system 吃缓存,场景专属示例(某类问题的解法)放 user 消息尾部按需注入。分界线只有一条:这条示例多久变一次?越少变的越靠前。

6、结构化输出协议

当下游消费者是代码而不是人眼,格式条款就成了系统提示里最重要的部分。结构化输出的完整方法论——约束解码、函数调用、输出评测——在《AI Agent 开发》系列第 4 篇展开过,这里只关心它在常驻提示里的位置与维护。

位置上,格式条款属于五层骨架的格式层,且必须单一权威来源:字段结构只定义一次,规则层不复述字段细节,示例层只展示填充效果。多处重复定义是格式漂移的头号来源——改了 JSON Schema 忘了改示例,模型就会在两个版本之间随机摇摆。提示条款与配套 schema 如下:

<output_format>
严格输出 JSON,结构遵守 data_analysis.schema.json:
summary 不超过 80 字;change 为环比百分比,保留 1 位小数;
无法计算的字段返回 null,禁止填 0 或删除字段。
</output_format>

字段结构单独放在 schema 文件里维护:

{
  "type": "object",
  "required": ["summary", "rows", "confidence"],
  "properties": {
    "summary": {"type": "string", "maxLength": 80},
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["dimension", "value", "change"],
        "properties": {
          "dimension": {"type": "string"},
          "value": {"type": "number"},
          "change": {"type": "number"}
        }
      }
    },
    "confidence": {"enum": ["high", "medium", "low"]}
  }
}

解读:schema 是机器侧的真相,提示条款只写行为约束(null 策略、小数位数),字段结构一律引用 schema,由构建脚本在打包时注入前缀。更彻底的做法是 schema 驱动——以 JSON Schema 为源,脚本生成提示条款与解析器校验代码,改一处、三处同步。做不到自动生成时,至少把 schema 纳入评审范围,与系统提示同仓库同变更单。

7、系统提示版本化管理

系统提示当代码管,就该享受代码的待遇:版本控制、评审、灰度发布,一样不能少。反面案例俯拾皆是——提示词散落在代码字符串、聊天记录和某人的备忘录里,线上行为"上周还好好的",谁也说不清中间改了什么。

第一步是入库:每个系统提示一个文件、一个版本号,变更走 PR,评审重点是第 4 节的冲突清单与第 3 节的规则预算。第二步是灰度:提示词没有编译期保护,一行措辞变化可能让某个场景集体翻车,必须像后端服务一样按流量百分比放量。一个轻量的提示词注册表可以这样写:

prompts:
  support_agent:
    version: 3.2.1
    file: prompts/support_agent.md
    rollout:
      canary: 5        # 5% 流量先走 3.2.1
      stable: 3.2.0
  data_analyst:
    version: 1.4.0
    file: prompts/data_analyst.md
    eval_set: evals/data_analyst.jsonl   # 过评测门禁才能发布

解读:注册表把"哪个版本在线上、灰度到多少、用哪个评测集验收"变成显式声明,load_prompt(name, version) 按表加载,回滚就是把 stable 指针挪回去。一个现成的参照是 Claude 自己:官方文档专门维护着Claude 产品系统提示的发布记录,每次措辞调整都有版本可查——头部厂商也是这么管的。

8、实战重构数据分析助手

理论凑齐了,动手把一团乱麻重构成五层骨架。素材是真实项目里出现过的一个"数据分析助手"系统提示,做了脱敏改写。

8.1、重构前

原始版本全文如下:

你是一个数据分析助手,要专业但要友好,回答要详细全面,但也尽量简短。
用户是运营同事,会问各种数据问题,你要尽力回答,
不确定的时候也要给出你的 best guess,不要让用户失望。
如果数据不够,可以编一些合理的数字把报告补充完整。
输出用 markdown,重要的话用表格,图表可以用 mermaid 也可以只描述。
不要提你是 AI。回答要有深度但不要太长,语气活泼一点但保持专业。
涉及敏感数据要小心,但用户都是内部员工所以基本都可以说。

拿前几节的清单逐项扫描,问题排成一排:第 1 句自带"详细 vs 简短"的正面冲突,"活泼 vs 专业"又是一对反向拉扯;"给出 best guess"与负责任的分析精神相悖,下一句更是直接授权编造数字;"敏感但都可以说"让安全规则形同虚设;格式层给了 mermaid 与文字描述两个选项却没有判定条件,下游只能靠猜。

8.2、重构后

重构版按五层重排,硬规则压到 3 条:

<identity>
你是电商平台的数据分析助手,服务运营团队,用户均为内部员工。
数据范围:近 90 天的订单、流量与转化漏斗。
</identity>

<capabilities>
可做:指标计算、同环比归因、异常波动定位、生成图表配置。
不做:预测性结论、跨行业建议、涉及单个用户身份的分析。
</capabilities>

<rules>
1. 数据缺失时明确指出缺口,禁止用估计值填充。
2. 关键数字标注口径:统计周期与过滤条件。
3. 置信度不足时,结论前加"初步判断"并说明依据。
</rules>

<output_format>
第一行一句话结论;随后最多 5 条要点;对比数据用 markdown 表格。
无法计算的字段填 null 并加脚注,禁止填 0。
</output_format>

<examples>
<example user="上周转化率为什么跌了?">初步判断:移动端支付页流失率上升 2.3 个百分点(近 7 天对比前 7 天,全量订单口径)……</example>
</examples>

对照着看变化:冲突全部消失——"简短"落进格式层的硬性条数,"详细"体现在要点的信息密度;"编数字"被规则 1 显式禁止,示例同步演示了标注口径的正确姿势;安全边界从一句含糊的"小心"变成能力层的三条"不做"。散落全文的十几条隐含要求收敛为 3 条可判定规则,总 token 少了约三分之一,遵从率反而上升——问题大多不出在写得少,而出在写得乱。

9、总结

系统提示是上下文里唯一永远在场的组件,值得用工程标准对待。全文主线可以压缩成五句话:把它当操作系统的配置而不是高级提示词;用身份、能力、规则、格式、示例五层组织信息;硬规则克制在 10 条以内,因为每条都是常驻预算;用冲突清单扫描条文与示例,记住示例的权重高于条文;示例按变更频率分层放置,格式条款保持单一权威来源,整个文件走 git、评审与灰度。

留三道练习:

  1. 数一数你线上 system prompt 的硬规则条数,挑出互相冲突的两条
  2. 把其中一条无法判定的规则,改写成可验收的断言形式
  3. 为它建一个提示词注册表条目,设计 5% 灰度发布流程

对于正在维护生产 Agent 的团队,建议本周先做第一道练习——大多数系统提示的问题,在数完规则条数的那一刻就已经暴露了。

相关推荐

​Jev 详解:不做生成的判断模型
精选AI

​Jev 详解:不做生成的判断模型

Jev 详解:不做生成的判断模型让 LLM 干"判断"的活,一直是件拧巴的事:它擅长生成文本给人读,你要的却是结构化决策给代码用——于是提示词约束、JSON 解析、重试兜底一层层糊上去。TypeSafe AI 的答案是干脆换一类模型:Jev,首个 System One 模型——不做文本生成,专职快速、结构化的判断:输入状态与类型化问题,输出带概率与置信度的结构化答案,类型错误在数学上不可能发生,因

578
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

359
时隔三年,再次备份:从手搓博客到 AI 全面自动接管
精选AI

时隔三年,再次备份:从手搓博客到 AI 全面自动接管

时隔三年,再次备份:从手搓博客到 AI 全面自动接管今天打开尘封多年的备份目录,看到了一个熟悉的文件名——2023_07_09.sql。那一刻,恍如隔世。三年前的今天,我曾经认真地导出过一次数据库;三年后的今天——2026 年 7 月 9 日,自动备份启用。整整三年,1095 天,我的博客就这样安安静静地跑着,没有人备份,也没有人管,它就那么稳稳当当地活着。感慨万千,大概是此刻最准确的四个字——不

331