尧图精选

系统提示词泄露归档:从Prompt工程到Agent工具调用实战

🕒 发布时间:2026/9/18 13:01:27 📁 来源:尧图网络
“system_prompts_leaks” 这个仓库我第一次撞见是在给一个客服 Agent 调提示词的第三个晚上。当时输出格式时好时坏约束写了七八条还是压不住模型自由发挥的毛病同事随手甩过来一个链接说里面归档了十几家模型的系统提示词原文让我照着看看别人是怎么写的。那天下午我把那些文件挨个读了一遍收获比翻十篇提示词工程教程都大——不是因为藏着什么独门咒语而是终于能看到真正跑在生产环境里的东西长什么样措辞有多啰嗦约束是怎么排布的哪些地方反复强调同一个意思。系统提示词泄露这个话题这几年热度一直没降过热搜上的 system_prompts 关键词隔三差五就冒一次。这个仓库干的事情说白了很朴素把散落在各个渠道的、公开可见的系统提示词文本收集起来统一命名、统一归档、标注来源和时间做成一个可以检索、可以对比、可以追溯版本的纯文本集合。它解决的是信息碎片化的问题——今天在某个技术博客看到一段明天在某个公开评测里瞥到半句后天官方文档又更新了一版靠脑子记根本记不住也做不了版本对比。这个内容适合三类人正在写生产级提示词的工程师、做 Agent 和工具调用编排的开发者、以及研究模型行为边界的技术人员。零基础也能看因为提示词本身就是自然语言不需要你会写代码才能读懂别人的思路。1. 系统提示词为什么值得被单独归档1.1 它到底是什么和用户输入有什么区别系统提示词是对话开始之前注入到上下文最前面的一段文本它不占用用户可见的对话回合但会持续影响之后每一轮的输出。你可以把它想成餐厅给新服务员的岗位手册客人看不到这本手册但服务员的每一句话、每一个动作都被它塑形。用户输入是“我要一份少辣的”系统提示词则是“你是本店服务员遇到菜品问题先道歉再给方案不要主动推荐酒水结账话术统一用这句”。技术上讲它处在消息序列的最前面角色通常是 system。它的作用是给模型设定身份、能力边界、输出格式、拒答策略、工具调用规范和风格基调。很多人以为它只是一句“你是一个有用的助手”实际生产环境里动辄两三千字结构比不少产品需求文档还严谨。1.2 这些文本是怎么进入公共视野的这一点必须先说清楚归档仓库处理的都是公开可获取的文本。常见来源有几类。一是厂商主动公开部分开源模型会把系统提示词连同模型卡一起发布或者在官方文档里给出推荐模板。二是开发者在使用公开接口时从自己的请求与响应调试信息中观察到的内容这部分属于自己账号的正常可见数据。三是模型在特定情况下复述自身指令被记录下来并公开讨论。四是研究者做公开评测时整理的对照材料。这些渠道的共同点是不涉及任何越权访问、不涉及对他人系统的不当操作全部发生在公开讨论的范围内。仓库的价值不在于“搞到了什么机密”而在于把本来零散、格式混乱、没有版本标记的公开文本整理成了可用的研究材料。我做类似归档的时候有一条铁律只收已经有人在公开场合贴出来的内容不去做任何主动索取的尝试。这条线守住了整个项目就是干净的技术整理工作。1.3 归档这件事本身的研究价值有人会问提示词不就是一段大白话抄下来有什么用。实际用起来你会发现三个价值点。第一是对照价值。同一个任务不同模型的系统提示词写法差异极大有的用大段自然语言描述有的用类似伪代码的标签嵌套有的靠大量示例堆出行为边界。把十几份放在一起看你很快能总结出哪些写法是共通的、哪些是特定模型才需要的。第二是版本追踪价值。模型的系统提示词会随版本迭代悄悄改动可能只是加了一句关于幻觉的处理说明或者调整了拒答的措辞。单看一版看不出什么把历史版本 diff 一下改动点一目了然这比读厂商的更新日志直观得多。第三是回归测试价值。你如果在自己的产品里参考了某段约束写法后来模型升级导致行为变了你就可以把归档里的新旧两版拿来对比判断是提示词变了还是模型本身变了排查方向会清晰很多。我踩过一次坑某个格式约束突然失效排查了半天以为是自己的解析代码出问题最后对比归档才发现是模型侧调整了输出习惯跟我的代码毫无关系。2. 一个可维护的归档仓库该怎么组织2.1 目录结构三条主线并行最简单也最耐用的组织方式是按“厂商 / 模型 / 版本”三层走但这三层不能生硬地全部铺开否则目录会深到点不动。我的做法是分成两条线按来源线和按能力线。按来源线就是vendors/vendor/model/date.md适合追踪某个具体模型的演进。按能力线是topics/topic/vendor-model.md比如topics/tool-calling/、topics/refusal/、topics/output-format/适合做横向对比。两条线指向同一批文件时用软链接或者生成脚本维护不要手动复制两份否则改一处忘一处半年后自己都不知道哪份是新的。archive/ ├── vendors/ │ ├── vendor-a/ │ │ └── model-x/ │ │ ├── 2024-06-01.md │ │ └── 2024-11-15.md │ └── vendor-b/ ├── topics/ │ ├── tool-calling/ │ ├── refusal-policy/ │ └── output-format/ ├── meta/ │ └── index.yaml └── tools/ ├── diff_prompt.py └── normalize.py2.2 文件命名与元数据别偷懒文件名带日期是底线格式统一用YYYY-MM-DD.md同一天有多个版本就加后缀-01、-02。不要用“最新版”“最终版”“真的最终版”这种命名三个月后你自己都会崩溃。元数据建议放在文件头部的 YAML 区块里字段我建议至少这几个来源类型、来源链接或出处描述、采集日期、文本完整度评级、是否为官方公开、备注。完整度评级很关键因为公开渠道拿到的往往是片段A 级是完整原文B 级是接近完整但有缺漏C 级是片段摘录。不标注的话半年后你分不清这段是不是被截断过。--- model: model-x vendor: vendor-a collected_at: 2024-11-15 source_type: official_doc # official_doc | dev_debug | public_discussion | research source_ref: 官方文档 3.2 节推荐配置 completeness: B verified: true notes: 工具调用段落疑似有省略与上一版对比缺失两行 ---2.3 为什么用纯文本而不是数据库我试过用 SQLite 存也试过做个小网页最后都退回到纯文本加 YAML 头部。原因有三个。一是可 diff。提示词研究最核心的操作就是版本对比纯文本天然适合 diffgit 一条命令就能看改动数据库里存 blob 你还要写脚本导出再比。二是可移植。纯文本不依赖任何运行时五年后你打开就是能读的内容数据库格式可能早就没人维护了。三是便于协作。别人想贡献一份材料提交一个 md 文件就行不需要理解你的 schema。数据库适合做查询统计但作为长期归档的载体纯文本的寿命长得多。我的做法是纯文本作为唯一真源需要检索的时候临时生成一个 SQLite 索引文件索引不进版本库。2.4 采集与去重的实际操作采集环节真正花时间的不是拿数据而是去重和判定来源可信度。同一个模型的同一版提示词可能被十几个人转发措辞有细微差异你得判断哪一版是原始出处。我的做法是先按内容相似度聚类用简单的文本相似度算法比如归一化后的编辑距离或者 shingle 哈希相似度超过阈值的归为一组然后在组内按来源可靠性排序取最可信的那一份作为主版本其余的作为“佐证来源”记在元数据里。归一化这一步也别省。全角半角、多余空行、Markdown 转义符号、复制粘贴带进来的零宽字符这些都会让 diff 结果充满噪声。写一个 normalize 脚本统一换行符、去掉行尾空格、把连续空行压成一个diff 的可读性能提升非常明显。import re, unicodedata def normalize(text: str) - str: text unicodedata.normalize(NFKC, text) text text.replace(\r\n, \n).replace(\r, \n) text re.sub(r[\u200b-\u200f\u202a-\u202e], , text) # 去零宽及方向控制符 text \n.join(line.rstrip() for line in text.split(\n)) text re.sub(r\n{3,}, \n\n, text) return text.strip() \n3. 系统提示词的结构骨架拆解3.1 通用骨架五个部分把十几份提示词叠在一起看结构上的共性非常明显基本跳不出这五块身份设定、能力范围、行为约束、工具与外部能力、输出格式。顺序上大多也是这个次序因为模型对靠前内容的注意力更强把身份和边界放前面是合理的选择。身份设定通常一两句话解决写清楚角色、服务对象、总体基调。能力范围说明模型能处理什么、不能处理什么。行为约束是最长的一块包含安全策略、拒答条件、敏感话题处理、不确定时的表达方式。工具部分在支持函数调用的模型里会详细描述每个工具的用途、参数、调用时机、失败重试策略。输出格式规定语言、语气、长度、结构化格式、是否允许使用列表等。一个值得注意的细节是越靠后的内容越容易被长对话稀释。所以如果你有一个绝对不能被违反的约束不要写在末尾放在身份设定之后紧接着的位置并且用明确的强约束措辞重复一次。我在自己的产品提示词里就吃过这个亏把“不要编造订单号”放在了倒数第二段长对话五六轮之后模型就开始瞎编了往前挪并加了强调之后才稳住。3.2 工具调用段落为什么最难写工具调用是整份提示词里最容易写崩的部分。难点在于模型需要同时判断三件事该不该调用、调哪个、参数怎么填。这三件事只要有一件模糊就会出现该调不调、乱调、参数填错的情况。好的写法是把触发条件写具体而不是写“需要时调用”。比如不要写“查询天气时调用天气工具”而要写清楚“当用户询问指定城市的实时天气、温度、降水概率时调用当用户只问是否需要带伞但未指定城市时先询问城市”。把否定条件也写上比只写肯定条件有效得多。参数描述里要标明单位、格式、默认值以及缺失时该怎么办。还有一个经验工具数量超过十个之后提示词里的工具描述要分组加一句总览比如“你手上有三类工具数据查询类、内容生成类、流程操作类”让模型先做一级分类再选具体工具。我实测下来这一个小改动工具选错的概率能降不少因为模型不需要在一长串列表里做扁平匹配了。3.3 拒答与安全策略的写法差异不同来源的提示词在这一块差异最大。有的写得非常具体逐条列举场景有的只给原则让模型自行判断有的给判定优先级比如“安全优先于有用性”。这三种写法各有取舍。逐条列举的好处是行为可预测坏处是永远列不全遇到列表外的情况模型就不知道怎么办。给原则的好处是泛化能力强坏处是不同模型的判断尺度差异大同样一句原则在不同模型上表现完全不同。给优先级的写法我比较推荐它相当于给模型一个决策函数遇到冲突时有明确的裁决顺序。实际操作里我倾向于组合先用一段讲原则和优先级再用几条具体例子说明边界在哪最后加一句兜底——“遇到无法确定是否合适的情况选择保守回答并说明原因”。这句兜底能省掉很多麻烦因为模型在模糊地带的默认行为往往偏激进明确告诉它模糊时保守效果好很多。3.4 输出格式约束的常见手法格式约束的写法大致分三档。最弱的是自然语言描述“回答尽量简洁用要点形式”。中间档是给出格式模板直接示范一个例子。最强的是给出格式规范加校验规则甚至给出错误示例。实测下来只要你对格式有硬性要求就必须用中间档以上。光说“用 JSON 输出”是不够的模型会在 JSON 前后加解释文字。你要给出确切的结构标明哪些字段必填、类型是什么、缺失时填什么并且明确说“只输出 JSON不要有任何其他文字”。一个很实用的小技巧是给出负面示例。比如“不要输出{result: ...}这种包裹结构直接输出数组”。负面示例对抑制模型的习惯性行为特别有效因为很多格式问题不是模型不懂而是它的默认倾向如此你需要明确把它按下去。约束强度写法适用场景稳定性弱自然语言描述风格闲聊、开放式问答一般中给模板加示例结构化输出、报告生成较好强规范正反示例校验规则接口返回值、机器解析好组合原则优先级兜底安全策略、冲突裁决好4. 从零搭一个同类归档仓库的完整流程4.1 环境与工具准备这个项目对环境的依赖极低一个 git 仓库加 Python 就够。我建议的工具组合是git 做版本管理它本身就是最好的 diff 工具Python 3.10 以上做文本处理difflib或git diff --word-diff做对比pyyaml处理元数据。如果要做相似度聚类装个rapidfuzz就够不需要上重型 NLP 库。目录初始化很直接mkdir -p archive/{vendors,topics,meta,tools} cd archive git init python -m venv .venv source .venv/bin/activate pip install pyyaml rapidfuzz有一件事要提前定好编码统一 UTF-8换行统一 LF。加一个.gitattributes把* textauto eollf写进去能避免跨平台协作时的整文件 diff 噪声这个坑我踩过两个人克隆下来一提交整个文件每一行都显示改动实际上只是换行符差异。4.2 采集记录与来源标注采集环节我的做法是维护一个inbox/目录所有待整理的材料先丢进去文件名随便但必须配一个同名的.meta文件写明出处。整理的时候再搬到正式目录并规范化命名。这个两步走的好处是采集和整理解耦你不会因为命名犹豫而中断采集也不会因为采集太随意而丢失来源信息。来源标注我要求自己写清楚三件事从哪看到的、什么时候看到的、当时的上下文是什么。第三点很多人会忽略但它其实最有价值。同一段文本出现在官方文档里和出现在技术讨论帖里可信度完全不同。记下上下文半年后你回来看才知道该怎么权衡。整理阶段要做的判定是这份材料和已有的哪一份是同一个东西判定标准不能只看开头几句要从中间随机抽几段比对。有些转发版本会被人为删改开头一致但中间被截断了只看开头会误判为重复。4.3 清洗、脱敏与格式统一清洗环节除了前面说的归一化还有一件必须做的事脱敏。公开渠道拿到的文本里偶尔会夹带个人标识、内部项目代号、具体的账号信息、测试用的真实数据片段。这些内容不该进归档库。我的做法是过一遍正则把邮箱、手机号、长串数字 ID、疑似密钥的字符串替换成占位符并在元数据里注明“已脱敏”。[EMAIL] 匹配常见邮箱格式 [PHONE] 匹配连续 11 位数字含常见分隔符 [ID_LIKE] 匹配 16 位以上连续字母数字混合串 [URL] 匹配 http(s) 链接保留域名路径截断这一步不能省也不要想当然觉得“公开的东西不会有隐私问题”。公开帖子里带出测试账号的情况我见过不止一次归档的时候顺手清掉是对自己也是对别人的负责。格式统一方面我建议保留原文的换行和缩进不要自作聪明重新排版。因为缩进和分行本身可能是结构信息的一部分特别是那些用缩进层级的伪代码式提示词。归一化只处理空白噪声不改变有意义的排版。4.4 版本对比脚本怎么写对比是归档库的核心使用场景。git diff已经很强了但提示词的改动往往是“一句话被拆成两句”“同义词替换”按行 diff 会把整段标红很难读。用词级 diff 效果好得多。git diff --word-diffcolor --word-diff-regex[^[:space:]] \ vendors/vendor-a/model-x/2024-06-01.md \ vendors/vendor-a/model-x/2024-11-15.md如果想做得更细可以写个 Python 脚本做句子级对齐把改动归类成“新增句”“删除句”“改写句”输出一份可读的变更摘要。我做这个脚本的时候加了一个语义过滤把“的/了/并且/以及”这类词替换成统一符号再比对这样纯语序调整就不会被当成实质改动能大幅降低误报。import difflib, re STOP {的, 了, 并且, 以及, 同时, 此外} def split_sents(t: str): parts re.split(r(?[。\n]), t) return [p.strip() for p in parts if p.strip()] def canon(s: str): for w in STOP: s s.replace(w, ) return re.sub(r\s, , s) def summarize(old: str, new: str): a, b split_sents(old), split_sents(new) sm difflib.SequenceMatcher(None, [canon(x) for x in a], [canon(x) for x in b]) out [] for tag, i1, i2, j1, j2 in sm.get_opcodes(): if tag equal: continue if tag delete: out.append((删除, \n.join(a[i1:i2]))) elif tag insert: out.append((新增, \n.join(b[j1:j2]))) else: out.append((改写, \n.join(a[i1:i2]) \n-\n \n.join(b[j1:j2]))) return out这个脚本跑出来的结果比裸 diff 好读太多尤其是对比两个长度相近的版本时几秒钟就能定位到实质变化。4.5 自动化把重复劳动压掉归档项目最容易死在中途原因是每次更新都要手动做一遍归一化、命名、加元数据、更新索引。我后来写了三个小脚本串成一条流水线ingest.py负责把 inbox 里的文件归一化并落位index.py扫描全库生成元数据索引和统计报告check.py校验每个文件是否都有完整元数据、命名是否合规。check.py尤其值得写它相当于仓库的体检工具。跑一遍就知道有没有漏元数据的文件、有没有重复内容、有没有超过半年没更新的条目。这几项检查都是人工翻不动的自动化之后维护成本一下降到很低。python tools/check.py --strict # 输出示例 # [WARN] vendors/vendor-b/model-y/2024-03-02.md 缺少 completeness 字段 # [ERROR] 命名不合规: vendors/vendor-b/model-y/latest.md # [INFO] topics/tool-calling/ 下 3 个条目超过 180 天未复核5. 这些提示词里真正能抄走的东西5.1 结构化标签的实际用法很多生产级提示词会用类似 XML 的标签把不同职能的段落包起来比如把行为准则包在一个标签里把输出格式包在另一个标签里。这么做不是为了好看而是给模型提供明确的分段信号减少跨段落串味。实际用的时候有两个细节要注意。一是标签名要有语义不要用无意义的缩写模型对标签名的语义是有感知的。二是一份提示词里标签层级不要太深两层足够三层以上收益递减而且容易让模型混淆边界。我自己试过四层嵌套结果模型经常把子标签里的约束当成全局约束反而变差。另一个常见手法是用重复来强化。同一约束在两个不同位置用不同措辞说一遍比在同一个位置加两个感叹号有效得多。这不是玄学是注意力机制的实际体现。5.2 约束条件的排序与优先级约束多了必然冲突冲突了模型就随机选一个执行这是最头疼的情况。解决办法是显式给出优先级并且把优先级放在约束列表的最前面而不是指望模型自己排序。我常用的模式是先声明一个裁决顺序然后再列具体约束。措辞大概是“当以下规则冲突时按此顺序执行安全规则、事实准确性、格式要求、风格偏好”。有了这一句模型在冲突时的行为会稳定很多。这个写法我是从归档里几份提示词对比中总结出来的它们在结构上的共同点就是在开头给了明确的裁决顺序。还有一点约束的数量要克制。我做过一个小实验同样任务下约束从 5 条加到 15 条前 5 条的执行率基本不变后面几条的执行率明显下滑。所以不要指望靠堆条目解决问题把最重要的几条写清楚剩下的交给示例。5.3 少样本示例该放几个示例数量不是越多越好。我的经验是三到五个质量高的示例胜过十几个质量参差的。挑选原则是每个示例覆盖一个不同的边界情况而不是同一类情况的多个变体。示例的格式要严格统一包括标点和换行。有一份归档里的提示词示例格式完全一致模型输出格式的稳定性明显更好另一份的示例格式前后不一致结果输出也跟着飘。这个细节很容易被忽略但它影响很大。另外示例里的内容要避免和真实场景太相似。如果你的示例本身包含容易被模型记住的具体实体模型在遇到新问题时可能直接照搬示例内容。我遇到过一次示例里用了某个具体的城市名结果用户问别的城市时模型也回那个城市排查了半天才发现是示例污染。5.4 不确定性与幻觉处理这一块是归档库里最值得细读的部分。不同提示词处理“不知道”的方式很不一样。有的要求模型在无把握时明确说明有的要求给出置信度有的要求先给答案再给不确定性提示。我的实践是分场景处理。事实类问题要求明确说“我不确定”并给出已知的部分操作类问题要求先确认关键参数再动手创作类问题不要求声明不确定性。一刀切会让创作类回答变得很啰嗦。还有一招很实用要求模型在给出结论前先列出依据。这个写法的效果是把推理过程外显化模型编造的时候更容易自相矛盾你自己也更容易发现问题。我把它加进客服提示词之后编造政策的概率降了一截。6. 常见问题与排查实录6.1 问题速查表现象可能原因排查动作处理方式格式约束时好时坏约束位置太靠后被长对话稀释看失败案例发生在第几轮把格式约束前移加负面示例该调工具却不调触发条件写得太笼统检查是否只写了肯定条件补否定条件和缺失参数时的处理输出语言混用示例里有另一种语言逐条检查示例语言一致性统一示例语言明确指定输出语言长对话后期跑偏关键约束被上下文淹没对比前几轮和后期输出差异缩短提示词核心约束重复一次照搬示例内容示例含具体实体检查示例是否过于贴近真实场景示例改用中性占位内容相似度聚类误合并只看开头判定重复从中间抽段比对改用全文 shingle 哈希6.2 整理过程中踩过的坑第一个坑是过度整理。刚开始我总觉得要把原文排版弄得整整齐齐结果把一些有意义的缩进和分行也改了后来对比的时候发现结构信息丢了。现在我的原则是只清理空白噪声和不可见字符不动有语义的排版。第二个坑是元数据补记。有几份材料我采集的时候想着“这个来源很清楚不用记”过了两个月回头看完全想不起来从哪弄的只能整份标记为低可信度。现在我的规矩是当场必记宁可写得啰嗦。第三个坑是版本判定过于自信。有两版文本我判定为同一版后来发现中间有一处关键差异导致基于这份对比得出的结论是错的。教训是判定同一版本至少要抽三处位置比对不要只看头尾。第四个坑是编码问题。有一次合并别人的贡献他的编辑器保存成了带 BOM 的 UTF-8导致整个文件首行多出一个不可见字符diff 结果全乱。加了.gitattributes和校验脚本之后这类问题基本绝迹。6.3 使用这些提示词时的三个误区第一个误区是直接照抄。归档里的提示词是为特定模型、特定产品形态写的直接搬到自己的场景大概率水土不服。正确用法是看懂它的结构思路然后按自己的需求重写。第二个误区是追求长度。看到别人的提示词两千字自己也堆到两千字结果里面一半是重复和废话反而稀释了核心约束。长度应该是需求的结果不是目标。第三个误区是一次改多处。调提示词最忌讳的就是同时改五六个地方然后测效果出了问题你不知道是哪一处导致的。我的做法是一次只改一个变量改完跑一组固定用例对比稳了再动下一处。这套流程慢但回滚成本低长期看反而快。6.4 维护一个长期归档的节奏感归档最容易犯的错是开头猛冲收了几十份之后就不管了。我现在固定每月花两个小时做一次复核跑一遍校验脚本看看有没有新出的公开材料需要补把超过半年的条目重新确认一次是否仍然有效。这个频率不高但能保证库是活的。另外建议给仓库写一份简短的贡献说明讲清楚来源要求、命名规范、元数据字段含义。别人想加材料的时候不用问你照着说明做就行。这事看起来是给别人方便实际上是给自己省事——没有规范的贡献比没有贡献更麻烦你还得回头收拾。我个人在实际操作中的体会是这类归档项目的价值会随时间复利增长。刚建的时候只有几十份文本看不出什么一年之后有了跨版本的对比材料你就能看出模型行为的演进轨迹也能总结出哪些提示词写法是长期稳定的、哪些只是某一代的权宜之计。这种判断力不是读教程能得到的只能靠一份份材料攒出来。如果你也在写生产级提示词我建议先别急着抄先花一个下午把归档里的十几份材料通读一遍重点看它们的结构安排和约束排序读完之后再回头看自己的提示词很多原本觉得没问题的地方会突然变得刺眼。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →