RAG + Agent双剑合璧:让AI不仅能查知识,还能用知识干活
上个月我接了个需求:做一个能帮用户查产品手册的客服助手。需求看起来平平无奇对吧?但客户提了个要命的条件——用户问完产品参数后,助手得能直接替用户去查库存、比价格、下订单。
光靠RAG查文档是搞不定的。Agent虽然能做动作,但没有实时知识又容易胡说八道。
折腾了两周,最后搞了个 RAG + Agent 的混合架构。今天聊聊这玩意怎么落地。
为什么光有RAG不够,光有Agent也不行
先说RAG。RAG说白了就是"你先去查资料,查到以后让大模型根据资料回答"。优点是答案靠谱,不会瞎编。缺点是——它只能回答,不能做事。
你问"帮我查一下这款产品的库存",RAG能回答"根据手册,型号XYZ-100的库存查询接口是/check-stock"。
但它不会帮你调接口。
Agent反过来。它能做事——调API、写文件、发邮件——但它靠的是大模型自身的知识。大模型知道"调库存接口"这个动作,但不知道你们公司接口的具体参数是什么,Token里什么格式,权限怎么签。
所以RAG做不了动作,Agent没有实时知识。
把两者结合起来,就成了。
核心架构:RAG给知识,Agent做决策
我最后搭的架构分三层。
第一层:路由层
用户提问进来,先过一个小模型判断意图。是"知识问答类"还是"操作执行类"?
- 知识问答类 → 直接走 RAG 查询,省Token
- 操作执行类 → 走 Agent 链路
- 混合类(问产品后要下单) → 先RAG查,再Agent执行
这个路由用GPT-4o-mini就够了,又快又便宜。我实测准确率大概87%,剩下的13%误判走错了也不致命,最多多花点Token。
def route_intent(query: str) -> str:
"""判断用户意图"""
prompt = f"""分类用户意图:
A - 知识问答(只需查资料回答)
B - 操作执行(需要调用外部工具/API)
C - 混合(先查知识再执行操作)
用户问题:{query}
只返回 A/B/C,不要多余内容。"""
result = llm_call(prompt, model="gpt-4o-mini")
return result.strip()
第二层:RAG知识层
这里的RAG不是传统RAG。传统RAG存的是文档段落,我存的是"工具元数据"。
什么意思呢?就是把每个API接口的描述、参数、返回格式、使用场景——当作文档片段索引起来。
# 存储到向量库的工具元数据结构
tool_doc = {
"tool_name": "check_stock",
"description": "查询产品实时库存",
"parameters": {
"product_id": "string, 产品ID",
"warehouse": "string, 可选,仓库编码"
},
"return_format": "json { quantity: int, status: string }",
"usage_scenarios": ["查询库存", "下单前校验"],
"auth_required": True,
"api_endpoint": "https://api.company.com/v1/stock"
}
这样当Agent需要决定调用什么工具时,先通过RAG检索出最相关的工具元数据,再决定调哪个。
这比把所有工具写在System Prompt里靠谱得多。我试过塞40个工具的System Prompt,模型直接傻了——不是记不住参数,就是混着调用。
第三层:Agent执行层
Agent部分不复杂,就是 OpenAI Function Calling 或者 Anthropic Tool Use。
关键区别在于:工具列表不是写死的,而是从RAG检索结果动态生成的。
def build_tools_from_rag(query: str, k: int = 3):
"""根据用户问题,从RAG中检索最相关的工具"""
results = vector_db.search(query, k=k)
tools = []
for r in results:
tool = {
"type": "function",
"function": {
"name": r["tool_name"],
"description": r["description"],
"parameters": {
"type": "object",
"properties": {
k: {"type": "string", "description": v}
for k, v in r["parameters"].items()
}
}
}
}
tools.append(tool)
return tools
这么搞的好处是:理论上支持无限多个工具。实际跑下来,一次检索3-5个工具,模型处理得游刃有余。
踩坑记录:三个让我头疼的问题
坑1:RAG检索到不相关工具,Agent硬要用
RAG检索工具时,如果检索精度不够,Agent可能会用错工具。比如用户说"帮我查库存",RAG检索到了"check_stock"和"create_order",Agent莫名其妙就跑去下单了。
解决办法:给每个工具加一个"relevance_score"字段。检索后过滤掉相关性低于阈值的结果。我设为0.75,低于这个值的不传入Agent。
坑2:上下文窗口膨胀
每次Agent执行前都要塞进3-5个工具描述,每个描述300-500字。多轮对话下来,Context越来越长。
解决办法:只保留当前轮次检索到的工具。上一轮调过的工具,只保留调用结果,不保留工具描述。这样每轮新增的Token量是固定的。
坑3:工具调用失败后的知识回退
Agent调用API失败了——比如库存接口挂了。如果Agent说"我调了,但报错了",用户一脸懵逼。
解决办法:加一个fallback机制。当Agent工具调用失败时,自动触发RAG查询对应工具的"常见问题"和"故障处理"文档,把解决方案返回给Agent,让它重新尝试。
def tool_call_with_fallback(tool_name: str, params: dict):
try:
return call_api(tool_name, params)
except APIError as e:
# 查询该工具的故障处理知识
fallback_knowledge = rag_retrieve(
f"{tool_name} 失败 常见问题"
)
return {
"status": "failed",
"error": str(e),
"fallback_advice": fallback_knowledge
}
实际效果:真香
这套架构跑了一个多月了,处理了大概1200多真实查询。
- 纯RAG能覆盖的:约40%(直接问知识类问题)
- 纯Agent能搞定的:约25%(简单操作类)
- 需要RAG+Agent配合的:约35%(先查再操作)
整体准确率从之前纯Agent方案的76%提升到了91%。多数错误出现在路由误判上,把混合类判成了纯知识类。
对于经常被问到的场景,我还加了缓存——同样的Query直接用缓存结果,响应时间从3-5秒降到了500ms以内。
写在最后
RAG和Agent不是二选一的关系。RAG负责让AI知道"这个世界是什么样的",Agent负责"让AI去改变这个世界"。两个缺一个,应用都跑不通。
如果你也在搭类似的系统,我建议从简单的路由+RAG开始,慢慢加Agent能力。别一上来就想搞全自动Multi-Agent——我见过太多团队死在第一步。
下一篇聊聊这个架构里怎么设计Agent的短期记忆和长期记忆,欢迎关注。
有问题评论区聊。
参考资料:
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)