ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多智能体+知识图谱+向量检索:企业级复杂问答系统架构实战

多智能体+知识图谱+向量检索:企业级复杂问答系统架构实战

简介:面向科研新手、硕博研究生与青年科研工作者的《Gemini科研手册指南》PDF,系统讲解借助Gemini完成从选题生成、文献检索、精读分析、综述撰写到科研绘图、数据可视化及汇报PPT制作的全流程,突出“研究方案先行”“方案可修订”与结构化输出,并结合多智能体协作、知识图谱与检索增强生成等前沿方向,构建可追溯、可复用、可迭代的科研路径。资源仅1个PDF文件,压缩包约12.15MB,内含大量可直接复用的提示词公式与操作示例,覆盖学术热点与产业趋势交叉分析、高可信文献检索、多篇论文横向对比、机制图与数据图智能生成,也支持与Canvas、Nano Banana等智能体协同。已有232人学习浏览,适合希望提升科研效率、规范研究流程、增强成果可视化表达的跨学科研究者;通过“描述即生成”的交互方式,可快速掌握从文本指令到高质量科研产出的闭环方法。

1. 企业级复杂问答为什么扛不住:把《Gemini科研手册指南》拆开看

把《Gemini科研手册指南》和多智能体、知识图谱、检索增强生成放在一起看,要解决的是同一个问题:企业级复杂问答靠单一大模型加向量检索扛不住多跳推理。售后场景里,“这台设备的故障记录和我上次投诉的备件是不是同一批次”要跨工单、设备、客户三个数据域做实体对齐和关系推理,传统RAG只是让答案看起来有依据,并没有真正把关系算出来。

这套方案把生成过程拆成多个专职智能体,用知识图谱承载实体与关系事实,用Gemini这类多模态大模型做任务规划、工具调用与最终生成,再通过企业级系统架构把权限、并发、成本兜住。适合做企业内部知识助手、智能问数、售后智能体的人:架构师看系统拆分,后端看代码骨架,算法看检索与抽取的坑。

2. 多智能体管线的角色拆分:Planner、Retriever、Graph Agent、Verifier各管什么

2.1 职责边界与交接协议:为什么角色之间只传JSON

我一般会把多智能体协同拆成四个角色。Planner负责把用户的复杂问题拆成子任务列表,每个子任务标注检索域——是去文档里找语义片段,还是去知识图谱里查关系事实。Retriever只做向量召回,Graph Agent只做图谱查询,Verifier最后用图谱事实核验草稿答案。四个角色之间不传自然语言段落,统一传结构化JSON。

这样做的好处是每一跳都可观测、可重试。比如用户问“华新精密的CNC-08主轴异响,处理人是不是李工”,Planner会拆成三个子任务:查客户与设备归属、查故障记录、查工单处理人。每个子任务的输入输出都是JSON,中间任何一步失败都能定位。如果角色之间互相传大段文字,多智能体很快会陷入模型自我对话,上下文爆炸,这是多智能体代码编排最常见的失控点。

交接协议长这样:

{ "task_id": "t2", "query": "CNC-08最近一次主轴异响对应的工单处理人", "domain": "graph", "deps": ["t1"], "constraints": {"time_range": "2024-01-01_2024-12-31"} }

Planner把这类子任务塞进state,retriever和graph_agent才能并行执行。domain字段是路由判据,比每次都让模型自己猜该查什么可靠得多。deps表示依赖关系,满足拓扑排序后才能进入生成阶段。

参数这里要固定下来:Planner和Graph Agent的temperature建议0.1,让输出结构稳定;LLM调用超时30秒;Retriever的top_k在8到12之间,相似度低于0.25的向量片段直接丢弃。这些数值不是拍脑袋,跑过一轮业务评测集就会发现,top_k超过12之后召回的边际收益明显下降,而token成本还在涨。

2.2 用LangGraph把多智能体协同编排跑起来:state与条件边

编排框架的选择上,常见做法是用LangGraph。它把每个Agent建模成图里的节点,状态在节点之间显式传递,天然适合“拆解—并行检索—生成—校验”这个流程。先看最小可用的代码骨架:

