1. 企业知识助手到底在解决什么问题
1.1 从“搜不到”到“问得准”的转变
企业里最让人头疼的事情之一,就是明明有文档、有规范、有历史记录,但真到用的时候就是找不到。新员工想查报销标准,翻了三层文件夹才找到一份两年前的PDF;技术支持想确认某个接口的兼容性,在群里问了半天没人回;产品经理想知道去年某个功能为什么被砍掉,只能靠老员工的模糊记忆。这些场景的共同点是:知识存在,但获取成本极高。
传统的做法是搭一个内部Wiki或者用全文检索,但用过的人都知道,关键词匹配的命中率非常依赖你“猜对了词”。你搜“差旅报销”,文档里写的是“出差费用核销”,那就匹配不上。而基于RAG(Retrieval-Augmented Generation,检索增强生成)的企业知识助手,核心思路是把文档切片、向量化之后存入向量数据库,用户用自然语言提问,系统先检索出语义最相关的片段,再交给大模型组织成通顺的回答。这就从“猜关键词”变成了“说人话就行”。
但光有RAG还不够。实际企业场景里,用户的问题往往不是单轮的事实查询,而是多步骤的任务。比如“帮我查一下上季度华东区的销售数据,然后对比一下前年同期,生成一份简报”。这需要助手能调用数据库查询、能执行计算、能生成文档,也就是从RAG进化到Agent(智能体)。Agent的核心能力是规划、工具调用和记忆,它能把一个复杂任务拆成若干步骤,逐步执行,并在过程中保留上下文。
这个项目要做的,就是把RAG和Agent结合起来,构建一个完整的企业知识助手。适合谁看?如果你已经了解RAG的基本流程,想进一步搞清楚Agent怎么落地;或者你正在做企业内部的知识管理工具,想知道从Demo到可用产品之间还差哪些东西,那这篇内容应该能给你一些直接能用的思路。
1.2 为什么不是“RAG加个对话框”就完事
很多人第一次做知识助手,思路很简单:文档丢进向量库,用户提问就检索,检索结果拼进Prompt让模型回答。这个方案在单轮事实查询上确实能用,但一碰到真实企业场景就会暴露问题。
第一个问题是多跳推理。用户问“我们和供应商A的合同里,关于延迟交付的违约金是怎么算的”,这个问题需要先找到合同文档,再定位到违约金条款,可能还要结合补充协议。单次检索很难保证把所有相关片段都召回。
第二个问题是动态数据。企业里的数据不全是静态文档,还有数据库里的订单、CRM里的客户信息、工单系统里的记录。这些数据不在向量库里,RAG检索不到,但用户会问。
第三个问题是操作需求。用户不只是想“知道”,还想“做到”。比如“帮我把这份周报发给张总”,这需要调用邮件工具;“帮我在项目管理工具里创建一个任务”,这需要调用API。
Agent架构就是为解决这些问题设计的。它通过Function Calling机制让模型能调用外部工具,通过Memory机制保留多轮对话和任务执行的上下文,通过规划能力把复杂任务拆解成可执行的步骤。RAG在这个架构里不再是全部,而是Agent的一个工具——当需要查文档时,Agent调用检索工具;当需要查数据库时,Agent调用查询工具。
2. 整体架构设计与技术选型
2.1 分层架构:从入口到执行
一个完整的企业知识助手,我倾向于把它分成四层来设计,这样每层的职责清晰,后续扩展也方便。
接入层负责和用户交互,可以是Web页面、企业微信机器人、Slack Bot或者API接口。这一层的关键是做好身份认证和权限控制,因为企业知识是有访问边界的,不同部门的人能查到的内容不一样。
编排层是Agent的核心,负责理解用户意图、规划任务步骤、决定调用哪些工具、管理对话记忆。这一层通常用一个Agent框架来实现,比如LangChain、LangGraph或者自己基于Function Calling手写编排逻辑。
能力层是各种工具和服务的集合,包括RAG检索工具、数据库查询工具、API调用工具、文档生成工具等。每个工具都有明确的输入输出定义,Agent通过Function Calling来调用它们。
数据层包括向量数据库、关系型数据库、文档存储、缓存等。向量数据库存文档的向量表示,关系型数据库存结构化数据,缓存用来加速频繁访问的内容。
这样分层的好处是,每一层可以独立演进。比如你想换一个向量数据库,只需要改数据层的适配代码,编排层和接入层不受影响。你想增加一个新的工具,只需要在能力层注册,Agent就能发现并调用它。
2.2 Agent框架选型:LangChain、Dify还是自己写
这是被问得最多的问题之一。我的建议是分阶段看。
如果你还在验证阶段,想快速搭一个能跑的原型,Dify这类低代码平台是最快的。它内置了RAG流程、Agent编排、工具管理,界面上拖拖拽拽就能出一个Demo。但缺点是定制能力有限,当你想实现一些特殊的检索策略或者复杂的多Agent协作时,会发现被框架限制住了。
LangChain生态最成熟,文档多、社区大、集成丰富。它的Agent模块支持ReAct、OpenAI Functions等多种模式,工具定义也很方便。但LangChain的抽象层次比较多,有时候调试一个问题要翻好几层源码。而且它的版本迭代快,API经常变,生产环境用的话建议锁定版本。
如果你对性能和控制力要求高,自己基于Function Calling手写编排是值得考虑的。核心逻辑其实不复杂:维护一个消息列表,把工具定义传给模型,模型返回工具调用请求时就执行对应函数,把结果追加到消息列表里继续循环。这样你完全掌控每一步,调试也直观。缺点是什么都要自己搭,记忆管理、错误重试、并发控制这些都要写。
我自己的做法是:用LangChain做快速验证,确认方案可行后,把核心编排逻辑抽出来自己实现,只保留LangChain里确实好用的组件(比如文档加载器和文本分割器)。
2.3 向量数据库的选择:Chroma、Milvus还是pgvector
向量数据库的选型主要看数据规模和运维成本。
Chroma适合本地开发和小规模部署,pip install就能用,不需要额外起服务。但它的性能和并发能力有限,数据量上到百万级就不太行了。
Milvus是专门为向量检索设计的,性能强、支持分布式、索引类型丰富。但运维复杂度高,需要单独部署和维护。
pgvector是我比较推荐的一个折中方案。它是PostgreSQL的扩展,如果你已经在用PostgreSQL,直接加个扩展就能用向量检索,不需要引入新的数据库组件。性能上对于千万级向量也能撑住,而且可以用SQL做元数据过滤,非常灵活。
选型的时候还要考虑一个因素:你的文档需不需要按权限过滤。比如销售部的人只能查销售部的文档,这时候元数据过滤就很重要。pgvector在这方面的优势明显,因为你可以直接用WHERE子句做过滤,和现有的权限系统无缝集成。
3. RAG检索质量的核心细节
3.1 文档切分:不是越小越好
文档切分是RAG流程里最容易被忽视但影响最大的环节。很多人直接用默认的CharacterTextSplitter,chunk_size设个500,chunk_overlap设个50就完事了。但实际效果往往不好。
切分策略要根据文档类型来定。对于结构化文档(比如产品手册、API文档),最好按标题层级切分,每个小节作为一个chunk,保留标题作为上下文。对于对话记录(比如客服工单),按对话轮次切分比按字符数切分更合理。对于长篇文章,可以用递归切分,先按段落切,段落太长再按句子切。
chunk_size的设置也有讲究。太小了,一个完整的语义单元被切碎,检索出来的是残缺信息;太大了,检索精度下降,而且浪费Token。我的经验值是:中文文档300-500字,英文文档200-400词。但这个不是绝对的,最好用实际数据做A/B测试。
还有一个技巧是父子文档策略。索引的时候用小的chunk(比如200字)来做向量检索,保证精度;但返回给模型的时候,返回这个chunk所属的父文档(比如2000字),保证上下文完整。LangChain的ParentDocumentRetriever就是做这个的。
3.2 检索策略:混合检索比纯向量更稳
纯向量检索的问题是,它对精确匹配不敏感。比如用户搜一个产品型号“XR-2000”,向量检索可能会返回“XR-1000”的文档,因为语义上很接近。但在企业场景里,型号差一个数字就是完全不同的产品。
所以生产环境我强烈建议用混合检索:向量检索 + 关键词检索(BM25),然后做融合排序。向量检索负责语义匹配,关键词检索负责精确匹配,两者互补。
融合排序的常用算法是RRF(Reciprocal Rank Fusion),它不需要调权重,直接把两个检索结果的排名做倒数求和。公式是:score = Σ(1/(k + rank)),k通常取60。这个算法简单有效,我实测下来比调权重的线性融合更稳定。
还有一个提升检索质量的技巧是查询改写。用户的原始问题往往比较口语化,直接拿去检索效果不好。可以先用一个小模型把问题改写成更适合检索的形式,或者生成多个查询变体,分别检索后合并结果。比如用户问“报销怎么弄”,可以改写成“差旅费用报销流程和标准”。
3.3 Rerank:最后一道质量关卡
检索出来的Top-K文档,顺序往往不是最优的。向量相似度高不代表和问题最相关。这时候需要一个Rerank模型来做精排。
Rerank模型通常是Cross-Encoder结构,它把问题和文档一起输入,输出一个相关性分数。因为它是全交叉注意力,精度比向量检索的双塔结构高很多,但速度慢,所以只适合对少量候选做精排。
常见的Rerank模型有Cohere Rerank、BGE Rerank、Jina Rerank等。如果不想用外部API,BGE Rerank可以本地部署,效果也不错。
流程是:向量检索召回Top-20,Rerank精排出Top-5,这5个片段拼进Prompt。实测下来,加了Rerank之后,回答的准确率能提升15-20个百分点。
注意:Rerank模型的选择要和你的文档语言匹配。中文文档用中文Rerank模型,英文文档用英文的,混用效果会打折扣。
4. Agent编排与Function Calling实战
4.1 工具定义:让模型知道它能做什么
Function Calling的核心是工具定义。你需要用JSON Schema描述每个工具的名称、功能、参数。模型会根据用户的问题,决定是否调用工具、调用哪个工具、传什么参数。
一个典型的工具定义长这样:
{ "name": "search_knowledge_base", "description": "在企业知识库中搜索相关文档片段。当用户询问公司政策、产品信息、技术文档等内容时使用此工具。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "搜索查询语句,应该是自然语言问题或关键词" }, "top_k": { "type": "integer", "description": "返回的文档片段数量,默认5", "default": 5 } }, "required": ["query"] } }工具描述写得好不好,直接影响模型的调用准确率。几个经验:描述要具体,说清楚什么时候该用这个工具;参数说明要详细,告诉模型每个参数的含义和格式;避免功能重叠,如果两个工具功能相似,模型会困惑。
4.2 编排循环:Agent的心跳
Agent的核心是一个循环:模型思考 → 决定调用工具 → 执行工具 → 把结果返回给模型 → 模型继续思考。这个循环直到模型认为任务完成,返回最终回答。
用伪代码表示就是:
messages = [system_prompt, user_question] while True: response = llm.chat(messages, tools=tool_definitions) if response.has_tool_calls: for tool_call in response.tool_calls: result = execute_tool(tool_call.name, tool_call.arguments) messages.append(tool_result_message(result)) else: return response.content这个循环看起来简单,但实际落地时要处理很多细节。比如最大循环次数,防止模型陷入死循环;工具执行超时,防止某个工具卡住整个流程;错误处理,工具执行失败时要把错误信息返回给模型,让它决定是重试还是换一种方式。
还有一个重要的是中间步骤的可见性。用户问一个问题,Agent可能调了三次工具才给出回答。如果用户只看到最后的结果,会觉得等待时间很长。所以最好把中间步骤展示出来,比如“正在检索知识库...”“正在查询数据库...”,让用户知道系统在工作。
4.3 Memory管理:短期和长期要分开
Agent的Memory分两种:短期记忆和长期记忆。
短期记忆就是当前对话的上下文,通常直接放在消息列表里。但消息列表不能无限增长,否则会超出模型的上下文窗口。常见的做法是保留最近N轮对话,或者当Token数超过阈值时,用模型对前面的对话做摘要,把摘要作为新的上下文。
长期记忆是跨对话的。比如用户上次问过什么问题、偏好什么回答风格、关注哪些业务领域。这些信息可以存在数据库里,每次新对话开始时检索出来,注入到System Prompt里。
LangChain提供了ConversationBufferMemory、ConversationSummaryMemory等多种记忆组件。但我自己的经验是,对于企业知识助手,短期记忆用Buffer + 摘要就够了,长期记忆要看具体场景,不是必须的。因为企业场景下用户更关心答案的准确性,而不是助手记不记得他上次问了什么。
提示:Memory的设计要考虑隐私和权限。如果多个用户共用同一个助手,不能把A用户的对话记忆泄露给B用户。所以Memory的Key要绑定用户ID。
5. 从Demo到生产:踩过的坑和解决方案
5.1 检索不到怎么办:排查思路
RAG系统最常见的问题就是“检索不到相关内容”。用户问了一个问题,系统返回“抱歉,我没有找到相关信息”,但你知道文档里明明有。
排查这个问题,我一般按这个顺序来:
第一步,确认文档是否真的入库了。有时候文档加载失败或者切分后为空,根本没进向量库。可以写个脚本统计一下向量库里的文档数量和原始文档数量是否一致。
第二步,检查检索的原始结果。不要只看最终回答,把向量检索返回的Top-K片段打印出来,看看有没有相关的。如果Top-K里没有,说明是检索环节的问题;如果有但最终回答没用到,说明是Prompt或者模型的问题。
第三步,检查Embedding模型。如果用的是中文文档但Embedding模型是英文的,效果会很差。中文场景建议用BGE、M3E或者text-embedding-3-large。
第四步,检查查询本身。用户的原始问题可能太短或者太模糊。可以试试用查询改写,或者手动构造一个更明确的查询,看看能不能检索到。
5.2 模型胡说八道:幻觉的抑制
RAG的一个核心价值就是减少幻觉,但如果检索质量不高,模型反而会基于不相关的片段编造答案。抑制幻觉有几个手段:
Prompt约束是最直接的。在System Prompt里明确要求“只基于提供的文档片段回答,如果文档中没有相关信息,就说不知道”。但光靠Prompt不够,模型有时候会忽略。
引用标注是更可靠的方式。要求模型在回答时标注每个信息的来源片段编号,这样用户可以验证。同时,如果模型标注的引用和实际内容不符,也能发现。
置信度过滤是在检索阶段做的。如果所有检索结果的相似度都低于某个阈值,直接返回“没有找到相关信息”,而不是硬让模型回答。
Rerank分数过滤更精准。Rerank模型输出的相关性分数如果低于阈值,说明检索结果和问题确实不相关,这时候应该拒绝回答。
5.3 性能优化:响应太慢怎么破
Agent架构的响应时间通常比纯RAG长,因为多了工具调用和模型推理的轮次。优化方向有几个:
并行工具调用。如果Agent需要调用多个不相关的工具,可以让它们并行执行。比如同时查知识库和查数据库,而不是串行。
流式输出。模型生成回答时用流式返回,用户能更快看到内容。虽然总时间没变,但感知上的等待时间短了很多。
缓存。对于常见问题,可以把检索结果和最终回答缓存起来。下次同样的问题直接返回缓存,不用重新走一遍流程。缓存要注意设置合理的过期时间,因为企业知识是会更新的。
小模型做路由。如果Agent的工具很多,可以用一个小模型先做意图分类,判断用户的问题属于哪个领域,然后只把相关的工具定义传给大模型。这样减少了Prompt长度,也提高了工具选择的准确率。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 检索不到相关内容 | 文档未入库或切分不当 | 检查向量库文档数量 | 重新加载文档,调整切分策略 |
| 回答与问题不相关 | Embedding模型不匹配 | 检查模型语言支持 | 换用中文Embedding模型 |
| 模型编造答案 | 检索质量差或Prompt约束不足 | 查看检索片段相关性 | 加Rerank,强化Prompt约束 |
| 工具调用错误 | 工具描述不清晰 | 检查工具定义JSON | 优化描述,增加示例 |
| 响应时间过长 | 串行调用过多 | 分析调用链路 | 并行化,加缓存 |
| 多轮对话丢失上下文 | Memory配置不当 | 检查消息列表长度 | 调整Memory策略,加摘要 |
6. 企业知识助手的扩展方向
6.1 多Agent协作:让专业的人做专业的事
单Agent架构在工具数量增多时会变得难以管理。一个Agent要同时懂HR政策、财务流程、技术文档,Prompt会非常臃肿,工具选择也容易出错。
多Agent架构的思路是:每个Agent负责一个领域,有一个路由Agent根据用户问题分发给对应的领域Agent。比如HR Agent只处理人事相关的问题,财务Agent只处理报销和预算,技术Agent只处理产品和技术文档。
Agent之间的通信可以通过共享消息队列或者直接函数调用。LangGraph提供了多Agent编排的能力,可以定义Agent之间的状态流转。
这种架构的好处是每个Agent的Prompt更聚焦,工具集更小,准确率更高。缺点是系统复杂度增加,调试更困难。
6.2 知识图谱增强:结构化知识的补充
向量检索擅长处理非结构化文本,但对于实体之间的关系查询,效果有限。比如“张三是哪个部门的,他的上级是谁,他负责哪些项目”,这种问题用知识图谱来回答更合适。
知识图谱增强的RAG(GraphRAG)是最近比较热的方向。它的做法是先从文档中抽取实体和关系,构建知识图谱,检索时同时查向量库和图谱,把两者的结果融合。
不过GraphRAG的构建成本比较高,需要实体抽取、关系抽取、图谱存储和查询。适合实体关系密集的场景,比如法律文档、医疗记录、企业组织架构。如果只是普通的文档问答,纯向量检索加Rerank就够了。
6.3 安全与权限:企业场景的底线
企业知识助手和通用聊天机器人最大的区别就是权限控制。不同级别的员工能访问的知识范围不同,助手必须严格遵守这个边界。
实现方式是在检索阶段做过滤。每个文档片段在入库时打上权限标签(比如部门、密级),检索时根据当前用户的身份过滤。pgvector可以用WHERE子句直接过滤,Milvus支持标量字段过滤,Chroma也支持metadata过滤。
另一个安全考虑是工具调用的权限。不是所有用户都能调用所有工具。比如只有财务人员才能调用“查询薪资”的工具。这需要在Agent编排层做检查,用户没有权限的工具不传给模型。
还有一个容易被忽视的是输出审核。模型生成的回答可能包含敏感信息,需要在返回给用户之前做一次过滤。可以用规则引擎或者另一个模型来做审核。
注意:权限控制要在检索层做,不能只在展示层做。因为如果检索到了无权限的文档,即使不展示给用户,模型也可能在回答中泄露信息。
6.4 评估与迭代:怎么知道做得好不好
知识助手上线之后,怎么评估效果?不能只看用户满意度,要有量化的指标。
检索指标:Recall@K(前K个结果中包含正确答案的比例)、MRR(平均倒数排名)。这些需要标注一批测试数据,人工判断每个问题应该检索到哪些文档。
生成指标:答案准确率、幻觉率、引用准确率。可以人工评估,也可以用模型自动评估(比如用GPT-4给回答打分)。
端到端指标:任务完成率、平均响应时间、用户反馈评分。
我自己的做法是维护一个测试集,每次修改检索策略或者Prompt之后,跑一遍测试集,看指标有没有提升。没有测试集的话,优化就是盲目的。
评估数据的构建可以从真实用户问题里采样,人工标注标准答案和应检索的文档。一开始不用很多,50-100条就能看出趋势。
7. 一些实操中的个人体会
做企业知识助手这个方向有一段时间了,踩过的坑不少,有几个体会比较深。
第一,数据质量比模型选择重要。我见过太多团队花大量时间对比GPT-4和Claude哪个好,但文档本身格式混乱、内容过时、重复冗余。这种情况下换什么模型都救不了。先把文档整理干净,切分策略调好,比换模型带来的提升大得多。
第二,不要追求一步到位。一开始就想做多Agent、知识图谱、自动学习,大概率会烂尾。先从单Agent + RAG做起,把检索质量打磨好,把工具调用跑通,再逐步扩展。每一步都验证有效之后再往下走。
第三,用户反馈是最宝贵的优化信号。上线一个反馈按钮,让用户能标记“这个回答有用/没用”。收集到的bad case是最真实的优化方向。很多时候你以为的问题和用户实际遇到的问题不一样。
第四,日志要记全。每次对话的完整链路——用户问题、检索结果、工具调用、模型回答——都要记录下来。出问题的时候才能回溯。日志要注意脱敏,不能把敏感信息明文存储。
第五,Prompt要版本管理。Prompt改了之后效果变差是常有的事,如果没有版本管理,回滚都回不去。可以把Prompt存在数据库或者配置文件里,每次修改记录版本号和变更内容。
这个方向还在快速演进,新的框架、新的模型、新的方法层出不穷。但核心逻辑是不变的:把正确的知识在正确的时间以正确的方式提供给需要的人。技术只是手段,解决问题才是目的。