做银行智能客服项目时,我踩过不少坑,也积累了不少经验。这里基于我实际落地的一个模拟项目——用Langchain加RAG(检索增强生成)路线搭建的某商业银行智能客服系统,把设计与开发过程中的关键决策、实现细节和避坑经验完整梳理出来,希望对正在做同类系统的朋友有实际参考价值。
1. 为什么银行客服系统需要Langchain与RAG的组合
1.1 传统客服系统的三大痛点
先说说我为什么没有沿用传统的意图识别加知识库问答方案。过去几年银行客服系统最常见的是基于规则或基于BERT类模型的多轮对话框架,实际运营中问题非常明显:第一,意图识别语料标注成本极高,一个支持几十个意图的客服系统,维护语料的人力投入远超预期;第二,知识库更新滞后,新上线产品或者业务规则调整后,往往需要业务人员重新整理FAQ并逐条配置,周期以周计;第三,对用户开放式提问的泛化能力弱,换个问法可能就识别不出,更不用说那种口语化很重的用户输入。
这些痛点意味着如果继续用传统NLP管道,系统永远不会具备真正意义上的"智能",只是把已知问题做了一次穷举。所以当这个模拟项目提出"基于大模型做银行客服"时,我重点考虑的是如何让大模型在回答时真正依托业务知识,而不是一本正经地胡说八道。
1.2 RAG的核心价值:为生成式模型外挂可信知识源
大模型本身缺乏对特定银行业务知识的了解,直接让它回答开户、转账、理财等专业问题,大概率会给出通用但不可用的答案。RAG的思路很简单:每次收到用户问题,先到专属知识库中检索出最相关的业务文档片段,把这些片段作为上下文交给大模型,让模型基于检索结果组织答案。
这个方案最让我看重的有两点。一是检索结果可溯源,系统最终给出答案时能够附带来源文档,这对银行合规要求来说几乎是硬指标;二是知识更新成本极低,业务人员只需要把新文档上传到知识库,无需重新训练模型,这在业务节奏快的场景下优势巨大。
1.3 RAG路线与模型微调路线的取舍
做技术选型时,不少团队会纠结是微调还是RAG。我的结论很明确:两者解决的问题完全不同,且可以互为补充。微调解决的是"模型说话风格与领域术语适配"的问题,RAG解决的是"模型不知道某个事实"的问题。银行客服中恰恰是后者占大头——客户问的是具体业务政策,这些信息更新频繁,微调模型去背这些事实性内容既不经济又难维护。
我当时对团队的建议是:基线版本纯粹走RAG,零微调。如果后期发现模型在专业术语表达上不够规范,再考虑用少量高质量对话数据做低秩适配。这样把风险拆解,每一步都可回退,不会出现微调后模型效果反而变差、又无法快速定位问题的尴尬局面。
2. 系统整体架构设计思路
2.1 四个核心模块的职责划分
整个系统在逻辑上拆成了四个模块,边界清晰。
第一个是知识库构建模块,负责把业务文档(产品说明、业务规则、常见问答)解析、切片、向量化后存入向量数据库。第二个是Query理解模块,负责对用户问题做预处理、改写、意图判定,同时识别当前会话是否在已配置的业务范围内。第三个是检索模块,负责从向量库中找到与当前问题最相关的知识片段,并进行重排序过滤。第四个是生成模块,负责组装上下文和提示词,调用大模型生成最终回答,并对回答做合规检查。
这个划分的好处在于每个模块可以独立调优和替换。比如检索效果不理想,我只需要替换Embedding模型或加入重排序层,生成模块和其他模块完全不受影响。
2.2 检索链路的三个关键阶段
用户提问进入系统后,先经过一个查询改写阶段。我一开始没有这一层,结果很多口语化问题召回效果很差,后来发现原因在于:用户的原始表达与知识库文档中的正式书面语之间存在严重的词汇鸿沟。加入改写层后,系统先用大模型把用户口语转为更接近文档表达方式的检索查询,再去做向量检索,召回率明显上了一个台阶。
第二阶段是混合检索。只靠向量检索是不够的,因为向量相似度对短文本、专有名词、产品代码这类信息不敏感。银行场景里,客户问"灵通快线"这种产品名时,文档里出现的可能是"TGXX(灵通快线)"这种带代码的写法。所以我在系统中同时保留了基于关键词的稀疏检索,与向量检索结果做加权融合,保障了这类硬匹配场景的命中率。
第三阶段是重排序。向量检索召回Top 20后,如果直接全部塞给大模型,不仅会超过上下文窗口限制,还会因为引入大量无关信息干扰回答。所以我增加了一个重排序层,用交叉编码器对召回结果逐条精排,取前5条作为最终上下文。这一步在实际上线评测中,将答案准确率提升了近10个百分点。
3. 核心实现细节与关键代码
3.1 文档清洗与切片策略
文档清洗是知识库建设中容易被忽视但影响极大的环节。我处理的文档格式五花八门:PDF、Word、Markdown、Excel表格都有。最开始我拿原始PDF直接解析,结果页眉页脚、目录页码、表格边框全混进了文本里,切片质量惨不忍睹。
我的做法是先做一套标准化的清洗流程:PDF统一转成文本后用正则去页码和页眉;表格类内容单独识别,转成Markdown格式保留行列结构;对重复出现的公共条款模板做去重处理。清洗完成后才进入切片环节。
切片参数经过多轮实验,最终采用的是按标题层级优先分段、每段不超过700字、重叠窗口100字的方案。这个方案考虑了银行文档的特性:很多业务规则是一、二、三级标题嵌套的树形结构,按标题切能最大程度保证语义完整性。重叠窗口的作用是避免某个关键规则恰好被切在边界处导致丢失。
from langchain.text_splitter import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter headers_to_split_on = [ ("#", "H1"), ("##", "H2"), ("###", "H3"), ] markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) sections = markdown_splitter.split_text(cleaned_doc) text_splitter = RecursiveCharacterTextSplitter( chunk_size=700, chunk_overlap=100, separators=["\n\n", "\n", "。", ";", ",", " ", ""] ) final_chunks = [] for section in sections: chunks = text_splitter.split_text(section.page_content) for chunk in chunks: final_chunks.append( f"{section.metadata.get('H1', '')} {section.metadata.get('H2', '')} {section.metadata.get('H3', '')}\n{chunk}" )这里有一个我踩过的比较典型的坑:切片时如果只做机械的长度切分,不保留文档上下文信息,后续检索时对条款的归属判断会很困难。我上面代码中把H1到H3的标题信息拼在每个切片前,就是为了让每个切片即使独立出现,也自带上下文锚点。回答理财类问题时,模型能清楚地知道前文在说哪个产品的哪个规则。
3.2 Embedding模型选型与向量库选择
Embedding模型决定了检索质量的天花板。我实验过多个模型,最终用的是某开源中文文本向量模型,在银行语料上的效果最好。选择依据不是公开榜单分数,而是我自己在领域数据上做的评测:从知识库中随机抽200条问题,人工标注正确答案后,对比不同模型的召回命中率。
向量库选的是轻量级的开源方案,部署简单,单机也能跑得很好。选它还有一个现实考量:项目期间团队没有专门的运维资源,越简单越不容易出问题。如果后续数据量超过千万级,再平滑迁移到分布式方案即可。
实际上很多人会问,为什么不直接用云厂商的向量数据库?我的看法是:对于知识库规模在几十万到几百万条的客服场景,本地开源方案完全够用,而且数据安全可控。银行数据合规要求严格,不出私有化环境永远是第一原则。
3.3 提示词模板与上下文组装
生成环节最关键的不是大模型本身,而是Prompt模板设计。我的模板包含四个部分:系统角色设定、检索到的参考内容、用户对话历史、当前问题。其中角色设定明确要求模型:只基于给定的参考内容作答,如果参考内容中没有相关信息,必须回答"抱歉,这个问题我暂时无法回答,建议您转人工"。这个兜底要求在银行场景中非常关键,宁可答不上来,不能瞎编。
下面是经过多次迭代后的Prompt模板主体:
SYSTEM_PROMPT = """你是某银行的智能客服助手,负责解答用户关于银行业务的咨询。 严格遵循以下规则: 1. 只能依据下方【参考内容】中的信息作答,不得使用外部知识或自行推测。 2. 当【参考内容】中找不到能回答用户问题的信息时,回复: "非常抱歉,我暂时无法准确回答您的问题,正在为您转接人工客服,请稍候。" 3. 回答需简洁规范,使用书面语,重要条款需完整引用原文。 4. 涉及金额、日期、利率时,必须与参考内容完全一致,不得修改或近似。 【参考内容】 {context} 【对话历史】 {chat_history} 【用户问题】 {question} 【回答】"""这个模板看起来简单,但每一句规则都对应着实际问题。第二条第2条解决了知识缺失时模型随意编造的问题,第4条解决了数字类信息幻觉的问题。我在测试中发现了模型有"四舍五入"导致利率数字错误的情况,所以明确加了"完全一致、不得修改"的约束。
组装上下文的顺序也影响回答质量。我采用先参考内容、再历史对话、最后问题的顺序,让模型在阅读大量检索片段后,依然能清楚判断当前最需要响应的目标。如果问题放中间,模型可能会把历史对话中的某个提问误当成当前任务。
3.4 引用溯源与拒答机制
银行客服对回答的每一个事实性结论都要求可追溯。我的做法是:检索模块在返回切片时保留文档ID和原文路径,通过元数据传递到生成模块。回答生成后,系统会做一个匹配检查:如果模型引用了某个具体条款,该条款文本能否在检索切片中找到对应原文。如果找不到或严重偏离,就直接拦截这条回答,改为走转人工流程。
拒答机制同样重要。除了Prompt中的兜底规则,我还在系统层面设置了两层防线:第一层通过意图分类判断问题是否在银行客服的服务范围内,如果问题与银行业务完全无关,直接礼貌拒绝回答;第二层在生成后对回答文本做一次检测,判断是否包含参考内容之外的自主断言。
def generate_answer(question, context_chunks): context = "\n\n".join([c.page_content for c in context_chunks]) prompt = ChatPromptTemplate.from_messages([ ("system", SYSTEM_PROMPT), ("human", "{question}") ]) chain = prompt | chat_model result = chain.invoke({ "context": context, "chat_history": format_history(session_history), "question": question }) if is_answer_safe(result.content, context_chunks): return result.content else: return FALLBACK_ANSWER最初上线时,我发现有少量回答声称来自"根据我行规定",但实际参考内容中根本没有对应条文。排查发现问题出在模型把训练时记忆的通用银行规则混进了回答。后来加入了上面的安全性校验,凡是在参考切片中找不到依据的结论性陈述,一律拦截替换为转人工话术。这个机制上线后,虚假信息占比从肉眼可见降到了几乎为零。
4. 实操过程与踩坑经验
4.1 开发环境与工具链搭建
开发过程中,我建议使用以下工具链组合:Langchain作为对话流程编排框架,一个开源向量数据库用于存储切片向量,一个本地推理服务用于运行Qwen基座模型。这一套组合的好处是全部可私有化部署,不依赖外部服务。模型推理我推荐使用国产开源底座,在银行领域中文表达能力强,且对金融术语的理解深度好于同量级通用模型。
环境搭建中最大的坑是用Langchain的版本兼容性。Langchain的API迭代很快,网上的示例代码经常用的是旧版本写法,直接用在新版本上会报错。我经历过某次升级后,原来的向量检索调用方式直接废弃,不得不重写调用代码。建议团队在项目开始时锁定版本,并显式写入依赖文件,不要使用最新版或按月中自动更新的浮动版本。
4.2 检索效果调优的完整路径
检索效果是整个系统效果的基石,我在调试过程中逐步摸索出了一套调优路径,并做成了每次效果回归时固定运行的测试集。这个测试集包含200条真实客服场景问题和人工标注的期望答案,每次改动后跑一遍对比准确率。没有这个测试集,所有优化都会变成无源之水。
第一轮优化是调整Embedding模型。换模型后125条记录中的命中数明显上升,效果直观可见。第二轮加入查询改写后,口语化问题的召回率大幅改善。第三轮加入重排序后,答案准确率显著提升,这是系统效果提升最明显的一轮。
另外我还调整了一个容易被忽略的参数——向量检索时返回的候选数量。一开始Top 20输入给重排序,发现有不少冗余内容干扰回答质量;后来减少为Top 15并用重排序取前5,回答的聚焦程度反而更好。这说明在知识库内容质量参差不齐时,过于贪婪的召回未必是好事。
4.3 生成环节参数调优与安全兜底
生成环节的温度参数我设置得非常保守,采用了较低的数值。客服场景几乎不需要创造性发挥,数值越低输出越稳定,这对数字类信息表述的准确性和合规性至关重要。过高的采样温度会导致同一个问题在不同轮次给出措辞不一致的答案,这在客服场景是硬伤。
此外,我还在生成时加了敏感信息过滤。如果检测到用户输入中包含银行卡号、身份证号等可能涉及隐私的信息,系统会先遮罩处理,仅保留后四位用于对应用户身份,完整信息不进入Prompt上下文。这样避免了大模型对话日志中留存完整敏感信息的合规风险。
4.4 性能优化与并发设计
上线前的压测让我发现了一个严重问题:大模型单次推理延迟在数秒级别,远高于传统客服系统的响应要求。解决方案分了两条腿走路。第一条是引入流式输出,前端逐字显示回答内容,这样虽然总耗时没变,但用户感知的等待时间大幅缩短。第二条是在知识库检索结束后,按相似度置信度做一个分级判断:如果检索结果置信度非常高,且问题类型属于简单FAQ级别的查询,走一个轻量级的短模板回复路径,不调用大模型,以此分流大部分简单问题,把大模型资源留给真正复杂的咨询。
另外一个细节是缓存策略。高频问题(如查余额、挂失、修改密码等)在一天内的重复率非常高,把这些问答结果按天缓存一份,第二天直接命中,能够大幅降低模型推理压力。实测在缓存生效后,系统整体平均响应时间直接降到了可用范围。
5. 常见问题与排查技巧实录
5.1 问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 回答完全偏离文档 | 检索未召回相关片段 | 检查向量检索Top结果中是否包含正确答案 | 优化切片粒度、调整Embedding模型、加入关键词检索 |
| 回答出现编造条款 | 模型幻觉 | 审查参考内容中是否包含该条款原文 | 加入回答安全性校验,拦截无依据陈述 |
| 数字类信息错误 | Prompt约束不足 | 对比回答与原文中的金额、日期 | Prompt中加入"完全一致、不得修改"约束 |
| 口语化问题答不上 | 查询改写失效 | 查看改写结果是否语义偏转 | 优化改写Prompt,加入银行业务术语词典辅助 |
| 回答内容不一致 | 采样温度过高 | 同一问题多次测试对比 | 降低温度参数,保持输出确定性 |
| 知识库更新不生效 | 向量库未重新写入 | 检查更新流程是否完整执行 | 增加增量更新机制,文档级版本比对 |
5.2 三个值得分享的实战经验
第一个经验是关于切片长度。最初我把切片长度设定得比较大,将近1000字,想着内容多了模型信息更全。结果发现检索召回的切片里经常只有一半内容与问题相关,另一半是无关规则。后来把切片降低至700字配合重叠窗口,整体问答准确率反而上升了。这说明切片粒度要和问题的知识单元粒度对齐,银行文档里一个完整的业务规则通常在几百字左右,切得太粗信息熵太高。
第二个经验是关于查询改写。改写Prompt如果不加约束,大模型会把用户的问句改写成完全不同的语义,反而让检索更差。后来我加了一个关键约束:仅替换同义表达,不新增信息,不补充问题之外的限定条件。这个约束能保证改写的安全边界,在此基础上做词汇层面的书面化替换效果最好。
第三个经验是不要忽略日志。这个项目中排查问题依赖的关键就是完整记录每次提问的检索结果与生成结果。我曾在某个问题无法回答的case中,靠日志发现是检索环节出了问题而非生成环节。没有日志,你面对一个答错的case去盲调,纯粹靠运气。我会把每次问答的查询改写文本、Top候选切片、精排结果、最终回答文本全部记录在案,并附带耗时、命中的知识库文档ID。有了这些数据,任何一次效果回退都能快速定位到具体环节,而不是全局瞎猜。
5.3 知识库增量更新机制
银行知识库更新频繁,存量文档里最重要的维护点是新旧政策切换。我设计了文档级别的版本标记:每个文档在入库时都会记录生效日期和版本号,检索阶段自动过滤已失效的旧版本。有一个真实的经验教训:曾经发布了一个新版理财产品说明,但旧文档权重更高,导致模型在回答时引用的是旧利率信息。加入版本过滤后,此类问题彻底绝迹。
增量更新的另一个关键点是处理"同一文档的局部修订"。简单粗暴的删旧插新会丢失切片间的关联关系,我的做法是按文档的目录结构做差分对比,只更新变化的分支章节,保持其他切片ID稳定不动。这样不仅省一次全量向量化计算,也保证了已被缓存回答的高频问题不会因为无关修订而失效。
6. 后续可做的扩展方向
这个项目落地后,我认为有几个方向的扩展价值最大。
第一是加入多轮对话的指代消解和意图补全能力。目前的系统对单轮问答已经比较成熟,但银行客服场景中大量问题依赖上下文,例如用户先说"我要挂失",再问"怎么收费",系统需要理解"收费"指的是挂失手续费。这类多轮补全用Langchain的对话记忆加一轮查询改写就能实现,改造成本不高。
第二是知识库质量主动监测。我计划做一个定时任务,周期性对知识库做全文检索效果巡检:随机采样一批问题,跑一遍检索链路,把低置信度结果汇总成报告,推送成待优化清单。这个思路本质上是把检索质量变成可观测指标,避免系统上线后效果衰减而不自知。
第三是智能转人工。目前拒答时只是返回固定话术,未来可以做成自动摘要——把用户问题、检索到的可能相关内容、系统回答失败的原因一并整理成结构化记录,随转人工工单推送给坐席,让坐席在接通用户前就已经了解问题全貌。这项体验优化能明显降低人工坐席的重复沟通成本。
从我个人的实际项目经验来说,用Langchain加RAG做银行智能客服的路线是走得通的,而且相对于传统方案优势明显。最大的价值在于知识迭代效率从以周为单位变成了以小时为单位,同时回答可溯源、可审查。当然,系统的上限取决于知识库的质量和切片策略的精细程度,这需要持续投入,而不是靠一套标准流程打天下。如果再做一次,我会在项目初期就把评测集和日志体系建好,这两项工具越早完善,后期迭代效率越高。希望这篇文档对正在规划同类系统的团队有参考意义。