from typing import TypedDict from langgraph.graph import StateGraph, END class QnAState(TypedDict): question: str history: str tasks: list # planner 输出的子任务列表 chunks: list # retriever 的向量召回结果 facts: list # graph agent 返回的图谱事实 draft: str # generator 生成的草稿 verdict: dict # verifier 的核验结果 retries: int # 当前重试次数 def planner(state: QnAState) -> dict: # 调用 Gemini function calling,拆分子任务,返回 {"tasks": [...]} return {"tasks": plan_llm(state["question"], state["history"])} def retriever(state: QnAState) -> dict: # 遍历 tasks,对 domain=="vector" 的任务做向量召回 return {"chunks": vector_batch_search(state["tasks"])} def graph_agent(state: QnAState) -> dict: # 遍历 tasks,实体链接 -> 生成 Cypher -> 查询 Neo4j return {"facts": graph_batch_query(state["tasks"])} def generator(state: QnAState) -> dict: # 把 chunks + facts 拼进提示词,生成草稿 return {"draft": generate_answer(state)} def verifier(state: QnAState) -> dict: # 从草稿抽断言,与 facts 比对,返回 {"verdict": {...}} return {"verdict": verify_draft(state["draft"], state["facts"])} graph = StateGraph(QnAState) graph.add_node("planner", planner) graph.add_node("retriever", retriever) graph.add_node("graph_agent", graph_agent) graph.add_node("generator", generator) graph.add_node("verifier", verifier) graph.set_entry_point("planner") graph.add_edge("planner", "retriever") graph.add_edge("planner", "graph_agent") graph.add_edge("retriever", "generator") graph.add_edge("graph_agent", "generator") graph.add_edge("generator", "verifier") graph.add_conditional_edges( "verifier", lambda s: "generator" if (not s["verdict"]["passed"] and s["retries"] < 2) else END, {"generator": "generator", END: END} ) app = graph.compile()

逻辑说明:planner之后retriever和graph_agent是两条并行边,LangGraph会用线程池同时跑;generator等两边结果都到齐后再触发。verifier返回失败且重试次数小于2时,回到generator做局部修订,否则直接结束。

参数说明:重试上限写死在条件边里,我一般设2次,超过就返回“部分结果+待人工复核”,避免死循环。每个节点的超时不要用全局默认值——规划类任务30秒,图谱查询3秒,向量检索1秒,分开设置才能定位瓶颈。这里还有一个容易忽略的点:LangGraph的并行是共享事件循环还是单独线程池,取决于节点声明为sync还是async,生产环境建议把retriever和graph_agent写成async节点,避免耗时操作阻塞整个编排。

2.3 为什么选Gemini做路由与生成:function calling与多模态输入

这个组合里,Gemini承担的是大脑角色:Planner的拆解、Graph Agent的实体链接与Cypher生成、Generator的最终回答,本质都是大模型调用,只不过提示词和输出约束各不相同。

选择Gemini而不是本地微调模型,一般是看中三件事。一是function calling成熟,直接把子任务列表的JSON Schema注册成工具,模型返回结构化参数,比让模型“按格式输出”稳定得多;二是超长上下文,企业问答经常要带合同条款、设备手册段落,百万级窗口让这类内容可以直接进上下文,不用提前做太多裁剪;三是多模态,2026年前后的多模态大模型已经能直接读设备照片、车间截图、Excel表格截图,这些输入在纯文本LLM里完全处理不了,而企业真实问题里带着图片问设备的案例比比皆是。

接入方式上,常见做法是用公司API网关统一接入官方接口,网关负责密钥管理、每用户QPS限制、日志审计。这里提醒一句:不要把密钥下发到前端或客户端,所有调用都走服务端网关,否则日志一查一个泄露。选型时还要考虑降级路径,一旦主模型限流,网关要能快速切换到备用模型,这个在架构设计阶段就要留好接口。

3. 企业级系统架构设计:六层划分与双通道检索

3.1 接入层到数据层:权限为什么要下沉到检索层

企业级系统架构设计和普通Demo最大的区别是边界。Demo可以一个进程把检索生成都串起来,企业级系统必须分层,因为权限、并发、监控、成本都要在特定层兜住。我一般按六层拆:接入层、对话管理层、编排层、检索层、生成层、数据层。

下面这张表把每层的关键职责和故障影响说清楚:

层关键组件主要职责故障影响
接入层API网关统一认证、限流、路由过载直接表现为超时
对话管理层会话服务用户身份、权限上下文、历史摘要权限错乱比答错更严重
编排层LangGraph多智能体调度、重试、超时死循环拖垮整个请求
检索层向量库+Neo4j双通道召回,权限过滤召回空导致生成胡编
生成层Gemini+提示模板草稿生成、格式约束高延迟、高成本
数据层文档库、图谱、ACL索引、图存储、权限表数据不一致直接答错

