
传统RAG vs Agentic RAG:全面对比分析,为什么说智能代理是下一代AI应用的核心? 原创 精华
用传统 RAG 构建 AI 应用,总感觉像在跟一个"健忘症患者"对话——它能找到相关文档,却无法真正理解问题的本质。问它"如何优化 RAG 系统的准确率",它只会机械地检索包含"RAG"和"准确率"的文档,然后拼凑出一个看似合理的答案。但如果问题稍微复杂一点,比如"比较不同 RAG 实现方案的优缺点,并给出在资源受限场景下的最佳实践",传统 RAG 就露怯了。
这就是 Agentic RAG 要解决的核心问题——让 AI 不仅会"找",更要会"想"。
从被动到主动
传统 RAG 的天花板
传统 RAG 的逻辑很简单:
用户提问 → 向量化 → 检索相似文档 → 生成答案
这个流程有个致命缺陷:它是纯被动的。系统不会思考"这个问题真正在问什么",不会判断"检索到的内容是否真的相关",更不会意识到"可能需要多步推理才能得到答案"。
打个比方,传统 RAG 像图书馆的检索系统——你输入关键词,它返回相关书籍。而 Agentic RAG 更像一个资深的图书管理员——他会理解你的真实需求,知道该查哪几本书,还会帮你交叉验证信息的准确性。
Agentic RAG 的核心理念
Agentic RAG 是动态的。AI 代理主动管理信息获取方式,将 RAG 整合到推理过程中。它不只是检索,而是精炼查询、将 RAG 作为精密工具使用,并随时间管理信息。这种智能方法让 AI 代理能更好地适应不断变化的情况。
核心区别在于引入了自主推理和决策能力:
- 感知(Perceiving):理解环境和情境
- 推理(Reasoning):分解复杂问题,制定解决策略
- 规划(Planning):创建分步行动计划
- 行动(Acting):执行任务,使用各种工具
架构设计:从线性到循环
核心组件解析
Agentic RAG 系统的架构包含几个关键组件,每个组件都扮演着特定的角色,共同构成一个智能的信息处理系统。
1. 推理引擎(Reasoning Engine)
推理引擎是整个系统的"大脑",它不仅处理逻辑推理,还负责协调其他组件的工作。想象一下,这就像一个经验丰富的侦探,能够从蛛丝马迹中推理出完整的真相。
推理引擎的核心职责包括:
- 意图理解:深入分析用户查询背后的真实需求,而不仅仅是表面的关键词
- 策略制定:根据问题的复杂度和类型,选择最合适的解决路径
- 结果评估:判断当前获得的信息是否足够回答问题
- 决策制定:决定是继续检索、调整策略还是生成最终答案
class ReasoningEngine:
"""Agentic RAG 的推理引擎
这个类实现了 Agentic RAG 的核心推理逻辑。它维护着对话的记忆,
能够制定和调整计划,并通过自我反思不断改进答案质量。
"""
def __init__(self, llm):
self.llm = llm # 大语言模型,用于执行推理
self.memory = ConversationMemory() # 对话记忆,保存上下文
self.planner = TaskPlanner() # 任务规划器,负责分解复杂任务
asyncdef reason(self, query: str, context: Dict):
"""推理流程的主函数
这个方法实现了完整的推理循环:
1. 首先理解用户的真实意图(不只是字面意思)
2. 基于意图制定执行计划(可能包含多个步骤)
3. 逐步执行计划,并在每一步后验证结果
4. 如果结果不满意,会重新调整计划
5. 最终综合所有信息生成答案
Args:
query: 用户的原始查询
context: 包含历史对话、用户偏好等上下文信息
Returns:
综合推理后的最终答案
"""
# 步骤1:意图理解 - 分析用户真正想要什么
# 例如:"最新的 AI 论文"可能意味着用户想了解前沿研究动态
intent = await self.understand_intent(query, context)
# 步骤2:制定计划 - 将复杂问题分解为可执行的子任务
# 例如:先检索论文列表,然后筛选最新的,最后总结要点
plan = await self.planner.create_plan(intent)
# 步骤3:执行并验证 - 这是 Agentic RAG 的核心循环
results = []
for step in plan:
# 执行当前步骤
result = await self.execute_step(step)
# 关键:自我反思和验证
# 这是 Agentic RAG 区别于传统 RAG 的重要特征
ifnot self.validate_result(result):
# 如果结果不满意,动态调整计划
# 可能会添加新的检索步骤或改变策略
plan = await self.planner.revise_plan(step, result)
results.append(result)
# 步骤4:综合答案 - 将所有收集的信息整合成连贯的回答
return self.synthesize_answer(results)
2. 记忆系统(Memory System)
记忆系统是 Agentic RAG 能够进行连贯对话和持续学习的基础。与传统 RAG 每次都"从零开始"不同,Agentic RAG 能够记住之前的交互,并从中学习。
记忆系统采用分层设计,模仿人类大脑的记忆机制:
class AgentMemory:
"""分层记忆系统
这个记忆系统模仿人类的记忆结构,包含三种不同类型的记忆:
1. 短期记忆:类似于人类的工作记忆,保存当前对话的即时信息
2. 长期记忆:类似于人类的语义记忆,存储重要的知识和概念
3. 情景记忆:类似于人类的经验记忆,记录成功的问题解决策略
"""
def __init__(self):
# 短期记忆:使用双端队列实现,自动丢弃旧的记忆
# maxlen=10 意味着只保留最近的10条交互记录
# 这避免了上下文过长导致的性能问题
self.short_term = deque(maxlen=10)
# 长期记忆:使用向量数据库实现,可以永久保存
# 支持语义搜索,能找到概念上相关的记忆
self.long_term = VectorStore()
# 情景记忆:记录成功的经验和策略
# 当遇到类似问题时,可以直接复用之前的成功策略
self.episodic = ExperienceBuffer()
def remember(self, interaction):
"""智能记忆存储
不是所有信息都值得记住。这个方法会:
1. 将所有交互先存入短期记忆
2. 评估信息的重要性
3. 重要信息会被提升到长期记忆
4. 成功的策略会被记录为经验
Args:
interaction: 包含查询、答案、策略等信息的交互记录
"""
# 所有交互都先进入短期记忆
self.short_term.append(interaction)
# 评估重要性:基于信息量、新颖性、用户反馈等因素
if self.is_important(interaction):
# 重要信息提升到长期记忆
# 会被向量化以支持语义搜索
self.long_term.add(interaction)
# 记录成功的策略供未来参考
# 这使得系统能够从经验中学习
if interaction.successful:
self.episodic.add_experience(interaction)
def recall(self, query: str, k: int = 5):
"""智能记忆检索
根据查询检索相关记忆,优先级:
1. 短期记忆(最相关的当前上下文)
2. 情景记忆(类似问题的成功经验)
3. 长期记忆(相关的背景知识)
"""
relevant_memories = []
# 从短期记忆中查找
for memory in self.short_term:
if self.is_relevant(memory, query):
relevant_memories.append(memory)
# 从情景记忆中查找类似经验
similar_experiences = self.episodic.find_similar(query, k=3)
relevant_memories.extend(similar_experiences)
# 从长期记忆中语义搜索
semantic_memories = self.long_term.search(query, k=k)
relevant_memories.extend(semantic_memories)
return relevant_memories
3. 工具系统(Tool System)
工具系统让 Agentic RAG 能够执行具体的任务,而不仅仅是生成文本。每个工具都是一个独立的功能模块,可以被推理引擎调用来完成特定任务。
工具的设计遵循统一的接口规范,这使得系统可以轻松扩展新的能力:
from typing import List, Callable
from pydantic import BaseModel
class Tool(BaseModel):
"""工具基类
所有工具都必须继承这个基类,确保接口的一致性。
这种设计让推理引擎可以用统一的方式调用不同的工具。
"""
name: str # 工具的唯一标识符
description: str # 工具的功能描述,帮助 LLM 理解何时使用
parameters: Dict # 工具需要的参数定义
asyncdef execute(self, **kwargs):
"""执行工具的抽象方法"""
raise NotImplementedError
class SearchTool(Tool):
"""搜索工具
这个工具展示了如何实现一个具体的功能。
它可以搜索互联网获取最新信息,这是传统 RAG 做不到的。
"""
name: str = "web_search"
description: str = "搜索互联网获取最新信息,适用于需要实时数据的查询"
asyncdef execute(self, query: str):
"""执行搜索
这个方法会:
1. 调用搜索 API(如 Google、Bing)
2. 解析返回的结果
3. 提取关键信息
4. 返回结构化的数据
"""
# 调用实际的搜索 API
results = await search_api(query)
# 解析和清洗结果
# 这一步很重要,因为原始搜索结果可能包含很多噪音
cleaned_results = self.parse_results(results)
# 返回结构化数据,方便后续处理
return {
"tool": self.name,
"query": query,
"results": cleaned_results,
"timestamp": datetime.now()
}
class DatabaseTool(Tool):
"""数据库查询工具
这个工具可以查询内部数据库,获取结构化数据。
这对于回答需要精确数据的问题非常重要。
"""
name: str = "db_query"
description: str = "查询内部数据库获取精确的业务数据"
asyncdef execute(self, sql: str):
"""安全地执行 SQL 查询
注意:这里需要特别注意 SQL 注入等安全问题
实际使用时应该:
1. 使用参数化查询
2. 限制查询权限
3. 添加查询超时
"""
# 验证 SQL 安全性
ifnot self.is_safe_query(sql):
raise ValueError("Unsafe SQL query detected")
# 执行查询
results = await db.execute(sql)
# 格式化结果
return self.format_results(results)
class CalculatorTool(Tool):
"""计算工具
用于执行数学计算,这在处理数据分析类问题时很有用。
"""
name: str = "calculator"
description: str = "执行数学计算,支持基本运算和科学计算"
asyncdef execute(self, expression: str):
"""安全地计算数学表达式
使用 AST(抽象语法树)来安全地计算表达式,
避免执行任意代码的安全风险。
"""
import ast
import operator
# 定义允许的操作符
allowed_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow
}
# 安全地解析和计算表达式
try:
tree = ast.parse(expression, mode='eval')
result = self.eval_expr(tree.body, allowed_ops)
return {"result": result, "expression": expression}
except Exception as e:
return {"error": str(e), "expression": expression}
ReAct 框架:推理与行动的统一
ReAct(Reasoning and Acting)框架是 Agentic RAG 最流行的实现模式。它将推理和行动交织在一起,形成一个动态的问题解决循环。
理解 ReAct 的关键是认识到它的三个核心阶段:
- Thought(思考):分析当前状态,决定下一步做什么
- Action(行动):执行具体的操作,如检索、计算等
- Observation(观察):观察行动的结果,为下一轮思考提供输入
class ReActAgent:
"""基于 ReAct 框架的代理
ReAct 框架的核心思想是将推理和行动交织进行:
- 不是先想好所有步骤再执行(传统规划方式)
- 而是想一步、做一步、看结果、再想下一步
这种方式更加灵活,能够根据中间结果动态调整策略。
"""
def __init__(self, llm, tools: List[Tool]):
self.llm = llm
# 将工具列表转换为字典,方便通过名称查找
self.tools = {tool.name: tool for tool in tools}
# 设置最大迭代次数,防止无限循环
self.max_iterations = 5
asyncdef run(self, query: str):
"""ReAct 主循环
这个方法实现了 ReAct 的核心循环:
1. Thought:基于当前信息思考下一步
2. Action:执行选定的行动
3. Observation:观察结果
4. 重复直到得到答案或达到最大迭代次数
这种循环模式的优势:
- 可以根据中间结果调整策略
- 能够处理需要多步推理的复杂问题
- 可以在过程中发现并纠正错误
"""
thoughts = [] # 记录所有的思考过程
observations = [] # 记录所有的观察结果
for i in range(self.max_iterations):
# Thought: 思考下一步
# 这里 LLM 会分析当前情况,决定:
# - 是否已经有足够信息回答问题
# - 如果没有,需要使用什么工具获取什么信息
thought = await self.think(query, thoughts, observations)
thoughts.append(thought)
# 检查是否已经得到最终答案
# 如果 LLM 认为已经有足够信息,就结束循环
if self.is_final_answer(thought):
return thought.answer
# Action: 执行行动
# 从思考结果中提取要执行的动作
action = self.parse_action(thought)
# Observation: 观察结果
# 调用相应的工具并获取结果
observation = await self.tools[action.tool].execute(**action.params)
observations.append(observation)
# 验证进展
# 这是一个重要的安全机制,防止陷入循环
ifnot self.is_making_progress(thoughts, observations):
# 如果没有进展,尝试改变策略
await self.revise_strategy()
# 如果达到最大迭代次数,基于现有信息生成答案
return self.synthesize_from_observations(observations)
asyncdef think(self, query, thoughts, observations):
"""生成思考
这个方法构造提示词,让 LLM 基于历史信息生成下一步的思考。
关键是提供足够的上下文,让 LLM 能做出明智的决策。
"""
prompt = f"""
Question: {query}
Previous thoughts: {thoughts}
Previous observations: {observations}
Based on the above information, think step by step:
1. What do we know so far?
2. What information is still missing?
3. What should be the next action?
If you have enough information to answer the question,
provide the final answer. Otherwise, specify what tool
to use next and with what parameters.
"""
# LLM 会返回结构化的思考结果
response = await self.llm.generate(prompt)
# 解析 LLM 的响应,提取思考内容和可能的动作
return self.parse_thought(response)
def is_making_progress(self, thoughts, observations):
"""检查是否在取得进展
这个方法防止代理陷入循环或重复相同的操作。
通过分析历史记录,判断是否:
1. 在获取新信息
2. 在接近答案
3. 陷入了循环
"""
if len(observations) < 2:
returnTrue# 刚开始,认为在进展
# 检查最近的观察是否带来新信息
last_obs = observations[-1]
prev_obs = observations[-2]
# 如果连续两次得到相似的结果,可能陷入循环
if self.are_similar(last_obs, prev_obs):
returnFalse
# 检查是否在重复相同的思考
if len(thoughts) > 2and thoughts[-1] == thoughts[-3]:
returnFalse
returnTrue
实战实现:用 LangGraph 构建 Agentic RAG
现在让我们通过一个完整的实例,展示如何使用 LangGraph 构建生产级的 Agentic RAG 系统。LangGraph 是 LangChain 推出的用于构建有状态的、多步骤的 AI 应用的框架。
基础架构搭建
首先,我们需要定义系统的状态和基本组件。在 Agentic RAG 中,状态管理特别重要,因为系统需要在多个步骤之间传递和更新信息。
from langgraph.graph import StateGraph, MessagesState
from langchain_openai import ChatOpenAI
from langchain.tools import Tool
from typing import List, Dict, Any
import asyncio
# 定义状态
class AgenticRAGState(MessagesState):
"""Agentic RAG 状态
这个状态类定义了在整个处理流程中需要传递的所有信息。
继承自 MessagesState,自动包含了消息历史记录的管理。
每个字段的作用:
- query: 用户的原始查询,保持不变,用于最终答案的生成
- plan: 任务分解后的执行计划,可能会根据执行情况动态调整
- retrieved_docs: 检索到的文档,累积存储所有相关信息
- reflections: 系统的自我反思记录,用于改进策略
- final_answer: 最终生成的答案
- iteration: 当前迭代次数,用于控制循环
- max_iterations: 最大迭代次数,防止无限循环
"""
query: str
plan: List[str] = []
retrieved_docs: List[Dict] = []
reflections: List[str] = []
final_answer: str = ""
iteration: int = 0
max_iterations: int = 5
# 初始化 LLM
# 使用 GPT-4,temperature=0 确保输出的一致性和可预测性
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 创建检索工具
def create_retriever_tool(vectorstore):
"""创建检索工具
这个函数将向量数据库封装成一个工具,使其可以被 Agentic RAG 调用。
关键特性:
1. 异步执行,提高性能
2. 返回结构化数据,包含内容和元数据
3. 支持调整检索数量(k 参数)
"""
asyncdef retrieve(query: str, k: int = 5) -> List[Dict]:
"""检索相关文档
Args:
query: 检索查询
k: 返回的文档数量
Returns:
包含文档内容和元数据的列表
"""
# 使用向量数据库进行语义搜索
docs = await vectorstore.similarity_search(query, k=k)
# 将文档对象转换为字典,方便后续处理
# 保留元数据很重要,可能包含来源、时间戳等信息
return [
{
"content": doc.page_content,
"metadata": doc.metadata,
"relevance_score": doc.score if hasattr(doc, 'score') elseNone
}
for doc in docs
]
return Tool(
name="retrieve",
func=retrieve,
description="检索相关文档,支持语义搜索"
)
节点函数实现
接下来,我们实现 Agentic RAG 的核心节点。每个节点负责特定的任务,通过组合这些节点,我们可以构建复杂的推理流程。
async def plan_node(state: AgenticRAGState):
"""规划节点:分解任务
这是 Agentic RAG 的第一步,也是最关键的一步。
它将用户的复杂查询分解为可执行的子任务。
为什么需要任务分解?
1. 复杂问题往往需要多个步骤才能解答
2. 分解后的子任务更容易检索到相关信息
3. 可以并行处理多个子任务,提高效率
例如,查询"比较 A 和 B 的优缺点"会被分解为:
- 了解 A 的特点
- 了解 B 的特点
- 分析 A 的优势
- 分析 B 的优势
- 对比两者的差异
"""
prompt = f"""
用户问题:{state.query}
请将这个问题分解为几个子任务,每个子任务应该:
1. 具体且可执行 - 能够通过检索或推理完成
2. 相对独立 - 每个任务聚焦一个方面
3. 逻辑有序 - 任务之间有合理的执行顺序
4. 完整覆盖 - 所有任务完成后能回答原始问题
返回 JSON 格式的任务列表,例如:
["任务1", "任务2", "任务3"]
"""
# 调用 LLM 生成执行计划
response = await llm.ainvoke(prompt)
# 解析 LLM 返回的 JSON
# 这里需要健壮的错误处理,因为 LLM 可能返回格式不正确的内容
try:
plan = parse_json(response.content)
except Exception as e:
# 如果解析失败,使用备用策略
plan = [state.query] # 降级为直接检索原始查询
return {"plan": plan}
asyncdef retrieve_node(state: AgenticRAGState):
"""检索节点:智能检索
这个节点负责执行实际的信息检索。
与传统 RAG 的区别在于:
1. 为每个子任务生成优化的查询
2. 评估检索结果的相关性
3. 可以根据需要进行多轮检索
检索策略:
- 初次检索:广泛搜索,获取更多可能相关的信息
- 精确检索:基于初次结果,进行更targeted的搜索
- 验证检索:交叉验证信息的准确性
"""
retrieved_docs = []
for task in state.plan:
# 步骤1:查询优化
# 不直接使用任务描述,而是生成更适合检索的查询
optimized_query = await optimize_query(task, state.query)
# 步骤2:执行检索
# 可能使用多种检索策略:
# - 语义检索(向量相似度)
# - 关键词检索(BM25)
# - 混合检索(结合多种方法)
docs = await retriever_tool.func(optimized_query)
# 步骤3:相关性评估
# 不是所有检索到的文档都真正相关
# 使用 LLM 或其他方法评估相关性
relevant_docs = await evaluate_relevance(docs, task)
# 步骤4:去重和合并
# 避免重复信息,保持结果的简洁性
for doc in relevant_docs:
ifnot is_duplicate(doc, retrieved_docs):
retrieved_docs.append(doc)
return {"retrieved_docs": retrieved_docs}
asyncdef optimize_query(task: str, original_query: str) -> str:
"""优化检索查询
这个函数将任务描述转换为更适合检索的查询。
优化策略包括:
1. 添加相关关键词
2. 扩展同义词
3. 考虑上下文
4. 使用领域特定的术语
"""
prompt = f"""
原始问题:{original_query}
当前子任务:{task}
请生成一个优化的检索查询,要求:
1. 保留核心语义
2. 添加相关关键词
3. 使用更专业的术语
4. 考虑可能的同义词
直接返回优化后的查询,不要解释。
"""
response = await llm.ainvoke(prompt)
return response.content.strip()
asyncdef evaluate_relevance(docs: List[Dict], task: str) -> List[Dict]:
"""评估文档相关性
这个函数筛选出真正相关的文档。
评估标准:
1. 内容相关性:文档是否包含任务所需的信息
2. 信息质量:信息是否准确、完整、最新
3. 来源可靠性:文档来源是否可信
"""
relevant_docs = []
for doc in docs:
prompt = f"""
任务:{task}
文档内容:{doc['content'][:500]} # 只看前500字符
这个文档对完成任务的相关性如何?
评分标准(1-10分):
- 直接相关(8-10分):包含任务所需的核心信息
- 部分相关(5-7分):包含有用但不完整的信息
- 边缘相关(3-4分):只有少量相关信息
- 不相关(1-2分):与任务无关
只返回数字分数。
"""
response = await llm.ainvoke(prompt)
try:
score = float(response.content.strip())
if score >= 5: # 只保留部分相关以上的文档
doc['relevance_score'] = score
relevant_docs.append(doc)
except:
pass# 忽略无法解析的响应
# 按相关性分数排序
relevant_docs.sort(key=lambda x: x.get('relevance_score', 0), reverse=True)
return relevant_docs[:5] # 只返回最相关的5个文档
asyncdef reflect_node(state: AgenticRAGState):
"""反思节点:自我评估
这是 Agentic RAG 的独特特性,系统会:
1. 评估当前收集的信息是否足够
2. 识别信息中的矛盾和空白
3. 决定是否需要调整策略
反思的重要性:
- 避免盲目执行,确保方向正确
- 及时发现问题,避免错误累积
- 持续优化策略,提高效率
"""
prompt = f"""
原始问题:{state.query}
执行计划:{state.plan}
检索到的文档数量:{len(state.retrieved_docs)}
请进行深度分析和反思:
1. 信息完整性评估
- 检索到的信息是否覆盖了所有子任务?
- 是否有关键信息缺失?
- 信息的深度是否足够?
2. 信息质量评估
- 信息是否可靠和准确?
- 是否存在矛盾的信息?
- 信息是否过时?
3. 策略有效性评估
- 当前的检索策略是否有效?
- 是否需要调整搜索方向?
- 是否需要使用其他工具或方法?
4. 下一步建议
- 如果信息充足,可以生成答案
- 如果信息不足,建议具体的改进措施
请给出你的分析和建议。
"""
# 为了让 LLM 更好地分析,我们提供文档摘要
doc_summary = summarize_documents(state.retrieved_docs)
prompt = prompt.replace("检索到的文档数量", f"检索到的文档摘要:\n{doc_summary}\n\n文档数量")
reflection = await llm.ainvoke(prompt)
# 更新状态
return {
"reflections": state.reflections + [reflection.content],
"iteration": state.iteration + 1
}
asyncdef answer_node(state: AgenticRAGState):
"""回答节点:综合生成答案
这是最后一步,将所有收集的信息综合成一个完整的答案。
答案生成策略:
1. 结构化:按照逻辑顺序组织信息
2. 完整性:确保回答了用户的所有问题
3. 准确性:基于检索到的事实,避免臆测
4. 可读性:语言清晰,易于理解
"""
# 准备答案生成的上下文
context = prepare_answer_context(state)
prompt = f"""
基于以下信息,请为用户问题提供一个全面、准确的答案。
用户问题:{state.query}
执行的任务分解:
{format_list(state.plan)}
检索到的关键信息:
{context['key_findings']}
系统反思和分析:
{format_list(state.reflections)}
要求:
1. 直接回答用户的问题,不要赘述过程
2. 如果信息不完整,诚实地指出局限性
3. 使用检索到的事实支撑你的观点
4. 保持客观中立,不要加入个人偏见
5. 如果有多个观点,公平地呈现各方观点
请生成最终答案:
"""
answer = await llm.ainvoke(prompt)
# 可以添加后处理步骤
# 例如:事实核查、格式优化、添加引用等
final_answer = post_process_answer(answer.content, state)
return {"final_answer": final_answer}
def should_continue(state: AgenticRAGState):
"""决定是否继续迭代
这个函数是 Agentic RAG 的"大脑",决定系统的下一步行动。
它需要平衡多个因素:
1. 信息是否充足
2. 是否达到迭代上限
3. 是否在取得进展
4. 成本考虑
这种动态决策机制是 Agentic RAG 的核心优势。
"""
# 检查是否达到最大迭代次数
if state.iteration >= state.max_iterations:
return"answer"# 强制结束,生成答案
# 分析最新的反思结果
if state.reflections:
latest_reflection = state.reflections[-1]
# 使用简单的关键词匹配判断
# 实际应用中可以用更复杂的方法
if"信息充足"in latest_reflection or"可以回答"in latest_reflection:
return"answer"
if"需要更多信息"in latest_reflection or"信息不足"in latest_reflection:
return"retrieve"
# 基于文档数量的启发式判断
if len(state.retrieved_docs) < 3:
return"retrieve"# 文档太少,继续检索
if len(state.retrieved_docs) > 20:
return"answer"# 文档足够多,可以回答
# 默认继续检索
return"retrieve"
构建和运行 Agentic RAG 图
现在我们将所有组件组合成一个完整的系统:
def build_agentic_rag_graph():
"""构建 Agentic RAG 图
这个函数使用 LangGraph 将所有节点和边组合成一个有向图。
图的结构定义了信息流和决策逻辑。
关键设计决策:
1. 单一入口点:从 plan 节点开始
2. 循环结构:retrieve -> reflect -> retrieve(如需要)
3. 明确的退出点:answer 节点后结束
"""
graph = StateGraph(AgenticRAGState)
# 添加节点
# 每个节点都是一个异步函数,处理特定任务
graph.add_node("plan", plan_node)
graph.add_node("retrieve", retrieve_node)
graph.add_node("reflect", reflect_node)
graph.add_node("answer", answer_node)
# 添加边 - 定义节点之间的连接
# 设置入口点 - 所有处理从规划开始
graph.set_entry_point("plan")
# plan 完成后总是进入 retrieve
graph.add_edge("plan", "retrieve")
# retrieve 完成后总是进入 reflect
graph.add_edge("retrieve", "reflect")
# 条件边 - 根据条件决定下一步
# 这是实现动态行为的关键
graph.add_conditional_edges(
"reflect", # 从 reflect 节点
should_continue, # 使用这个函数决定
{
"retrieve": "retrieve", # 继续检索
"answer": "answer" # 生成答案
}
)
# answer 节点后结束
graph.add_edge("answer", END)
# 编译图 - 优化执行
return graph.compile()
# 使用示例
asyncdef main():
"""主函数 - 演示如何使用 Agentic RAG 系统"""
# 创建 Agentic RAG 系统
agentic_rag = build_agentic_rag_graph()
# 复杂查询示例
# 这种查询需要多方面的信息和深度分析
query = """
比较 LangChain 和 LlamaIndex 在构建 RAG 系统时的优缺点,
并推荐适合初创公司的方案,考虑因素包括:
1. 开发效率
2. 性能表现
3. 成本控制
4. 可维护性
"""
# 执行查询
print("开始处理查询...")
print(f"查询内容:{query}\n")
result = await agentic_rag.ainvoke({
"query": query,
"messages": [], # 初始化空消息列表
"plan": [],
"retrieved_docs": [],
"reflections": [],
"iteration": 0
})
# 输出结果
print("=" * 60)
print("最终答案:")
print("=" * 60)
print(result['final_answer'])
print("\n" + "=" * 60)
print(f"执行统计:")
print(f"- 迭代次数:{result['iteration']}")
print(f"- 检索文档数:{len(result['retrieved_docs'])}")
print(f"- 任务分解数:{len(result['plan'])}")
# 显示执行过程(用于调试和优化)
print("\n" + "=" * 60)
print("执行计划:")
for i, task in enumerate(result['plan'], 1):
print(f"{i}. {task}")
print("\n" + "=" * 60)
print("反思记录:")
for i, reflection in enumerate(result['reflections'], 1):
print(f"\n第 {i} 次反思:")
print(reflection[:200] + "..."if len(reflection) > 200else reflection)
# 运行主函数
if __name__ == "__main__":
asyncio.run(main())
多代理协作系统
在复杂场景中,单个代理往往力不从心。多代理系统通过专业化分工和协作,可以处理更复杂的任务。这就像一个专家团队,每个成员都有自己的专长。
from typing import Optional
from enum import Enum
class AgentRole(Enum):
"""代理角色定义
每个角色代表一种专业能力。
这种设计模式类似于微服务架构,每个代理负责特定领域。
"""
MASTER = "master" # 主控代理 - 协调者
RETRIEVER = "retriever" # 检索专家 - 信息获取
ANALYZER = "analyzer" # 分析专家 - 数据分析
VALIDATOR = "validator" # 验证专家 - 质量控制
SYNTHESIZER = "synthesizer"# 综合专家 - 信息整合
class SpecializedAgent:
"""专门化代理基类
这个基类定义了所有专业代理的共同接口。
每个代理都有:
1. 特定的角色和职责
2. 专门的工具集
3. 领域知识
"""
def __init__(self, role: AgentRole, llm):
self.role = role
self.llm = llm
self.tools = self._initialize_tools()
self.expertise = self._load_expertise()
def _initialize_tools(self):
"""初始化角色特定工具
不同角色需要不同的工具:
- 检索专家需要各种搜索工具
- 分析专家需要统计和数据处理工具
- 验证专家需要事实核查工具
"""
if self.role == AgentRole.RETRIEVER:
return [
SearchTool(), # 网络搜索
DatabaseTool(), # 数据库查询
APITool(), # API 调用
DocumentLoader() # 文档加载
]
elif self.role == AgentRole.ANALYZER:
return [
StatisticalTool(), # 统计分析
NERTool(), # 命名实体识别
SentimentTool(), # 情感分析
TrendAnalyzer() # 趋势分析
]
elif self.role == AgentRole.VALIDATOR:
return [
FactCheckTool(), # 事实核查
SourceVerificationTool(), # 来源验证
ConsistencyChecker(), # 一致性检查
BiasDetector() # 偏见检测
]
elif self.role == AgentRole.SYNTHESIZER:
return [
Summarizer(), # 摘要生成
ReportGenerator(), # 报告生成
VisualizationTool() # 数据可视化
]
return []
asyncdef process(self, task: Dict, context: Dict):
"""处理任务的抽象方法
每个专业代理需要实现自己的处理逻辑
"""
raise NotImplementedError
class MasterAgent(SpecializedAgent):
"""主控代理:协调其他代理
主控代理是整个系统的指挥官,负责:
1. 分析用户需求
2. 制定执行策略
3. 分配任务给合适的代理
4. 协调代理之间的协作
5. 整合最终结果
这种设计模式类似于管理者模式(Manager Pattern)。
"""
def __init__(self, llm, sub_agents: Dict[AgentRole, SpecializedAgent]):
super().__init__(AgentRole.MASTER, llm)
self.sub_agents = sub_agents
self.execution_history = []
asyncdef process(self, query: str):
"""协调处理流程
这个方法实现了复杂的多代理协作逻辑。
关键步骤:
1. 理解问题的复杂性和需求
2. 设计合适的执行策略
3. 智能地分配任务
4. 处理代理间的依赖关系
5. 确保结果的质量
"""
# 步骤1:分析查询,理解需求的复杂性
analysis = await self.analyze_query(query)
# 步骤2:制定执行策略
# 根据查询的特点决定:
# - 需要哪些代理参与
# - 任务的执行顺序
# - 是否可以并行处理
strategy = await self.create_strategy(analysis)
# 步骤3:分解任务
# 将大任务分解为各个代理可以处理的小任务
tasks = await self.decompose_tasks(strategy, query)
# 步骤4:执行任务
# 这里展示了并行和串行执行的混合策略
results = []
# 并行执行独立任务
parallel_tasks = [t for t in tasks ifnot t.get('depends_on')]
if parallel_tasks:
parallel_results = await asyncio.gather(*[
self.delegate_task(task) for task in parallel_tasks
])
results.extend(parallel_results)
# 串行执行依赖任务
sequential_tasks = [t for t in tasks if t.get('depends_on')]
for task in sequential_tasks:
# 等待依赖任务完成
await self.wait_for_dependencies(task, results)
result = await self.delegate_task(task)
results.append(result)
# 步骤5:整合结果
final_result = await self.integrate_results(results, query)
# 记录执行历史,用于学习和优化
self.execution_history.append({
'query': query,
'strategy': strategy,
'results': final_result,
'performance': self.measure_performance()
})
return final_result
asyncdef delegate_task(self, task: Dict):
"""委派任务给合适的代理
这个方法实现了智能的任务分配:
1. 选择最合适的代理
2. 准备任务上下文
3. 监控执行过程
4. 处理失败情况
"""
# 选择合适的代理
agent_role = self.select_agent(task)
agent = self.sub_agents[agent_role]
# 准备上下文
context = self.prepare_context(task)
try:
# 执行任务
result = await agent.process(task, context)
# 验证结果质量
if self.needs_validation(result):
validator = self.sub_agents[AgentRole.VALIDATOR]
validation_result = await validator.process(result, task)
# 如果验证失败,可能需要重试
ifnot validation_result['is_valid']:
returnawait self.handle_validation_failure(
task, result, validation_result
)
return result
except Exception as e:
# 错误处理和恢复
returnawait self.handle_agent_failure(task, agent_role, e)
def select_agent(self, task: Dict) -> AgentRole:
"""选择最合适的代理
基于任务特征选择代理:
- 信息获取任务 → 检索专家
- 数据分析任务 → 分析专家
- 质量检查任务 → 验证专家
- 报告生成任务 → 综合专家
"""
task_type = task.get('type', '')
if task_type in ['search', 'retrieve', 'fetch']:
return AgentRole.RETRIEVER
elif task_type in ['analyze', 'compute', 'extract']:
return AgentRole.ANALYZER
elif task_type in ['verify', 'validate', 'check']:
return AgentRole.VALIDATOR
elif task_type in ['summarize', 'synthesize', 'report']:
return AgentRole.SYNTHESIZER
else:
# 默认使用检索专家
return AgentRole.RETRIEVER
class RetrieverAgent(SpecializedAgent):
"""检索专家:负责信息获取
检索专家是信息获取的专家,掌握各种检索技术:
1. 多源检索:从不同来源获取信息
2. 查询优化:改写查询以获得更好的结果
3. 结果融合:合并和去重多源结果
4. 相关性排序:确保最相关的信息排在前面
"""
asyncdef process(self, task: Dict, context: Dict):
"""智能检索流程
这个方法实现了高级的检索策略,
不只是简单的关键词匹配,而是真正理解信息需求。
"""
query = task["query"]
# 步骤1:查询理解和改写
# 一个查询可能需要多种表述才能找到所有相关信息
rewritten_queries = await self.rewrite_query(query, context)
# 步骤2:多源并行检索
all_results = []
# 为每个改写的查询执行多种检索
for rq in rewritten_queries:
# 并行执行不同类型的检索
search_tasks = [
self.vector_search(rq), # 语义检索
self.keyword_search(rq), # 关键词检索
self.graph_search(rq), # 知识图谱检索
self.structured_search(rq) # 结构化数据检索
]
# 等待所有检索完成
results = await asyncio.gather(*search_tasks)
# 合并结果
for result_set in results:
all_results.extend(result_set)
# 步骤3:结果处理
# 去重
unique_results = self.deduplicate(all_results)
# 相关性评分
scored_results = await self.score_relevance(unique_results, query)
# 重排序
reranked_results = await self.rerank(scored_results, query, context)
# 步骤4:结果增强
# 添加元数据、摘要等
enhanced_results = await self.enhance_results(reranked_results)
return {
"query": query,
"results": enhanced_results[:10], # 返回前10个结果
"total_found": len(unique_results),
"search_strategies": ["vector", "keyword", "graph", "structured"]
}
asyncdef rewrite_query(self, query: str, context: Dict) -> List[str]:
"""查询改写
生成多个查询变体以提高召回率:
1. 同义词替换
2. 概念扩展
3. 上下文补充
4. 领域特定术语
"""
rewrites = [query] # 保留原始查询
# 使用 LLM 生成查询变体
prompt = f"""
原始查询:{query}
上下文:{context.get('history', '')}
请生成3-5个查询变体,包括:
1. 使用同义词的版本
2. 更具体的版本
3. 更宽泛的版本
4. 使用专业术语的版本
每行一个查询。
"""
response = await self.llm.ainvoke(prompt)
variants = response.content.strip().split('\n')
rewrites.extend(variants)
return rewrites[:5] # 限制最多5个查询
class ValidatorAgent(SpecializedAgent):
"""验证专家:确保信息质量
验证专家是质量把关者,负责:
1. 事实核查
2. 来源验证
3. 一致性检查
4. 偏见检测
这对于生成可信赖的答案至关重要。
"""
asyncdef process(self, result: Dict, task: Dict):
"""验证流程
多维度验证确保信息的可靠性。
每个维度都有具体的检查标准和方法。
"""
validations = []
# 维度1:事实准确性
# 检查信息是否与已知事实一致
fact_check = await self.fact_check(result["content"])
validations.append({
"type": "fact_check",
"score": fact_check["accuracy"],
"issues": fact_check.get("issues", [])
})
# 维度2:来源可靠性
# 评估信息来源的权威性和可信度
source_check = await self.verify_sources(result.get("sources", []))
validations.append({
"type": "source_verification",
"score": source_check["reliability"],
"trusted_sources": source_check["trusted"],
"questionable_sources": source_check["questionable"]
})
# 维度3:内部一致性
# 检查信息是否自相矛盾
consistency_check = await self.check_consistency(
result["content"],
task.get("context", {})
)
validations.append({
"type": "consistency",
"score": consistency_check["consistency_score"],
"contradictions": consistency_check.get("contradictions", [])
})
# 维度4:偏见检测
# 识别可能的偏见和片面性
bias_check = await self.detect_bias(result["content"])
validations.append({
"type": "bias_detection",
"score": 1 - bias_check["bias_level"], # 转换为质量分数
"detected_biases": bias_check.get("biases", [])
})
# 综合评分
# 使用加权平均计算总体可信度
weights = {
"fact_check": 0.4,
"source_verification": 0.3,
"consistency": 0.2,
"bias_detection": 0.1
}
confidence_score = sum(
v["score"] * weights[v["type"]]
for v in validations
)
# 生成验证报告
return {
**result, # 保留原始结果
"validations": validations,
"confidence": confidence_score,
"is_valid": confidence_score > 0.7, # 阈值判断
"recommendations": self.generate_recommendations(validations)
}
def generate_recommendations(self, validations):
"""基于验证结果生成改进建议
帮助系统理解如何改进信息质量。
"""
recommendations = []
for validation in validations:
if validation["score"] < 0.6:
if validation["type"] == "fact_check":
recommendations.append("需要更多事实核查")
elif validation["type"] == "source_verification":
recommendations.append("寻找更可靠的信息来源")
elif validation["type"] == "consistency":
recommendations.append("解决信息矛盾")
elif validation["type"] == "bias_detection":
recommendations.append("平衡不同观点")
return recommendations
创建和使用多代理系统
现在让我们看看如何将这些代理组合成一个完整的系统:
async def create_multi_agent_system():
"""创建多代理 RAG 系统
这个函数组装整个多代理系统。
关键设计考虑:
1. 代理的初始化顺序
2. 代理之间的依赖关系
3. 通信机制
4. 错误处理
"""
# 初始化共享的 LLM
# 所有代理使用相同的 LLM,但可以有不同的配置
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 创建专门化代理
# 每个代理都有自己的专业领域和工具集
retriever = RetrieverAgent(AgentRole.RETRIEVER, llm)
analyzer = AnalyzerAgent(AgentRole.ANALYZER, llm)
validator = ValidatorAgent(AgentRole.VALIDATOR, llm)
synthesizer = SynthesizerAgent(AgentRole.SYNTHESIZER, llm)
# 配置代理的特定参数
retriever.max_results = 20
analyzer.confidence_threshold = 0.8
validator.strict_mode = True
synthesizer.output_format = "detailed"
# 创建主控代理
# 主控代理需要知道所有子代理
master = MasterAgent(llm, {
AgentRole.RETRIEVER: retriever,
AgentRole.ANALYZER: analyzer,
AgentRole.VALIDATOR: validator,
AgentRole.SYNTHESIZER: synthesizer
})
# 设置代理间通信
# 可以添加消息队列、事件总线等机制
setup_inter_agent_communication(master, [retriever, analyzer, validator, synthesizer])
return master
# 使用多代理系统
asyncdef run_multi_agent_query():
"""运行多代理查询示例
这个例子展示了多代理系统如何处理复杂查询。
"""
# 创建系统
system = await create_multi_agent_system()
# 复杂查询示例
# 这种查询需要多个代理协作:
# - 检索:获取行业数据和新闻
# - 分析:趋势分析和模式识别
# - 验证:事实核查和可靠性评估
# - 综合:生成报告和预测
query = """
分析过去三个月科技行业的并购趋势,
预测未来六个月可能的大型并购案,
并评估对相关公司股价的潜在影响。
要求:
1. 基于真实数据
2. 考虑多个信息源
3. 提供置信度评估
4. 给出具体的投资建议
"""
print("开始多代理协作处理...")
print(f"查询:{query}\n")
# 执行查询
start_time = time.time()
result = await system.process(query)
end_time = time.time()
# 输出结果
print("=" * 60)
print("分析结果:")
print("=" * 60)
print(f"\n{result['analysis']}")
print("\n" + "=" * 60)
print("预测:")
print("=" * 60)
for i, prediction in enumerate(result['predictions'], 1):
print(f"\n{i}. {prediction['company']}")
print(f" 可能性:{prediction['probability']:.1%}")
print(f" 预期影响:{prediction['impact']}")
print("\n" + "=" * 60)
print("投资建议:")
print("=" * 60)
print(result['recommendations'])
print("\n" + "=" * 60)
print(f"置信度评估:{result['confidence']:.1%}")
print(f"处理时间:{end_time - start_time:.2f} 秒")
print(f"参与代理:{result['agents_involved']}")
print(f"信息源数量:{result['sources_count']}")
# 运行示例
if __name__ == "__main__":
asyncio.run(run_multi_agent_query())
高级技术:提升 Agentic RAG 性能
1. 自适应查询改写
查询改写是提高检索质量的关键技术。好的查询改写可以显著提高召回率和准确率。
class AdaptiveQueryRewriter:
"""自适应查询改写器
这个类实现了多种查询改写策略,并能根据效果动态调整。
核心思想:
1. 不同类型的查询需要不同的改写策略
2. 从历史成功案例中学习
3. 根据反馈持续优化
"""
def __init__(self, llm):
self.llm = llm
self.rewrite_history = [] # 记录改写历史
self.strategy_performance = {} # 策略性能统计
asyncdef rewrite(self, query: str, context: Dict) -> List[str]:
"""多策略查询改写
这个方法综合使用多种改写策略,
每种策略针对不同的问题:
- 查询太复杂 → 分解
- 缺少上下文 → 补充
- 术语不准确 → 同义词扩展
- 没有历史 → 学习优化
"""
rewrites = []
# 策略1:分解复杂查询
# 复杂查询往往包含多个子问题
if self.is_complex(query):
sub_queries = await self.decompose_query(query)
rewrites.extend(sub_queries)
# 记录策略使用
self.record_strategy_use("decomposition", query, sub_queries)
# 策略2:上下文增强
# 添加对话历史中的相关信息
contextualized = await self.add_context(query, context)
rewrites.append(contextualized)
self.record_strategy_use("contextualization", query, [contextualized])
# 策略3:同义词和概念扩展
# 使用不同的表达方式可能找到不同的信息
expanded = await self.expand_synonyms(query)
rewrites.append(expanded)
self.record_strategy_use("expansion", query, [expanded])
# 策略4:基于历史学习的优化
# 从成功的历史改写中学习模式
if self.rewrite_history:
optimized = await self.learn_from_history(query)
rewrites.append(optimized)
self.record_strategy_use("learning", query, [optimized])
# 去重并返回
return self.deduplicate(rewrites)
def is_complex(self, query: str) -> bool:
"""判断查询是否复杂
复杂查询的特征:
- 包含多个子句
- 有多个并列的问题
- 包含条件判断
- 字数较多
"""
# 简单的启发式判断
indicators = [
len(query.split()) > 20, # 词数多
'并且'in query or'and'in query.lower(), # 并列关系
'如果'in query or'if'in query.lower(), # 条件关系
query.count(',') > 2or query.count(',') > 2, # 多个子句
'比较'in query or'compare'in query.lower() # 比较关系
]
return sum(indicators) >= 2
asyncdef decompose_query(self, query: str) -> List[str]:
"""分解复杂查询
将一个复杂查询分解为多个简单查询。
每个子查询应该:
1. 聚焦单一方面
2. 保持完整的语义
3. 可以独立检索
"""
prompt = f"""
请将以下复杂问题分解为2-4个简单的子问题:
{query}
要求:
1. 每个子问题聚焦一个方面
2. 子问题应该是完整的句子
3. 所有子问题合起来能回答原问题
4. 避免重复
直接列出子问题,每行一个。
"""
response = await self.llm.ainvoke(prompt)
sub_queries = response.content.strip().split('\n')
# 过滤和清理
return [q.strip() for q in sub_queries if len(q.strip()) > 10]
asyncdef add_context(self, query: str, context: Dict) -> str:
"""添加上下文信息
很多查询缺少必要的上下文,
比如"他"指谁,"这个"指什么。
这个方法补充这些信息。
"""
ifnot context ornot context.get('history'):
return query
prompt = f"""
原始查询:{query}
对话历史:{context['history'][-3:]} # 最近3轮对话
请补充必要的上下文信息,使查询更明确。
例如:
- 将代词替换为具体名称
- 补充时间、地点等信息
- 明确模糊的指代
直接返回改写后的查询。
"""
response = await self.llm.ainvoke(prompt)
return response.content.strip()
asyncdef learn_from_history(self, query: str) -> str:
"""从历史中学习最佳改写模式
这个方法分析历史上成功的改写案例,
找出模式并应用到当前查询。
这是一种简单的在线学习机制。
"""
# 找出最成功的改写案例
successful_rewrites = [
h for h in self.rewrite_history
if h.get("success_score", 0) > 0.8
]
ifnot successful_rewrites:
return query
# 找出最相似的历史查询
similar_cases = self.find_similar_queries(query, successful_rewrites)
ifnot similar_cases:
return query
prompt = f"""
当前查询:{query}
类似的成功改写案例:
{self.format_cases(similar_cases[:3])}
基于这些成功案例的模式,改写当前查询。
直接返回改写结果。
"""
response = await self.llm.ainvoke(prompt)
return response.content.strip()
def record_strategy_use(self, strategy: str, original: str, rewrites: List[str]):
"""记录策略使用情况
用于后续分析和优化。
"""
record = {
"strategy": strategy,
"original": original,
"rewrites": rewrites,
"timestamp": datetime.now()
}
self.rewrite_history.append(record)
def update_performance(self, strategy: str, success: bool):
"""更新策略性能统计
跟踪每种策略的成功率,
可以用于动态调整策略选择。
"""
if strategy notin self.strategy_performance:
self.strategy_performance[strategy] = {"success": 0, "total": 0}
self.strategy_performance[strategy]["total"] += 1
if success:
self.strategy_performance[strategy]["success"] += 1
2. 动态工具选择
不同的查询需要不同的工具。动态选择工具可以提高效率和准确性。
class DynamicToolSelector:
"""动态工具选择器
这个类实现了智能的工具选择机制:
1. 分析查询特征
2. 评估工具相关性
3. 考虑历史性能
4. 动态调整选择策略
目标是为每个查询选择最合适的工具组合。
"""
def __init__(self, tools: List[Tool], llm):
self.tools = tools
self.llm = llm
# 使用 defaultdict 自动初始化统计数据
self.tool_usage_stats = defaultdict(lambda: {"success": 0, "total": 0})
self.query_patterns = {} # 查询模式与工具的映射
asyncdef select_tools(self, query: str, context: Dict) -> List[Tool]:
"""智能选择工具
这个方法综合考虑多个因素来选择工具:
1. 查询的语义特征
2. 工具的功能描述
3. 历史使用效果
4. 当前系统负载
返回按优先级排序的工具列表。
"""
# 步骤1:分析查询特征
# 提取查询的关键特征,如意图、实体、时态等
query_features = await self.analyze_query_features(query)
# 步骤2:计算每个工具的相关性分数
tool_scores = []
for tool in self.tools:
# 基于语义相关性的分数(0-1)
relevance_score = await self.calculate_relevance(
tool,
query_features
)
# 基于历史性能的分数(0-1)
performance_score = self.get_performance_score(tool)
# 基于当前负载的分数(0-1)
# 避免过度使用某些工具
load_score = self.get_load_balance_score(tool)
# 综合评分
# 可以调整权重以改变选择策略
final_score = (
0.5 * relevance_score + # 相关性最重要
0.3 * performance_score + # 历史性能次之
0.2 * load_score # 负载均衡最后
)
tool_scores.append((tool, final_score))
# 步骤3:选择得分最高的工具
# 按分数排序
tool_scores.sort(key=lambda x: x[1], reverse=True)
# 选择策略:
# - 如果最高分很高(>0.8),只选择最好的1-2个
# - 如果分数都不高,选择前3个以提高覆盖率
# - 如果是复杂查询,可能需要更多工具
if tool_scores[0][1] > 0.8:
# 高置信度,选择最好的
selected = [tool for tool, score in tool_scores[:2] if score > 0.6]
elif self.is_complex_query(query_features):
# 复杂查询,需要更多工具
selected = [tool for tool, score in tool_scores[:4] if score > 0.4]
else:
# 一般情况,选择前3个
selected = [tool for tool, score in tool_scores[:3] if score > 0.5]
# 记录选择决策,用于学习
self.record_selection(query, query_features, selected)
return selected
asyncdef analyze_query_features(self, query: str) -> Dict:
"""分析查询特征
提取有助于工具选择的特征:
- 查询类型(事实、分析、创造等)
- 时间特征(历史、当前、预测)
- 数据类型(文本、数字、图表等)
- 复杂度
"""
prompt = f"""
分析以下查询的特征:
{query}
请识别:
1. 查询类型:[事实查询|分析查询|比较查询|预测查询|创造性查询]
2. 时间范围:[历史|当前|未来]
3. 需要的数据类型:[文本|数值|图表|代码|混合]
4. 复杂度:[简单|中等|复杂]
5. 关键实体:列出主要实体
返回 JSON 格式。
"""
response = await self.llm.ainvoke(prompt)
try:
features = json.loads(response.content)
except:
# 解析失败时的默认特征
features = {
"type": "fact",
"time": "current",
"data_type": "text",
"complexity": "simple",
"entities": []
}
return features
asyncdef calculate_relevance(self, tool: Tool, features: Dict) -> float:
"""计算工具相关性
基于工具描述和查询特征计算相关性。
这里可以使用更复杂的方法,如:
- 语义相似度
- 规则匹配
- 机器学习模型
"""
# 使用 LLM 评估相关性
prompt = f"""
工具信息:
名称:{tool.name}
描述:{tool.description}
查询特征:
{json.dumps(features, indent=2)}
评估这个工具对处理该查询的相关性(0-10分)。
只返回数字。
"""
response = await self.llm.ainvoke(prompt)
try:
score = float(response.content.strip())
return score / 10# 归一化到 0-1
except:
return0.5# 默认中等相关性
def get_performance_score(self, tool: Tool) -> float:
"""获取工具的历史性能分数
基于成功率计算性能分数。
新工具给予探索奖励。
"""
stats = self.tool_usage_stats[tool.name]
if stats["total"] == 0:
# 新工具,给予探索奖励
return0.7
# 计算成功率
success_rate = stats["success"] / stats["total"]
# 考虑使用次数,避免因少量失败就放弃工具
# 使用贝叶斯平均
confidence = min(stats["total"] / 10, 1.0)
# 加权平均,新工具有更多机会
return success_rate * confidence + 0.5 * (1 - confidence)
def get_load_balance_score(self, tool: Tool) -> float:
"""计算负载均衡分数
避免过度使用某些工具,
促进工具使用的多样性。
"""
total_uses = sum(s["total"] for s in self.tool_usage_stats.values())
if total_uses == 0:
return1.0
tool_uses = self.tool_usage_stats[tool.name]["total"]
# 使用率越高,分数越低
usage_rate = tool_uses / total_uses
# 使用反sigmoid函数,使分数变化更平滑
return1 / (1 + math.exp(10 * (usage_rate - 0.3)))
def update_stats(self, tool: Tool, success: bool):
"""更新工具使用统计
记录工具的使用结果,用于持续优化选择策略。
"""
self.tool_usage_stats[tool.name]["total"] += 1
if success:
self.tool_usage_stats[tool.name]["success"] += 1
# 定期打印统计信息(用于监控)
if sum(s["total"] for s in self.tool_usage_stats.values()) % 100 == 0:
self.print_statistics()
def print_statistics(self):
"""打印工具使用统计
用于监控和调试。
"""
print("\n工具使用统计:")
print("-" * 40)
for tool_name, stats in self.tool_usage_stats.items():
if stats["total"] > 0:
success_rate = stats["success"] / stats["total"]
print(f"{tool_name}:")
print(f" 使用次数:{stats['total']}")
print(f" 成功率:{success_rate:.2%}")
3. 结果验证与自我纠正
Agentic RAG 的一个关键优势是能够验证和纠正自己的输出。
class SelfCorrectingAgent:
"""自我纠正代理
这个类实现了自我验证和纠正机制。
核心理念:
1. 不盲目相信第一次的输出
2. 通过多维度验证发现问题
3. 基于问题进行针对性纠正
4. 迭代改进直到满意
"""
def __init__(self, llm):
self.llm = llm
self.correction_attempts = 0
self.max_corrections = 3
self.validation_history = []
asyncdef generate_with_verification(self, query: str, context: Dict):
"""生成并验证答案
这个方法实现了完整的生成-验证-纠正循环。
关键特点:
1. 多次尝试机会
2. 每次纠正都基于具体问题
3. 保留所有尝试的历史
4. 最终选择最佳答案
"""
attempts = [] # 记录所有尝试
while self.correction_attempts < self.max_corrections:
# 生成答案
# 如果是纠正尝试,会包含之前的错误信息
answer = await self.generate_answer(query, context)
# 验证答案
validation_result = await self.validate_answer(answer, query)
# 记录尝试
attempts.append({
"answer": answer,
"validation": validation_result,
"attempt": self.correction_attempts
})
# 判断是否满意
if validation_result.is_valid:
return answer
# 准备纠正
# 关键:基于验证结果准备针对性的纠正上下文
context = await self.prepare_correction_context(
answer,
validation_result,
context
)
self.correction_attempts += 1
print(f"第 {self.correction_attempts} 次纠正...")
# 达到最大尝试次数,选择最佳答案
return self.select_best_answer(attempts)
asyncdef validate_answer(self, answer: str, query: str):
"""全面验证答案质量
多维度验证确保答案质量:
1. 相关性:是否回答了问题
2. 完整性:是否覆盖所有方面
3. 一致性:是否自相矛盾
4. 准确性:事实是否正确
每个维度都有具体的检查方法。
"""
validations = []
# 维度1:相关性检查
relevance = await self.check_relevance(answer, query)
validations.append(("relevance", relevance))
# 维度2:完整性检查
completeness = await self.check_completeness(answer, query)
validations.append(("completeness", completeness))
# 维度3:一致性检查
consistency = await self.check_consistency(answer)
validations.append(("consistency", consistency))
# 维度4:事实准确性检查
accuracy = await self.check_factual_accuracy(answer)
validations.append(("accuracy", accuracy))
# 综合评估
# 所有维度都需要达到阈值
is_valid = all(score > 0.7for _, score in validations)
# 识别主要问题
problems = [
name for name, score in validations
if score < 0.7
]
# 生成改进建议
suggestions = self.generate_suggestions(validations)
return ValidationResult(
is_valid=is_valid,
validations=dict(validations),
problems=problems,
suggestions=suggestions
)
asyncdef check_relevance(self, answer: str, query: str) -> float:
"""检查答案相关性
评估答案是否真正回答了用户的问题。
"""
prompt = f"""
问题:{query}
答案:{answer}
评估答案对问题的相关性(0-10分):
- 10分:完全回答了问题
- 7-9分:基本回答了问题,有小的偏差
- 4-6分:部分回答了问题
- 1-3分:答案偏题
- 0分:完全不相关
只返回数字分数。
"""
response = await self.llm.ainvoke(prompt)
try:
return float(response.content.strip()) / 10
except:
return0.5
asyncdef check_completeness(self, answer: str, query: str) -> float:
"""检查答案完整性
评估答案是否覆盖了问题的所有方面。
"""
prompt = f"""
问题:{query}
答案:{answer}
分析问题包含的所有方面,评估答案的完整性(0-10分):
- 识别问题的所有子问题
- 检查每个子问题是否被回答
- 评估答案的深度是否足够
只返回数字分数。
"""
response = await self.llm.ainvoke(prompt)
try:
return float(response.content.strip()) / 10
except:
return0.5
asyncdef check_consistency(self, answer: str) -> float:
"""检查内部一致性
检测答案中是否有自相矛盾的地方。
"""
prompt = f"""
检查以下答案的内部一致性:
{answer}
寻找:
1. 逻辑矛盾
2. 事实冲突
3. 数字不一致
4. 时间线混乱
如果完全一致,返回10分;
每发现一个问题,减2分;
最低0分。
只返回数字分数。
"""
response = await self.llm.ainvoke(prompt)
try:
return float(response.content.strip()) / 10
except:
return0.8# 默认认为基本一致
asyncdef prepare_correction_context(
self,
answer: str,
validation: ValidationResult,
original_context: Dict
) -> Dict:
"""准备纠正上下文
这是自我纠正的关键:
基于验证结果,准备针对性的纠正指导。
"""
correction_context = original_context.copy()
# 添加之前的答案和问题
correction_context["previous_answer"] = answer
correction_context["validation_results"] = validation.validations
correction_context["problems"] = validation.problems
# 生成具体的改进指导
improvement_guide = []
if"relevance"in validation.problems:
improvement_guide.append("重新聚焦用户的核心问题")
if"completeness"in validation.problems:
improvement_guide.append("补充遗漏的方面")
# 可能需要额外检索
missing_info = await self.identify_missing_information(
answer,
original_context["query"]
)
if missing_info:
correction_context["missing_information"] = missing_info
if"consistency"in validation.problems:
improvement_guide.append("解决答案中的矛盾")
contradictions = await self.identify_contradictions(answer)
correction_context["contradictions"] = contradictions
if"accuracy"in validation.problems:
improvement_guide.append("纠正事实错误")
# 触发事实核查
fact_checks = await self.fact_check_details(answer)
correction_context["fact_corrections"] = fact_checks
correction_context["improvement_guide"] = improvement_guide
return correction_context
def select_best_answer(self, attempts: List[Dict]) -> str:
"""选择最佳答案
当所有尝试都不完美时,
选择相对最好的答案。
"""
ifnot attempts:
return"无法生成满意的答案"
# 计算每个答案的综合分数
scored_attempts = []
for attempt in attempts:
validation = attempt["validation"]
# 加权平均
score = (
validation.validations.get("relevance", 0) * 0.4 +
validation.validations.get("completeness", 0) * 0.3 +
validation.validations.get("consistency", 0) * 0.2 +
validation.validations.get("accuracy", 0) * 0.1
)
scored_attempts.append((score, attempt["answer"]))
# 返回得分最高的答案
scored_attempts.sort(reverse=True)
return scored_attempts[0][1]
评估与优化:让系统持续进化
评估框架
评估 Agentic RAG 系统比评估传统 RAG 更复杂,因为需要考虑推理质量、工具使用效率等多个维度。
class AgenticRAGEvaluator:
"""Agentic RAG 评估器
全面评估系统的各个方面:
1. 检索质量
2. 推理能力
3. 答案质量
4. 系统效率
5. 成本控制
评估结果用于系统优化和调参。
"""
def __init__(self):
self.metrics = {
"retrieval": RetrievalMetrics(),
"reasoning": ReasoningMetrics(),
"generation": GenerationMetrics(),
"efficiency": EfficiencyMetrics(),
"cost": CostMetrics()
}
self.evaluation_history = []
asyncdef evaluate(self, system, test_set):
"""全面评估系统
使用测试集评估系统的各个维度。
测试集应该包含:
- 查询
- 预期答案
- 相关文档
- 难度等级
"""
results = {
"retrieval_precision": [],
"reasoning_accuracy": [],
"answer_quality": [],
"latency": [],
"cost": [],
"tool_efficiency": []
}
for test_case in test_set:
print(f"评估测试用例:{test_case['id']}")
# 记录执行轨迹
# 这是评估的基础数据
trace = await self.trace_execution(system, test_case)
# 评估各个维度
# 1. 检索质量
retrieval_score = self.evaluate_retrieval(trace, test_case)
results["retrieval_precision"].append(retrieval_score)
# 2. 推理质量
reasoning_score = self.evaluate_reasoning(trace, test_case)
results["reasoning_accuracy"].append(reasoning_score)
# 3. 答案质量
answer_score = self.evaluate_answer(trace, test_case)
results["answer_quality"].append(answer_score)
# 4. 系统效率
results["latency"].append(trace.total_time)
results["tool_efficiency"].append(self.evaluate_tool_usage(trace))
# 5. 成本
cost = self.calculate_cost(trace)
results["cost"].append(cost)
# 聚合结果
aggregated = self.aggregate_results(results)
# 生成评估报告
report = self.generate_report(aggregated, test_set)
# 保存评估历史
self.evaluation_history.append({
"timestamp": datetime.now(),
"results": aggregated,
"report": report
})
return report
def evaluate_retrieval(self, trace, test_case):
"""评估检索质量
衡量标准:
- 准确率:检索到的相关文档比例
- 召回率:相关文档被检索到的比例
- MRR:平均倒数排名
- NDCG:归一化折损累积增益
"""
retrieved_docs = trace.retrieved_docs
ground_truth = test_case.get("relevant_docs", [])
ifnot ground_truth:
# 没有标注数据,使用启发式评估
return self.heuristic_retrieval_evaluation(retrieved_docs, test_case)
# 计算标准指标
retrieved_ids = [doc["id"] for doc in retrieved_docs if"id"in doc]
truth_ids = [doc["id"] for doc in ground_truth]
# 准确率
if retrieved_ids:
precision = len(set(retrieved_ids) & set(truth_ids)) / len(retrieved_ids)
else:
precision = 0
# 召回率
if truth_ids:
recall = len(set(retrieved_ids) & set(truth_ids)) / len(truth_ids)
else:
recall = 1# 没有相关文档,认为召回完整
# F1分数
if precision + recall > 0:
f1 = 2 * precision * recall / (precision + recall)
else:
f1 = 0
# MRR(平均倒数排名)
mrr = self.calculate_mrr(retrieved_ids, truth_ids)
return {
"precision": precision,
"recall": recall,
"f1": f1,
"mrr": mrr
}
def evaluate_reasoning(self, trace, test_case):
"""评估推理质量
这是 Agentic RAG 特有的评估维度。
衡量标准:
- 推理步骤的逻辑性
- 计划的合理性
- 工具使用的恰当性
- 自我纠正的有效性
"""
reasoning_steps = trace.reasoning_steps
scores = {}
# 1. 逻辑一致性
# 检查推理步骤之间是否有逻辑关系
scores["logical_consistency"] = self.check_logical_consistency(reasoning_steps)
# 2. 计划质量
# 评估任务分解是否合理
if trace.plan:
scores["plan_quality"] = self.evaluate_plan_quality(
trace.plan,
test_case["query"]
)
# 3. 工具使用恰当性
# 评估是否选择了正确的工具
scores["tool_appropriateness"] = self.evaluate_tool_selection(
trace.tool_calls,
test_case["query"]
)
# 4. 自我纠正效果
# 如果有纠正,评估纠正的效果
if trace.corrections:
scores["correction_effectiveness"] = self.evaluate_corrections(
trace.corrections
)
# 5. 推理深度
# 评估推理的深度和完整性
expected_steps = test_case.get("expected_reasoning_steps", 3)
actual_steps = len(reasoning_steps)
scores["reasoning_depth"] = min(actual_steps / expected_steps, 1.0)
# 综合评分
weights = {
"logical_consistency": 0.3,
"plan_quality": 0.2,
"tool_appropriateness": 0.2,
"correction_effectiveness": 0.1,
"reasoning_depth": 0.2
}
overall_score = sum(
scores.get(key, 0.5) * weight
for key, weight in weights.items()
)
return {
"overall": overall_score,
"details": scores
}
def evaluate_answer(self, trace, test_case):
"""评估答案质量
多维度评估答案:
- 正确性
- 完整性
- 清晰度
- 有用性
"""
generated_answer = trace.final_answer
scores = {}
# 如果有参考答案,计算相似度
if"expected_answer"in test_case:
scores["correctness"] = self.calculate_answer_similarity(
generated_answer,
test_case["expected_answer"]
)
# 完整性:是否回答了所有子问题
scores["completeness"] = self.check_answer_completeness(
generated_answer,
test_case["query"]
)
# 清晰度:答案是否组织良好
scores["clarity"] = self.evaluate_answer_clarity(generated_answer)
# 有用性:答案是否提供了实用信息
scores["usefulness"] = self.evaluate_answer_usefulness(
generated_answer,
test_case["query"]
)
# 综合评分
overall = sum(scores.values()) / len(scores)
return {
"overall": overall,
"details": scores
}
def evaluate_tool_usage(self, trace):
"""评估工具使用效率
衡量工具使用的效率和效果。
"""
tool_calls = trace.tool_calls
ifnot tool_calls:
return1.0# 没有使用工具,认为效率最高
# 计算各种效率指标
# 1. 成功率
successful_calls = [tc for tc in tool_calls if tc.get("success")]
success_rate = len(successful_calls) / len(tool_calls)
# 2. 重复率(越低越好)
unique_calls = len(set(
(tc["tool"], tc.get("query", ""))
for tc in tool_calls
))
duplication_rate = 1 - (unique_calls / len(tool_calls))
# 3. 相关性(工具选择是否恰当)
relevance_scores = [
tc.get("relevance_score", 0.5)
for tc in tool_calls
]
average_relevance = sum(relevance_scores) / len(relevance_scores)
# 综合效率分数
efficiency_score = (
0.4 * success_rate +
0.3 * (1 - duplication_rate) +
0.3 * average_relevance
)
return efficiency_score
def generate_report(self, results, test_set):
"""生成评估报告
生成详细的评估报告,包括:
- 总体性能
- 各维度分析
- 问题诊断
- 优化建议
"""
report = []
report.append("=" * 60)
report.append("Agentic RAG 系统评估报告")
report.append("=" * 60)
# 总体性能
report.append("\n## 总体性能")
report.append(f"测试用例数:{len(test_set)}")
report.append(f"平均检索精度:{results['retrieval_precision']:.2%}")
report.append(f"平均推理准确率:{results['reasoning_accuracy']:.2%}")
report.append(f"平均答案质量:{results['answer_quality']:.2%}")
report.append(f"平均响应时间:{results['avg_latency']:.2f} 秒")
report.append(f"平均成本:${results['avg_cost']:.4f}")
# 性能分布
report.append("\n## 性能分布")
report.append(f"最快响应:{results['min_latency']:.2f} 秒")
report.append(f"最慢响应:{results['max_latency']:.2f} 秒")
report.append(f"P95 延迟:{results['p95_latency']:.2f} 秒")
# 问题诊断
report.append("\n## 问题诊断")
if results['reasoning_accuracy'] < 0.7:
report.append("⚠️ 推理准确率偏低,建议:")
report.append(" - 优化任务分解策略")
report.append(" - 加强推理链的逻辑验证")
if results['avg_latency'] > 10:
report.append("⚠️ 响应时间过长,建议:")
report.append(" - 优化工具调用策略")
report.append(" - 实现并行处理")
report.append(" - 添加缓存机制")
if results['avg_cost'] > 0.1:
report.append("⚠️ 成本偏高,建议:")
report.append(" - 减少不必要的 LLM 调用")
report.append(" - 使用更经济的模型")
report.append(" - 优化 prompt 长度")
# 优化建议
report.append("\n## 优化建议")
recommendations = self.generate_recommendations(results)
for rec in recommendations:
report.append(f"• {rec}")
return"\n".join(report)
总结
Agentic RAG 不只是 RAG 的升级版——它代表了一种全新的思维方式。这种转变的意义是深远的。在传统 RAG 中,我们在构建"更好的搜索引擎";而在 Agentic RAG 中,我们在构建"会思考的合作伙伴"。
当然,这项技术还在快速演进中。成本、延迟、可靠性等问题仍需解决。但方向是明确的——未来的 AI 系统将更加智能、自主、可靠。
记住:不是每个钉子都需要锤子,但当你真的需要锤子时,请选择最好的那把。
最近建了
langchain&langgraph
的交流群,想加这个群的朋友可以点赞关注之后加我备注 langgraph 即可,不以学习为目的的就不要进了
本文转载自AI 博物院 作者:longyunfeigu
