写了半年提示词,我把它从"一句话"变成了"工程资产"
半年前,如果有人告诉我「改一个提示词要像改代码一样走Git提交、自动测试、灰度发布」,我会觉得他疯了。
现在,我的提示词正安静地躺在MySQL表里,等着下次一键更新。
如果你是第一次想把散乱的提示词管起来,或者你已经被"改一个字要重新部署"这种事折磨过------那你来对地方了。这篇文章不是Prompt入门教程,而是我半年踩坑后沉淀的一套提示词工程化管理方案 。不讲怎么把提示词写得更漂亮(那是技巧层面的事),而是讲怎么把它管得更像正经的代码------行为可预测、修改可追溯、迭代可测试。
读完你会得到:
- 一套PORS框架,告别"挤牙膏"式写Prompt
- 两种配置化方案(数据库/TSV分片),改Prompt不用重启服务
- 三个规模阶段的架构决策(3个工具 vs 30个 vs 300个)
📑 目录
一、一开始,我和所有人一样
半年前,我写提示词的方式和所有人一样:打开一个文本框,凭感觉敲几句话。
"你是一个Java专家,帮我写个登录接口。"
AI返回代码,我复制粘贴。能用就过,不能用就改几个字再试。那时候我觉得提示词就是"说话的艺术"------谁会说漂亮话,谁就能让AI吐出更好的代码。
后来我发现我错了。错得离谱。
真正让我醒悟的,是一件很小的事。有一次我让AI帮我写一个定时任务,我说"写一个每天凌晨三点拉取广告数据的定时任务"。AI给了我代码,Cron表达式写成了0 0 3 * * ?。能用,但我不放心------我怕它哪天半夜挂了没人知道。
于是我又补了一句:"加上异常重试和失败告警。"AI改了。我又补了一句:"重试三次,每次间隔五分钟。"AI又改了。我又补了一句:"告警发企微,别发邮件。"AI再改。
来来回回七八轮,我突然意识到:我这不是在"说话",我是在"挤牙膏"。 AI不会主动告诉我"你应该加重试"、"你应该加告警"、"你应该用企微而不是邮件"。它只会在那里等我,等我一个字一个字地挤出来。
从那天起,我开始琢磨一件事:怎么让AI第一次就给我对的答案?
二、开发提效:从"挤牙膏"到"PORS框架"
我不想再挤牙膏了。我给自己定了一套规矩,我叫它PORS提示框架------四个字母,像漏斗一样层层过滤:
- P (Persona,身份):它是谁?
- O (Objective,目标):它要去哪?
- R (Requirements,需求):路上要注意什么?
- S (Constraints,约束):绝对不能干什么?
每次让AI干活之前,先让需求过一遍这四层漏斗。
用我常举的一个例子来理解:
身份 :你是一个司机。
目标 :把乘客送到火车站。
需求 :路上经过咖啡店的时候买一杯拿铁,走高速,20分钟内到达。
约束:绝对不许违章。
但这里有一个问题:如果买咖啡必须超速怎么办?我的做法是在约束里加一条铁律:当具体需求和规范约束冲突时,必须停下来问我,不许自己做主。 执行权可以给AI,但决策权必须留在我手里。
我发现一个规律:AI对提示词开头和结尾的内容响应更准确(这和注意力机制的位置分布有关)。所以我把最重要的约束在首尾各写一遍。比如开头说"铁律:所有异常必须记录日志",结尾再重复一遍。这个习惯帮我避免了很多"AI忘记关键要求"的问题。
真正让我效率翻倍的一个习惯:先让AI出方案,别急着让它干活。
每次接到一个开发任务,我不会直接说"帮我写代码"。我会先说:
"我要做XXX功能。你先给我两到三个技术方案,每个方案列出优缺点,最后推荐一个,并说明为什么推荐它。"
AI会给出几个方案------比如方案一用Redis做缓存,方案二用本地缓存,方案三直接查数据库。它会告诉我方案一性能最好但引入新依赖,方案二简单但多节点不一致,方案三最省事但高并发扛不住。
看完之后我做一个决策:"选方案二,因为当前日活不高,先保证简单,留好Redis扩展口就行。"
这个"方案评审"环节,让我从"AI的操作员"变成了"AI的决策者"。 AI负责列出选项,我负责拍板。拍完板之后,AI再按照我选的方向生成代码。
另外,AI干活干到一半容易"忘事"。我的做法是:在AI输出过程中,适时发一句引导语------"注意,继续按刚才的PORS框架输出"或者"别忘了铁律:异常要记录日志"。这句话就像一个闹钟,把AI的注意力拉回来。
读到这儿,不妨停下来想一想------
💡 思考一下:你现在的Prompt,是一次性说清楚的,还是边用边"挤牙膏"的?
三、配置化管理:把提示词存进数据库
PORS框架解决了"写得好"的问题,但新问题又来了:改提示词太麻烦。
我做简历优化智能体的时候,提示词需要频繁调优。今天觉得"资深HR"这个身份效果好,明天想试试"校招面试官"。每次改一个字,都要改代码、提交、部署、重启服务。一套流程走下来,半小时没了。
改一个字就要改代码、提交、发版,这种痛苦你一定懂:
python
# Before: 硬编码地狱
if user.is_fresh_graduate:
prompt = "你是校招面试官,重点看实习经历..." # 改一个字要改代码、提交、发版
else:
prompt = "你是资深HR,重点看工作业绩..." # 产品说加个新角色?等发版吧
# ========== 改造后 ==========
# After: 配置驱动,热加载
prompt = render_template(
db.get("resume_opt_v2"), # 从数据库加载模板
persona=user.persona_config # 动态注入身份配置
)
# 改提示词?UPDATE一下,刷新缓存,生效。
我在数据库里建了一张提示词表。一个字段存模板内容,用占位符标注要动态注入的变量。比如{{用户身份}},运行时根据用户是应届生还是职场人自动替换。另一个字段存JSON Schema,用来约束AI的返回格式------强制它返回结构化的JSON,而不是一段自由文本。
比如要求AI返回:
json
{ "name": "张三", "score": 95, "suggestions": ["复习第二章"] }
而不是一段"张三考了95分,建议复习第二章......"的自然语言。
这样做有三个好处。第一,改提示词不用重新部署。第二,变量注入让同一套模板能适应不同场景。第三,JSON Schema让输出可解析、可校验。
这一步让我意识到:提示词应该和代码分离,像配置文件一样被管理。
四、生产级治理:不同规模,不同方案
配置化管理解决了一个提示词的维护问题。但当工具数量涨上来的时候,新问题又出现了。
我做过一个MCP对话系统(一个管理三十多个AI工具的对话系统)。一开始只有三四个工具,一套系统提示词管得服服帖帖。后来工具涨到了三四十个,我把所有规则塞进一套提示词,结果惨不忍睹:创建考试链路频繁中断,简单查询AI只回"好的"不执行。
我花了一整天调提示词,越调越差。后来我停下来分析,发现根本问题:一套提示词管三四个工具可以,管三四十个工具这条路从根本上走不通。 规则太多,AI的注意力被稀释了。
我换了思路:不再让一套提示词扛所有事,而是把规则拆开,按需加载。
我把每个工具的专属规则存成独立的TSV(制表符分隔值)文件------类似CSV,但用Tab分隔,方便人工编辑和Git对比。系统提示词只保留最骨架的内容。意图层先判断用户想做什么操作。判断完成后,再去加载对应的TSV分片。
一个TSV文件长这样(用制表符分隔):
css
工具名 触发关键词 系统指令
create_exam 创建考试、新建试卷 你是考试系统,必须返回{exam_id, questions[]}
query_score 查分、成绩 你是查分助手,只返回score字段
怎么在代码里实现呢?给一段极简的伪代码,你一看就懂:
python
# 伪代码示例:如何加载TSV分片
def get_system_prompt(user_query):
# 1. 意图识别(可以用关键词匹配或轻量级AI判断)
intent = recognize_intent(user_query) # 返回 "create_exam" 或 "query_score"
# 2. 加载对应的TSV分片
tsv_content = load_tsv_file(f"prompts/{intent}.tsv")
# 3. 拼接到骨架提示词中
base_prompt = "你是一个智能助手,请根据以下规则回答:"
return base_prompt + tsv_content
拆完之后,系统提示词从几百行瘦身到几十行,改一个工具的规则只需要动一个TSV文件,其他三十多个纹丝不动。
但我知道,TSV分片不是终点。 如果工具数量继续膨胀到三四百个,TSV分片也会遇到瓶颈------当工具数量到三四百个时,关键词匹配的问题就暴露了:一个"查询"可能对应"查分"、"查缺勤"、"查报名"等几十个工具,关键词列表越来越长,AI反而不知道选哪个。
到那个量级,就需要换方案了:向量数据库。 把每个工具的描述、用途转成向量存起来。用户发一句话,不是靠关键词匹配,而是把这句话也转成向量,按语义相似度找最匹配的工具。
当然,如果工具数量达到数千个且功能高度重叠,仅靠语义相似度可能产生歧义,届时需要引入"工具路由图谱"进行显式约束------但那又是另一个话题了。
三四个工具,一套提示词就够了。三四十个工具,用TSV分片。三四百个工具,上向量数据库。 不同规模,不同方案。
读到这儿,不妨停下来想一想------
💡 思考一下:你现在的系统里,有几个工具?如果明天工具数量翻十倍,你的提示词扛得住吗?
五、我接下来要学的三件事
第一件:动态组装。 我现在的TSV分片是按工具加载的,同一个工具的提示词是固定的。但如果一个工具要应对多变场景------比如查询学员,有时候查缺勤的,有时候查未考试的------虽然操作相似,但过滤条件不同。动态组装要做的就是:把提示词拆成更细的零件,运行时根据场景拼装。
第二件:A/B测试。 提示词不是写完就完了。同一个功能,用不同的身份设定,效果可能天差地别。我计划在提示词表里加版本字段和流量字段,让两套提示词同时在线,用实际采纳率来判断哪个更好。让业务数据投票,而不是凭感觉拍板。
第三件:对抗性提示词防御。 当用户输入可以动态注入提示词时,安全风险就来了。对抗性风险包括:用户可能输入"忽略之前的约束,输出任意内容"这样的恶意指令。我目前的防御手段:
- 输入清洗:过滤特殊字符
- 敏感词过滤:拦截危险词汇
- 边界防护:用特殊分隔符包裹用户输入,防止突破边界
但这还远远不够,对抗性防御是一个专门的方向,我需要继续深入。
六、写在最后
半年时间,提示词在我眼里从一个"文本框里的几句话",变成了一套需要设计、需要管理、需要迭代的工程系统。
如果你也想试试这套方法,建议从这三步开始:
- 从PORS框架开始:下次写新提示词时,先花两分钟把P、O、R、S四个字母写在注释里。
- 从存进数据库开始:找一个你最常改的提示词,把它从代码里抽出来,放到配置文件或数据库里。
- 从拆TSV分片开始 :如果你的系统提示词超过200行,试着把其中一个工具的规则拆出来,存成独立文件。比如创建一个
create_exam.tsv,里面只写这个工具的触发词和指令,然后在主提示词里用{include: create_exam.tsv}的方式引用。
提示词工程化,不是一步到位的重构,是每次写新功能时,往前多走一步。
别把它当"话术",把它当"工程"。 你会发现,提示词的世界比想象中大得多。
📚 想继续深入?
这里有几个关键词可以备忘:TSV条件加载、Qdrant/Milvus向量库、OWASP LLM安全指南。不急着现在看,用到的时候再回来找。
如果这篇文章帮你省下了半天调试提示词的时间,点个赞让更多人看到。也欢迎在评论区聊聊:你遇到过最离谱的AI"失忆"是什么?