1. 项目概述:从零到一,打造一个能“听懂人话”的智能客服
最近一周,我几乎把所有业余时间都“肝”在了一个智能客服助手的项目上。起因很简单,团队内部需要一个能处理日常高频、重复性咨询的工具,比如“年假怎么请”、“报销流程是什么”、“会议室怎么预定”。市面上成熟的SaaS产品要么太贵,要么定制化不够灵活,功能臃肿。于是,一个念头冒出来:为什么不自己动手,用现在触手可及的AI能力,搭一个轻量、聪明、专属于我们自己业务场景的客服助手呢?
这个项目,我称之为“智能客服助手”,它的核心目标不是取代人工,而是成为人工客服的“超级副驾”。它需要能理解员工用自然语言提出的各种问题,准确识别其背后的真实意图(是想查制度、办流程还是问信息),然后从知识库中精准找到答案,并用流畅、自然的对话方式反馈给用户。整个过程,要尽可能接近和一个有经验的行政或HR同事对话的体验。
听起来是不是有点像给聊天机器人接上了“大脑”?没错,其技术内核正是当前大热的大语言模型和智能体技术。但和单纯调用API的聊天不同,我们要构建的是一个有明确任务边界、能可靠执行查询动作、并且能管理多轮对话状态的AI Agent。这一周,我深入折腾了意图识别、会话管理、工具调用以及如何让LLM的输出更稳定可控。接下来,我就把这几天趟过的路、踩过的坑,以及最终跑起来的这套系统,毫无保留地分享给你。无论你是想为自己团队打造一个效率工具,还是对AI应用开发感兴趣,相信这些实战经验都能给你带来直接的参考。
2. 核心设计思路:构建一个“思考-行动”循环的AI智能体
在开始敲代码之前,最关键的是想清楚这个智能客服应该以何种架构运行。直接让一个大语言模型“自由发挥”去回答所有问题,在严肃的工作场景下是行不通的,它可能会幻觉出不存在的规定,或者把不同部门的信息张冠李戴。因此,我们必须为它设计一套严谨的工作流程,引导它“先思考,再行动”。
我采用的架构核心是“规划-执行”循环,这也是当前AI Agent主流的范式。整个助手的“大脑”由一个LLM驱动,但它并不直接生成最终答案,而是扮演一个“调度中心”和“决策者”的角色。其工作流程可以拆解为以下几个关键步骤:
2.1 意图识别与分类:听懂用户的“弦外之音”
这是对话的起点,也是最容易出错的环节。用户问“我怎么休假”,可能意图是“查看年假余额”,也可能是“发起休假申请流程”。传统的基于关键词匹配的规则引擎在这里显得力不从心,而LLM强大的语义理解能力正好派上用场。
我的做法是,将意图识别设计为一个独立的LLM调用任务。具体来说,我预先定义好了客服助手能处理的所有意图类别,例如:查询政策制度、办理业务流程、询问联系方式、获取文档模板、闲聊/无法处理。当用户输入一句话后,系统会将用户问题连同定义好的意图列表,一起提交给LLM,指令它:“请判断用户问题最属于以下哪个意图类别,仅返回类别名称。”
这里有一个重要的技巧:少样本提示。仅仅给出类别名称,LLM可能判断不准。我会在每个类别后,附加1-2个典型的示例问题。例如:
查询政策制度:例如“年假有多少天?”、“加班工资怎么算?”办理业务流程:例如“我要申请报销”、“怎么预定投影仪?”
通过提供示例,相当于给了LLM一个判断的“锚点”,能显著提高意图分类的准确率。实测下来,对于工作场景下的规范用语,这种方法的准确率能达到95%以上。
2.2 会话状态管理:记住我们聊到哪了
单轮对话很简单,但客服场景常常涉及多轮交互。比如用户问:“报销流程是什么?”(意图:查询政策制度)。助手回答后,用户可能接着问:“那需要哪些发票?”(意图:查询政策制度)。如果没有会话管理,助手会把第二个问题当作全新的独立问题,可能又从头解释一遍报销流程,显得很傻。
因此,必须引入会话上下文管理。我为每个对话会话创建一个唯一的ID,并维护一个上下文窗口。每次LLM调用时,不仅传入当前用户的问题,还会附带上最近几轮的对话历史。这样,LLM就能知道“我们刚才在聊报销,现在用户问的是其中的细节”,从而给出连贯的答复。
技术实现上,可以用简单的内存字典(针对单机/短期)或Redis(针对分布式/长期)来存储会话ID和对应的消息列表。上下文长度需要权衡,太短会遗忘,太长会增加LLM的Token消耗和成本。我一般保留最近5-10轮对话,这已经能覆盖绝大多数工作场景下的连续追问。
2.3 工具调用与执行:让LLM学会“动手”
识别出意图后,就需要执行具体的动作来获取答案。这就是工具调用的核心。例如,识别到“查询政策制度”,就应该调用“知识库检索工具”;识别到“办理业务流程”,则可能调用“流程引擎接口”或返回一个特定的指导链接。
我并没有使用LangChain这类重型框架,而是采用了更轻量、更可控的方式:Function Calling。这是目前主流LLM API(如OpenAI GPT、DeepSeek等)都支持的特性。具体做法是:
- 将我定义好的“工具”描述清楚,包括工具名称、功能描述、所需参数及其类型。例如:
{ "name": "search_knowledge_base", "description": "根据问题从公司知识库中搜索相关的制度、政策或Q&A文档。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "用于搜索的关键词或问题摘要" } }, "required": ["query"] } } - 在调用LLM时,将这些工具定义作为参数传入。
- LLM在分析用户问题后,如果认为需要调用某个工具来解答,它就不会直接生成答案,而是返回一个结构化的JSON,指明它想调用哪个工具,以及传入什么参数。
- 我的后端程序收到这个JSON后,解析它,并真正去执行对应的函数(如查询数据库、调用API)。
- 将函数执行得到的结果(如搜索到的政策条文)再次提交给LLM,让它基于这些确凿的事实,组织成一段通顺、友好的回复给用户。
这个过程,完美地将LLM的“思考”能力与外部系统的“执行”能力结合了起来。LLM负责理解和规划,外部工具负责提供准确的数据和动作,从而保证了回答的准确性和可操作性。
2.4 回复生成与格式化:说人话,办明白事
最后一步,是将工具执行的结果转化为用户能听懂的回复。这里同样由LLM来完成,但需要给予明确的指令进行约束,我称之为“格式化指令”。
指令会要求LLM:
- 基于提供的上下文:强调答案必须严格来自上一步工具返回的事实,不能自行编造。
- 结构化呈现:如果信息较多,要求分点说明。
- 语气友好:使用“您好”、“请问”等礼貌用语,模拟专业客服口吻。
- 引导下一步:在答案末尾,可以视情况加上“如果您需要办理,请点击这里...”或“您还有其他问题吗?”等引导语。
通过这一整套“意图识别 -> 会话管理 -> 工具调用 -> 回复生成”的循环,我们就构建了一个既能理解复杂意图,又能可靠执行任务,还能进行连贯对话的智能客服助手核心逻辑。
3. 技术选型与核心组件拆解
确定了架构,接下来就要选择趁手的“兵器”。这一部分,我会详细讲解各个核心组件的技术选型考量、具体配置,以及为什么这么选。
3.1 大语言模型:是选巨舰重炮,还是轻装快艇?
LLM是整个系统的大脑,它的选择直接决定了助手的理解能力、响应速度和成本。我的评估维度主要在以下几点:能力、速度、成本、可控性。
- 云端大模型:我首先尝试了GPT-4。它的理解能力和指令跟随能力无疑是最顶尖的,在复杂的意图分类和回复生成上表现非常稳定。但缺点也很明显:API调用有延迟,成本较高,且所有数据需出境,对很多企业场景是硬伤。后来我转向了国内的一些主流API服务,它们能力稍逊于GPT-4,但在中文场景和成本上更有优势。
- 本地开源模型:为了追求极致的数据隐私和可控性,我也测试了在本地部署开源模型。例如使用Ollama一键部署Qwen2.5或Llama 3.2的7B/14B版本。本地部署的好处是数据完全私有,无网络延迟,长期成本低。但挑战在于:需要一定的GPU资源;模型的指令跟随和工具调用能力可能不如顶尖的商用API稳定;需要自己处理模型加载、推理优化等问题。
我的最终方案是混合模式:在开发调试和对外服务时,使用国内可靠的商用API,保证稳定性和强大能力。同时,在内部网络准备一套本地化部署的轻量级模型作为备用和特定场景使用。对于智能客服这种对准确性要求高、但单次交互Token量可控的场景,商用API的性价比是可以接受的。
3.2 向量数据库与知识库检索:让助手“有据可查”
当用户询问“年假规定”时,助手需要从海量的公司文档中找到相关段落。基于关键词的搜索(如“年假”)可能返回一堆无关文档。这里我引入了“向量检索”技术。
其原理是:将我所有的知识文档(员工手册、规章制度、Q&A等)通过一个嵌入模型转化为高维向量(可以理解为一段数字“指纹”),并存储到向量数据库中。同时,将用户的问题也转化为向量。检索时,不再比较关键词,而是计算问题向量与所有文档向量之间的相似度,返回最相似的几个文档片段。
- 嵌入模型:我选用了开源的
text2vec或BGE系列模型,它们在中文语义相似度任务上表现很好,并且可以本地部署,避免数据泄露。 - 向量数据库:ChromaDB和Milvus是两大热门选择。Chroma轻量、易集成,适合快速起步和中小规模知识库。Milvus功能强大、性能卓越,支持分布式,适合海量数据和高并发场景。本项目初期知识库文档不超过一万份,我选择了ChromaDB,它的Python API非常简单,几行代码就能完成存储和查询。
实操心得:文档预处理是关键。直接扔一整本员工手册进去,检索效果会很差。我的做法是:
- 分段:将长文档按段落或自然章节(如“第三章 休假制度”)切分成小块,每块大约200-500字。
- 清洗:去除页眉页脚、无关图表、特殊字符。
- 添加元数据:为每个片段附加来源信息,如“文档标题:2024版员工手册,章节:3.2 年假规定”。这样在返回结果时,不仅能给出文本,还能告诉用户出处。
- 建立索引:将处理好的文本片段批量转换为向量,存入ChromaDB。
这样,当用户提问时,系统会将问题向量化,在Chroma中搜索出最相关的3-5个文本片段,将这些片段作为“参考材料”提供给LLM,让它来合成最终答案。这也就是RAG的经典架构,它能极大减少LLM的“幻觉”,让回答有源可溯。
3.3 后端框架与工具调用:打造灵活的“中枢神经”
后端需要串联起LLM调用、工具执行、会话管理等多个环节。我选择了FastAPI,因为它异步性能好,自动生成API文档,非常适合构建这种需要处理大量IO(网络请求)的AI应用。
工具调用的实现,我避开了重型框架,采用了一种清晰直观的模式:
# 1. 定义工具函数 def search_knowledge_base(query: str): # 连接向量数据库,执行检索 results = vector_db.similarity_search(query, k=3) return "\n".join([f"来源:{r.metadata['source']}\n内容:{r.page_content}" for r in results]) def get_leave_balance(user_id: str): # 模拟调用HR系统API return f"员工{user_id}的年假剩余天数为15天。" # 2. 将工具描述准备好,用于LLM函数调用 tools = [ { "type": "function", "function": { "name": "search_knowledge_base", "description": "从知识库搜索制度政策。", "parameters": {...} # 参数schema } }, # ... 其他工具 ] # 3. 在对话循环中 response = llm_client.chat.completions.create( model="gpt-4", messages=messages, # 包含对话历史 tools=tools, # 传入工具定义 tool_choice="auto", # 让模型决定是否调用工具 ) # 4. 检查模型是否想调用工具 if response.choices[0].message.tool_calls: tool_call = response.choices[0].message.tool_calls[0] if tool_call.function.name == "search_knowledge_base": arguments = json.loads(tool_call.function.arguments) tool_result = search_knowledge_base(arguments["query"]) # 将结果追加到消息中,再次调用LLM生成最终回复 messages.append({ "role": "tool", "content": tool_result, "tool_call_id": tool_call.id }) final_response = llm_client.chat.completions.create(...)这种模式将控制权牢牢掌握在自己手中,每一步都清晰可见,易于调试和扩展。
4. 分步实现与核心代码解析
理论说再多,不如一行代码。这一部分,我将带你走一遍核心功能的实现路径。
4.1 环境搭建与依赖安装
首先,创建一个干净的Python环境,安装核心依赖。我使用requirements.txt来管理。
# requirements.txt fastapi==0.104.1 uvicorn[standard]==0.24.0 # ASGI服务器 openai==1.3.0 # 或其他LLM API客户端 chromadb==0.4.18 # 向量数据库 sentence-transformers==2.2.2 # 用于本地嵌入模型 pydantic==2.5.0 # 数据验证 python-dotenv==1.0.0 # 管理环境变量使用pip install -r requirements.txt安装所有依赖。将API密钥等敏感信息放入.env文件。
4.2 知识库的构建与向量化
这是个体力活,但一劳永逸。我编写了一个脚本build_knowledge_base.py。
import os from chromadb import Documents, EmbeddingFunction, Chroma from sentence_transformers import SentenceTransformer import PyPDF2 # 假设处理PDF # 1. 加载嵌入模型(本地) embed_model = SentenceTransformer('BAAI/bge-small-zh-v1.5') # 2. 自定义嵌入函数,供Chroma使用 class LocalEmbeddingFunction(EmbeddingFunction): def __call__(self, input: Documents): return embed_model.encode(input).tolist() # 3. 初始化Chroma客户端,指定嵌入函数 client = Chroma( collection_name="company_policies", embedding_function=LocalEmbeddingFunction(), persist_directory="./chroma_db" # 数据持久化目录 ) # 4. 读取、分割文档 def split_document(file_path): # 这里简化处理,实际需根据PDF、Word等格式解析 with open(file_path, 'r', encoding='utf-8') as f: text = f.read() # 简单的按句号分割,实际可用更智能的分段器 chunks = [chunk.strip() for chunk in text.split('。') if chunk.strip()] return chunks # 5. 遍历文档目录,添加至向量库 documents = [] metadatas = [] ids = [] doc_id = 0 for root, dirs, files in os.walk("./knowledge_docs"): for file in files: if file.endswith(".txt"): path = os.path.join(root, file) chunks = split_document(path) for i, chunk in enumerate(chunks): documents.append(chunk) metadatas.append({"source": file, "chunk_id": i}) ids.append(f"doc{doc_id}_chunk{i}") doc_id += 1 # 6. 批量添加到集合 if documents: client.add( documents=documents, metadatas=metadatas, ids=ids ) print(f"成功添加 {len(documents)} 个文本片段到知识库。")4.3 实现意图识别与工具调用的对话循环
这是后端API的核心,我将其放在main.py中。
from fastapi import FastAPI, HTTPException from pydantic import BaseModel import json from typing import List, Optional # 假设的LLM客户端和向量数据库客户端 from llm_client import get_llm_response from vector_db import search_similar_text app = FastAPI(title="智能客服助手API") # 定义请求/响应模型 class ChatMessage(BaseModel): session_id: str user_input: str class AssistantResponse(BaseModel): reply: str session_id: str tools_called: Optional[List[str]] = None # 预定义的意图列表(带示例) INTENT_LIST = """ 1. query_policy: 用户想查询公司制度、政策、规定。例如:“年假有多少天?”、“加班申请流程是什么?” 2. handle_process: 用户想办理或发起一个业务流程。例如:“我要报销差旅费”、“申请一台新电脑”。 3. ask_contact: 用户想询问某个部门或同事的联系方式。例如:“IT支持电话是多少?”、“找谁咨询薪资问题?” 4. other: 无法归类或属于闲聊的问题。例如:“你好”、“今天天气怎么样?” """ # 工具定义 TOOLS = [ { "type": "function", "function": { "name": "search_policy", "description": "当用户询问公司制度、政策、规定时,调用此工具从知识库搜索答案。", "parameters": { "type": "object", "properties": { "search_query": {"type": "string", "description": "用于搜索的关键词或问题"} }, "required": ["search_query"] } } }, # 可以定义更多工具,如 get_contact, start_process 等 ] @app.post("/chat", response_model=AssistantResponse) async def chat_with_assistant(message: ChatMessage): session_id = message.session_id user_input = message.user_input # 第一步:意图识别 intent_prompt = f""" 你是一个意图分类器。请根据用户输入,判断其最符合以下哪个意图类别。 仅返回意图类别的名称,不要有任何其他解释。 意图类别: {INTENT_LIST} 用户输入:{user_input} """ intent = await get_llm_response(intent_prompt, temperature=0) # temperature=0使输出更确定 # 第二步:根据意图,准备对话消息和工具 messages = [ {"role": "system", "content": "你是一个专业、友好的公司智能客服助手。请根据工具返回的结果,准确、清晰地回答用户问题。如果工具结果中没有相关信息,请如实告知用户你不知道。"}, {"role": "user", "content": user_input} ] available_tools = [] if "query_policy" in intent: available_tools = [TOOLS[0]] # 只提供政策搜索工具 # 第三步:调用LLM,允许其使用工具 llm_response = await get_llm_response( messages=messages, tools=available_tools if available_tools else None, tool_choice="auto" if available_tools else "none" ) reply_message = llm_response.choices[0].message tool_calls = reply_message.tool_calls called_tool_names = [] # 第四步:如果模型要求调用工具,则执行 if tool_calls: for tool_call in tool_calls: function_name = tool_call.function.name called_tool_names.append(function_name) function_args = json.loads(tool_call.function.arguments) if function_name == "search_policy": # 执行知识库检索 search_results = search_similar_text(function_args["search_query"], k=3) # 将结果作为工具调用的返回内容,追加到消息中 messages.append(reply_message) # 先追加助理的消息(包含工具调用请求) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(search_results, ensure_ascii=False) }) # 第五步:将工具执行结果送回LLM,生成最终回复 final_response = await get_llm_response(messages=messages, tools=None) final_reply = final_response.choices[0].message.content else: # 如果没有工具调用,直接使用LLM的回复 final_reply = reply_message.content # 第六步:更新会话历史(此处简化,实际应持久化存储) # session_manager.update(session_id, user_input, final_reply) return AssistantResponse( reply=final_reply, session_id=session_id, tools_called=called_tool_names if tool_calls else None )4.4 前端简单对接:一个聊天界面
为了让测试和演示更方便,我用简单的HTML和JavaScript写了一个聊天界面。
<!DOCTYPE html> <html> <head> <title>智能客服助手</title> <style> /* 简单的样式 */ #chatbox { height: 400px; border: 1px solid #ccc; overflow-y: scroll; padding: 10px; } .user { text-align: right; color: blue; } .assistant { text-align: left; color: green; } </style> </head> <body> <h2>智能客服助手</h2> <div id="chatbox"></div> <input type="text" id="userInput" placeholder="请输入您的问题..." style="width: 70%;"> <button onclick="sendMessage()">发送</button> <script> let sessionId = 'session_' + Math.random().toString(36).substr(2, 9); function appendMessage(sender, text) { const chatbox = document.getElementById('chatbox'); const msgDiv = document.createElement('div'); msgDiv.className = sender; msgDiv.innerHTML = `<strong>${sender}:</strong> ${text}`; chatbox.appendChild(msgDiv); chatbox.scrollTop = chatbox.scrollHeight; } async function sendMessage() { const input = document.getElementById('userInput'); const userText = input.value.trim(); if (!userText) return; appendMessage('用户', userText); input.value = ''; try { const response = await fetch('http://localhost:8000/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId, user_input: userText }) }); const data = await response.json(); appendMessage('助手', data.reply); } catch (error) { appendMessage('系统', '抱歉,服务暂时不可用。'); console.error(error); } } </script> </body> </html>将上述前端代码保存为index.html,用浏览器打开,后端服务运行在localhost:8000,一个最简单的智能客服对话界面就完成了。
5. 避坑指南与性能优化实战
在实际开发中,我遇到了不少预料之外的问题。这里总结几个最具代表性的“坑”和解决方案。
5.1 意图识别漂移与边界模糊
问题:初期,LLM在意图分类时,偶尔会将“报销流程是什么?”(应归类为query_policy)错误地归类为handle_process(办理流程)。这是因为问题本身确实同时包含了“查询”和“办理”的语义。
解决方案:
- 细化意图定义:将
query_policy和handle_process的描述写得更具区分度。例如:query_policy: “用户想了解、查询现有的制度、政策内容、规定细节。问题通常是关于‘是什么’、‘有多少’、‘怎么规定的’。”handle_process: “用户想发起、办理、操作一个具体的、需要审批或执行的动作。问题通常包含‘我要’、‘怎么申请’、‘如何办理’等动词。”
- 增加拒绝意图:明确增加一个
cannot_handle意图,用于处理超出范围或模糊不清的问题,并设计对应的回复话术,如“这个问题我暂时无法处理,您可以联系XX部门”。 - 引入置信度阈值:在调用LLM进行意图识别时,可以要求其同时输出一个置信度分数(如果API支持)。对于置信度低于某个阈值(如0.7)的结果,不直接采用,而是转入人工确认流程或引导用户澄清问题。
5.2 工具调用不稳定:LLM“乱”调用或不调用
问题:有时用户明明在问政策,LLM却调用了获取联系人的工具;有时该调用工具时,它却尝试自己编造答案。
解决方案:
- 优化工具描述:工具的名称和描述必须极其清晰、无歧义。描述中应明确指出该工具的适用场景和输入要求。例如,
search_policy的描述可以写成:“仅当用户明确询问公司内部规章制度、管理办法、操作手册等已有文档记载的内容时使用。输入应为简明的关键词或问题摘要。” - 系统指令强化:在每次对话的System Prompt中,反复强调助手的行为准则:“你必须根据用户问题的意图,决定是否使用工具。在回答关于公司制度、流程、联系信息的问题前,必须先使用相应的工具获取准确信息,严禁凭空编造。”
- 后置校验:在代码层面,当LLM返回工具调用请求时,可以做一个简单的规则校验。例如,如果用户输入中明显包含“怎么联系”而LLM却调用了政策搜索工具,可以忽略此次工具调用,强制LLM重新思考或直接给出预设回复。
5.3 知识库检索“答非所问”
问题:向量检索返回的文本片段,有时语义上相关,但并非问题直接对应的答案。比如问“年假有几天”,可能返回“病假申请流程”的片段,因为里面都提到了“假”。
解决方案:
- 优化查询向量:不要直接将原始用户问题作为检索查询。可以先用LLM对用户问题进行重写或摘要,提炼出更核心、更利于检索的关键词。例如,将“我如果想请年假,该怎么操作啊?”重写为“年假 申请 流程 步骤”。
- 混合检索:结合向量检索和关键词检索。先用向量检索找到语义相关的文档,再用BM25等传统算法对结果进行精排,过滤掉关键词匹配度极低的结果。
- 元数据过滤:在存储文档时,为不同类别的文档打上标签(如
document_type: leave_policy)。检索时,可以先根据意图识别结果,在向量数据库中增加元数据过滤条件,缩小搜索范围。
5.4 响应速度与成本控制
问题:每次对话涉及多次LLM API调用(意图识别、可能的主LLM调用、回复生成),导致响应慢、成本高。
解决方案:
- 意图识别模型轻量化:意图分类任务相对简单,不需要动用最强大的模型。可以换用更小、更快的模型(如GPT-3.5-Turbo,或专门的轻量级分类模型),甚至训练一个小的文本分类模型,速度更快,成本极低。
- 缓存策略:
- 答案缓存:对于高频、标准的问题(如“公司地址”),其答案一旦生成,可以缓存起来。下次遇到相同或高度相似的问题时,直接返回缓存答案,绕过LLM和检索。
- 向量缓存:用户问题和经过重写的问题向量也可以缓存,避免重复计算。
- 设置超时与降级:为LLM API调用设置合理的超时时间(如5秒)。如果超时,则触发降级策略,例如返回一个预设的“正在思考,请稍后”的提示,或者从更简单的本地模型中获取一个基础答案。
5.5 会话上下文过长导致性能下降
问题:随着对话轮数增加,传入LLM的上下文越来越长,导致API调用Token消耗剧增,响应变慢,甚至可能超过模型的最大上下文限制。
解决方案:
- 摘要式记忆:不要无脑地将所有历史对话原文都塞进上下文。可以在每轮对话后,或用定时任务,用一个LLM对之前的对话历史进行摘要,然后用摘要代替冗长的原文,作为下一轮对话的历史。这样既能保留核心信息,又能大幅缩短上下文。
- 选择性记忆:只保留与当前任务最相关的历史对话。例如,如果当前意图是
query_policy,可以只保留历史上同属query_policy的对话轮次。 - 设定上下文窗口上限:硬性规定只保留最近N轮对话(如10轮)。这是一种简单粗暴但有效的方法,适用于大多数短对话场景。
6. 效果评估与未来迭代方向
经过一周的密集开发和调试,这个“肝”出来的智能客服助手已经可以处理团队内部大约70%的常见咨询。它的优势在于回答准确、有据可查,并且能进行简单的多轮对话。我将它集成到了内部办公软件的群聊机器人中,初期由人工客服在旁监督,纠正它的错误回答,这些纠正数据又反过来成为了优化模型的宝贵素材。
当前效果评估:
- 准确率:在政策查询类问题上,由于严格依赖向量检索提供的片段,准确率很高,幻觉率低于5%。
- 用户体验:响应速度在2-5秒内,对话流畅度尚可,但面对非常口语化或包含大量背景信息的复杂问题时,意图识别仍会出错。
- 成本:平均单次对话(含意图识别和主回复)的API调用成本控制在几分钱以内,对于内部使用完全可以接受。
未来的迭代想法:
- 流程自动化集成:当前的
handle_process意图还只是返回一个链接或说明。下一步是真正对接OA、财务等系统的API,实现“一句话发起流程”。例如,用户说“我要请三天年假”,助手能自动填写请假单并发起审批。 - 多模态输入:支持用户上传图片(如发票、单据),通过视觉模型识别内容,并结合到对话中。
- 持续学习与优化:建立一个简单的反馈系统,让用户可以对回答进行“点赞”或“点踩”。收集到的错误案例,可以定期用于优化意图分类模型、补充知识库、或调整提示词。
- 个性化:结合员工身份信息,提供个性化的回答。例如,同问“年假余额”,不同员工得到的是各自真实的剩余天数。
这一周的实践让我深刻体会到,基于LLM构建应用,技术实现只是第一步,更关键的是对业务场景的深度理解、对边界的清晰定义,以及持续迭代优化的耐心。这个项目就像一个“数字员工”,你需要像培训新人一样去“培训”它,而这个过程本身,充满了挑战和乐趣。希望我的这些经验,能为你启动自己的AI项目提供一块坚实的垫脚石。