权限是这六层里最容易被忽略的企业级问题。复杂问答的价值恰恰在于能把多个数据域关联起来,但能关联不等于有权限看。做法是把权限过滤下沉到检索层:生成层只拿到用户有权访问的chunks和facts,不能靠提示词让模型自觉遵守权限。图谱侧要按实体类型和关系类型做ACL过滤,向量侧按文档标签过滤。

我见过一个反面案例:答案生成正确,但把另一个部门的内部合同摘要带了出来。问题不在模型,而在生成层能拿到全部召回结果,权限过滤放在了前端展示。所以权限必须物理隔离在检索层,生成层永远接触不到无权数据。

3.2 向量召回与图谱查询并行:双通道的时序与参数

检索层的核心设计是双通道并行。并行两个字很重要。早期版本我把图谱查询放前置,实体链接失败率接近一半,整体延迟多出2秒;后来改成两条通道同时发起,图谱通道失败时自动等待向量通道,整体P95才降下来。

时序上,Planner把子任务拆完后,retriever处理vector域,graph_agent处理graph域,两路互不阻塞。需要控制的是汇总等待时间:

import asyncio async def dual_channel_search(state): vector_task = asyncio.create_task(vector_search(state["tasks"])) graph_task = asyncio.create_task(graph_query(state["tasks"])) chunks, facts = await asyncio.wait_for( asyncio.gather(vector_task, graph_task, return_exceptions=True), timeout=5 ) return chunks, facts

逻辑说明:asyncio.gather让两路并发执行,wait_for的5秒是最长等待窗口,超时则返回已拿到的一路结果,另一路标记为超时。这样即使图谱通道卡住,请求也不会跟着卡死。

参数上,我一般把双通道参数统一放在一个配置里,方便调优:

参数建议值说明
向量top_k8~12语义片段召回数量
score_threshold0.25~0.35低于阈值直接丢弃
实体链接置信度阈值0.80低于阈值放弃图谱查询
Cypher查询超时3秒长路径查询必须带LIMIT
双通道汇总超时5秒并行等待上限
校验重试上限2次超过返回“待人工复核”

3.3 会话上下文管理与成本控制:滑动窗口与限流参数

会话上下文是另一个和成本强相关的设计点。企业问答场景用户习惯连续追问,但不可能把整段历史都传给Gemini,token烧得太快。做法是滑动窗口压缩:历史被压缩成一段摘要,加上最近3轮原文,一起作为history传给Planner。摘要生成用轻量模型异步做,不让用户请求等在那个环节。

成本控制分三层。第一层是按用户限流,普通用户每分钟10次,VIP 30次,防止单用户占满预算;第二层是按会话设置token上限,超限自动降级到轻量模型;第三层是按部门按天核算token消耗,达到阈值后在网关直接降级到备用模型,业务代码无感知。

这三层都在网关和调度层实现,不要在应用代码里写死。我见过一个翻车案例:限流写在应用里,线上调参要发版,限流参数半年没人动过,高峰期全靠网关硬扛。正确的做法是网关配置热更新,应用层只关心业务逻辑。

4. 知识图谱构建与查询:从工单文档到Cypher检索

4.1 用Gemini从非结构化文档批量抽取三元组:提示词与JSON输出

知识图谱构建的第一步不是画图模型,而是把非结构化文档转成三元组。企业里最典型的数据源是工单、故障记录、设备台账、客户合同。常见做法是用大模型批量离线抽取,而不是问答时在线抽取——在线抽取每问一次就跑一轮LLM,成本和延迟都不可控;离线抽好后入库,在线只查Cypher。

抽取请求长这样,以Gemini的generateContent接口为例:

curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [{"text": "你是知识图谱抽取器。从运维工单中抽取(实体、关系、实体)三元组,实体限定在设备、客户、故障类型、工单、处理人。只输出合法JSON数组,不要解释。工单内容:【华新精密的CNC-08在2024年5月报主轴异响,维修更换主轴轴承,耗时4小时,处理人工号10023,工单号WO-2024-0113】"}] }], "generationConfig": { "temperature": 0.1, "responseMimeType": "application/json" } }'

逻辑说明:把抽取任务限定在预定义的实体类型里,比让模型自由发挥稳定得多。temperature设0.1,事实抽取要确定性;responseMimeType强制JSON输出,省去解析自然语言的麻烦。接口地址以官方最新文档为准,模型型号也要按账户实际开通的列表替换。

抽取结果示例:

