Prompt 把 system 与 user 指令混用:分清通道再修

持久规则塞进 user message,模型分不清优先级。把它们挪到 system / developer 通道。

你写了一条很长的 user message,把角色(“你是一名资深工程师”)、全局规则(“始终返回 JSON、不用 emoji”)、任务(“重写下面这个函数”)和输入代码全捆在一起。模型有时遵守规则,有时无视。聊到第 5 轮,规则就完全消失了——因为它们和任务挤在同一条通道里,被更新的输入埋掉。模型没坏,是你把持久规则放进了单轮通道。System 指令、project / custom instructions、单轮 user message 的生命周期各不相同,混在一起就会把模型依赖的优先级层级拍平。

最快的修法: 把所有应该每轮生效的规则(角色、输出格式、语气)从 user message 里拿出来,放进持久通道——API 里的 system 参数,或聊天 UI 里的 Custom Instructions / Project instructions。user message 里只留本轮任务和输入。这一步通常就能让整段对话的行为重新稳定下来。

本文讲清楚为什么有这种分层、截至 2026 年 6 月各主流平台把持久通道叫什么,以及怎么把 prompt 各部分路由到对的地方。

你属于哪一类?

症状可能原因跳到
一条 user message 里装着规则 + 任务全挤在一条通道Step 1、Step 2
每轮都重打同样的规则没用持久通道Step 2
规则撑 2-3 轮就开始跑偏被新轮次埋掉Step 2、Step 3
输出语气 / 格式随机乱跳system 和 user 矛盾Step 6
Claude messages[] 里的 role: "system" 被忽略Anthropic 用错了通道Step 2(Anthropic 注意事项)
工具被当建议而非合约工具定义写在 prose 里Step 5

常见原因

1. 全塞进一条 user message

你写了一条长 message,没用 system prompt,于是规则和任务在同一通道里争注意力。

如何判断:API 调用里只有一条 role 为 user 的消息,或聊天 UI 里所有规则都打在输入框里。

2. 完全没用持久通道

很多平台把持久通道藏在 “Custom Instructions""Project instructions” 或 “Saved info” 这类名字后面,不少用户根本不知道它存在。

如何判断:你有想每轮都生效的规则,却每次都重打一遍。

3. 每条 user message 都重申全局规则

你发现模型会忘,于是每轮都重贴。这能工作,但浪费 token、脆弱,而且其中一些重贴的规则会和你之前设过的 system prompt 静默冲突。

如何判断:每条 user message 开头都是同样的 5 行 preamble。

4. system prompt 和 user message 矛盾

你昨天写的 system prompt 说 “be formal”,今天又打 “keep it casual”。模型拿到两个方向相反的权威来源,结果就摇摆。

如何判断:你已经记不清 system prompt 里写了什么。

5. tools / functions 配置在 user message 里

Agent 工作流里,工具定义应该有自己的槽位。塞进 user message 能被解析,但模型会把它当成软建议,而不是带 schema 验证的合约。

如何判断:工具描述写在 user prose 里,而不在 tools 参数里。

动手前先确认

  • 列出当前 prompt 的每一行。
  • 给每行分类:system(持久)、user(单轮)、tool(能力)、assistant(历史)。
  • 标出哪些每轮会变、哪些是持久的。
  • 查清你平台到底支持什么——聊天 UI 和原始 API 暴露通道的方式不一样。
  • 计划好每部分放哪里。

手头有的话再收集:完整发送的 prompt、你当前的 system / project instructions、对话历史、那条无视规则的输出,以及确切的模型和平台。

最短修复路径

Step 1:分清持久 vs 单轮

持久(system / custom instructions):
- 角色:"You are a senior backend engineer."
- 输出格式:"Return only valid JSON matching schema X."
- 语气规则:"No emoji. No exclamation marks."

单轮(user message):
- "把下面这个函数加上指数退避的重试逻辑。"
- <粘代码>

分开之后两半都更清爽,也更好调试。

Step 2:把持久规则挪到持久通道

OpenAI API。 对 GPT-5.5 以及 o1 之后的每个模型,承载指令的角色名叫 developer,不再是 system。旧的 system 角色仍然能用(它被当作别名映射到 developer),但 developer 是现在的叫法;平台强制固定的权威顺序 platform > developer > user,所以 developer 指令压过用户输入的任何东西。

messages = [
  {"role": "developer", "content": "<所有持久规则>"},
  {"role": "user", "content": "<仅本轮任务>"}
]

Anthropic(Claude)API。 Claude 使用 role: "system" 的消息。System prompt 是请求上的顶层 system 参数,独立于 messages 数组。把 {"role": "system", ...} 放进 messages 是常见错误——很多 Anthropic 兼容端点会拒绝或忽略它。

import anthropic
client = anthropic.Anthropic()

client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    system="<所有持久规则>",                    # 顶层参数,不是 message
    messages=[{"role": "user", "content": "<仅本轮任务>"}],
)

