如果你正在开发或计划集成AI助手到你的产品中,金尼药房的案例是一个必须研究的“反面教材”。这家连锁药店因数百起客户投诉,最终下架了其AI手机助手“Burt”。这不仅仅是一个商业新闻,更是给所有技术决策者、产品经理和开发者敲响的一记警钟:一个技术先进但体验糟糕的AI产品,其破坏力远超一个功能简单的传统应用。
表面上看,这是一个AI客服翻车的故事。但深入技术层面,它暴露的是AI应用在工程化落地时一系列被忽视的“暗礁”:对话设计、意图识别、上下文管理、异常处理,以及最关键的——如何定义AI的职责边界。很多团队在拥抱大模型时,只关注“能不能做”,却很少系统性地思考“应该怎么做”以及“做错了怎么办”。
本文将从一个技术复盘的角度,拆解“Burt”这类AI助手可能踩中的典型技术坑。我们不会停留在新闻事件的表面,而是会深入探讨:
- AI助手常见的架构缺陷:从接收到响应的链条中,哪些环节最脆弱?
- 对话设计与工程实现的脱节:产品经理的“美好设想”如何被糟糕的技术实现毁掉?
- 可观测性与快速迭代的缺失:当投诉如潮水般涌来时,技术团队为何可能陷入“盲人摸象”的困境?
- 一套可落地的AI助手“避坑”开发框架:从设计原则到代码示例,构建一个更健壮、更可控的AI交互系统。
无论你是正在开发智能客服、个人助理,还是任何需要自然语言交互的功能,这篇文章都将帮助你避开那些让用户愤怒、让品牌受损的陷阱。
1. 从“Burt”事件看AI产品落地的核心矛盾
金尼药房引入AI助手“Burt”的初衷无疑是好的:降低人工客服成本,提供7x24小时即时服务,处理药品查询、门店定位、订单跟踪等高频但简单的事务。这符合当前AI赋能产业的大趋势。然而,数百起投诉集中爆发,说明问题不是偶发的BUG,而是系统性的设计失败。
这个矛盾的核心在于:AI的能力边界与用户的无限期望之间的巨大落差。技术团队可能使用了一个强大的大语言模型(LLM),认为它“什么都能聊”,但用户的具体场景是复杂、模糊且充满情绪的。
- 用户的真实场景:“我吃了这个药胃不舒服,是不是副作用?我该停药吗?”(涉及医疗建议、个人健康,高度敏感且责任重大)
- AI的简单处理:基于药品说明书生成一段标准副作用描述,可能无法判断情况的紧急性,甚至给出“如果不适请停药”这类过于笼统、可能存在风险的建议。
- 引发的灾难:用户感到被敷衍,问题未解决,对药房的专业性产生严重怀疑。
另一个矛盾是确定性与随机性。传统软件的输出是确定的(输入A,必然得到B)。而生成式AI的输出具有随机性(温度参数>0时),同一问题可能得到不同回答。在医疗、金融等严谨领域,这种不确定性是致命的。
技术判断:“Burt”事件的根源,很可能不是底层大模型不够聪明,而是应用层架构缺失了必要的“护栏”(Guardrails)和“决策树”。AI不应该被直接抛向用户,而应该在一个受控的、流程化的框架内工作。
2. AI助手基础架构与核心风险点
一个典型的AI助手(Agent)技术栈可以分为四层,每一层都存在风险点:
用户输入 -> 输入处理层 -> 决策与执行层 -> 大模型生成层 -> 输出过滤层 -> 用户 (意图识别) (技能路由) (内容生成) (安全合规)2.1 输入处理层:意图识别的“第一道防线”
这是误解的开始。如果无法准确理解用户想干什么,后面全错。
- 风险:依赖纯LLM进行意图分类,在用户表述模糊、口语化、带错别字时效果不稳定。
- 改进思路:采用“LLM + 规则 + 分类器”混合模式。先用规则处理明确指令(如“查订单”),再用精调的小模型或关键词进行粗分类,最后用LLM进行细粒度意图和槽位填充。
2.2 决策与执行层:技能路由与边界管控
这是决定AI“做什么”和“不做什么”的大脑。
- 风险:让LLM自行决定调用哪个工具(技能)。LLM可能会“幻觉”出一个不存在的技能,或把敏感查询(如医疗诊断)误路由到普通问答技能。
- 改进思路:实现一个明确的“技能路由表”。根据输入处理层输出的意图,硬编码或通过配置决定调用哪个技能模块。对于高风险意图(如健康咨询、投诉),直接路由到人工客服或终止对话。
2.3 大模型生成层:提示工程与上下文管理
这是AI的“嘴巴”,怎么说很重要。
- 风险:
- 提示词(Prompt)设计不佳:未明确AI的角色、职责和禁忌,导致回答越界。
- 上下文管理混乱:长对话中遗忘关键信息,或上下文被无关历史污染。
- 温度(Temperature)参数过高:导致回答随机性大,不专业。
- 改进思路:设计系统化的提示词模板,严格限定身份和回答范围。采用高效的上下文窗口管理策略,如摘要历史、关键信息提取。
2.4 输出过滤层:最后的“安全网”
在回答送达用户前,必须进行审查。
- 风险:完全没有后置过滤,任由可能有害、不准确或不专业的回答发出。
- 改进思路:部署一个轻量级的“安全过滤器”,检查输出是否包含敏感词、是否在禁忌话题上表态、是否符合事实(可对接知识库做简单验证)。
“Burt”很可能在2.2和2.4层存在严重缺失,导致AI在处理复杂、敏感查询时“信口开河”。
3. 构建一个健壮的AI助手:环境与设计原则
在开始编码前,我们必须确立几个核心设计原则,这些原则是避免“Burt”式失败的基础。
原则一:AI是助理,不是专家明确AI助手的定位是信息提供、流程引导和简单任务处理,而非替代医生、律师、金融顾问等专业人士做判断。所有涉及专业建议的回答,必须包含“请咨询相关专业人士”的免责声明和转人工通道。
原则二:确定性高于智能性在关键业务流程(如订单查询、价格确认)上,优先使用基于API的确定性查询,而非LLM的自由生成。即使使用LLM,也要通过严格的提示词和输出解析(如要求以固定JSON格式回复)来锁定输出范围。
原则三:可观测与可干预整个对话流程必须被完整日志记录,包括用户输入、识别出的意图、调用的技能、LLM的原始输出、过滤后的输出。这不仅是排查问题的依据,更是优化模型和规则的数据燃料。同时,后台需提供人工实时介入的接口。
原则四:优雅的失败策略当AI无法处理或信心不足时,必须有清晰的退路:明确告知用户“这个问题我暂时无法处理”,并顺畅地引导至人工客服、帮助文档或更简单的菜单选择。
技术栈选择建议(示例):
- 语言模型:可根据场景选择OpenAI GPT、Claude API,或本地部署的Llama、Qwen等开源模型。关键点:选择那些在“遵循指令”和“拒绝不当请求”方面经过优化的模型。
- 开发框架:利用LangChain、LlamaIndex等框架快速搭建Agent原型,但切记,生产环境需要对其抽象层进行拆解和加固,尤其是技能路由和工具调用部分。
- 后端服务:Python (FastAPI/Flask) + Redis(上下文缓存)。
- 监控:Prometheus + Grafana(监控性能指标),ELK Stack(收集和分析对话日志)。
4. 核心流程拆解:从用户问到AI答
让我们用一个“药店AI助手”的简化场景,拆解一个健壮的流程应该如何运作。
流程步骤:
- 用户输入:“我昨天买的阿莫西林,吃了两次,身上起红点,痒得厉害,是不是过敏了?我该怎么办?”
- 输入处理与意图识别:
- 规则引擎:检测到“阿莫西林”(药品名)、“过敏”(症状关键词)。
- 意图分类器:将意图分类为【药品副作用咨询】,并标记风险等级为【高】。
- 技能路由与边界检查:
- 路由表根据【药品副作用咨询-高风险】,触发两个动作:
- 动作A(回复):调用预设的“紧急情况提示”模板。
- 动作B(流程):立即生成工单,并通知后台药师优先处理。
- 路由表根据【药品副作用咨询-高风险】,触发两个动作:
- 内容生成(受限):
- 由于是高风险查询,不调用通用LLM进行自由生成。
- 直接使用“紧急情况提示”模板填充药品名称,生成固定回复。
- 输出过滤与最终交付:
- 安全过滤器检查固定回复,确认无篡改。
- 将回复与人工客服入口一同返回给用户。
5. 关键代码实现示例
下面我们用Python和伪代码展示几个关键环节的实现。我们假设使用FastAPI作为后端,并简化了部分细节。
5.1 意图识别与路由(混合模式)
# 文件路径:app/services/intent_router.py import re from enum import Enum from typing import Optional, Dict, Any from pydantic import BaseModel class IntentType(Enum): GREETING = "greeting" DRUG_QUERY = "drug_query" # 药品信息查询 SIDE_EFFECT_CONSULT = "side_effect_consult" # 副作用咨询(高风险) STORE_LOCATOR = "store_locator" ORDER_TRACK = "order_track" COMPLAINT = "complaint" # 投诉(高风险) UNKNOWN = "unknown" class IntentResult(BaseModel): intent: IntentType confidence: float entities: Dict[str, Any] # 提取的实体,如药品名、订单号 risk_level: str # "low", "medium", "high" class IntentRouter: def __init__(self): # 1. 预定义高风险关键词规则(优先匹配) self.high_risk_patterns = { IntentType.SIDE_EFFECT_CONSULT: [r'过敏|红肿|痒|呼吸困难|副作用|不舒服'], IntentType.COMPLAINT: [r'投诉|举报|不满意|生气|垃圾'], } # 2. 可以加载一个简单的文本分类模型(如scikit-learn)处理其他意图 # self.classifier = load_classifier_model() def route(self, user_input: str) -> IntentResult: """混合意图识别与路由""" user_input_lower = user_input.lower() # 第一步:高风险规则匹配(最高优先级) for intent, patterns in self.high_risk_patterns.items(): for pattern in patterns: if re.search(pattern, user_input_lower): # 简单实体提取示例 entities = {"keywords": re.findall(pattern, user_input_lower)} return IntentResult( intent=intent, confidence=0.95, # 规则匹配,置信度高 entities=entities, risk_level="high" ) # 第二步:基于模型的分类(处理中低风险意图) # predicted_intent, confidence = self.classifier.predict(user_input) # 此处为演示,假设我们调用一个LLM API进行细粒度识别(实际可缓存、批量处理) predicted_intent = self._call_llm_for_intent(user_input_lower) # 第三步:根据意图映射风险等级 risk_map = { IntentType.SIDE_EFFECT_CONSULT: "high", IntentType.COMPLAINT: "high", IntentType.DRUG_QUERY: "medium", IntentType.GREETING: "low", IntentType.STORE_LOCATOR: "low", IntentType.ORDER_TRACK: "low", } return IntentResult( intent=predicted_intent, confidence=0.8, # 假设值 entities={}, # 实际应从LLM或NER模型提取 risk_level=risk_map.get(predicted_intent, "medium") ) def _call_llm_for_intent(self, text: str) -> IntentType: """调用LLM进行意图分类(简化示例)""" # 这里模拟一个LLM调用,实际使用OpenAI/Claude等API # 提示词示例:你是一个意图分类器,将用户问题分类为:问候、药品查询、副作用咨询、门店查询、订单跟踪、投诉、其他。 # 只返回意图标签。 # ... 调用LLM API ... # response = llm_client.chat.completions.create(...) # 解析response,返回IntentType # 为演示,返回一个默认值 return IntentType.DRUG_QUERY # 使用示例 router = IntentRouter() result = router.route("我吃了阿莫西林身上起红点,是不是过敏?") print(f"识别意图: {result.intent.value}, 风险等级: {result.risk_level}") # 输出: 识别意图: side_effect_consult, 风险等级: high关键点:规则引擎优先捕获高风险模式,确保敏感问题不被误判为普通查询。LLM用于处理更复杂的、非结构化的中低风险意图。
5.2 技能路由与执行器
# 文件路径:app/services/skill_executor.py from app.services.intent_router import IntentResult, IntentType from app.templates.responses import ResponseTemplates import asyncio from app.models import CustomerServiceTicket class SkillExecutor: def __init__(self): self.templates = ResponseTemplates() async def execute(self, intent_result: IntentResult, user_input: str, user_id: str, session_id: str) -> Dict: """根据意图执行对应技能""" intent = intent_result.intent risk_level = intent_result.risk_level # 根据意图和风险等级路由 if risk_level == "high": return await self._execute_high_risk(intent, user_input, user_id, session_id, intent_result.entities) else: return await self._execute_low_medium_risk(intent, user_input, user_id, session_id, intent_result.entities) async def _execute_high_risk(self, intent: IntentType, user_input: str, user_id: str, session_id: str, entities: Dict): """处理高风险意图:固定回复 + 创建人工工单""" response_text = "" system_actions = [] if intent == IntentType.SIDE_EFFECT_CONSULT: drug_name = entities.get("drug_name", "该药品") # 使用固定模板,禁止自由发挥 response_text = self.templates.get_medical_emergency_response(drug_name) # 创建紧急工单 ticket = await self._create_urgent_ticket( user_id=user_id, session_id=session_id, category="drug_reaction", description=user_input ) system_actions.append({"action": "ticket_created", "ticket_id": ticket.id}) elif intent == IntentType.COMPLAINT: response_text = self.templates.get_complaint_acknowledgment() ticket = await self._create_urgent_ticket( user_id=user_id, session_id=session_id, category="complaint", description=user_input ) system_actions.append({"action": "ticket_created", "ticket_id": ticket.id}) return { "response": response_text, "actions": system_actions, "should_end_session": False, # 保持会话,等待人工接入 "risk_handled": True } async def _execute_low_medium_risk(self, intent: IntentType, user_input: str, user_id: str, session_id: str, entities: Dict): """处理中低风险意图:可调用LLM或确定性子服务""" if intent == IntentType.DRUG_QUERY: # 示例:先查知识库,再决定是否用LLM补充 drug_info = await self._query_drug_knowledge_base(entities.get("drug_name")) if drug_info: response_text = drug_info["description"] else: # 知识库没有,用LLM生成,但提示词严格限制 response_text = await self._call_safe_llm_for_drug_info(user_input) return {"response": response_text, "actions": [], "should_end_session": False} elif intent == IntentType.ORDER_TRACK: # 调用确定性API查询订单 order_status = await self._query_order_system(entities.get("order_number")) response_text = f"您的订单状态是:{order_status}" return {"response": response_text, "actions": [], "should_end_session": False} # ... 其他意图处理 async def _create_urgent_ticket(self, **kwargs): """创建人工客服工单(伪代码)""" # 写入数据库,并可能触发消息通知(如短信、内部IM) ticket = CustomerServiceTicket.create(**kwargs) # 模拟异步通知 asyncio.create_task(self._notify_customer_service(ticket.id)) return ticket async def _query_drug_knowledge_base(self, drug_name: str): """查询内部药品知识库(伪代码)""" # 返回结构化信息,确保准确性 return {"description": "【阿莫西林】是一种青霉素类抗生素,用于治疗..."} if drug_name else None async def _call_safe_llm_for_drug_info(self, query: str): """调用带有严格提示词的LLM(伪代码)""" safe_prompt = f""" 你是一个专业的药店AI助手,只提供公开的药品基本信息。 如果用户询问健康建议、副作用判断、用药指导,你必须拒绝回答,并建议咨询医师或药师。 用户问题:{query} 请根据公开药品说明书信息,只回答药品的通用名称、主要用途和常规用法用量。不要提供任何个人建议。 """ # ... 调用LLM API,例如OpenAI # response = await openai_client.chat.completions.create(model="gpt-4", messages=[{"role":"system", "content": safe_prompt}]) # return response.choices[0].message.content return "根据公开信息,该药品主要用于... 具体用药请遵医嘱。" # 模板示例 class ResponseTemplates: def get_medical_emergency_response(self, drug_name: str) -> str: return f"""您描述的服用【{drug_name}】后出现的症状(如红点、瘙痒)可能与药物反应有关。这是一个需要高度重视的情况。 **【重要提示】** 1. **请立即停止服用此药物。** 2. **建议您尽快前往就近的医疗机构(如医院急诊科或诊所)就诊,并告知医生您服用过的药物。** 3. **在医生指导下进行后续处理。** 本AI助手无法提供医疗诊断。为了您的健康安全,我们已为您创建加急服务工单,我们的药师将在5分钟内通过电话与您联系(请保持电话畅通)。您也可以直接拨打我们的紧急联系电话:XXX-XXXX-XXXX。""" def get_complaint_acknowledgment(self) -> str: return """非常抱歉给您带来了不好的体验。您的问题我们已经收到,并已列为优先处理事项。 我们已为您创建投诉处理工单,专属客服经理将在15分钟内主动与您联系,为您核实情况并解决问题。 感谢您的反馈,这帮助我们不断改进服务。"""关键点:SkillExecutor是核心控制器。它将高风险意图与低风险意图的处理路径彻底分开。对于高风险场景,完全摒弃LLM的自由生成,转而使用预定义的、经过法务和医学审核的固定模板,并自动触发人工服务流程。
5.3 安全过滤与输出后处理
# 文件路径:app/services/safety_filter.py import re class SafetyFilter: def __init__(self): # 定义安全词库和风险模式(实际应从配置文件或数据库加载) self.blocked_keywords = ["自杀", "自残", "仇恨言论", "具体暴力方法"] # 示例 self.medical_advice_phrases = ["你应该吃", "我建议你", "我认为你得", "必须用这个药", "保证能治好"] def filter_response(self, response_text: str, intent: str, risk_level: str) -> Dict: """ 对AI生成的回复进行安全检查。 返回过滤后的文本和检查结果。 """ issues = [] # 1. 基础敏感词过滤 for keyword in self.blocked_keywords: if keyword in response_text: issues.append(f"包含违禁词: {keyword}") # 高风险词直接拦截,返回默认安全回复 return { "passed": False, "filtered_text": "您的问题涉及敏感内容,我无法提供相关信息。如需帮助,请联系人工客服。", "issues": issues } # 2. 针对医疗场景的越界建议检查(如果意图是药品咨询) if intent in ["drug_query", "side_effect_consult"]: for phrase in self.medical_advice_phrases: if phrase in response_text: issues.append(f"可能包含越界医疗建议: {phrase}") # 不直接拦截,但记录日志告警,并可选择替换部分内容 # response_text = self._sanitize_medical_response(response_text) # 3. 长度检查(防止模型“胡言乱语”生成过长无意义内容) if len(response_text) > 1000: issues.append("回复过长,可能包含冗余信息") # 可进行摘要处理,此处简单截断 response_text = response_text[:500] + "..." if issues: # 记录到监控系统,供后续分析优化 self._log_safety_issues(intent, risk_level, issues) return { "passed": True, # 通过了基础拦截,但有警告 "filtered_text": response_text, "issues": issues, "needs_human_review": len(issues) > 2 # 如果问题较多,标记需要人工复核 } else: return { "passed": True, "filtered_text": response_text, "issues": [], "needs_human_review": False } def _log_safety_issues(self, intent: str, risk_level: str, issues: list): """将安全问题记录到日志或监控系统""" print(f"[SAFETY_LOG] Intent: {intent}, Risk: {risk_level}, Issues: {issues}") # 实际应写入ELK、Sentry或专门的审计日志 # 在SkillExecutor的execute方法最后调用 # filter_result = safety_filter.filter_response(raw_response, intent_result.intent.value, intent_result.risk_level) # if not filter_result['passed']: # final_response = filter_result['filtered_text'] # if filter_result['needs_human_review']: # # 可以触发一个低优先级的后台人工审核任务关键点:安全过滤器是最后的防线。它不仅要拦截明显的有害内容,更要针对特定领域(如医疗)检查AI是否做出了超出其权限的“建议”。所有过滤动作都必须记录日志,用于持续优化规则和模型。
6. 系统集成与API接口示例
将上述模块整合到一个Web服务中。
# 文件路径:app/main.py from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel import logging from app.services.intent_router import IntentRouter from app.services.skill_executor import SkillExecutor from app.services.safety_filter import SafetyFilter app = FastAPI(title="Pharmacy AI Assistant API") router = IntentRouter() executor = SkillExecutor() safety_filter = SafetyFilter() logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class ChatRequest(BaseModel): message: str user_id: str session_id: str # 可包含上下文历史 conversation_history: list = [] class ChatResponse(BaseModel): reply: str session_id: str requires_human: bool = False ticket_id: Optional[str] = None # 如果创建了工单 confidence: float @app.post("/chat", response_model=ChatResponse) async def chat_endpoint(request: ChatRequest): """处理用户消息的核心端点""" try: # 1. 意图识别 intent_result = router.route(request.message) logger.info(f"User:{request.user_id}, Intent:{intent_result.intent}, Risk:{intent_result.risk_level}") # 2. 技能执行 execution_result = await executor.execute( intent_result=intent_result, user_input=request.message, user_id=request.user_id, session_id=request.session_id ) # 3. 安全过滤(对非高风险固定模板的回复进行过滤) raw_response = execution_result.get("response", "") if not execution_result.get("risk_handled", False): # 如果不是高风险已处理流程 filter_result = safety_filter.filter_response( raw_response, intent_result.intent.value, intent_result.risk_level ) final_reply = filter_result["filtered_text"] if filter_result.get("needs_human_review"): logger.warning(f"Response flagged for review. Session: {request.session_id}") else: final_reply = raw_response # 高风险回复已使用安全模板,跳过过滤 # 4. 组装返回 return ChatResponse( reply=final_reply, session_id=request.session_id, requires_human=execution_result.get("should_end_session", False) or len(execution_result.get("actions", [])) > 0, ticket_id=execution_result.get("actions", [{}])[0].get("ticket_id") if execution_result.get("actions") else None, confidence=intent_result.confidence ) except Exception as e: logger.error(f"Error processing chat request: {e}", exc_info=True) # 发生任何未捕获异常,返回友好错误并转人工 return ChatResponse( reply="抱歉,系统暂时出了点小问题。我们已经记录此错误,并已为您连接人工客服。", session_id=request.session_id, requires_human=True, confidence=0.0 ) # 运行服务: uvicorn app.main:app --reload --host 0.0.0.0 --port 80007. 部署、监控与常见问题排查
7.1 部署注意事项
- 环境隔离:测试环境、预发布环境、生产环境严格分离。所有对话模板和风险规则需经过测试环境验证和预发布环境灰度。
- 配置外置:将敏感词库、意图规则、回复模板等作为配置文件或数据库存储,支持热更新,无需重启服务。
- 弹性伸缩:AI模型调用(尤其是云API)可能成为性能瓶颈和成本中心。需要实施限流、重试、降级策略(如LLM服务超时后,降级到规则引擎)。
- 依赖管理:明确LLM API、数据库等外部依赖的健康状态监控和熔断机制。
7.2 监控指标
必须建立完善的监控体系,而不是等到用户投诉才发现问题。
| 监控维度 | 关键指标 | 告警阈值/行动 |
|---|---|---|
| 性能 | API响应时间(P95/P99)、LLM调用延迟、服务错误率 | >3秒(P95)告警;错误率>1%告警 |
| 业务 | 会话总量、高风险意图触发量、人工转接率、用户满意度(如有埋点) | 人工转接率突增 >50%;高风险意图日环比增长>100% |
| 安全/质量 | 安全过滤器触发次数、被拦截回答类型分布、工单创建量 | 单日安全拦截>100次,需复核规则 |
| 成本 | LLM API调用次数与Token消耗 | 日消耗超预算80%告警 |
7.3 常见问题排查清单
当AI助手出现异常时,可按此顺序排查:
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 用户投诉“答非所问” | 意图识别错误 | 1. 查看该会话日志,检查intent_result。2. 检查用户输入是否模糊、有错别字。 3. 检查规则和分类模型是否覆盖该场景。 | 1. 优化意图分类模型训练数据。 2. 增加同义词和模糊匹配规则。 3. 对于无法识别的意图,设计澄清话术。 |
| AI给出危险或越界建议 | 1. 高风险意图未正确路由。 2. 安全过滤规则缺失或未生效。 3. LLM提示词约束力不足。 | 1. 确认该查询的意图和风险等级日志。 2. 检查安全过滤器日志是否记录。 3. 复核LLM调用的提示词。 | 1. 补充高风险关键词到规则引擎。 2. 加强安全过滤规则,特别是领域相关禁忌。 3. 使用System Prompt强化角色限定,或换用更“听话”的模型。 |
| 回答内容事实错误 | 1. 知识库数据过时。 2. LLM产生“幻觉”。 | 1. 核对知识库中对应条目的准确性。 2. 检查是否过度依赖LLM生成事实性内容。 | 1. 建立知识库定期更新机制。 2. 事实性查询优先走知识库或确定性API,LLM仅用于润色或处理未知情况,并注明“信息可能不完整”。 |
| 对话上下文丢失 | 上下文管理逻辑错误或缓存失效。 | 1. 检查会话session_id是否在请求中保持一致。2. 检查Redis等缓存服务是否正常。 | 1. 确保前端正确传递会话ID。 2. 实现对话历史的摘要机制,避免超出模型上下文长度。 3. 增加缓存降级策略。 |
| 服务响应缓慢 | 1. LLM API延迟高。 2. 自身服务资源不足。 3. 数据库查询慢。 | 1. 监控LLM API的响应时间。 2. 检查服务器CPU/内存使用率。 3. 检查慢查询日志。 | 1. 为LLM调用设置超时和降级(返回兜底话术)。 2. 扩容服务实例。 3. 优化数据库查询和索引。 |
8. 最佳实践与工程建议
- 渐进式上线与A/B测试:不要一次性全量替换人工客服。先让AI处理最明确、最低风险的场景(如门店营业时间查询),逐步扩大范围。同时设置A/B测试,对比AI和人工在相同问题上的解决率和满意度。
- 建立“红队”测试机制:组建内部团队,专门尝试用各种刁钻、古怪、恶意的问题“攻击”你的AI助手,提前发现漏洞。
- 设计完善的兜底和上报流程:任何时候,都要让用户能一键找到“转人工”的入口。AI处理失败或信心不足时,必须清晰告知用户并平滑过渡。
- 数据驱动迭代:持续分析对话日志。哪些问题AI处理得好?哪些总是需要转人工?哪些回答引发了后续追问?用这些数据反哺意图分类模型、知识库和对话策略的优化。
- 法律与合规审查:在医疗、金融、法律等领域,所有固定的回复模板、免责声明都必须经过相关领域专家和法务团队的审核。
- 明确责任边界:在用户协议和AI助手的使用说明中,明确告知用户AI的能力范围和限制,声明其建议仅供参考,不构成专业意见。
金尼药房“Burt”的下架,是一个代价高昂但极具教育意义的技术产品案例。它清晰地表明,在严肃的商业场景中,一个AI产品的成功,技术先进性只占一部分,而系统的可靠性、安全性和对边界的敬畏心,往往决定了它的生死。
对于开发者而言,这意味着我们不能只沉迷于调用最新的LLM API,而必须像构建金融交易系统一样,为AI应用设计严谨的架构:清晰的意图边界、可靠的路由逻辑、固若金汤的安全过滤,以及永远在线的人工后备。本文提供的架构思路和代码示例,正是为了将这种理念付诸实践。下一次当你设计AI功能时,不妨先问自己:如果这个回答出错,最坏的后果是什么?我的系统能否阻止它发生?