[ {"head": "华新精密", "relation": "拥有设备", "tail": "CNC-08", "domain": "customer_device"}, {"head": "CNC-08", "relation": "发生故障", "tail": "主轴异响", "domain": "device_fault"}, {"head": "CNC-08", "relation": "关联工单", "tail": "WO-2024-0113", "domain": "device_workorder"}, {"head": "WO-2024-0113", "relation": "处理人", "tail": "10023", "domain": "workorder_staff"} ]

拿到原始三元组后要先做实体链接再接图谱:同一实体的不同叫法,比如“CNC-08”和“八号机床”,需要先对齐到同一个实体ID。批量抽取的并发控制在4到8路,带指数退避重试,不然很容易触发模型接口限流。工业知识图谱场景里实体密度高、同义词多,这一步不做,后面查询全是空结果。

4.2 Neo4j图模型设计:实体类型、关系与Cypher约束

图谱存储我一般选Neo4j,模型设计围绕查询模式展开。以设备售后为例,最小可用的图模型是五类实体、四类关系:

实体类型关键属性对应索引
Customercustomer_id, namecustomer_id唯一约束
Devicedevice_id, model, statusdevice_id唯一约束
FaultTypefault_code, namefault_code唯一索引
WorkOrderwo_id, created_at, statuswo_id唯一约束
Staffstaff_id, namestaff_id唯一约束

关系方面,Customer有HAS_DEVICE指向Device,Device有HAS_FAULT指向FaultType,WorkOrder有RELATES_TO指向Device,WorkOrder有HANDLED_BY指向Staff。Cypher约束建好后,查询才能走索引:

CREATE CONSTRAINT customer_id IF NOT EXISTS FOR (c:Customer) REQUIRE c.customer_id IS UNIQUE; CREATE CONSTRAINT device_id IF NOT EXISTS FOR (d:Device) REQUIRE d.device_id IS UNIQUE; CREATE CONSTRAINT wo_id IF NOT EXISTS FOR (w:WorkOrder) REQUIRE w.wo_id IS UNIQUE;

常见查询是“某客户名下所有在修设备的故障分布”:

MATCH (c:Customer)-[:HAS_DEVICE]->(d:Device) WHERE c.name = $name AND d.status = 'maintaining' OPTIONAL MATCH (d)-[:HAS_FAULT]->(f:FaultType) RETURN f.name AS fault_type, count(d) AS device_count ORDER BY device_count DESC LIMIT 10

逻辑说明:参数化查询避免Cypher注入风险;OPTIONAL MATCH保证没有故障记录的设备不会被JOIN过滤掉;LIMIT限制返回行数,避免长路径扫描拖慢整体。这里的$name由Graph Agent的实体链接结果填充,不是用户原话。

4.3 图谱查询与RAG的混合召回:RRF融合与降级策略

双通道的召回结果最后要并进生成提示词。图谱结果和向量结果性质不同,向量结果是“可能相关”的文档切片,图谱结果是“确定成立”的关系事实,混在一起时我一般用RRF做融合,避免两种分数直接相加的量纲问题:

def rrf(ranked_lists, k=60): scores = {} for ranked in ranked_lists: for rank, item in enumerate(ranked): doc_id = item["id"] scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k + rank) return sorted(scores.items(), key=lambda x: x[1], reverse=True)

逻辑说明:k是平滑参数,k越小排名靠前的项权重越大;k=60是常见默认值,可以根据结果稳定性调整。实体链接置信度高的图谱结果,我会在排序后给一个保底加分,因为图谱事实的确定性高于向量片段。

降级策略上,我坚持一条原则:图谱通道失败时,整个检索不会失败,只会退化为纯向量RAG,同时在生成提示词里注明“当前缺少图谱证据,请给出带不确定性的回答”。千万不要在图谱查询为空时让模型硬编事实。这个降级逻辑要写在编排层的条件边里,而不是靠模型自觉。

5. 企业级落地避坑记录:四个高频问题从现象到解决

5.1 多Agent循环不收敛:工作记忆设计错了

现象:一个简单问题,verifier挑出一点小毛病就整篇打回generator,generator改完又引入新的表述偏差,verifier再打回,来回四五轮,token消耗翻倍,答案反而和第一版本不同。

原因:工作记忆没有隔离。verifier把整篇草稿传给generator,generator是在修正一篇自己生成的长文,模型容易被自己过去的输出带偏。这是多智能体协同最容易踩的坑。

解决:verifier只返回“缺失事实列表”,而不是返回草稿全文。generator收到的修订指令是“补充以下事实:CNC-08主轴异响由工单WO-2024-0113处理”,草稿原文只允许局部修改。同时把重试上限设成2,超过就直接输出“部分结果+待人工复核”。这条血泪经验是:多智能体不是轮次越多越聪明,要尽快收敛。