聊天 UI(截至 2026 年 6 月):

  • ChatGPT — 点头像,再进 Settings -> Personalization -> Custom instructions。打开 customization 开关,把持久规则放进去(长文本框每个上限约 1500 字符)。常做的活儿可以建一个 Project,它的指令会对里面每个对话生效。
  • Claude — 在 Settings -> Profile 里设持久规则(那个对所有对话生效的 “personal preferences” 框),或者更好的做法是建一个 Project,用它的 Project instructions / custom instructions,让规则自动套用到该 Project 里每个对话。
  • Gemini — 用 Saved info(用 Gemini Gems 的话还有 Gem 自己的指令)把规则带到各个对话。
  • Cursor — 项目级规则放 .cursor/rules(老配置用顶层 .cursorrules 文件)。
  • Claude Code — 项目级规则放仓库根目录的 CLAUDE.md

Step 3:user message 只聚焦本轮任务

单轮 message 只放新请求、新输入、以及会变的东西。任何每轮通用的都不要放。

差:  "记住:返回 JSON、不用 emoji、你是资深工程师。
       现在:重写这个函数。"
好:  "重写这个函数:"
      <代码>

Step 4:API 里持久通道放最前

对 OpenAI,developer(或 system)消息放数组第一条。对 Claude,system 是顶层参数,所以 messages 内部的顺序对它不适用。

messages = [
  {"role": "developer", "content": "..."},   # OpenAI 放第一条
  {"role": "user", "content": "..."},
  {"role": "assistant", "content": "..."},
  {"role": "user", "content": "..."}
]

在 OpenAI 上,如果承载指令的消息出现在对话中间,行为会更难预测——把持久规则留在第一条指令消息里。

Step 5:能力用 tools / functions

Agent 工作流里,把工具定义在 API 的 tools 参数里,不要写在 prose。模型会把声明出来的工具当成带 schema 验证的合约,而不是埋在文字里的软建议。

tools = [{
  "type": "function",
  "function": {
    "name": "search_codebase",
    "description": "...",
    "parameters": {"type": "object", "properties": {}}
  }
}]

(在 Anthropic API 上,对应结构里每个工具用 namedescriptioninput_schema。)

Step 6:审矛盾、做取舍

打印你的 system / developer 指令,再打印最新的 user message,找冲突。持久通道一般压过 user message;如果你想让 user message 在某一点上赢,就显式说明:“本轮覆盖 system prompt 中关于 X 的规则。“

怎么确认已经修好

  • 同一批规则跨 10 轮以上都成立,不需要任何重贴。
  • user message 短而聚焦任务。
  • 删掉某条 user message 里的旧 preamble,行为变——规则在持久通道里活着。
  • 不同轮次任务输出不同,但语气、格式、角色一致。
  • 单轮 token 用量明显下降(你不再每轮重发 preamble 了)。

如果还是没修好

  1. 你的平台可能没暴露真正的持久通道——查文档里有没有 “custom instructions” 或 “system prompt” 支持。
  2. 有些平台会截断过长的持久指令(ChatGPT 的 custom-instruction 框每个上限约 1500 字符)——精简并排优先级。
  3. 模型可能不严格 system 优先于 user。故意写一条矛盾的 user message 测一下,看哪条赢。
  4. 在 Claude 上,再确认你用的是顶层 system 参数,而不是 messages[] 里的 role: "system" 消息。
  5. 聊天 UI 如果没有持久通道,就切到 API,那里的通道分离由请求 schema 强制。

常见问答

system prompt 和 user message 有什么区别? system / developer 通道承载持久指令——角色、输出格式、语气、硬规则——对每轮都生效。user message 是单轮的:具体请求和它的输入。持久规则放进 user message 会被后续轮次埋掉、丢失优先级。

OpenAI:2026 年 6 月该用 system 角色还是 developer 角色?developer。对 o1 之后的每个模型(含 GPT-5.5),developer 是承载指令角色的现行名字。旧的 system 角色仍被接受并映射到 developer,所以老代码还能跑,但新代码应该用 developer

为什么我 Claude 的 role: "system" 消息被忽略了? 因为 Claude API 不接受 messages[] 里的 system 消息。它接受请求上的顶层 system 参数。把规则挪到和 messages 并列的 system="...",别放进数组。

ChatGPT 现在的 Custom Instructions 在哪? 头像菜单 -> Settings -> Personalization -> Custom instructions(打开 customization 开关)。Project 也会对里面每个对话带上它自己的指令。

把规则贴进每条 user message 行不行? 基本行,但既浪费又脆弱:你每轮都要为 preamble 付 token,而且贴进去的规则可能和你之前设过的 system prompt 静默矛盾。不如在持久通道里设一次。

system 规则一定压过 user message 吗? 通常会,但不是绝对。OpenAI 强制 platform > developer > user,所以 developer 指令压过用户。强弱因模型而异,所以关键规则务必用一条矛盾的 user message 测,并把规则留在持久通道里。

预防建议

  • 默认:持久规则放 system / project / custom-instructions 通道;user message 只放本轮任务。
  • 新对话发第一条 user message 之前,先设好持久通道。
  • 定期审 project / custom instructions——过期规则会悄悄污染新任务。
  • 团队工作流把 system prompt 和代码一起版本化。
  • 想在 user message 里重打某条规则时,先反问:“这是不是该放进持久通道?”
  • API agent 工作流里,把能力声明成 tools/functions,而不是 prose。

相关阅读

标签: #Prompt #排查