1. 为什么做 RAG?直接用之前的知识库系统有什么问题吗?RAG 带来什么好处?
之前在一个客服知识库项目里经历过完整的“从传统搜索到 RAG”的切换,体会很深。传统知识库本质就是 ES 分词 + BM25 + 问答对库,主要有三个致命问题:
- 语义鸿沟:用户问“账户被锁了怎么弄”,知识库里标题是“登录失败处理”,关键词匹配根本挂不上,召回率惨不忍睹。
- 无法组合答案:一个问题需要综合三篇文档里的信息(比如退费政策、审核流程、时效),传统系统只能甩给用户一堆文章链接,还得自己看。
- 维护噩梦:运营天天往里面堆 QA 对,穷举不完,还容易冲突。我们当时那个库有 5 万多条 QA,仍然有很多长尾问题覆盖不到。
RAG 带来的好处不是一点半点:检索用向量做语义匹配,泛化强,新文档灌进去马上就能搜到;生成模型负责整合信息,可以直接给用户一句完整带上下文的答案,而且我们能强制它附上引用来源。知识更新简单了,文档改了之后把向量重新灌入库就行,模型不用重训。在合同审核那个项目里,我们用 RAG 后,答案采纳率从 62% 提到了 84%。
2. RAG 中遇到哪些问题?怎么优化的?
几乎每个环节都踩过坑,我举几个典型的。
第一个坑是分块策略。 一开始按 512 token 硬切,经常把“甲方权利”和“甲方义务”切断,检索到的片段前言不搭后语。后来我们改成递归分块+滑动窗口,根据标题层级分,每个 chunk 256 token,但和前一个 chunk 重叠 32 token;检索时取到目标 chunk 后,自动把前后各一个 chunk 也送进去,上下文完整度一下就上来了。
第二个坑是“问了但没答到点子上”,即检索召回的内容不包含答案,模型就开始编。我们做了三件事:一是上 bge-reranker-v2-m3 做精排,先拿 top-20 然后用 reranker 压缩到 top-5,准确率提升很明显;二是在 prompt 里加死约束:“如果没有明确信息,直接回答‘根据现有资料无法确认’”,并且用 few-shot 强化;三是建设拒答回归测试集,每次发版前自动跑,保证不会乱说。
第三个坑是多步推理问题,比如“A 产品的赔付率和 B 产品比哪个高?”,需要分别查出两个产品的数据再比较。我们做了查询分解,先用一个轻量模型把复杂问题拆成子问题,逐个检索,最后汇总生成。类似 HyDE 的思路我们也试过,生成假设答案再检索,对那种无法直接命中的问题帮助很大。
第四个是性能,最初串行方案首字延迟太高。我们做了全链路异步化:检索和生成流水线化,用户问题一来,向量检索和模型加载并行;还把高频问题答案缓存到 Redis,命中率超过 30%,这些请求延迟直接从 2 秒降到 80 毫秒。
3. 什么是 Modular RAG,和 Agentic RAG 有什么区别?
这个我可以结合架构演进说。我们第一版 RAG 就是典型的 Naive RAG:固定“检索-拼接-生成”管道。后来发现不同业务要换不同的分块、不同的 embedding 模型,硬编码改不动,于是我们就往 Modular RAG 上走。
Modular RAG 的核心是把整个管道拆成独立模块:分块器、向量化器、检索器(稠密/稀疏/混合)、后处理器、生成器,每个模块对外暴露接口,可以在配置里自由插拔。比如我们合同业务用法律领域的 Embedding 模型,客服业务用通用多语言模型,只需要换一个组件,不需要重写整套代码。
Agentic RAG 就完全不一样了,它引入了“智能体”的决策能力。不再是给定 query 就死板跑一次检索,而是让 LLM 自己判断:这个问题我需要检索吗?需要拆成子问题吗?检索结果够不够,要不要再搜一次?我们后来在反欺诈分析场景里就用了这种模式,模型会自主决定调用不同的工具(查规则库、查案例库、查实时指标),并根据返回结果动态调整下一步行动。简单说,Modular RAG 是管道的模块化,Agentic RAG 是流程的自主化。两者不矛盾,可以组合。
4. 为什么微调?微调数据集怎么构造的?你怎么确保你的 SFT 数据能微调出结果呢?
我们最开始大量用提示工程,但很快发现两个问题:一是业务要求输出固定 JSON 结构,带特定字段和类型,靠 few-shot 经常出错;二是安全合规,有些敏感词绝对不能出现,基模会有概率漏。这就逼着我们做 SFT。
数据集构造我们坚持一个原则:从真实日志里生长出来,而不是凭空编。 具体步骤:
- 从线上捞一个月内的高质量人工会话,筛选出格式正确、答案被采纳的对话。
- 由业务专家按照“指令-输入-理想输出”改写,同时标注出所有约束条件(如必须带免责声明、金额单位必须大写等)。
- 刻意构造边界样本和对抗样本,比如用户骂人、输入非法字符、要求忽略指令等,补充拒答和安全回复。
- 最后总共攒了大概 3500 条高质量数据,覆盖常规、边界、安全三大类。
怎么确保能微调出效果? 我们不会一上来就全量训。先用 LLaMA-Factory 在 Qwen-7B 上用 100 条数据做小规模试探,跑一个 epoch 看 loss 和 eval 指标;然后拿这版模型和 base 模型进行对比评测:构造 50 个典型 case,看格式合规率、拒答准确率、关键信息覆盖率有没有明显提升。小规模实验有明显收益后,再扩展到全量数据训练 70B 模型。此外还引入 GPT-4 作为自动评分裁判,和人工抽检交叉验证,确保微调是正向的。
5. 怎么评测 RAG Agent 和微调后的模型?
评测体系是我们吃了不少亏才建起来的。最早我们只看生成内容“像不像人话”,结果上线后被业务投诉:“你们这个回答倒是通顺,但把金额单位搞错了,元写成了万元。”我们才发现评测不能只看流畅度,得跟业务指标强绑定。
现在我们的评测分三层:
检索层:就看召回,指标是 Recall@5 和 MRR。意思是正确的 chunk 有没有出现在 top-5 里。这个指标是生成质量的“天花板”,检索漏了,生成再好也没用。我们内部有个红线,召回率低于 90% 的项目不能上线。
生成层:用 RAGAS 框架,核心看三个东西。一是忠实度,生成内容是不是能从检索到的文档里找到依据,这个直接对应幻觉率;二是答案相关度,生成的内容跟用户问题对得上;三是上下文召回率,检索到的内容里关键信息是不是都被用上了。人工还会抽检打分,1 到 3 分,3 分是“可直接采纳”。我们要求 3 分比例不低于 75%。
Agent 层:除了上面那些,还要看任务成功率和工具调用准确率。我们有一个反欺诈分析 Agent,要调 3 个工具才能出结论。我们会构造一批标准任务,看它能不能在规定步数内给出正确结论,以及每次调用的工具对不对、参数对不对。这个测试集每次发版都跑一遍,一个 case 失败就要排查原因。
微调模型:除了 loss,我们重点看格式合规率、拒答准确率、关键信息覆盖率。这些指标全部做成自动测试集,训练完一版模型就跑一次,出报告。有一个指标降了就卡住不让上线。
最后线上还要做 A/B 实验,分 5% 流量给新模型,对比最终的业务指标:用户点赞率、转人工率、解决率。技术指标是过程指标,业务指标才是最终裁决。
6. 模型选型:为什么 A 业务选 Qwen,B 业务选 DeepSeek?
这个问题我可以说一下当时的决策过程,不是事后诸葛亮,是我们真的花了两周做横向评测才拍板的。
A 业务是合同条款审核,典型的中文长文档场景,要求对法律术语的边界把握很严。我们当时拉了 4 个候选模型:Qwen-72B、DeepSeek-V2、Llama-3-70B 和一个国产法律微调版 ChatGLM。评测分两条线:一条是通用的中文法律理解 benchmark,另一条是我们自己从真实合同里抽出来的 100 道题,覆盖条款识别、风险点提取、金额比对。Qwen 在我们自建集上的准确率比第二名高了近 7 个点,尤其是对“甲方”“乙方”权利义务归属的判断,其他模型经常会搞混。还有一个隐性因素是 Qwen 的量化版本非常成熟,我们直接用官方 GPTQ 量化,SFT 后精度损失不到 1 个点,部署成本能省一张卡。所以当场就定了。
B 业务是代码生成助手,目标用户是我们内部的后端开发。我们测了 DeepSeek-Coder-V2 和几个开源代码模型,重点看 HumanEval 和 MBPP,还有我们自己整理的内部代码库补全任务。DeepSeek 在 Python 和 Go 上的生成准确率明显领先。但最终让我们下决心的,不是准确率差的那几个点,而是推理效率。它的 MoE 架构在同样 A100 上跑,生成 token 的吞吐几乎是我们测的另一个 70B 稠密模型的 1.5 倍。因为我们预估代码助手这个业务 QPS 会很高,每个开发都在用,成本是长期要考虑的。所以在精度达到门槛的前提下,我们选了推理更便宜的。
总结一句话:不是选最好的模型,是选“在这个业务上刚好够用、且长期成本最低”的那个。
7. 显卡部署了多少?各个资源使用情况怎样?
我们当时线上主力是 4 张 A800-80G,用 vLLM 部署两个 72B 模型实例,每个实例 TP=2,这样两张卡跑一个模型,另外四卡再跑一个备用或不同版本,也可以灵活调配。
资源占用:模型权重 FP16 加载后约 145GB,每张卡上放一半约 72GB,剩下 8GB 分配给 KV Cache。KV Cache 我们按最大 4096 context 预分配,大概吃掉十几 GB,加上 vLLM 的 block 管理开销,单卡显存稳态在 90% 左右,留了 10% 缓冲给突发长文本。Embedding 和 Reranker 服务单独放在两台 T4 上,模型不大,显存占用不足 3GB,CPU 内存倒是因为请求排队吃了不少。
我们还监控 GPU 利用率,发现并不是越高越好。一旦到 95% 以上,排队时延会指数上升,所以我们在调度层限流,把利用率控制在 85% 附近,保证 P99 稳定。
8. 怎么压测的大模型?有哪些指标?
我们压测不用现成的工具,是自己写的 Python 脚本配合 Locust 框架,因为要模拟真实用户行为和带 system prompt 的上下文。
流程:准备 1000 条真实业务 query 作为压测语料,从 5 QPS 开始起压,每隔 1 分钟增加 5 QPS,直到出现大量超时或错误。重点关注的指标:
- TTFT(首 Token 延迟):用户体感最直接,我们目标中位数 < 500ms。
- TPOT(每 Token 间隔时间):反映生成流畅度。
- 吞吐量(tokens/s):衡量系统总处理能力。
- P50/P95/P99 延迟:看长尾。
- 成功率:非 2xx 或超时即为失败。
- 排队长度、GPU 利用率、KV Cache 命中率。
通过压测我们找到拐点:QPS 达到 18 的时候,TTFT P99 从 600ms 跳到 1.8s,排队长度暴涨,于是就把服务限流设置在 QPS 15,保证体验。
9. 为了达到你们的 QPS 和响应时间,做了哪些优化?
这个问题我想先说一下我们当时的排查思路,因为优化最忌讳的就是上来就到处改,不知道瓶颈在哪。
我们压测时发现,QPS 卡在 8 就上不去了,P99 延迟飙到 3 秒多。我们就一层一层拆链路:先看检索,发现 embedding 那一步在 CPU 上跑,一次请求平均 80ms,这部分是串行的,成为首字延迟的硬地板;再看模型推理,发现长文本请求和短文本请求混在一起排队,一个长生成任务能把后面的短任务堵几百毫秒;最后看显存,KV Cache 分配是静态的,没用的空间浪费了,能塞的 batch size 上不去。
找到这三个瓶颈后,我们才逐一动手:
检索:把 embedding 模型从 CPU 挪到 GPU 上,用 TensorRT 做推理加速,同时把 FAISS 索引也放 GPU 内存。这一步直接把检索延迟从 80ms 压到 10ms 以内,首字延迟的“地板”一下降下来了。
调度:在 vLLM 前面加了一层请求调度,按生成长度分长短队列,短队列优先。这个改动对 P50 延迟影响不大,但对 P99 改善极其明显,长尾用户体感好了一大截。
推理:vLLM 本身的 continuous batching 和 PagedAttention 已经解决了动态 batch 问题,我们额外开启了 prefix caching,因为所有请求共用 system prompt,这一块 KV Cache 只算一次,首 Token 延迟又降了大概 40%。
缓存:分析日志发现,有大概四分之一的问题是高频重复问题,比如“如何重置密码”。我们把这些问题的最终答案直接缓存到 Redis,命中就直接返回,延迟降到 50ms 以内,还能帮模型扛住不少并发。
这一套组合拳下来,QPS 从 8 提到了 15,P95 首字延迟控制在 800ms 以内。最后还留了一个后手:FP8 量化,吞吐再提了约 30%,但我们在合同金额提取任务上发现准确率掉了大概 1.5 个点,就跟业务方商量,他们能接受,于是全量开了。
10. 你们怎么保证大模型应用的稳定性?上线后怎么监控?怎么做的负载均衡、熔断和降级?
我先说一次事故,那次之后我们才把稳定性体系建起来的。
有次半夜模型服务突然大面积超时,告警没响,因为我们的告警阈值设的是错误率 5%,但那次全是超时不是 5xx,没触发。后来发现是一台 GPU 机器的散热出了问题,显卡自动降频,性能只有正常的三分之一。用户反馈都炸了我们才知道。
这件事之后我们做了几件事:
监控:把超时率也纳入告警,和错误率同级对待。全链路打点,从网关到检索到模型推理每一步的耗时都上报 Prometheus,用 Grafana 画面板。关键告警线设三条:TTFT P99 > 2s、错误率 > 1%、单卡显存 > 92%,任何一条触发直接飞书群 @人。
负载均衡:API 网关用的 APISIX,多个推理实例注册上去,负载策略用最小连接数,同时结合请求的输入 token 数做加权,防止一个长文本请求把所有流量打到一台机器上。APISIX 还做健康检查,心跳连续失败两次自动摘节点。
熔断:我们搞了个简单的熔断器,一个模型实例连续 5 次返回超时或 5xx,就自动熔断 30 秒,所有请求走降级链路。
降级链路分两层:第一层切到备用的 7B 小模型,精度会差一点但服务不断;第二层如果备用模型也扛不住,直接返回静态兜底话术“系统繁忙,请稍后再试”。这里有个细节,降级时我们不能让用户有感知,所以兜底话术是提前准备好、跟业务一起审过的,不能随便写一句“系统错误”就扔出去。
扩容:K8s 上配了 HPA,GPU 利用率超过 80% 就自动扩容一个新 Pod,模型镜像是预热的,大概 30 秒内就能接流量。这套机制上线后,也出过几次半夜 GPU 故障,但用户侧基本没感觉。
11. 你们从开发到上线用了多长时间?让你从 0 搭建一个大模型应用怎么做?
第一个 RAG 应用从 0 到上线大概 5 周。从 0 搭建我会这么走:
第一周 业务对齐和验证:明确要解决什么问题、验收指标是什么,先用手动跑几个 case 验证可行性,确定技术路线。
第二周 数据工程:清洗文档、设计分块策略、选 embedding 模型、建索引,做初步的检索质量验证,召回率必须达到某个基线。
第三周 原型搭建:用 LangChain 或直接写 Python 把检索+生成串起来,调好 prompt,再找人内测,收集反馈快速迭代 prompt。
第四周 工程化:包装成 API,加缓存、限流、日志、监控,做容灾和灰度发布准备。同时做一轮压测,确定限流阈值。
第五周 上线灰度:先放 1% 流量,观察业务指标和系统指标,无问题后全量。上线后持续收集 bad case 反哺数据和 prompt 优化。
12. Dify 和 LangChain 有什么区别吗,你们为什么用 Dify?有什么线上问题吗?
LangChain 是一个 LLM 应用开发框架,给你各种组件,但得自己写代码组装,灵活性极高,适合深度定制。Dify 则是可视化编排平台,把 RAG 流程、prompt 管理、模型接入、日志都做成拖拽和配置,业务人员也能上手搭应用。
我们选 Dify 主要是因为业务团队需要快速试错,产品经理自己就能改 prompt 和检索策略,不用一直等开发排期,极大缩短迭代周期。但对于复杂的分支逻辑,Dify 的 DSL 编排还是有点局限,我们就把核心生成服务独立出来,通过 API 调用,Dify 只做前端工作流和对话管理。
线上踩过的坑:高并发下 Dify 工作流引擎性能不够,尤其是多节点循环时 CPU 飙升,有一次做活动并发一上来,整个编排节点卡死。后面我们把繁重任务全部移到外部服务,Dify 只做轻量调度。另外版本升级兼容性不好,我们干脆冻结了一个稳定版本,不再追新,只打补丁。
13. 最近 OpenClaw 这么火,你知道它的原理吗?这一块你有什么想法?
OpenClaw 我关注过,它本质是一个社区驱动、支持人机协同的多智能体平台。核心设计是“技能模块化”和“中央通信总线”。开发者可以把各种工具封装成“技能”,Agent 按需加载这些技能,并通过总线共享任务上下文和记忆。它强调图形化工作流和记忆持久化,很适合搭建自动化办公助手这类长流程任务。
我的看法是,这种架构大大降低了多 Agent 协作的开发门槛,但难点在于任务分配和冲突解决。当多个 Agent 同时操作同一份文件或者同一个数据库记录时,如何协调优先级、回滚操作,目前还没有看到特别成熟的标准。如果要用,我会先在非关键业务上试,比如日报自动生成,逐步验证稳定性。
14. OpenClaw 的记忆系统怎么做语义搜索的?纯向量检索还是别的方案?
我看资料分析,它的记忆系统不是纯向量检索,而是混合搜索 + 结构化加权。具体来说:记忆条目会生成稠密向量用于语义近似召回(ANN),同时保留关键词索引做 BM25 精确匹配。召回后,再根据记忆的元数据(如时间戳、重要性评分、对话阶段)进行加权排序。另外,对于过长的记忆,它会做摘要压缩,生成一个“线索摘要”存入向量库,原始记忆持久化到数据库,需要时再拉取。这样既能靠向量找到模糊相似的经历,又能通过关键词精确命中事实,效果比较平衡。
15. ReAct 是什么?
ReAct 是我们当初解决多跳问答和工具调用问题的核心范式。它把推理和行动交替:模型先“思考”下一步要做什么(Reason),然后产生一个“行动”(Act),比如调用搜索 API 或查询数据库,获得观察结果后,再进入下一轮推理。
我们有一个理赔咨询 Agent,用户问“我前天在你们 APP 提交的理赔到哪一步了?”,用 ReAct 时,模型首先推理:“我需要知道用户的身份和最近的理赔单号”,然后调用“用户信息”和“理赔查询”两个工具,拿到结果后判断状态,再组织语言回复。这种方式比一次性生成强太多了,能处理动态、多步、依赖外部信息的问题。实现上我们在 prompt 里定义 Thought-Action-Observation 循环,并且限制最大步数防止死循环。
16. 向量数据库的 ANN 是什么?为什么要用它?
ANN 是近似最近邻搜索。我们用的 Milvus,底层就是各种 ANN 算法,比如 HNSW 图。向量是 1024 维浮点,全量精确匹配计算量太大,延迟不可接受。
举个实际数据:我们库里有 200 万条文本 chunk,如果暴力计算余弦相似度,单次查询要算 200 万次浮点乘加,在 CPU 上得几十毫秒甚至上百毫秒。用 HNSW 索引后,大部分不相似的节点在搜索早期就被剪枝,实际只计算几千个向量,延迟降到个位数毫秒,同时召回率能维持 98% 以上。ANN 就是 RAG 能在几十毫秒内完成语义检索的关键,没有它,高并发场景根本扛不住。
17. MCP 和 Function Calling、Skills 有什么区别?
这三者常常被混淆,但它们是不同层面的东西。
Function Calling 是模型本身的能力,模型根据你给的 JSON Schema 输出一个函数调用请求(函数名+参数),然后由外部系统执行并返回结果给模型。它是“模型请求工具”的协议。
MCP(Model Context Protocol) 是 Anthropic 提出的一个标准化工具连接协议,它规定了一个 client-server 结构,让任何 AI 应用都能通过统一的方式发现和调用各类工具、资源和提示模板,而不需要针对每个工具写适配代码。相当于工具界的 USB 接口,解决了工具生态碎片化问题。
Skills 的概念在微软 Semantic Kernel 里比较多,它是一组功能(函数、提示、记忆)的封装,可以语义化调用,比如一个“邮件技能”内含发邮件、查收件等操作。它是更高层次的能力单元。
打个比方:Function Calling 是模型的“手”,MCP 是“手”和“工具”之间的标准插座,Skills 是把多个工具打包成瑞士军刀。我们目前在用 Function Calling,正在调研用 MCP 来统一管理越来越多的业务工具,这样未来新增工具不需要改模型侧的适配代码。