别只调提示词:让 AI 功能可靠的四道护栏
AI 功能能演示,不等于能交付。把不确定性留在边界,产品才敢让用户真正使用。
做 AI 功能时,最容易掉进一个舒服的循环:改一句提示词,结果好一点;再改一句,Demo 又亮眼一点。
但真正上线后,用户不会只给你一段干净的示例文本。他会贴半截聊天记录、混进错别字、在高峰期连续点击,还会把模型的回答当成事实。要让 AI 功能可靠,关键不是把回答调得更像人,而是把不确定性关在产品边界里。
这件事通常靠四道护栏:明确任务、约束输入输出、给答案配依据,以及让高风险动作可撤回。它们不花哨,却比再试十个提示词更值得先做。
第一件事:把“聊天”改成一个具体任务
“帮我分析这份数据”是一个很难验收的需求。模型写得再流畅,也没人知道它到底完成了什么。
不如先把任务缩到可判断的范围。例如客服场景里,不要让模型“处理投诉”,而是让它完成下面这件事:
根据工单内容,生成一份回复草稿;标出情绪等级;列出需要人工确认的承诺。
这样一来,输出有没有用就很清楚了:回复能不能发、情绪判断是否离谱、有没有把退款或赔偿说得太满。
一个实用的判断方法是:如果你无法写出“什么情况下算失败”,任务还没有定义好。
对于刚开始做 AI 的小团队,我更倾向从“建议”而非“代办”起步。让它写摘要、找线索、生成草稿;付款、删数据、改权限这类动作,先由人确认。这不是保守,是给后续迭代留空间。
第二件事:让程序检查模型,而不是相信模型
模型输出的是文本,业务需要的却往往是结构化数据。两者之间必须有一层校验。
比如,我们希望模型给出工单优先级、回复草稿和风险项。与其从一大段自然语言里“猜”,不如要求固定字段,再用代码验证:
import { z } from "zod";
const replyDraft = z.object({
priority: z.enum(["low", "normal", "high"]),
draft: z.string().min(20).max(800),
risks: z.array(z.string()).max(3),
needsHumanReview: z.boolean(),
});
const result = replyDraft.safeParse(modelOutput);
if (!result.success) {
// 记录原始结果,并转为人工处理或一次受限重试
}
这里有个常被忽略的细节:校验失败不代表“再问一次直到成功”。重试次数要有限,也要记录失败内容。否则模型偶尔给出一个格式正确、内容却不可靠的答案,你反而更难发现问题。
输入也该有边界。用户粘贴的内容过长时,先截断并提示;涉及个人信息时,先脱敏;来自网页、邮件或文档的文字,默认都不是指令。把其中的“忽略之前要求”原样交给模型,很容易让外部内容干扰任务。
第三件事:让答案带着来处
AI 最让人不安的时刻,通常不是它说“我不知道”,而是它用非常肯定的语气说错了。
如果回答依赖知识库、订单记录或内部文档,界面上应该能看到它参考了什么。一个好用的回答可以长这样:
- 结论:该订单尚未出库,可以修改收货地址。
- 依据:订单状态为“待发货”;配送规则第 3.2 节。
- 不确定处:系统未显示仓库是否已拣货,建议人工确认。
这会让用户更愿意使用,也会让团队排错容易得多。发现问题时,你能分清是检索错了、文档过期了,还是模型理解偏了。
不要把“引用”只做成界面装饰。保存请求 ID、使用的资料版本、模型版本和耗时。出了问题,至少能把一次回答还原出来;否则线上复盘只会变成“刚才好像是这样”。
第四件事:把高风险动作放进确认层
有些操作一旦执行,就不是“回答错了”那么简单:发邮件、退款、删除文件、修改权限。
这类能力可以交给 AI 提议,但别直接交给它执行。一个够用的流程是:
- 模型生成动作草稿和理由。
- 系统展示影响范围,例如收件人、金额、将删除的文件数。
- 用户或有权限的人确认后,后端才真正调用工具。
- 记录操作人、参数和结果;能撤回的就提供撤回窗口。
这层确认会多一次点击,却能省下不少解释、补救和信任成本。尤其在产品早期,宁可让流程显得笨一点,也别让错误跑得太快。
从哪里开始:先做一个“可复盘”的小闭环
如果只能选一件事做,我建议先建立一组真实样本:二三十条用户会遇到的输入,包含正常情况、模糊表达、空内容和明显越界的请求。每次改提示词、模型或检索策略,都跑一遍。
不需要一开始就搭复杂的评测平台。一个版本号、一张表、几项人工评分已经足够:是否完成任务、是否有依据、是否越权、是否需要人工接手。
AI 产品的成熟,不在于它偶尔说出多惊艳的话,而在于它在普通的一天里,持续给出可用、可解释、出了问题也找得到原因的结果。先把这四道护栏立住,再去追求更聪明的回答,顺序会舒服很多。