直接回答
Agentic RAG 是一种由 AI 智能体 决定是否检索、检索哪里以及迭代几次的检索增强架构,而不是执行一次固定搜索。生产环境的核心单元不是不受约束的“思考循环”,而是有边界的证据控制循环:检索工具必须经过授权并返回类型化结果,证据保留来源,系统限制迭代、Token 和时限,并用明确终态决定基于充分证据生成、向用户澄清或拒答。只有当它相对强固定 RAG 基线提升验收答案或 Goodput 时,智能体化才创造了价值。
核心要点
- 检索下沉为策略控制的工具,但 LLM 选中了某份材料,不代表材料可信。
- Self-RAG、CRAG 与 Adaptive-RAG 是不同研究方法,不能把任何“带评分器的循环”都冠以这些名称。
- 循环必须显式限制步骤、工具、Token、时限、证据时效与副作用。
- LLM 评分只是噪声信号,不能授予权限、证明事实有依据,也不能清除提示词注入。
- 上线前应分别评估路由、检索、证据、答案、拒答、安全和运行效率。
什么让 RAG 具备智能体特征?
当检索决策从固定应用流程下沉到运行时控制策略,RAG 才具备智能体特征。标准 RAG 本来就可以包含混合检索、重排、元数据过滤与引用;Agentic RAG 额外引入以下条件决策:
- 跳过检索、检索一个数据源,或查询多个已授权数据源;
- 将多部分问题分解为证据任务;
- 在检索失败时改写查询,但不改变用户目标;
- 检查已取回证据的缺口与冲突;
- 结束、澄清、拒答,或执行下一次有边界的检索。
这一定义不要求展示思维链、不要求多智能体,也不绑定某个框架,但必须具备状态和条件控制。Agentic RAG 综述 给出的分类维度比常见营销文章更广,包括智能体数量、控制结构、自主程度与知识表示。因此,“路由、多步、纠错、适应性”只是常用示例,不是业界公认且穷尽所有系统的四种标准模式。
该循环属于 Agentic Workflow,但检索仍是有独立契约的只读操作。修改订单、发起退款等业务动作应使用不同工具,并配置独立授权、确认、幂等与审计策略。
不同研究方法对应不同契约
高频出现的适应性检索方法解决的是不同问题。把它们当成同义词,会产生错误实现和不成立的效果声明。
| 方法 | 核心机制 | 不能据此证明什么 |
|---|---|---|
| Self-RAG | 训练同一个模型按需检索,并生成关于相关性、支持度与效用的反思 Token | 普通提示词评分器不自动等于 Self-RAG |
| CRAG | 通过检索评估器与置信度触发不同纠错动作;论文还探索了 Web 搜索与文档精炼 | 一个二分类“相关性”提示词不能复现论文,也不保证纠错有效 |
| Adaptive-RAG | 使用训练得到的复杂度分类器,在无检索、单步检索和迭代检索之间选择 | 手写“简单/复杂”提示词不能继承论文实验结果 |
| Agentic RAG | 智能体在控制策略下选择并迭代检索工具的系统模式 | 该标签本身不保证质量、延迟、安全或成本 |
Self-RAG 的关键是训练模型生成特殊反思 Token。调用多个普通模型 API 来批评答案,可以称为提示词驱动的自反思编排,但不等于论文中的训练方法。
Corrective RAG 评估检索质量,并根据评估结果选择不同纠错动作。评估器仍会出错;如果回退到外部 Web 搜索,系统还改变了信任、授权、时效和引用边界。
Adaptive-RAG 根据任务结果与数据集信号学习问题复杂度分类器。无检索、单步和迭代三条路径属于该论文实验设计,并非对所有业务问题都成立的通用分类。
论文结论只对其模型、数据集、检索器和评测协议负责。工程团队可以学习机制,但必须重新建立本地证据。
把检索工具设计为安全边界
检索工具应是应用拥有的接口,而不是裸数据库连接或任意 URL 抓取器。输入输出 Schema 要显式表达授权与证据契约。
{
"name": "search_support_runbooks",
"purpose": "检索当前租户可访问的已审核运维手册",
"input": {
"query": "string",
"tenant_id": "服务端注入",
"product": "白名单枚举",
"top_k": "1 到 8 的整数"
},
"output": {
"evidence": [
{
"source_id": "不可变文档 ID",
"revision": "不可变版本",
"title": "展示标题",
"excerpt": "长度受限的文本",
"retrieved_at": "时间戳",
"acl_scope": "授权范围"
}
]
},
"limits": {
"timeout_ms": 1500,
"max_result_bytes": 40000
}
}
微软当前的 Agentic RAG 架构指南 同样强调明确工具描述、类型化参数、返回 Schema、元数据与过滤器。这些约束能改善工具选择,但服务端仍须在模型之外推导身份与租户范围。
授权必须发生在检索之前
智能体不得自行填写 tenant_id、访问过滤器、数据库名或凭据。应用从认证上下文推导范围,在搜索前将请求与策略求交集。文档级授权应先于排序执行,避免未授权候选影响分数、日志、缓存或模型上下文。
检索内容是不可信数据
文档、网页、工单与工具输出都可能包含提示词注入。系统应隔离证据、保留来源身份,并明确检索文本无权改变系统策略或授权新工具。删除“忽略此前指令”等关键词不是安全边界;真正的边界是代码层对白名单工具、参数、目的地、数据量与副作用的强制约束。
每次变换都必须保留来源
查询改写、切块、重排、摘要与去重都可能切断来源链。每条证据需要不可变来源与版本,并维护生成声明到支持片段的映射。仅保存 URL 不够,因为其内容可能变化。
建立有边界的证据控制循环
生产循环必须具备明确状态和状态转移。“一直继续直到模型有信心”不是停止策略,因为模型置信度既未必校准,也无法强制执行。
状态契约
至少记录:
- 请求、用户可见目标、身份范围与策略版本;
- 规划版本与证据任务;
- 每个工具名、归一化参数、结果 ID、耗时与故障;
- 累计步骤、检索、模型调用、Token、墙钟时间与成本;
- 证据覆盖、冲突、时效与授权状态;
- 停止原因,以及最终每条声明实际使用的证据。
默认不要记录完整敏感提示词或文档。应保存脱敏引用,并让原始内容访问具备用途限制、时间限制与审计。
预算契约
执行前设置硬限制:
| 预算 | 避免的问题 |
|---|---|
| 总步骤数与单工具调用数 | 无限改写或搜索循环 |
| 墙钟截止时间 | 用户或网关超时后请求仍在运行 |
| 输入与输出 Token | 上下文和账单失控 |
| 结果字节数与证据条数 | 上下文灌入和内存压力 |
| 单数据源重试次数 | 持续冲击故障依赖 |
| 外部域名白名单 | 任意联网与数据外泄 |
NVIDIA 当前的 Agentic RAG Blueprint 默认关闭智能体路径,并建议按请求启用,因为额外规划与验证会增加模型调用和延迟。它的具体实现属于厂商版本,但通用边界成立:只有复杂请求需要额外控制时,才应支付这部分成本。
停止契约
循环应收敛到少量机器可读终态:
answered
clarification_required
insufficient_evidence
conflicting_evidence
authorization_denied
budget_exhausted
dependency_failed
policy_blocked
“用已有上下文强行生成”是不安全的。证据不足或互相冲突时,应返回缺口与安全的下一步。系统可以输出部分答案,但必须把有依据的声明与未知项明确分开。
可运行的轨迹门禁
以下无第三方依赖的 Python 程序在结果晋级前检查执行轨迹。它不判断事实真假,而是强制执行 LLM 无权覆盖的确定性不变量。
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
TERMINAL_REASONS = {
"answered",
"clarification_required",
"insufficient_evidence",
"conflicting_evidence",
"authorization_denied",
"budget_exhausted",
"dependency_failed",
"policy_blocked",
}
def is_number(value: Any) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool)
def validate(trace: dict[str, Any]) -> list[str]:
errors: list[str] = []
policy = trace.get("policy")
steps = trace.get("steps")
result = trace.get("result")
if not isinstance(policy, dict):
return ["policy must be an object"]
if not isinstance(steps, list):
return ["steps must be a list"]
if not isinstance(result, dict):
return ["result must be an object"]
max_steps = policy.get("max_steps")
max_tokens = policy.get("max_tokens")
allowed_tools = policy.get("allowed_tools")
allowed_sources = policy.get("allowed_source_scopes")
if not isinstance(max_steps, int) or isinstance(max_steps, bool) or max_steps < 1:
errors.append("policy.max_steps must be a positive integer")
if not isinstance(max_tokens, int) or isinstance(max_tokens, bool) or max_tokens < 1:
errors.append("policy.max_tokens must be a positive integer")
if not isinstance(allowed_tools, list) or not all(
isinstance(item, str) and item for item in allowed_tools
):
errors.append("policy.allowed_tools must contain strings")
allowed_tools = []
if not isinstance(allowed_sources, list) or not all(
isinstance(item, str) and item for item in allowed_sources
):
errors.append("policy.allowed_source_scopes must contain strings")
allowed_sources = []
if isinstance(max_steps, int) and len(steps) > max_steps:
errors.append("step budget exceeded")
total_tokens = 0
evidence_ids: set[str] = set()
for index, step in enumerate(steps):
if not isinstance(step, dict):
errors.append(f"steps[{index}] must be an object")
continue
tool = step.get("tool")
if tool not in allowed_tools:
errors.append(f"steps[{index}] uses unauthorized tool {tool!r}")
tokens = step.get("tokens", 0)
if not is_number(tokens) or tokens < 0:
errors.append(f"steps[{index}].tokens must be non-negative")
else:
total_tokens += tokens
for item in step.get("evidence", []):
if not isinstance(item, dict):
errors.append(f"steps[{index}] has malformed evidence")
continue
evidence_id = item.get("id")
scope = item.get("scope")
if not isinstance(evidence_id, str) or not evidence_id:
errors.append(f"steps[{index}] evidence needs an id")
else:
evidence_ids.add(evidence_id)
if scope not in allowed_sources:
errors.append(
f"steps[{index}] evidence uses unauthorized scope {scope!r}"
)
if isinstance(max_tokens, int) and total_tokens > max_tokens:
errors.append("token budget exceeded")
reason = result.get("stop_reason")
if reason not in TERMINAL_REASONS:
errors.append("result.stop_reason is invalid")
citations = result.get("citation_ids", [])
if not isinstance(citations, list) or not all(
isinstance(item, str) and item for item in citations
):
errors.append("result.citation_ids must contain strings")
citations = []
missing = set(citations) - evidence_ids
if missing:
errors.append(f"result cites unknown evidence: {sorted(missing)}")
if reason == "answered" and not citations:
errors.append("answered result must cite evidence")
return errors
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("trace", type=Path)
args = parser.parse_args()
try:
value = json.loads(args.trace.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
parser.error(str(error))
if not isinstance(value, dict):
parser.error("trace root must be an object")
errors = validate(value)
if errors:
for error in errors:
print(f"ERROR: {error}")
raise SystemExit(1)
print(f"trace passed: {args.trace}")
if __name__ == "__main__":
main()
运行方式:
python agentic_rag_trace_gate.py trace.json
轨迹若使用未授权工具、跨越数据源范围、超过步骤或 Token 预算、引用未知证据,或返回非法终态,就会被拒绝。内容质量仍需在独立评测阶段完成。
分层评测 Agentic RAG
Agentic RAG 必须定位结果究竟在哪一层改善或失败。单一 LLM-as-judge 总分会遮蔽路由、检索、证据、生成与运行故障。
| 层级 | 示例指标 |
|---|---|
| 路由与规划 | 工具选择准确率、分解覆盖、非必要检索率 |
| 检索 | Recall@k、nDCG/MRR、过滤正确性、授权结果率 |
| 证据 | 声明覆盖、冲突识别、时效、引用蕴含 |
| 答案 | 任务验收、正确性、完整性、校准后的拒答 |
| 安全 | 跨租户泄漏、提示词注入抵抗、禁用工具尝试 |
| 运行 | TTFT、端到端延迟、工具/模型调用、Token、故障恢复 |
| 效率 | 在 SLO 内单位时间或成本的验收答案数 |
LLM-as-judge 可以辅助评估开放式输出,但必须用人工标签校准,测试位置与表达风格偏差,并保留确定性检查。评分模型绝不能授权数据访问或业务动作。
建立查询切片
版本化评测集至少覆盖:
- 单数据源事实检索;
- 多数据源比较;
- 具有已知证据链的多跳问题;
- 应向用户澄清的歧义请求;
- 应拒答的不可回答请求;
- 过期、冲突或已撤销文档;
- 租户边界问题与未授权干扰项;
- 检索内容中的提示词注入;
- 依赖超时与部分结果。
至少比较三种变体:强固定 RAG、只有路由但无迭代的 RAG,以及完整有边界循环。消融实验可以回答查询分解、纠错或验证是否值得新增成本。
衡量 Goodput,而非原始完成量
只有同时满足答案质量、引用、授权与延迟目标的请求才算有效。增加检索步骤可能提高某个离线答案分数,却因超时或预算耗尽降低用户可见 Goodput。
2026 年的 智能体编排适应性 RAG 研究 报告:分解在一个结构化领域有帮助,却降低了多跳基准的排序精度;反思提高引用准确性,但显著增加延迟。这不是通用基准,却有力说明每个智能体组件都需要按查询切片做消融。
观测决策,而不是暴露敏感内容
有用轨迹应解释系统做了什么,同时避免默认保存完整提示词。
应采集:
- 策略、Prompt、模型、检索器、索引与语料版本;
- 被选工具与归一化参数;
- 来源 ID、版本、分数、过滤器与授权范围;
- 路由、改写、纠错、停止原因和预算计数器;
- 引用映射与输出 Schema 校验;
- 每一步延迟、Token、重试、缓存状态与错误。
遥测前应脱敏密钥与个人信息,限制原始证据访问,并按用途设置保留期。可以把轨迹接入 智能体可观测工作流,但不要把模型生成的解释当作权威内部轨迹。
把上线设计成可逆策略变更
Agentic RAG 同时改变答案行为与资源消耗,应该按查询切片或租户发布,而不是不可逆地替换所有请求。
- 冻结基线、语料、索引和评测集。
- 离线回放轨迹并审阅分歧。
- 影子运行智能体策略,但不向用户返回其答案。
- 只对存在明确故障、且循环能够解决的查询切片灰度。
- 质量、泄漏、延迟、Token 或依赖错误回归时自动回滚。
- 持续保留固定路径,关闭循环不应依赖重新构建系统。
常见失败模式
把评分器当成事实真值
评分器可能误解问题、偏好冗长文档,或服从检索内容中的注入指令。应使用有标签的检索数据、确定性元数据检查与经过校准的阈值,并把不确定案例路由到澄清或人工审核。
没有新信息却反复重试
针对同一索引改写同义句,可能只消耗预算而不改善召回。记录检索签名,并要求下一轮在数据源、过滤器、查询或待补证据上发生实质变化。
查询分解后丢失授权
子查询必须继承原用户范围。规划器请求了更宽范围,不代表可以扩大租户、时间、项目或数据分级。
引用了检索文本,但文本不支持声明
文本出现在上下文中不等于支持答案。每个关键声明都应由引用片段蕴含;证据冲突时应暴露冲突,而不是选择最方便的一份。
混合检索与副作用
查询订单和发起退款是两种能力。检索证据可以支撑行动建议,但业务动作需要独立授权、确认、幂等和结果对账。
常见问题
Agentic RAG 一定优于固定 RAG 吗?
不一定。一次授权搜索可以解决问题时,固定流程更容易测试,延迟和成本也更低。只有动态选源、分解或纠错在目标查询切片上产生实测收益,且通过延迟和成本门禁时,才应启用智能体路径。
Agentic RAG 能消除幻觉吗?
不能。更多检索与自评仍可能选择无关证据、服从注入内容、引用不支持答案的片段,或综合出错误结论。系统必须做声明级支持检查,覆盖不可回答案例,校准拒答,并对高影响决策保留人工审核。
内部检索失败后,是否应该自动搜索公网?
只有策略明确允许时才可以。公网搜索会改变数据分级、来源质量、时效、版权、隐私和提示词注入风险。应设置域名白名单、保留引用、标记外部证据,并严禁把敏感内部上下文发送给公网搜索工具。
Agentic RAG 应允许多少次检索?
不存在通用次数。应根据代表性轨迹、用户截止时间、依赖容量和 Token 预算确定上限,同时设置全局硬限制和更小的单工具或单证据缺口限制,并持续监控预算耗尽终态。
系统何时应该澄清或拒答?
缺少一个用户选择就能安全消除歧义时,应请求澄清;所需证据缺失、冲突、未授权、超过时效,或无法在预算内获得时,应拒答。不能把这些状态包装成自信陈述。
总结
Agentic RAG 适用于检索本身需要运行时决策的场景,但“自主”不是目标。真正目标是在明确质量、授权、延迟与成本约束下提升验收答案。工程上应把检索建模为类型化且经过授权的工具,保留证据来源,限制所有循环,在证据失败时安全终止,分层对比强基线,并把上线设计成可回滚策略。
参考资料
- Agentic Retrieval-Augmented Generation: A Survey on Agentic RAG
- Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection
- Corrective Retrieval Augmented Generation
- Adaptive-RAG: Learning to Adapt Retrieval-Augmented LLMs through Question Complexity
- Microsoft:Develop an Agentic RAG Solution
- NVIDIA RAG Blueprint:Agentic RAG
- RAG 原理与评测指南
- 混合检索与重排优化