5.2 图谱查询大面积空结果:实体链接没做别名对齐

现象:图谱查询耗时很低,但命中率惨淡。用户问“华新的CNC-08现在什么状态”,Cypher查不到任何东西,只能靠向量通道兜底,图谱的价值没发挥出来。

原因:用户说的是简称“华新”,图谱里存的是全称“华新精密有限公司”;或者实体链接模型把“CNC-08”和“八号机床”当成两个实体。实体链接置信度只有0.55,低于0.80的阈值,Graph Agent直接放弃了。

解决:先建实体词典再做抽取。别名表是必须的,把“华新”“华新精密”“华新精密有限公司”映射到同一个customer_id。实体链接阶段要返回候选列表,不只是返回最高分。阈值从0.80降到0.75可以,别低于0.70,否则误链会把不相关事实拽进来。空结果时强制走向量通道,并给出一句兜底话术。

5.3 Gemini接口超时与限流:网关熔断与降级

现象:上线压测时P95延迟从8秒飙到25秒,日志里全是模型接口限流错误,单个请求重试了三四次,把等待队列彻底堵死。

原因:所有并发请求都打同一个密钥,网关没做限流;LLM调用超时设成了全局30秒,重试逻辑是“失败就重试”,在限流状态下等于火上浇油。

解决:在API网关做三层控制。第一层每用户QPS限制,普通5 QPS,VIP 20 QPS;第二层LLM调用分类型设超时,路由类15秒、生成类60秒;第三层全局熔断,连续失败率超20%就切换到备用模型。重试最多1次且必须幂等。密钥绝不能出现在前端或客户端代码里,服务端日志也要脱敏。

5.4 评测指标好看但业务不买账:基准集和业务集落差太大

现象:在公开问答基准上F1不错,一到真实工单就露馅。业务反馈“答案读起来对,但没说到点子上”,比如把“设备是否在保内”答成“设备在2024年5月报修过”。

原因:公开基准的推理方式和企业问法差别太大。企业真实问题是口语化、带错别字、跨数据域、高度依赖业务口径的,公共基准考的策略性推理覆盖不到“在保状态怎么判定”这种业务规则。

解决:自建业务评测集,从真实工单和聊天记录里抽200到300条,标好标准答案和可接受答案,离线跑评估;再抽10%流量在线灰度,同用户池对比新旧版本的问题解决率。打分上,用LLM-as-judge按忠实性、完整性、安全性三个维度打分,同时监控“拒答率”——模型对没依据或没权限的问题说“不知道”,比硬答重要得多。

6. 验证这套架构值不值得上:成本核算与灰度评估

多智能体+知识图谱不是免费午餐,它比单Agent RAG多消耗的token是实打实的。以我的经验,完整问答链路里planning约占总token的15%,verifier每回退一轮多耗约10%到15%。所以上不上这套架构,先把三个数字测出来:graph_hit_rate(图谱通道命中率)、verifier_retry_rate(校验打回率)、avg_tokens_per_query(单问答平均token数)。

离线阶段,业务评测集先跑两轮:第一轮单Agent RAG,第二轮多智能体+图谱,对比忠实性得分和拒答率。得分差小于5分,说明这个业务场景图谱价值有限,不如先回头把文档切片的召回质量做好。在线灰度阶段,抽10%流量到新版本,观察两个指标:P95延迟和“用户是否结束对话”的二次反馈率。延迟涨幅超过40%就要回头优化图谱查询的超时和并行度。

成本核算可以按千次问答做一张表,把模型调用、向量库、图数据库的消耗分开记:

指标单Agent RAG多智能体+图谱
avg_tokens_per_query约2500约4000
graph_hit_rate不适用70%以上
verifier_retry_rate不适用低于15%
P95延迟约5秒约8秒

这里的数值是常见量级,不是固定值。如果graph_hit_rate上不去,先回头查实体词典和别名表,而不是急着调提示词。多智能体系统的价值用一句话概括:用更多token换更多确定性。值不值得,把上面三个数字跑出来再下结论。

最后说一个我的习惯。我早期做这套系统时,把图谱通道放前置,实体链接失败率接近一半,平均延迟多出2秒,差点把这个方向砍掉。后来改成双通道并行、图谱失败自动降级到向量RAG,graph_hit_rate从45%爬到70%以上,系统才算真正稳定下来。现在的教训是:每接入一个Agent,先问它“失败时谁兜底”,答不出来就不要上。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进