写在前面
我本职工作是做通信协议的,业务流程开发时必须严格按照rfc规范,很多细节问题需要比对rfc原文判断表现是否合理。用LLM解决这个问题的过程中,我发现大语言模型经常产生幻觉,引用不存在的“原文”,或所引段落与rfc存在出入(注:与模型能力,联网搜索等均有关,2026年3月市面主流模型表现已经好不少了)。在此时,我了解到RAG的概念,当时就想天呐这简直绝配。于是,我的第一个agent:rfcExpertAgent破壳诞生了。借此文总结我的agent学习/思考/尝试过程。
项目链接: NetworkExpertAgent
迭代记录
v0.1.0 - 实现基本功能
最初的版本思路很简单,我给模型提供了两种工具:
- add工具:下载rfc存进向量数据库
- search工具:在向量数据库中检索
@mcp.tool()
async def add_rfc(rfc_id: str) -> str:
"""
Download and index an RFC document into the vector database.
Args:
rfc_id: The RFC number (e.g., "7540" or "rfc7540").
"""
@mcp.tool()
async def search_rfc_knowledge(query: str) -> str:
"""
Search the indexed RFC knowledge base for relevant sections.
Args:
query: The search query or question about a protocol.
"""
核心流程是两层循环,外层循环读用户输入,内层循环llm判断何时调用工具,何时返回结果结束循环。 后面看了learn claude code发现这正是最简单最朴素的实现方式,详细可参考这个视频,思路是一样的Mini Claude Code-V1-模型即代理
v0.1.1 - 遇到的第一个问题
在问了几个问题后,我发现llm响应速度明显变慢了,尤其是不直接在问题中指明rfc id以及问llm没有回答过的协议。为了找到性能开销点,我给agent接入了langSmith,能够看到llm每一步思考过程,发现llm在遇到新问题时总是优先去调用search工具,查到很多无关问题的内容,大量污染上下文。
我朴素的解决方案是:每次search完,由llm判断索引结果与用户问题是否相关,如果不相关则标记为polluted,根据一张local rfc map尝试重新下载更新向量数据库。这样确实一定程度解决的问题,但是又面临两个新的难题:
- 性能极其差:一旦遇到“污染”的索引结果,需要花几分钟判断相关性并重走回答流程
- local rfc map不是一个好想法,如果map里没有,重试时agent不知道要怎么做
这两个问题很明显通过小修小补难以解决,于是我开启了第一次重构
v0.2.0 - 引入langGraph管理llm思考流程
本次重构的目标是:
- 提升响应速度:通过路由层过滤简单问题,减少不必要的 LLM 调用和工具使用。
- 优化架构:采用模块化设计,引入路由网关和细分领域 Agent(优先实现 RFC Expert)。
- 增强智能:利用 LangGraph 构建状态机,规范 Agent 的思考和执行流程,减少幻觉和错误调用。
- 规范工程:遵循开源项目结构,使用
uv进行依赖管理。 - 建立评测:引入自动化测试脚本,基于
quiz.md进行效果评分。
第一个比较大的变化是路由网关,让llm根据问题类型分给不同的agent回答,算是一个简单的workflow。这样做是一方面为了避免与网络无关的提问也走复杂的工具调用,比如“你好”这样的提问,llm自身即可回答;另一方面,网络领域不只有rfc知识,还包括组网解决方案,网络产品等等细分领域,引入路由网关实现了一定的可扩展性,为接入其他细分领域agent留出空间。
第二个大的变化是通过langGraph状态机管理rfcExpertAgent工作流程,人工约束了agent工作边界,避免agent在向量数据库没有相关信息的情况下重复search的行为。具体流程如下:
- Analyze (思考):
- 分析用户问题,提取关键协议名称或 RFC 编号。
- 判断是否需要查阅 RFC 文档。
- CheckLocal (检查本地):
- 检查所需的 RFC 文档是否已存在于本地向量库。
- 状态转移:
- 存在 -> 进入 Search (检索)。
- 不存在 -> 进入 Download (下载)。
- 无法确定/不需要 -> 请求用户输入更多相关信息。
- Download (下载):
- 调用工具下载指定 RFC 文档并入库。
- 状态转移 -> Search (检索)。
- Search (检索):
- 在向量库中搜索相关内容。
- 状态转移 -> Answer (回答)。
- Answer (回答):
- 综合检索到的上下文,回答用户问题。
v0.2.1 引入自动化测试用例
引入自动化测试用例有三个原因。一是我需要量化感知agent能力,当前有两个维度:准确性(占比70%)和回答速度(占比30%)。准确性本质是通过一个llm对比期望输出和真实输出,按0-10分评判关联性。回答速度采用的是简单的分级制度。二是大模型生成的代码有时候不能一次性跑通,提供自测用例是给ai一个途径验证修改代码是否真正可用。三是我想设计长时任务,让ai自己根据反馈结果优化代码。目前这个评估脚本测试用例还很少,评估方法也比较简陋,后面需要完善了整合成一个skill。
v0.2.2 引入web页面
使用openai的frontend skill设计了前端页面。对于我一个完全不懂前端的人来说,真的好厉害呀!虽然堆砌了一些无用文字,但是整体风格我还是挺喜欢的。
v0.3.0 - 尝试vercel部署
一开始整个项目使用本地模型sentence-transformers/all-MiniLM-L6-v2做embedding,用本地向量库chroma做RFC持久化。当我希望部署这个网站使其能被在线访问时,出现了一堆问题:
- vercel的只读文件系统对本地持久化并不友好,不可能把整个chromaDB上传到vercel,需要替换为在线数据库
- 本地
embedding导致python依赖包体过大,超出vercel免费部署的限制 - 状态机中的
download流程在首次请求会做下载 RFC + 切块 + 生成 embedding + 写库,很容易造成响应超时、链路不稳定、回答时间过长
解决前两个问题的思路很简单,把所有本地依赖全部改为在线服务。向量库我选择了supabase + pgvector,embedding则通过API远程调用text-embedding-3-small模型。
第三个问题目前的解决方案是通过脚本预热数据库,将download流程从agent状态机中剥离出来,放到服务开始前。
- Analyze (思考):
- 分析用户问题,提取关键协议名称或 RFC 编号。
- 判断是否需要查阅 RFC 文档。
- CheckLocal (检查本地):
- 检查所需的 RFC 文档是否已存在于本地向量库。
- 状态转移:
- 存在 -> 进入 Search (检索)。
- 不存在 -> 进入 Answer (回答)。
- Search (检索):
- 在向量库中搜索相关内容。
- 状态转移 -> Answer (回答)。
- Answer (回答):
- 综合检索到的上下文,回答用户问题。 这样做排除了状态机中的不稳定因素,但是对于数据库中没有的RFC,只能利用llm回答,不具备动态更新的能力。
v0.3.1 - 解决embedding模型切换后索引不准确的问题
我发现切换了embedding模型后,对于同样的问题(IGMP协议query interval默认值是多少),从向量数据库中索引出的top 5 RFC原文没有包含问题真正相关的答案,导致agent最后还是基于自身训练知识回答。我认为问题原因可能有:
- 我在做
embedding时用的是简单的按回车和字符串长度切块,削弱了rfc本身结构化文本的特征,导致按语义查询时可能查不到正确的chunk - 我期望的答案不包含在问题中,语义搜索中“答案”的相关性就比较低
我最终将embedding的切块方式改为按章节切块,如果章节过长,则章节内按字符串长度进一步切块这样的方式解决了问题。不过,这个问题很明显是更泛化的,尤其第二个因素很值得研究。
思考一: RAG(Retrieval-Augmented Generation)的本意是检索增强生成,只要能检索到有用信息喂给llm就行,没有约束检索方法,只是在大模型火之后,因为其语义搜索的需求,很大程度上与向量数据库数据库绑定了。在这个问题中,关键词也许会是更有效的检索方法,可以考虑supabase的混合搜索,结合关键词和语义搜索(甚至还可以扩展加入其他检索方法)共同查找相关的chunk。
思考二:看到过一种方法,先让llm对问题做出假设性回答,再讲回答做embedding做语义检索,可以解决“答案不在问题中”这类问题。我觉得还有一些需要更深入思考的点:一个是判断何时需要做这种假设性回答;二是在llm模型能力不同,同一个问题有的模型能直接准确回答,有的模型则需要RAG辅助,如何判断这种差异?
v0.3.2 - 细化自测评估标准
为了评估RAG查询原文是否准确,agent 输出会被解析为统一三字段:结论、出处定位、协议原文节选。结论正确性由 LLM judge 评分,出处定位准确性通过 RFC 编号与 section / appendix 匹配评分,置信度通过回答中的 协议原文节选 与 quiz.md 中期望 RFC 原文的匹配度评分,用于衡量幻觉程度。最终得分 = 结论 40% + 出处定位 20% + 置信度 20% + 耗时 20%。
写在后面
本项目当前最大问题是除了学习用途完全没用。现在大模型训练数据基本都包含了RFC这种公开文档,用公开数据做RAG意义已经不大了,需要RAG的也许更多是企业内部非公开数据。
(未完待续,欢迎大家提供好的建议或发表真知灼见...)