系统提示词是 AI 模型在对话开始前收到的一组指令,它对之后的每条消息都生效。它设定模型的角色、读者、规则和默认的回答格式,让你不必在每条消息里重复。用户编写提示词,而系统提示词决定了模型如何回答所有这些提示词。
每个聊天应用都已经在用系统提示词。当你打开 ChatGPT、Claude 或 Gemini 时,模型已经带着背后公司给它的指令,只是聊天窗口不会显示出来。开发者在模型之上构建应用时,正是通过系统提示词,把一个通用模型变成编程辅导老师、客服助手或提交信息生成器。
系统提示词的作用
在聊天中,模型的输入是有结构的:最前面是系统提示词,然后是用户消息和助手消息交替组成的对话。系统提示词位于整个对话的最上方,所以无论聊天多长,模型每次回复时都能看到它。
这个位置让它成为存放任何应当始终有效的内容的合适地方:模型在和谁说话、可以做什么、不可以做什么、回答默认应该是什么样子。模型还被训练成在两者冲突时,更重视系统提示词而不是用户消息。但这只是一种倾向,而不是一把锁。执意要这么做的用户有时能说服模型违背系统提示词,所以它绝不应该是你唯一的防护措施。
系统提示词与用户消息对比
| 系统提示词 | 用户消息 | |
|---|---|---|
| 谁来写 | 开发者,或你在应用设置中填写 | 你,在聊天框中输入 |
| 作用范围 | 整个对话 | 一次请求 |
| 典型内容 | 角色、读者、规则、事实、默认格式 | 任务及其所需的材料 |
| 多久变一次 | 很少变 | 每一轮都变 |
| 冲突时的权重 | 通常更高 | 通常更低 |
一个实用的判断方法:如果一句话对聊天中的每条消息都成立,它就属于系统提示词;如果它只关于这一次请求,它就属于用户消息。“回答不超过 100 字”放在哪里都可以;“总结下面这封邮件”则永远是用户消息。
在哪里设置系统提示词
在 API 中,系统提示词是一个单独的输入。使用 OpenAI 的 Python 库时,它是消息列表中的第一条消息,角色为 system。OpenAI 的文档对较新的模型也把同类指令称为 developer message(开发者消息)。
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": "You are a patient Python tutor. Give hints, not full solutions."},
{"role": "user", "content": "Why does my loop stop at 9?"},
],
)
print(response.choices[0].message.content)
使用 Anthropic 的 Python 库时,它是一个顶层的 system 参数,messages 列表里只放对话内容:
import anthropic
client = anthropic.Anthropic()
MODEL = "your-model-id" # e.g. from your provider's model list
message = client.messages.create(
model=MODEL,
max_tokens=500,
system="You are a patient Python tutor. Give hints, not full solutions.",
messages=[{"role": "user", "content": "Why does my loop stop at 9?"}],
)
print(message.content[0].text)
Google 的 Gemini API 把它叫作 system instruction(系统指令)。名字不同,思路一样。
在聊天应用中,你拿不到原始的 system 字段,因为应用已经有自己的系统提示词了。你能用的是一个添加固定指令的地方,应用会替你把这些指令加进去。在 ChatGPT 中,这些是自定义指令、项目指令和自定义 GPT 的指令。在 Claude 中,是项目指令和个人资料里的偏好设置。在 Gemini 中,是 Gem 的指令。各个应用究竟如何把你的文字和它自己的指令合并,并没有完整的文档说明,但在实际使用中,它们的表现就像是你和应用共用的一个系统提示词。
系统提示词里该写什么
- 角色和读者。 “你是一位辅导老师,学生是刚学编程一个月的新手”决定了每个回答的用词和难度。角色提示讲了角色能改变什么、不能改变什么。
- 每一轮都适用的规则。 模型必须始终做或绝不能做的事:给提示而不是完整答案,只回答关于产品的问题,用用户的语言回复。
- 不确定时怎么办。 “如果下面的事实里没有答案,就说你不知道”比笼统的一句“要准确”更能防止编造答案。
- 模型需要的事实。 产品细节、政策、当前日期、用户的套餐。模型无法凭训练数据回答关于你的应用的问题。
- 默认格式。 长度、结构、是否使用代码块。如果是程序读取输出,就给出确切的结构;见结构化输出。
什么不该写
机密信息。 系统提示词并不是私密的。用户可以要求模型复述它的指令,或者诱导它说出来,而模型并不能可靠地拒绝。API 密钥、密码以及任何你不希望公开的内容都不应该放在里面。把系统提示词当作用户总有一天可能读到的文字,并看看提示词注入,了解指令是如何被覆盖的。
某一条消息的任务。 如果系统提示词写的是“总结这篇文章”,那么聊天中的第二条消息就无处安放了。
一堆相互重叠的规则。 在不同时间写下的二十条规则往往会相互矛盾,模型只能猜哪一条优先。把规则分组,删掉重复的,并在两条规则可能冲突时说明优先级。
凑字数的内容。 系统提示词会随每次请求重新发送,并占用模型的上下文窗口,所以每一段多余的文字,每一轮都要为它付出代价。
系统提示词示例
第一个示例是一个产品助手。系统提示词设定了范围、长度,以及遇到答不了的问题时的规则,并带上了模型需要的事实。在标签页之间切换,给它发送不同的消息:同一个系统提示词决定了这三个回复。
在交易记录中打开这笔消费,点击拆分。然后选择食品杂货和家居这两个类别,分别输入属于每个类别的金额。
第二个示例是你可以放在聊天应用的自定义指令或项目指令里的那类文字。它介绍了你自己以及你希望得到什么样的回答,这样每次聊天都从这里开始。
import { readFile } from "node:fs/promises";
const config = JSON.parse(await readFile("config.json", "utf8"));
传入编码参数后,readFile 会返回一个字符串,再由 JSON.parse 把它转换成对象。顶层 await 只能在 ES 模块中使用(package.json 中设置了 "type": "module",或者使用 .mts 文件);否则就把这个调用包在一个 async 函数里。如果文件可能不存在或格式有误,就加上 try/catch。
没有系统提示词时,像“怎样读取一个 JSON 文件?”这么简短的问题,会让模型去猜你用的是什么语言,回复常常是 Python 或普通的 JavaScript,还附带一段介绍 JSON 是什么的文字。有了系统提示词,回复会选对语言和运行环境,跳过基础知识,并遵循你要求的结构。
同样的结构适用于你设置的任何助手:它服务于谁、有哪些规则、不确定时怎么办、需要哪些事实,以及回答的格式。写好之后,用一个正常的问题、一个跑题的问题和一个试图打破规则的问题来测试它,不断调整,直到三个回复都符合你的要求。
常见问题
系统提示词是什么?
系统提示词是在对话开始前交给 AI 模型的一组指令,它对之后的每条消息都生效。它通常设定角色、读者、规则和默认的回答格式。在 API 中,它是一个单独的 system 字段或消息;在聊天应用中,自定义指令和项目指令起的是同样的作用。
系统提示词和用户提示词有什么区别?
系统提示词包含对整个对话都生效的固定指令,通常由开发者编写,或在设置中一次性设定。用户提示词是一条只要求做一件事的消息。两者冲突时,模型被训练成更重视系统提示词,但这只是一种倾向,而不是保证。
能在 ChatGPT、Claude 或 Gemini 里设置系统提示词吗?
不能直接设置原始的 system 字段,但有很接近的功能。ChatGPT 有自定义指令、项目指令和自定义 GPT 的指令。Claude 有项目指令和个人资料中的偏好设置。Gemini 有 Gem 的指令。应用会把你在这些地方写的内容和它自己的系统指令合并,应用到你的对话中。
用户能看到或套出系统提示词吗?
通常可以。用户可以要求模型复述它的指令,或者诱导它说出来,而模型并不能可靠地保密。绝不要把密码、API 密钥或商业机密写进系统提示词,也不要把它当作安全防线。
系统提示词应该写多长?
能覆盖对每条消息都适用的内容就够了,不要更长。个人用的系统提示词三四句话就行。产品用的系统提示词常常有一页长,包括角色、规则、事实和输出格式。每个字都会随每次请求一起发送,所以每次也都要消耗 token。