前阵子我接了个活儿,要把两个互不相干、都快被时代忘掉的传统系统,统一接到大模型上。一个是最早那批老库存系统,另一个是散落着大量 FAQ 和操作文档的客服知识库。名字我就不说了,但在很多公司里你一定见过类似的东西——界面停在十年前,业务逻辑藏在 SQL 和 Excel 里,换个人就玩不转。
这个项目做完之后,最大的感受是:用大模型做集成,真正值钱的不是“聊天”,而是给它装上一双能理解旧系统的“手”。这篇文章想把整个思路、选型过程、落地步骤,以及我踩过的坑原原本本写出来。无论你是想给公司老系统做个 AI 入口,还是单纯好奇大模型怎么和遗留系统打交道,这篇应该能给你一份可以直接参考的底稿。
1. 项目拆解:两套传统体系,到底卡在哪里
1.1 第一套体系:跑了快十年的老库存系统
先说第一套。这套库存系统是公司最早自己开发的,后端用的还是那几年流行的 Java 版单体框架,前端是 PC 时代的传统界面。数据库倒是不小,所有仓库、采购、退换货的数据都在里面。但问题也很典型:对外只开了很少的接口,大部分数据得靠直接连数据库查,业务逻辑大量写在存储过程里,加了什么表、改了什么状态,没有完整文档。
最头疼的是,这套系统里有大量“人肉规则”。比如某个型号的库存低于某个数值就要自动触发采购提醒,但临界值不在配置表里,而是写死在代码中的;再比如某些商品在特定时间段不允许出库,这个规则没人说得全,只有操作了几年的老员工知道。
我在梳理它的接口时,发现正式文档只描述了十来个子系统的功能,但实际能被稳定调用的也就五六个。想做集成,光搞清楚“哪个字段对应哪个含义”就够呛。
1.2 第二套体系:没人愿意维护的知识文档库
第二个体系看起来不像“系统”,更像一堆散落文档。客服团队维护了一份知识库,包含产品的常见问题、退换货流程、话术模板;仓库团队又有自己的操作手册;采购部门偶尔还会往里丢几份供应商说明。这些内容主要存在共享盘里,格式极不统一,有 Word、有 PDF、还有聊天记录导出的文本。
表面上看,知识库应该有很清晰的结构,但实际内容严重重复且互相矛盾。比如客服规范里说退货要“原包装完整”,仓库手册里写的是“外包装无明显破损即可”。这类信息人眼都容易看岔,更别说程序了。新员工培训期很长,很大程度上就是因为“真正的规矩”不在文档里,都在老同事脑子里。
这第二套体系最大的问题不是“没有数据”,而是“数据太乱”。结构化的库存数据在一个系统里,非结构化的文档知识又在另一个地方,两套体系长期割裂。过去有人问“某型号库存够不够、能不能发”,客服需要先去查库存系统,再回来翻文档,最后靠经验判断。流程又慢又容易出错。
1.3 为什么不能推倒重来
方案讨论时,有人提过干脆把两套系统全部替换掉,重新上一套现代化系统。但被我否了。原因不复杂:老库存系统承载的业务流程非常具体,所有上下游系统早就按它的接口做了对接,贸然替换涉及大量旧数据迁移、接口重新适配、人员重新培训,成本极高,而且业务部门能接受的上线时间窗口非常短。
知识文档也同理,里面的内容虽然乱,但覆盖了几年来的真实问题,是宝贵的业务积累。如果推倒重来,这些积累就丢了。与其推翻,不如在不动原有体系的前提下,加一层新能力,让旧系统继续跑,同时让 AI 能“看懂”它们、调用它们。
所以最后定了方向:给两套传统体系外面包一层大模型接口,不碰老系统内部逻辑,只做连接和翻译。这也是标题里“装进 AI”的真正含义。
2. 选型思路:为什么最后定了大模型当“总调度”
2.1 我也试过:RPA 脚本加硬编码接口
一开始我看过市面上挺常见的套路:用 RPA 模拟人在老系统界面里的点击,配合一些固定脚本,把数据从一个系统搬到另一个系统。这种方式对简单场景确实有效,比如每天定时把库存表同步到某个报表里。
但真做下来就会发现,RPA 处理的是“固定流程”,只要老系统界面按钮挪个位置,或者弹窗文案变一下,脚本就崩。库存系统虽然没有大改版,但时不时会有一些小升级,每次升级我们都要排查一堆脚本问题,维护成本非常高。而且 RPA 只能模拟操作界面,无法理解用户问的到底是库存量还是库位,更不能在多个系统之间做动态判断。
2.2 我也试过:上中间件做企业服务总线
另一个被提到的方案是引入中间件,把两套系统的接口统一注册到一个服务总线上,通过配置流程把它们串起来。这个思路在企业集成领域很成熟,适合系统接口规范、流程明确的场景。
问题是,我们面对的旧库存系统根本谈不上“接口规范”,很多交互要靠数据库视图去补;客服知识库更是连正式 API 都没有。把这类系统强行往中间件上靠,需要先写大量适配代码,等于给每个旧系统再造一套服务层,工作量不亚于重构。而且中间件的配置和部署都比较重,一个小团队很难在短周期内撑起来。
2.3 大模型做“翻译官”和“调度器”的逻辑
后来我换了个角度想:既然问题核心是“两套系统各自为政、交互方式不同”,那能不能让一个更聪明的层来做翻译?大模型具备理解自然语言、识别意图、生成结构化输出、调用外部工具的能力,正好适合当这个“翻译官”。
具体来说,我不需要大模型去理解库存系统底层怎么存储,只需要给它定义好“能调用的工具清单”,它就能根据用户的问题自动选择工具、生成参数、发起调用。对客服知识库,大模型可以结合检索技术,从文档里抽取答案,再叠加库存系统的鲜活数据,给出一个综合回复。
这个方案的选型逻辑没有那么玄乎,核心就一句话:让擅长理解的人(大模型)去协调几个不善表达的老系统,而不是逼老系统学会新规矩。相比之下,这种方式既有灵活性,又不用动老系统的根基。
3. 落地实操:把两套老系统“接”进 AI 的四步走
3.1 第一步:给老系统清点“家底”,列工具清单
任何集成工作,第一步永远是盘点。我把库存系统的数据库表、存储过程、接口文档全部过了一遍,列出一份“老系统能力清单”。清单很细,包括接口名、入参出参、能做什么、有什么限制。比如“查询库存余额”这个工具,入参是 SKU 编码和仓库编码,返回库存数量、预留数量、可用数量。
知识库这边,我把所有文档做了归类,分成客服话术、商品知识、仓储规则、退换货策略几类。然后给每类文档做清洗——去重、纠错、统一格式。这里注意,清洗不是改写业务内容,而是把明显冲突和无效信息处理掉,为后面做检索做准备。
盘点完,我生成了工具清单一览表。这个表非常重要,后面大模型的调度能力全靠它来驱动。
| 工具编号 | 所属体系 | 工具名 | 入参 | 出参 |
|---|---|---|---|---|
| T01 | 库存系统 | 查询实时库存 | SKU、仓库编码 | 总库存、可用库存、预留库存 |
| T02 | 库存系统 | 查询流转记录 | SKU、起止时间 | 入库单、出库单明细 |
| T03 | 库存系统 | 查询采购在途 | SKU | 采购单号、预计到货时间 |
| T04 | 知识库 | 检索相关文档 | 问题描述 | 文档片段、关联来源 |
| T05 | 知识库 | 查询退换货政策 | 品类、时间 | 政策文本摘要 |
3.2 第二步:用 Function Calling 定义模型的“手”
工具清单出来后,下一步就是让大模型知道这些工具存在。现在主流的大模型都支持函数调用机制,原理非常简单:给模型一份工具描述,它遇到相应问题时,会输出一个结构化的调用指令,而不是自己拍脑袋回答。
以库存系统为例,我定义了一个查询库存的工具描述。关键部分如下:
{ "name": "query_inventory_balance", "description": "根据SKU和仓库编码查询实时库存余额", "parameters": { "type": "object", "properties": { "sku": { "type": "string", "description": "商品编号,例如 SKU-10023" }, "warehouse": { "type": "string", "description": "仓库编码,例如 WH-01" } }, "required": ["sku", "warehouse"] } }这里有个细节值得说:描述字段写得越清楚,模型调用准确率越高。比如“仓库编码”如果不注明格式,模型可能把中文仓库名直接填进去,导致下游报错。所以定义工具时,把格式、边界条件写在里面,是减少后期脏数据的核心手段。
对知识库系统,我没有走函数调用,而是用了另一种方式。先把文档切块并向量化,建了一个小规模的检索库。用户提问时,先做一次向量检索,把相关片段拉出来,再拼接进提示词,让大模型基于这些片段回答。这种做法就是常说的 RAG(检索增强生成)。实际上,它也是一种“工具”,只不过工具的返回内容是文档片段。
3.3 第三步:写一个联接层,藏住老系统的脾气
工具定义好了,还不能直接让用户请求去碰老系统。我在中间加了一个轻量的联接服务,专门负责两件事:一是接收大模型的调用指令,转成老系统能识别的请求;二是把老系统的原始返回,转成大模型能理解的规范结构。
这个联接层我用的是一个简单的后端服务,里面封装了各系统的客户端。比如查询库存时,大模型给出了 sku 和 warehouse,联接层负责去调老库存系统暴露的接口,把结果转成 JSON 返回。老系统返回慢、偶尔超时,这些异常也都在联接层处理,不会赤裸裸抛给用户。
加这一层还有一个好处:权限审计变得简单。所有请求都经过联接层,谁在什么时间问了什么、调用了什么工具,都可以记录。老系统不知道大模型的存在,也不需要为它改造,这就大大降低了项目风险。
3.4 第四步:接入 RAG,把文档知识也变成可查询的内容
第四步是把前面讲的检索能力真正嵌入对话流程。我设计了一个简单但有效的流程:
用户提问题,先由调度模块判断问题适合调用工具还是检索文档,还是两者都做。比如“XX 型号库存还有多少,现在能退货吗”,这个问题的答案既需要实时库存数据,也需要退换货政策。系统就会同时触发库存查询工具和文档检索,然后把两部分结果一起交给大模型组织语言。
为了不让检索结果过于死板,我把知识文档做了二次整理:重要政策按“场景-条件-结论”重写,比如“退货条件:商品需未使用,配件齐全,签收后 7 天内可申请”。这种结构化文本的检索效果,比把大段原文切块再检索要好很多,因为答案更聚焦,大模型生成时不容易被无关信息带偏。
4. 关键细节:模型不乱说话、旧系统不崩盘的兜底设计
4.1 提示词里放“系统手册”,而不是命令堆砌
很多人以为让大模型调用工具,就是把工具堆在提示词里就行。实际不是。我更推荐在提示词里维护一份“系统手册”,明确告诉模型三件事:我是谁、我能调用什么、我绝不能做什么。
下面是当时系统提示词的压缩版示例:
你是一个企业智能助手,负责处理与库存和客服知识相关的问题。 你可以调用外部工具获取实时库存和流转记录,也可以参考知识库文档片段回答规则类问题。 规则: - 如果用户要求查询库存,必须先调用查询工具,不要根据猜测回答。 - 如果文档片段与实时数据冲突,优先以实时数据为准。 - 如果工具返回错误或超时,不要编造结果,请明确告知系统暂时无法获取数据。 - 回答时使用简洁的书面语,不要编造不存在的单据编号。这套提示词的精髓不是让模型更“聪明”,而是给它设边界。边界越清楚,越不会出现模型自己编一个库存数来糊弄人的情况。我在实际测试中发现,加上这几条规则后,幻觉概率大幅下降。
4.2 返回结果用 JSON Schema 和枚举控制
模型输出不稳定是常见问题。比如它回答“可用库存是 120 件”可以是自然语言,也可以是一段话里带着表格。为了让下游稳定解析,我规定了统一输出格式:所有答案必须包含两个字段——summary(给用户看的摘要)和data(结构化数据)。
我还做了更严格的约束:像“库存状态”这类字段,我直接在提示词里限定只能是可用、预警、缺货三个枚举值,不允许模型自由发挥。类似地,单据编码必须匹配指定正则格式。有了这些约束,即使模型上下文有歧义,最终生成的结构也能被程序安全消费。
4.3 超时重试和降级策略
老系统的接口稳定性一般,高峰时段经常要等好几秒。我的联接层做了三层兜底:第一层,接口超时时间设为 5 秒,超过就返回超时标记;第二层,对可重试的查询做一次重试,最多重试两次;第三层,如果重试仍失败,就降级为只返回知识库检索结果,并在回复里注明“实时库存暂不可用,已为您展示参考信息”。
这套降级策略非常实用。因为用户问的问题往往是可以容忍部分信息的——比如“能不能退货”,即使库存查不到,只要政策信息够,也能给出基本答复。不要追求所有问题都必须完成全部工具调用,让系统在部分失败时仍能优雅应对,才是生产环境该有的状态。
5. 踩坑记录与排查速查表
5.1 坑一:老系统接口对输入格式极挑剔
第一次联调时,我发现库存接口返回空结果。排查了半天,原因是日期格式,老系统只接受“yyyy年MM月dd日”这种格式,而我的联接层默认传的是“yyyy-MM-dd”。这种问题用大模型根本没法发现,只能在联接层里做统一的格式转换,把老系统的“方言”提前消化掉。
教训:在给大模型定义工具前,先花时间把老系统所有参数的格式边界摸清楚,宁可多试几次,也别假设它符合常识。
5.2 坑二:大模型一本正经地编造字段
测试阶段,用户问“某商品在途订单到货时间”,模型在没有调用采购在途查询工具的情况下,直接回答“预计下周二到货”。这种错误很危险,因为看起来非常可信。
后面我在工具定义和提示词里都做了强化:凡是涉及入库时间、库存数量的查询,必须先调用对应工具,且工具返回无数据时不允许推测。加了一轮自动检测逻辑,程序判断答案中没有携带工具调用标记时,就强制复审。这段时间消掉了绝大多数幻觉问题。
5.3 坑三:把整个手册塞给模型反而更傻
最开始做知识库检索时,我图省事,把整个客服手册一次性拼进提示词里。结果模型回答时抓不住重点,经常把很久之前的前提条件也引用出来,答得又长又偏。
后来改成“先检索再拼接”,只把和当前问题最相关的 3 到 5 个片段传给模型,回答质量立刻上来。这说明,给模型的信息不是越多越好,给对了才有效。这就是 RAG 的真正价值——它不是在炫技,而是帮模型筛掉噪音。
5.4 排查速查表
| 问题 | 常见信号 | 原因 | 对策 |
|---|---|---|---|
| 接口返回为空 | 日志显示请求成功但无数据 | 日期格式、编码格式不匹配 | 在联接层统一转换格式 |
| 模型编造库存数 | 用户询问实时库存,模型直接回答 | 工具调用缺失 | 强制要求先调用工具 |
| 回答过长且偏离 | 用户问简单问题,回复很冗长 | 提示词塞入过多无关信息 | 改用检索增强方式 |
| 老系统响应超时 | 用户等待过久 | 老系统性能不足 | 超时重试、降级策略 |
| 文档答案冲突 | 不同片段说法不一致 | 原始文档本身矛盾 | 清洗阶段统一口径 |
这套系统上线后,我给它起名叫“智能中转站”,实际用下来,最大的价值不在于某个单点能力,而在于把两套老体系的“语言”统一了。旧系统继续跑它的流程,大模型在中间当一个靠谱的调度员,用户只面对一个对话入口,不用再关心背后是哪个系统。
最后再分享一个心得。这种集成项目,技术难度不是最大的,最难的是跟旧系统“和解”——你得接受它的格式怪、文档少、规则乱,然后用一层巧妙的适配代码把这些问题藏起来。大模型的确让这件事容易了非常多,但前提是你得先把它需要遵循的边界定义清楚。边界清楚了,模型才不会乱来。希望我的这些踩坑经历能给你省点时间,下次真遇到类似项目,顺着这套思路走,基本能绕开大多数暗坑。