1. 这篇文章真正要解决的问题
做 AI 客服,最让开发团队头疼的问题往往不是模型不够聪明,而是模型“太聪明”。
所谓“太聪明”,是指大模型在回答顾客问题时,会根据它的“理解”自由发挥。比如客人问“发货了吗”,模型可能会自己推测“预计明天发货”;客人问“能便宜点吗”,模型可能会说“可以给你 9 折优惠”;更严重一点,客人问“怎么投诉你们客服”,模型甚至会一本正经道歉并承诺“赔偿 50 元优惠券”。
这些回复如果出现在知乎回答里,没有任何问题。但出现在真实电商客服、银行客服、政务客服里,每一个都是事故。
为什么?因为客服场景的核心要求不是“会说话”,而是“按规矩说话”。哪些话能说,哪些话不能说,哪些优惠能承诺,哪些政策不能变通,这些不是模型能自己判断的,而是业务方用白纸黑字定下来的规则。业务规则是刚性的,模型输出是概率性的,两者天然存在矛盾。
本文想解决的问题就是:如何在不放弃大模型理解能力的前提下,让 AI 客服严格按业务规则做事,而不是自由发挥。
我会从技术架构角度,把 AI 客服的“自由发挥”问题拆解为提示词约束、结构化输出、工作流编排、模型微调四个层级,并给出一个可以直接运行的代码示例。
如果你是正在做 AI Agent、AI 客服、RAG 项目的开发者,这篇文章会帮你理清一个关键判断:很多时候,问题不在模型,而在你根本没有给模型建立“不可越界”的机制。
2. 基础概念:为什么大模型天生不适合做客服
要理解 AI 客服为什么会“自由发挥”,先要理解大模型的工作机制。
2.1 大模型是“写手”,不是“执行器”
大模型本质是一个概率语言模型。它生成每一个词,都是基于前文语境计算出来的“最可能的下一个词”。
这个机制决定了两个特点:
第一,它没有“规则引擎”概念。你说“如果订单包含生鲜商品,则不支持退款”,它会理解这句话的语义,但在生成回复时,它不会像代码一样判断“订单是否包含生鲜”,它只会根据自己的语言习惯写一段看起来合理的回复。
第二,它的输出天生不稳定。同一个问题,换一种问法,或者换一次采样参数,结果可能不同。这在写文案、做翻译时是优势,但在客服场景是致命伤——客服回复必须稳定,必须可预期,必须能追责。
2.2 AI 客服的系统边界:理解、决策、表达
一个标准的 AI 客服系统,可以拆成三部分:
| 环节 | 责任 | 由什么实现 |
|---|---|---|
| 理解 | 分析用户意图、抽取关键信息 | 大模型 + 意图识别 + 实体抽取 |
| 决策 | 判断该做什么、能做什么、做到什么程度 | 业务规则 / 工作流 / 状态机 |
| 表达 | 把决策结果转成自然语言 | 大模型 / 模板 |
很多团队只做了第一层和第三层,把“决策”也交给了大模型。于是模型既当运动员,又当裁判员,结果就是自由发挥。
正确的设计思路是:让大模型做理解和表达,让规则来做决策。这句话是整个 AI 客服架构的核心。
2.3 一个容易混淆的问题:这属于提示词工程、RAG 还是微调?
现在网上有很多人问:AI 客服的约束做法属于提示词工程、RAG 检索,还是模型微调?
我的判断是:它不是一个单纯的技术层级问题,而是一个工程架构问题。
- 如果你只是把规则写在 System Prompt 里,告诉模型“不要承诺优惠”,这是提示词工程。
- 如果你把规则文档存入向量数据库,让模型在回答时检索相关规则,这是 RAG。
- 如果你准备了一批规则对话样本,让模型学着输出,这是微调。
- 如果你建了一个规则引擎,先判断用户请求能做什么,再让模型生成话术,这是 Agent 编排。
真实项目中,这四个层级往往被组合使用。但如果你只能选一个优先做,我的建议始终是:先做 Agent 编排和规则引擎,再考虑优化提示词,最后才考虑微调。
原因很简单:规则引擎的方案确定性强、可测试、可回滚,而模型方案无论怎么调,都无法 100% 保证边界。
3. 让 AI 按规则做事的四种方案对比
明确了目标之后,我们需要选型。下面这四种方案是 AI 客服项目中常见的约束手段。
3.1 方案一:提示词强约束
在 System Prompt 里写明规则,是零成本、见效最快的方式。
你是XX电商平台的客服助手。请严格遵守以下规则: 1. 不得承诺用户无法确认的优惠。 2. 退换货政策以售后页面为准,不得自行延长退货时间。 3. 不讨论竞品,不评价其他平台。 4. 用户情绪激动时,先安抚后解释,不反击。优点:实现简单,迭代快。
缺点:大模型对 prompt 的遵循是概率性的。你可以把规则写得很细,但模型仍然可能在某些边角场景“忘了”规则。更麻烦的是,提示词注入攻击可以诱导模型忽略上述规则。
结论:适合做第一道防线,不能做唯一防线。
3.2 方案二:RAG 检索业务规则
把规则文档向量化,用户提问时检索相关规则,把规则片段拼入 prompt 上下文。
这种方法比单纯提示词约束更有依据——模型能看到具体的政策条文。但它有一个大坑:检索到不一定意味着遵循。
如果你的知识库里有 100 条规则,检索召回 5 条相关规则,模型可能会基于这 5 条规则进行“合理推导”。推导出来的结果,可能并不符合规则的真正意图。
结论:RAG 适合提供“参考资料”,不适合直接作为“可执行规则”。
3.3 方案三:工作流与规则引擎
这是最接近传统软件工程的方案。系统先判断用户的意图,然后进入预定的工作流节点,每个节点用 if-else、状态机、决策表来执行规则,最后再把结果交给大模型生成话术。
这才是 AI Agent 正确的落地姿势。
Agent 这个词被过度包装了。在工程层面,一个 Agent 就是一个“能调用工具和流程的决策执行器”,它不等于“让模型自由地决定一切”。把 Agent 做“僵”一点,反而更可靠。
结论:这是最值得投入、最可控的方案。
3.4 方案四:模型微调
微调适合让模型学会某种固定的表达风格,或记住特定领域的术语,但它不适合用来“固化”复杂的业务规则。
原因有两点:
- 微调后模型依然存在随机性,业务规则必须具有确定性。
- 业务规则经常变。微调一次要准备数据、跑训练、评估、上线,周期太长。规则改一个字段,难道要重新训一次模型吗?
结论:微调可以作为辅助,但永远不能替代规则引擎。
3.5 方案对比
| 方案 | 确定性 | 实现成本 | 更新维护 | 适用场景 |
|---|---|---|---|---|
| 提示词约束 | 低 | 低 | 简单 | 第一道防线、话术风格 |
| RAG 检索 | 中 | 中 | 简单 | 提供规则/知识参考 |
| 工作流/规则引擎 | 高 | 中高 | 需要发版/配置中心 | 退款、退换货、物流、投诉 |
| 模型微调 | 中 | 高 | 复杂 | 领域术语、表达风格 |
核心判断:AI 客服的可靠性,70% 来自架构,30% 来自模型。规则引擎才是让 AI 不自由发挥的关键。
4. 实战设计:电商客服拒绝承诺超范围优惠
接下来,我们用电商场景来做一个完整示例。
业务背景:一个电商平台客服 Agent。用户咨询订单退货。运营团队规定:
- 普通商品支持 7 天无理由退货。
- 生鲜食品不支持无理由退货。
- 会员等级为 V3 及以上的用户,可享受免费上门取件。
- 客服客服人员(即 AI)无权承诺任何额外补偿,包括优惠券、现金赔偿。
问题:用户问“我收到的东西坏了,能退吗?能补偿我 20 元吗?”
如果直接用大模型回答,它很可能说“可以给您申请 20 元补偿”,这就是越权。
我们需要设计一个规则驱动的 AI 客服 Agent,让大模型在权限范围内说话。
4.1 Agent 内部模块划分
一个简单的客服 Agent 包含以下模块:
- 意图识别模块
- 订单信息查询模块
- 规则决策模块
- 话术生成模块
用户输入 ↓ [意图识别:退货/换货/退款/物流/投诉/优惠] ↓ [查询订单信息:订单号、商品类型、会员等级] ↓ [规则决策:判断是否可退货,是否有补偿权限] ↓ [话术生成:基于决策结果生成自然语言回复]关键点:大模型只负责意图识别和话术生成,规则决策由代码完成。
4.2 业务规则定义
为了演示清晰,我们用一个 JSON 文件定义规则。
{ "return_policy": { "normal_product": { "support_return": true, "return_days": 7 }, "fresh_food": { "support_return": false, "reason": "生鲜食品不支持无理由退货" } }, "free_pickup": { "min_member_level": "V3" }, "compensation_permission": { "ai_agent_can_promise": false, "max_compensation_amount": 0 } }这段 JSON 就是业务规则的“唯一事实来源”。当运营政策变化时,只需要修改配置,不需要改代码。
4.3 结构化输出:让模型先"填空",再"说话"
很多团队喜欢让模型直接输出回复话术,这是错误的做法。
正确的做法是:让模型先输出结构化的决策结果,再由代码审核,最后根据审核结果生成话术。
我们可以用 Pydantic 定义一个结构化输出模型。
from pydantic import BaseModel from typing import Optional class OrderInfo(BaseModel): """从用户输入中抽取的订单信息""" order_id: str member_level: str product_type: str # true 表示商品有问题,false 表示无理由 is_defective: bool class DecisionResult(BaseModel): """规则决策输出""" can_return: bool can_promise_compensation: bool return_days: Optional[int] = None reject_reason: Optional[str] = None pickup_free: bool = False class AgentDecision(BaseModel): """Agent 综合决策结果""" decision: DecisionResult reply_template: str reply_params: dict使用 Pydantic 的好处是可以约束模型输出的结构。如果模型输出的 JSON 缺少字段,程序会直接报错,而不会进入话术生成环节。
5. 核心流程拆解与代码实现
5.1 环境准备
本文示例使用 Python 3.10+,需要安装以下依赖:
pip install openai pydantic langchain注意:OpenAI 只是一个示例,你可以替换为任意兼容 OpenAI 接口的大模型服务,包括国内模型厂商的 API。关键是掌握流程,而不是绑定某家厂商。
5.2 意图识别:由模型完成
首先定义意图识别提示词。这里使用langchain的PydanticOutputParser来保证输出结构化。
from langchain.output_parsers import PydanticOutputParser from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI intent_parser = PydanticOutputParser(pydantic_object=IntentResult) intent_prompt = ChatPromptTemplate.from_messages([ ("system", "你是电商客服意图识别器。" "分析用户问题属于哪种意图:return(退货)、exchange(换货)、" "refund(退款)、logistics(物流)、complaint(投诉)、discount(优惠咨询)、other。" "只输出 JSON 结构。"), ("human", "{user_input}\n{format_instructions}") ]) model = ChatOpenAI(model="gpt-4o-mini", temperature=0) intent_chain = intent_prompt | model | intent_parser5.3 信息抽取:由模型完成
同样用结构化输出,从用户输入中抽取订单信息。
class IntentResult(BaseModel): intent: str confidence: float related_order_id: Optional[str] = None这里需要注意:模型抽取的订单号可能是用户随便说的,也可能是从对话上下文中推断的。在真实项目中,你应该用订单号去调用订单系统,而不是直接信任模型抽取的结果。
5.4 规则决策:由代码完成
现在到了最关键的一步:规则决策不是模型来判断,而是由代码判断。
import json from typing import Optional class RuleEngine: """ 规则引擎:根据订单信息和用户意图,做出决策。 这是整个系统中唯一的"决策者"。 """ def __init__(self, rule_config_path: str): with open(rule_config_path, "r", encoding="utf-8") as f: self.rules = json.load(f) def decide_return(self, order: OrderInfo, intent: str) -> DecisionResult: # 先判断是否有退货条件 if intent == "return": # 生鲜不支持无理由退货 if order.product_type == "fresh_food" and not order.is_defective: return DecisionResult( can_return=False, can_promise_compensation=False, reject_reason="fresh_food_no_return" ) # 支持无理由退货 if order.product_type == "normal_product": return_days = self.rules["return_policy"]["normal_product"]["return_days"] return DecisionResult( can_return=True, can_promise_compensation=False, return_days=return_days, pickup_free=self._check_pickup_free(order.member_level) ) # 其他意图暂不展开 return DecisionResult( can_return=False, can_promise_compensation=False, reject_reason="unsupported_intent" ) def _check_pickup_free(self, member_level: str) -> bool: min_level = self.rules["free_pickup"]["min_member_level"] # 简单比较,真实项目应定义等级数字映射 level_score = {"V1": 1, "V2": 2, "V3": 3, "V4": 4} return level_score.get(member_level, 0) >= level_score.get(min_level, 3)这段代码的思路很清晰:
RuleEngine只依赖配置文件和订单信息,不依赖大模型。- 如果订单是生鲜且非质量问题,无论如何都不能退货。
- AI Agent 的补偿权限在规则里写死为 false,模型没有权力改变这一点。
5.5 话术生成:由模型完成,但带约束
规则引擎已经给出了决策结果。最后一步是让模型基于这个结果生成自然的客服话术。
def generate_reply(order: OrderInfo, decision: DecisionResult, user_input: str) -> str: prompt = f""" 你是电商平台的客服助手。请根据系统决策结果回复用户。 决策结果: - 是否可退货:{decision.can_return} - 退货天数:{decision.return_days} - 是否免费上门取件:{decision.pickup_free} - 拒绝原因:{decision.reject_reason} 已有的用户订单信息: - 商品类型:{order.product_type} - 会员等级:{order.member_level} 用户问题:{user_input} 请输出一段客服回复。要求: 1. 必须基于决策结果,不要承诺决策结果之外的信息。 2. 语言温和专业。 3. 如果决策为不可退货,请说明原因和可替代方案。 4. 不要提及"系统决策"等内部条款。 """ resp = model.invoke(prompt) return resp.content这里有一个容易被忽视的细节:话术生成的 prompt 中不包含业务规则的原文,只包含决策结果。
为什么?因为如果你在话术生成阶段再次给模型“所有的规则”,模型又有可能“创造性发挥”。而只给它决策结果,就相当于把它变成了“一个说人话的格式化工具”,它的发挥空间被压缩到了最小。
5.6 主流程串联
def ai_customer_service(user_input: str, rule_config_path: str = "rules.json"): # 1. 意图识别 intent_result = intent_chain.invoke({"user_input": user_input}) # 2. 信息抽取 order_info = extract_order_info(user_input) # 3. 规则决策 rule_engine = RuleEngine(rule_config_path) decision = rule_engine.decide_return(order_info, intent_result.intent) # 4. 话术生成 reply = generate_reply(order_info, decision, user_input) return reply同样,extract_order_info也是用结构化输出实现的,你可以把它理解为从用户输入中抽取订单号、商品类型、会员等级等信息。
def extract_order_info(user_input: str) -> OrderInfo: extract_prompt = ChatPromptTemplate.from_messages([ ("system", "你是订单信息抽取器。从用户输入中抽取订单号、会员等级、商品类型、是否有质量问题。" "没有的信息填 unknown。不能编造。"), ("human", "{user_input}\n{format_instructions}") ]) chain = extract_prompt | model | PydanticOutputParser(pydantic_object=OrderInfo) return chain.invoke({"user_input": user_input})6. 运行结果与效果验证
我们用一个例子来验证。
python main.py假设用户输入:
我昨天买的水果坏了,能退吗?可以补偿我20元吗?系统会执行:
- 意图识别:
return - 信息抽取:
product_type=fresh_food, is_defective=true - 规则决策:
- 生鲜 + 质量问题 → 可以退货。
- 但补偿权限为 false。
- 话术生成。
输出示例:
非常抱歉给您带来不好的体验。您购买的属于生鲜商品,经核实属于质量问题,可以为您办理退款。 关于补偿问题,我们暂时无法直接提供额外补偿,建议您提交商品质量问题的照片,我们的售后专员会为您跟进处理。注意:模型没有承诺 20 元补偿,因为决策结果里根本没有这一项。
再测一个例子:
我不想要了,能七天无理由退货吗?我 V1 会员。- 产品类型:
normal_product - 会员等级:
V1 - 决策:可退货,7 天,不免费上门取件。
输出示例:
您购买的商品支持 7 天无理由退货。由于您当前是 V1 会员,暂不符合免费上门取件条件。您可以在售后页面申请退货,自行寄回。如果模型在生成话术时,说“V1 会员也免费取件”,这就是架构没控制住。但现在没有这个可能,因为模型根本不知道“免费取件”的规则,它只知道自己收到的决策结果是 False。
这个设计非常好的地方是:决策结果与规则详情分离。模型只看到结论,看不到推导过程,也就没有机会“自作主张”。
7. 常见问题与排查思路
在实际项目中,即使你采用了上面的架构,仍然会遇到各种问题。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型仍然给出了规则外承诺 | 话术生成 prompt 里带了过多业务规则 | 检查发给模型的最终 prompt 内容 | 只传决策结果,不传规则原文 |
| 意图识别不准确 | 用户表达模糊,或意图类别设计不合理 | 查看意图识别置信度,收集失败样本 | 增加 Few-shot 示例,细化意图类别 |
| 信息抽取到错误订单号 | 模型对上下文信息过度推理 | 检查抽取结果,对比订单系统数据 | 增加校验,抽不到时让用户确认 |
| JSON 解析失败 | 模型输出格式不符合 Pydantic 结构 | 查看原始输出日志 | 增加重试机制,或换更强模型 |
| 规则修改后上线不及时 | 规则文件缓存 | 检查配置加载机制 | 引入配置中心或热加载 |
| 用户恶意诱导模型 | 提示词注入/越狱攻击 | 检查用户输入是否含特殊指令 | 增加输入过滤,限制模型对 tool 的调用权限 |
下面重点说两个高频坑。
7.1 模型还是“看到了”不该看到的规则
很多开发者会在话术生成阶段,把整个 System Prompt 带上,里面包含所有业务规则,然后要求模型“遵守”。但模型是概率模型,它有可能忽略某些规则,尤其当用户问题非常具体、情绪强烈时。
正确做法是:话术生成 prompt 中只放决策结果和少量表达约束,不放业务规则原文。
这样模型就像一个“翻译器”,它把结构化的决策翻译成自然语言,而不是一个“决策者”。
7.2 意图识别太粗,导致规则引擎无法决策
如果你的意图类别只有“退货”和“其他”,那么规则引擎就无法区分“无理由退货”和“质量问题退货”。这会导致引擎无法正确决策。
建议在意图识别阶段,就把业务相关的关键维度拆细。例如:
- 退货原因:无理由 / 质量问题 / 配送损坏
- 商品类型:普通 / 生鲜 / 大件
- 用户诉求:退货 / 退款 / 换货 / 补偿
这些信息应该作为结构化字段抽取出来,作为规则引擎的输入参数。
8. 最佳实践与工程建议
8.1 把业务规则当代码管理
业务规则不能写死在提示词里。建议把规则独立成配置文件,并纳入版本管理,像代码 MR 一样评审上线。
这样做有三个好处:
- 规则变更可以被审计。
- 规则可以写单元测试。
- 规则可以灰度发布。
8.2 为规则引擎写单元测试
规则引擎是纯代码逻辑,非常适合测试。
def test_fresh_food_no_return(): order = OrderInfo( order_id="A001", member_level="V3", product_type="fresh_food", is_defective=False ) engine = RuleEngine("rules.json") decision = engine.decide_return(order, "return") assert decision.can_return is False assert decision.reject_reason == "fresh_food_no_return"每次修改规则,跑一遍测试再上线,能避免大量线上事故。
8.3 设计好“拒绝话术”
做 AI 客服,真正考验话术水平的地方不是顺畅沟通,而是拒绝用户。
“暂时无法提供补偿”和“我们不能补偿你”听起来完全不同。建议针对常见的拒绝场景,提供一组人工撰写的高质量模板。模型生成话术时,可以基于模板进行微调,而不是从零生成。
拒绝补偿模板: 非常理解您的心情。关于{补偿类型},目前我们暂时无法直接处理。 您看是否可以通过{替代方案},由售后专员为您进一步评估?8.4 建立完整的日志追踪体系
AI 客服出问题是必然的。关键是出问题时,你能不能快速定位是哪一环出的问题。
- 记录用户原始输入。
- 记录意图识别结果。
- 记录信息抽取结果。
- 记录规则决策结果。
- 记录发给模型的最终 prompt。
- 记录模型输出。
每一环都要打日志。尤其是“发给模型的最终 prompt”,这是排查“模型为什么乱说”的第一手证据。
8.5 永远保留人工兜底
再完善的规则,也覆盖不了所有新场景。在 AI Agent 的设计中,必须有一条“人工接管”通道。
当用户投诉升级、情绪失控、要求超出规则覆盖范围时,Agent 应主动转接人工,并输出带上下文的交接摘要。这既是用户体验的底线,也是系统安全的底线。
8.6 权限与安全边界
AI 客服 Agent 能访问订单系统、会员系统,这意味着它有权限风险。
在真实项目中,必须遵循最小权限原则:
- 客服 Agent 只能查询订单,不能修改订单。
- 客服 Agent 不能直接发放优惠券,只能生成“优惠券申请单”。
- 客服 Agent 的 API Key 必须与人工客服区分。
9. 总结与后续学习方向
回到本文开头的问题:AI 客服能不能不自由发挥?
答案是能,但靠的不是换一个更聪明的模型,而是改变系统架构。
核心心法就三句话:
- 让大模型做理解和表达,让代码做决策。
- 决策结果与规则详情分离,模型只看到结论。
- 结构化输出 + 规则引擎 + 话术模板,三层一起控制输出边界。
从技术选型上看,提示词约束、RAG、微调都有价值,但它们是辅助手段。真正的主干是工作流与规则引擎。不要把 Agent 理解成“让模型自由行动”,而应该把 Agent 理解成“有能力调用工具的流程执行器”。
如果你想继续深入,建议按以下顺序学习:
- 结构化输出约束:Pydantic 输出解析、JSON Schema、Function Calling。
- 规则引擎设计:Rete 算法、决策表、状态机。
- Agent 编排:LangChain / LangGraph 中的工具调用与流程控制。
- 测试与评估:怎么为客服 Agent 构建评测集,怎么量化“越权率”。
- 生产级治理:配置中心、日志追踪、权限管理。
下一步,你可以自己动手做一个最小示例:选三个意图(退货、物流查询、优惠咨询),配置简单的 JSON 规则,把代码跑通。跑通之后再逐步增加意图,增加规则,最后接入历史对话上下文,做成一个完整的客服 Agent。
真正在生产环境中可用的 AI 客服,不是靠模型“自觉”的,而是靠系统“强制”的。想清楚这一点,你的 Agent 离上线就不远了。