对许多团队来说,RAG 的初始阶段目标仅仅是“让模型具备检索能力”;然而,当系统真正投入生产环境后,挑战会迅速演变为“让系统懂得该查什么、查几次、查哪里、何时停止、如何兜底、怎样审计”。
此时,你需要的已不再是一条简单的检索增强问答链路,而是一套集规划、执行、反思、治理与可观测能力于一体的 Agentic RAG 系统。


一、为什么传统 RAG 很快会撞到天花板

传统 RAG 的经典链路是:

用户问题 -> Query Embedding -> TopK 检索 -> 上下文拼接 -> LLM 生成答案

这个模型在 Demo 阶段非常有效,因为它足够简单、成本可控、实现速度快。但在真实生产场景里,它往往会在三个阶段出现明显失效。

1.1 第一类失效:问题并不是“查一次就能答”

例如下面几类问题:

  1. “某客户昨天升级后为什么调用风控接口一直超时,和网关限流策略是否相关?”
  2. “我们当前合同审批链路里,哪些节点和上季度的合规要求冲突?”
  3. “比较 A 版本与 B 版本知识库中关于退款策略的差异,并给出当前生效结论。”

这类问题天然具备以下特征:

  1. 需要多跳推理,不是单段文档能直接回答。
  2. 需要跨源整合,既要查制度文档,又要查变更记录、FAQ、工单、甚至运行日志摘要。
  3. 需要判断证据是否充分,而不是盲目把 TopK 拼给大模型。

传统 RAG 没有“决策闭环”,只能机械执行一次检索,因此很容易出现“检索命中但答案错误”的假象。

1.2 第二类失效:系统不知道该查谁

企业知识从来不是一个干净统一的向量库,它通常分散在多个域:

  1. 产品文档库
  2. API 参考手册
  3. 运维 Runbook
  4. 工单系统
  5. CRM / ERP / OA
  6. 结构化数据库
  7. 图谱或关系网络
  8. 外部检索源

如果系统不能判断“当前问题应优先走哪个知识源、哪个召回策略、哪个排序器”,那它检索出来的内容再多,也只是噪声堆积。

1.3 第三类失效:系统没有自我修正能力

传统 RAG 默认一次检索就足够,但线上真实情况是:

  1. 查询词可能表述含糊。
  2. 用户问题可能缺少上下文。
  3. 文档切分可能破坏语义边界。
  4. Embedding 召回可能遗漏关键术语。
  5. 最新事实可能只存在于结构化事件或工单记录中。

如果系统不会在“证据不足”时自动改写查询、拆解子任务、切换检索工具或回退到澄清提问,那么它只能在低质量上下文上生成看似完整、实则不可靠的答案。

1.4 本质原因:传统 RAG 是检索管道,不是决策系统

传统 RAG 的核心是“静态流程”:

固定输入 -> 固定召回 -> 固定拼接 -> 固定生成

而复杂知识问答需要的是“动态控制”:

理解意图 -> 制定计划 -> 选择工具 -> 执行检索 -> 评估证据 -> 再决策 -> 输出答案

这就是 Agentic RAG 的出发点。


二、Agentic RAG 的本质:把“检索增强”升级为“决策增强”

2.1 什么是 Agentic RAG

Agentic RAG 并不只是“多轮检索”,也不只是“在 RAG 上加一个 Agent”。
它的本质是:让系统围绕回答目标,自主完成规划、检索、评估、修正和生成的闭环控制。

一个成熟的 Agentic RAG,至少应具备五个能力:

  1. 任务理解:判断问题类型、风险级别、是否需要外部知识。
  2. 检索规划:决定使用哪些工具、哪些数据源、采用什么召回策略。
  3. 证据执行:并发调用检索器、数据库、图谱、缓存或工作流。
  4. 结果评估:判断当前证据是否充分、是否冲突、是否过时。
  5. 输出治理:对答案进行引用标注、置信度控制、安全审查和审计留痕。

2.2 它和几种常见形态的边界

很多团队会把几个概念混在一起,导致架构设计一开始就偏了。

形态 核心特征 是否等于 Agentic RAG
传统 RAG 单轮检索 + 生成
Workflow RAG 固定流程编排,多步但不自主决策
GraphRAG 借助图结构做关系检索与组织 否,但可成为 Agentic RAG 的检索能力之一
Tool Calling 模型能调工具 否,只是基础能力
Multi-Agent 多角色协作 不一定,可能只是复杂化
Agentic RAG 围绕回答目标做检索决策闭环

一个常见误区是:
“我已经能 function calling 了,所以我做的是 Agentic RAG。”

这是不准确的。

Function Calling 只是“能动手”,而 Agentic RAG 强调的是“何时动手、怎么动手、动几次、失败后如何换路”。

2.3 Agentic RAG 的闭环模型

可以把它抽象为一个 OODA 风格的检索控制回路:

  1. Observe:感知问题、租户上下文、历史会话、工具状态。
  2. Orient:识别意图、分类问题、评估风险与复杂度。
  3. Decide:选择检索计划、排序器、工具链路和停止条件。
  4. Act:执行查询、聚合证据、验证冲突、生成答案。

与传统问答不同,Agentic RAG 的核心价值不在“调用了多少次模型”,而在“每次调用是否在减少不确定性”。

2.4 决策闭环里最关键的四个判断

生产系统里,最有价值的不是“让模型自由发挥”,而是把以下四个判断做实:

  1. 是否需要检索
    并非所有问题都要查库。定义类、常识类、流程解释类问题,可能直接回答更稳、更快、更便宜。
  2. 应该检索哪些源
    技术问题优先产品文档和 API 手册;异常排查类问题优先日志摘要、工单、变更记录;制度类问题优先规章和版本生效记录。
  3. 当前证据是否够用
    不是 TopK 达到阈值就算够,而是要看是否覆盖关键实体、关键时间、关键约束和关键结论。
  4. 什么时候必须停止
    不能无限循环检索。系统必须有最大步数、最大 Token、最大耗时和置信度阈值。

三、生产级 Agentic RAG 的总体架构

3.1 从“问答链”转向“控制面 + 数据面”

做生产系统时,最重要的架构跃迁是把 Agentic RAG 拆成两个面:

  1. 控制面
    负责任务理解、策略路由、工具编排、权限控制、成本治理、可观测与灰度发布。
  2. 数据面
    负责离线数据摄取、解析切分、索引构建、在线检索、重排、缓存、结果聚合和证据返回。

这两个面分离后,系统才能真正具备可扩展性。

3.2 整体分层架构

┌─────────────────────────────┐                               │         Client Layer         │                               │ Web / API / Bot / Workflow   │                               └──────────────┬──────────────┘                                              │                               ┌──────────────▼──────────────┐                               │      Access Gateway          │                               │ Auth / RateLimit / Audit     │                               └──────────────┬──────────────┘                                              │                    ┌─────────────────────────▼─────────────────────────┐                    │                 Agent Control Plane                │                    │ Intent Router / Planner / Policy / State / Eval   │                    └──────────────┬──────────────────────┬─────────────┘                                   │                      │                    ┌──────────────▼────────────┐ ┌──────▼──────────────┐                    │     Tool Orchestration     │ │     Memory Layer     │                    │ Search / SQL / Graph / KB  │ │ Session / Cache / LT │                    └──────────────┬────────────┘ └──────┬──────────────┘                                   │                      │          ┌────────────────────────▼──────────────────────▼────────────────────────┐          │                               Data Plane                                │          │ Vector Search / BM25 / Graph Query / Rerank / Metadata Filter / SQL    │          └────────────────────────┬──────────────────────┬────────────────────────┘                                   │                      │                ┌──────────────────▼────────────┐ ┌──────▼────────────────────────┐                │     Offline Index Pipeline      │ │      Online Observability      │                │ Parse / Chunk / Embed / Build   │ │ Metrics / Traces / Feedback    │                └─────────────────────────────────┘ └────────────────────────────────┘

3.3 核心模块说明

3.3.1 Access Gateway

统一处理:

  1. 身份认证与租户识别
  2. 限流、熔断和并发配额
  3. 敏感字段脱敏
  4. 请求级审计与追踪 ID 注入
3.3.2 Agent Control Plane

这是 Agentic RAG 的“大脑”,负责:

  1. 意图识别与问题分类
  2. 检索计划生成
  3. 工具路由与工具权限控制
  4. 多轮状态机推进
  5. 结果评估与停止决策
  6. 模型版本、Prompt 版本、策略版本灰度切换
3.3.3 Tool Orchestration

这里不是简单“把工具列表暴露给模型”,而是要做可治理的工具层:

  1. 工具注册中心
  2. 输入 Schema 校验
  3. 工具级 ACL
  4. 超时、重试、熔断、隔离舱
  5. 幂等控制与调用审计
3.3.4 Data Plane

这是检索实际发生的地方,通常包括:

  1. 稠密向量检索
  2. 稀疏全文检索
  3. 图谱检索
  4. 元数据过滤
  5. Cross Encoder 重排
  6. 上下文压缩与证据去重
3.3.5 Offline Index Pipeline

生产级知识系统最容易被忽略的,其实不是在线问答,而是离线建库质量。
没有高质量索引,就不会有高质量 Agent 决策。

典型离线链路:

文档接入 -> 解析清洗 -> 结构化抽取 -> Chunk 切分 -> 标签补齐 -> Embedding -> 索引构建 -> 版本发布

对于高质量场景,通常还要补齐:

  1. 文档血缘
  2. 生效时间与失效时间
  3. 文档可信级别
  4. 所属租户与部门域
  5. 文档版本与审批状态

四、Agentic RAG 的核心执行模型

4.1 最小可用状态机

一个生产级 Agentic RAG,建议至少具备如下状态:

INIT  -> ROUTE  -> PLAN  -> RETRIEVE  -> EVALUATE  -> REFLECT  -> ANSWER  -> SAFE_GUARD  -> DONE

其中最关键的是三个环节:

  1. PLAN:生成可执行检索计划,而不是直接“让模型自己搜”。
  2. EVALUATE:判断证据是否足够,不足则进入下一轮。
  3. SAFE_GUARD:在输出前执行风险控制,包括引用完整性、越权检测、敏感内容规避和低置信度兜底。

4.2 检索计划不应只是字符串

很多实现里,Planner 的输出只是“改写后的 query”。
这太弱了。

生产系统里,Planner 输出应是结构化计划,至少包含:

  1. 原始问题
  2. 子问题列表
  3. 目标知识域
  4. 工具选择
  5. 过滤条件
  6. 排序策略
  7. 截止条件
  8. 风险等级

例如:

{  "intent": "incident_analysis",  "risk_level": "high",  "sub_queries": [    "风控接口超时的直接原因",    "过去24小时相关网关策略变更",    "是否存在租户级限流命中"  ],  "tools": [    "runbook_search",    "change_log_search",    "ticket_search"  ],  "filters": {    "tenantId": "t-001",    "timeRange": "last_24h",    "env": "prod"  },  "stop_condition": {    "max_steps": 4,    "max_latency_ms": 4500,    "min_confidence": 0.76  }}

4.3 证据评估要看“覆盖度”,不是只看分数

传统做法常常用向量相似度或 rerank score 决定“检索质量是否足够”。
这在线上非常危险。

真正应该关注的是:

  1. 实体覆盖度:关键对象是否都被召回,例如系统名、租户名、版本号、时间区间。
  2. 结论覆盖度:关键问题是否都有证据支撑,而不是只有背景说明。
  3. 时间有效性:证据是否来自当前生效版本。
  4. 冲突程度:不同知识源是否给出相互矛盾的结果。
  5. 引用完备性:最终答案中的关键结论是否都能溯源到证据。

4.4 Reflection 不是“无限重试”,而是“受控修正”

Reflection 阶段的目标是降低不确定性,而不是让模型不停重复。

建议只允许有限的几种修正动作:

  1. 改写查询词
  2. 扩大/收窄过滤条件
  3. 切换知识源
  4. 拆解子问题
  5. 请求用户补充关键上下文

凡是不能明确降低不确定性的反思动作,都不应该继续消耗 Token。


五、离线建库:决定上限的不是 Agent,而是知识质量

5.1 文档接入不是“丢进向量库”这么简单

如果离线链路粗糙,在线 Agent 再聪明也没用。
离线建库至少要完成以下步骤:

  1. 文档采集:从对象存储、知识库、Git 仓库、数据库、工单系统接入原始数据。
  2. 内容解析:解析 PDF、Word、Markdown、HTML、表格、图片 OCR。
  3. 结构重建:识别标题层级、代码块、表格、流程步骤、章节边界。
  4. 元数据打标:租户、业务域、文档类型、作者、审批状态、生效时间、版本号。
  5. Chunk 切分:按语义边界切分,而不是机械按字数。
  6. 双路索引:同时构建向量索引和全文索引。
  7. 版本发布:以“知识版本”的形式发布到在线系统。

5.2 为什么一定要做双路索引

纯向量检索适合语义相近,但对以下内容很容易失真:

  1. 错误码
  2. 接口路径
  3. 版本号
  4. 英文缩写
  5. 配置项
  6. SQL 字段名

因此生产环境通常采用 Hybrid Retrieval:

FinalScore = a * DenseScore + b * BM25Score + c * FreshnessScore + d * AuthorityScore

其中:

  1. DenseScore 负责语义召回
  2. BM25Score 负责关键词精确命中
  3. FreshnessScore 负责时间新鲜度
  4. AuthorityScore 负责可信源优先

5.3 Chunk 切分的工程原则

最忌讳的就是按固定 500 字、带 50 字 overlap 直接切。

更合理的原则是:

  1. 优先基于标题层级切分
  2. 流程步骤要完整保留
  3. 表格与表头不可拆断
  4. 代码块必须整体保留
  5. FAQ 问答对要成组保存
  6. 对高价值术语建立别名映射

5.4 元数据过滤在企业场景中极其关键

没有元数据过滤,系统就无法保证:

  1. 多租户隔离
  2. 版本生效控制
  3. 区域法规差异
  4. 环境区分
  5. 文档可信级别控制

典型过滤维度包括:

维度 示例
tenant_id finance-cn-01
domain payment / risk / crm
doc_type runbook / manual / api / faq
env prod / staging
version v2.4.3
effective_from 生效时间
authority_level 官方 / 草稿 / 社区

六、在线链路设计:高并发场景下如何跑稳

6.1 一条生产请求是怎么走的

一个高并发 Agentic RAG 请求,推荐按如下链路执行:

接入鉴权-> 意图分类-> 读取租户策略-> 读取会话状态-> 生成检索计划-> 并发召回-> 证据聚合与重排-> 结果评估-> 必要时进入反思回路-> 输出答案-> 写入审计与反馈事件

这条链路的关键不在于“步骤多”,而在于“每一步都要可控、可追踪、可降级”。

6.2 高并发场景必须做的五件事

第一,限流

限流不能只做 API 入口限流,还要做:

  1. 租户级并发限制
  2. 模型调用配额
  3. 检索工具并发控制
  4. 外部数据源熔断
第二,缓存

缓存至少分三层:

  1. Query Result Cache
    对标准化问题和高频问法缓存检索结果。
  2. Retrieval Fragment Cache
    对热点片段、热点 rerank 结果做短 TTL 缓存。
  3. Session State Cache
    缓存多轮会话上下文和执行状态,降低状态存储压力。
第三,超时切片

不能把整个链路交给一个总超时。
建议为每一层设置独立预算:

  1. 路由 100ms
  2. 计划 300ms
  3. 检索 1200ms
  4. 重排 300ms
  5. 生成 1500ms
  6. 审计 200ms

如果某一层超时,应该走降级策略,而不是让整条链路雪崩。

第四,降级

推荐设计明确的降级顺序:

  1. 关闭低价值外部检索源
  2. 降低召回路数
  3. 跳过成本高的 rerank
  4. 退化为单轮 Hybrid RAG
  5. 最后输出“证据不足”的受控答复
第五,异步化

不是所有动作都应该在同步链路完成。
像下面这些动作更适合异步:

  1. 用户反馈写入
  2. 长文本摘要预热
  3. 热点问题缓存构建
  4. 低优先级知识对齐
  5. 离线评估样本沉淀

6.3 控制面与数据面的容量解耦

很多系统一开始把 Planner、Retriever、Reranker、LLM Gateway 全部堆在一个服务里。
这在 QPS 上来之后很快就会崩。

推荐拆分为:

  1. agent-gateway
  2. agent-orchestrator
  3. retrieval-service
  4. rerank-service
  5. llm-gateway
  6. session-state-service
  7. index-pipeline-service

好处有三个:

  1. 各层可以按瓶颈独立扩缩容
  2. 可以在检索层与模型层之间做熔断隔离
  3. 可以清晰核算各层成本

七、生产级代码骨架设计

下面给出一套偏企业 Java 风格的实现骨架。
重点不在于把所有代码写满,而在于明确生产系统应有哪些边界对象、状态流和治理钩子。

7.1 核心领域对象

package com.example.agenticrag.domain;import java.time.Instant;import java.util.List;import java.util.Map;public record RagRequest(        String requestId,        String tenantId,        String sessionId,        String userId,        String question,        Map<String, Object> attributes,        Instant requestTime) {}public record RetrievalPlan(        String intent,        RiskLevel riskLevel,        List<SubQuery> subQueries,        List<ToolSelection> tools,        FilterCondition filterCondition,        StopCondition stopCondition) {}public record SubQuery(        String id,        String question,        QueryPurpose purpose) {}public record ToolSelection(        String toolName,        int topK,        boolean rerank,        int timeoutMs) {}public record FilterCondition(        String tenantId,        String domain,        String version,        String env,        Map<String, Object> extraFilters) {}public record StopCondition(        int maxSteps,        int maxPromptTokens,        long maxLatencyMs,        double minConfidence) {}public record Evidence(        String sourceId,        String sourceType,        String title,        String content,        double score,        double freshnessScore,        double authorityScore,        Map<String, Object> metadata) {}public record RagAnswer(        String requestId,        String answer,        double confidence,        List<Citation> citations,        boolean fallback,        String fallbackReason) {}public record Citation(        String sourceId,        String title,        String locator) {}public enum RiskLevel {    LOW, MEDIUM, HIGH}public enum QueryPurpose {    FACT, PROCEDURE, DIAGNOSIS, COMPARISON, DECISION}

7.2 状态机上下文

package com.example.agenticrag.runtime;import com.example.agenticrag.domain.Evidence;import com.example.agenticrag.domain.RagRequest;import com.example.agenticrag.domain.RetrievalPlan;import java.time.Instant;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class AgentExecutionContext {    private final RagRequest request;    private final Instant startTime = Instant.now();    private final AtomicInteger currentStep = new AtomicInteger(0);    private RetrievalPlan plan;    private final List<Evidence> evidences = new ArrayList<>();    private final List<String> reflections = new ArrayList<>();    private boolean sufficient;    private boolean fallback;    private String fallbackReason;    public AgentExecutionContext(RagRequest request) {        this.request = request;    }    public RagRequest request() {        return request;    }    public int nextStep() {        return currentStep.incrementAndGet();    }    public int currentStep() {        return currentStep.get();    }    public RetrievalPlan plan() {        return plan;    }    public void plan(RetrievalPlan plan) {        this.plan = plan;    }    public List<Evidence> evidences() {        return evidences;    }    public List<String> reflections() {        return reflections;    }    public boolean sufficient() {        return sufficient;    }    public void sufficient(boolean sufficient) {        this.sufficient = sufficient;    }    public boolean fallback() {        return fallback;    }    public void fallback(boolean fallback) {        this.fallback = fallback;    }    public String fallbackReason() {        return fallbackReason;    }    public void fallbackReason(String fallbackReason) {        this.fallbackReason = fallbackReason;    }    public Instant startTime() {        return startTime;    }}

7.3 检索工具统一抽象

package com.example.agenticrag.tool;import com.example.agenticrag.domain.Evidence;import com.example.agenticrag.domain.SubQuery;import com.example.agenticrag.runtime.AgentExecutionContext;import java.util.List;public interface RetrievalTool {    String name();    boolean supports(String intent);    List<Evidence> retrieve(SubQuery subQuery, AgentExecutionContext context);}

7.4 Planner:输出结构化计划,而不是自由文本

package com.example.agenticrag.agent;import com.example.agenticrag.domain.FilterCondition;import com.example.agenticrag.domain.QueryPurpose;import com.example.agenticrag.domain.RiskLevel;import com.example.agenticrag.domain.StopCondition;import com.example.agenticrag.domain.SubQuery;import com.example.agenticrag.domain.ToolSelection;import com.example.agenticrag.domain.RetrievalPlan;import com.example.agenticrag.runtime.AgentExecutionContext;import com.example.agenticrag.service.IntentClassifier;import com.example.agenticrag.service.PolicyService;import java.util.List;public class PlannerAgent {    private final IntentClassifier intentClassifier;    private final PolicyService policyService;    public PlannerAgent(IntentClassifier intentClassifier, PolicyService policyService) {        this.intentClassifier = intentClassifier;        this.policyService = policyService;    }    public RetrievalPlan plan(AgentExecutionContext context) {        String intent = intentClassifier.classify(context.request().question());        var policy = policyService.loadTenantPolicy(context.request().tenantId(), intent);        List<SubQuery> subQueries = switch (intent) {            case "incident_analysis" -> List.of(                    new SubQuery("q1", "定位直接异常现象及影响范围", QueryPurpose.DIAGNOSIS),                    new SubQuery("q2", "查询最近变更、发布与配置调整", QueryPurpose.FACT),                    new SubQuery("q3", "确认当前可执行恢复动作", QueryPurpose.PROCEDURE)            );            case "policy_compare" -> List.of(                    new SubQuery("q1", "抽取当前版本结论", QueryPurpose.FACT),                    new SubQuery("q2", "抽取历史版本差异", QueryPurpose.COMPARISON)            );            default -> List.of(                    new SubQuery("q1", context.request().question(), QueryPurpose.FACT)            );        };        return new RetrievalPlan(                intent,                policy.highRisk() ? RiskLevel.HIGH : RiskLevel.MEDIUM,                subQueries,                policy.recommendedTools(),                new FilterCondition(                        context.request().tenantId(),                        policy.domain(),                        policy.knowledgeVersion(),                        policy.environment(),                        policy.extraFilters()                ),                new StopCondition(4, 12000, 4500, 0.78D)        );    }}

7.5 Executor:并发调度检索工具

package com.example.agenticrag.agent;import com.example.agenticrag.domain.Evidence;import com.example.agenticrag.domain.SubQuery;import com.example.agenticrag.runtime.AgentExecutionContext;import com.example.agenticrag.tool.RetrievalTool;import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.Map;import java.util.concurrent.CompletableFuture;import java.util.concurrent.Executor;import java.util.stream.Collectors;public class RetrievalExecutor {    private final Map<String, RetrievalTool> toolRegistry;    private final Executor executor;    private final EvidenceReranker reranker;    public RetrievalExecutor(Map<String, RetrievalTool> toolRegistry,                             Executor executor,                             EvidenceReranker reranker) {        this.toolRegistry = toolRegistry;        this.executor = executor;        this.reranker = reranker;    }    public List<Evidence> execute(AgentExecutionContext context) {        List<CompletableFuture<List<Evidence>>> futures = new ArrayList<>();        for (SubQuery subQuery : context.plan().subQueries()) {            for (var selection : context.plan().tools()) {                RetrievalTool tool = toolRegistry.get(selection.toolName());                if (tool == null || !tool.supports(context.plan().intent())) {                    continue;                }                futures.add(CompletableFuture.supplyAsync(                        () -> tool.retrieve(subQuery, context), executor                ));            }        }        List<Evidence> merged = futures.stream()                .map(CompletableFuture::join)                .flatMap(List::stream)                .collect(Collectors.toList());        List<Evidence> reranked = reranker.rerank(context.request().question(), merged);        return reranked.stream()                .sorted(Comparator.comparingDouble(Evidence::score).reversed())                .limit(12)                .toList();    }}

7.6 Evaluator:判断当前证据是否足够

package com.example.agenticrag.agent;import com.example.agenticrag.domain.Evidence;import com.example.agenticrag.runtime.AgentExecutionContext;import java.util.List;import java.util.Set;import java.util.stream.Collectors;public class EvidenceEvaluator {    public EvaluationResult evaluate(AgentExecutionContext context, List<Evidence> evidences) {        double avgScore = evidences.stream()                .mapToDouble(Evidence::score)                .average()                .orElse(0D);        boolean hasFreshEvidence = evidences.stream()                .anyMatch(e -> e.freshnessScore() >= 0.7D);        Set<String> sourceTypes = evidences.stream()                .map(Evidence::sourceType)                .collect(Collectors.toSet());        boolean enoughSourceDiversity = sourceTypes.size() >= 2;        boolean sufficient = avgScore >= context.plan().stopCondition().minConfidence()                && hasFreshEvidence                && enoughSourceDiversity;        String reason = sufficient                ? "evidence_sufficient"                : "low_confidence_or_low_coverage";        return new EvaluationResult(sufficient, avgScore, reason);    }    public record EvaluationResult(boolean sufficient, double score, String reason) {}}

7.7 Reflector:只做受控修正

package com.example.agenticrag.agent;import com.example.agenticrag.domain.RetrievalPlan;import com.example.agenticrag.domain.ToolSelection;import com.example.agenticrag.runtime.AgentExecutionContext;import java.util.ArrayList;public class ReflectionAgent {    public RetrievalPlan reflect(AgentExecutionContext context, String reason) {        context.reflections().add(reason);        var oldPlan = context.plan();        var newTools = new ArrayList<>(oldPlan.tools());        newTools.add(new ToolSelection("graph_search", 5, true, 400));        return new RetrievalPlan(                oldPlan.intent(),                oldPlan.riskLevel(),                oldPlan.subQueries(),                newTools,                oldPlan.filterCondition(),                oldPlan.stopCondition()        );    }}

7.8 统一编排入口

package com.example.agenticrag.agent;import com.example.agenticrag.domain.RagAnswer;import com.example.agenticrag.domain.RagRequest;import com.example.agenticrag.runtime.AgentExecutionContext;import com.example.agenticrag.service.AnswerComposer;import com.example.agenticrag.service.AuditService;public class AgenticRagOrchestrator {    private final PlannerAgent plannerAgent;    private final RetrievalExecutor retrievalExecutor;    private final EvidenceEvaluator evidenceEvaluator;    private final ReflectionAgent reflectionAgent;    private final AnswerComposer answerComposer;    private final AuditService auditService;    public AgenticRagOrchestrator(PlannerAgent plannerAgent,                                  RetrievalExecutor retrievalExecutor,                                  EvidenceEvaluator evidenceEvaluator,                                  ReflectionAgent reflectionAgent,                                  AnswerComposer answerComposer,                                  AuditService auditService) {        this.plannerAgent = plannerAgent;        this.retrievalExecutor = retrievalExecutor;        this.evidenceEvaluator = evidenceEvaluator;        this.reflectionAgent = reflectionAgent;        this.answerComposer = answerComposer;        this.auditService = auditService;    }    public RagAnswer handle(RagRequest request) {        AgentExecutionContext context = new AgentExecutionContext(request);        context.plan(plannerAgent.plan(context));        while (context.currentStep() < context.plan().stopCondition().maxSteps()) {            context.nextStep();            var evidences = retrievalExecutor.execute(context);            context.evidences().clear();            context.evidences().addAll(evidences);            var evaluation = evidenceEvaluator.evaluate(context, evidences);            if (evaluation.sufficient()) {                context.sufficient(true);                break;            }            if (context.currentStep() >= context.plan().stopCondition().maxSteps()) {                context.fallback(true);                context.fallbackReason("max_steps_exceeded");                break;            }            context.plan(reflectionAgent.reflect(context, evaluation.reason()));        }        RagAnswer answer = answerComposer.compose(context);        auditService.record(context, answer);        return answer;    }}

7.9 AnswerComposer:生成必须带引用和兜底策略

package com.example.agenticrag.service;import com.example.agenticrag.domain.Citation;import com.example.agenticrag.domain.RagAnswer;import com.example.agenticrag.runtime.AgentExecutionContext;import java.util.List;public class AnswerComposer {    private final LlmGateway llmGateway;    public AnswerComposer(LlmGateway llmGateway) {        this.llmGateway = llmGateway;    }    public RagAnswer compose(AgentExecutionContext context) {        if (context.fallback()) {            return new RagAnswer(                    context.request().requestId(),                    "当前证据不足以给出高可信结论,建议补充时间范围、系统名称或具体异常现象后重试。",                    0.42D,                    List.of(),                    true,                    context.fallbackReason()            );        }        String prompt = PromptTemplates.answerPrompt(                context.request().question(),                context.evidences()        );        String answer = llmGateway.chat(prompt);        List<Citation> citations = context.evidences().stream()                .limit(5)                .map(e -> new Citation(e.sourceId(), e.title(), "section:" + e.metadata().get("section")))                .toList();        return new RagAnswer(                context.request().requestId(),                answer,                0.86D,                citations,                false,                ""        );    }}

7.10 检索工具示例:Hybrid Search

package com.example.agenticrag.tool.impl;import com.example.agenticrag.domain.Evidence;import com.example.agenticrag.domain.SubQuery;import com.example.agenticrag.runtime.AgentExecutionContext;import com.example.agenticrag.tool.RetrievalTool;import org.opensearch.client.opensearch.OpenSearchClient;import java.util.List;import java.util.Map;public class HybridKnowledgeSearchTool implements RetrievalTool {    private final OpenSearchClient openSearchClient;    public HybridKnowledgeSearchTool(OpenSearchClient openSearchClient) {        this.openSearchClient = openSearchClient;    }    @Override    public String name() {        return "knowledge_search";    }    @Override    public boolean supports(String intent) {        return true;    }    @Override    public List<Evidence> retrieve(SubQuery subQuery, AgentExecutionContext context) {        String tenantId = context.plan().filterCondition().tenantId();        // 真实项目里,这里应同时执行向量召回、BM25 召回和元数据过滤,        // 然后在服务端完成融合排序,而不是完全交给模型判断。        return List.of(                new Evidence(                        "doc-1001",                        "knowledge_base",                        "支付服务限流治理手册",                        "当下游风控接口持续超时超过 3s 时,应先检查网关租户级限流与最近发布记录。",                        0.91D,                        0.88D,                        0.95D,                        Map.of("tenantId", tenantId, "section", "4.2")                )        );    }}

7.11 Spring Boot 配置示例

server:  port: 8080spring:  application:    name: agentic-rag-service  data:    redis:      host: redis-cluster      port: 6379  kafka:    bootstrap-servers: kafka-1:9092,kafka-2:9092,kafka-3:9092agentic-rag:  default-timeout-ms: 4500  max-steps: 4  max-prompt-tokens: 12000  tools:    knowledge-search:      enabled: true      timeout-ms: 400      top-k: 8    graph-search:      enabled: true      timeout-ms: 300      top-k: 5    ticket-search:      enabled: true      timeout-ms: 250      top-k: 6  fallback:    enable-safe-answer: true    min-confidence: 0.78

7.12 事件审计与异步反馈

package com.example.agenticrag.service;import com.example.agenticrag.domain.RagAnswer;import com.example.agenticrag.runtime.AgentExecutionContext;import org.springframework.kafka.core.KafkaTemplate;import java.util.Map;public class AuditService {    private final KafkaTemplate<String, Object> kafkaTemplate;    public AuditService(KafkaTemplate<String, Object> kafkaTemplate) {        this.kafkaTemplate = kafkaTemplate;    }    public void record(AgentExecutionContext context, RagAnswer answer) {        Map<String, Object> event = Map.of(                "requestId", context.request().requestId(),                "tenantId", context.request().tenantId(),                "question", context.request().question(),                "planIntent", context.plan().intent(),                "stepCount", context.currentStep(),                "evidenceCount", context.evidences().size(),                "fallback", answer.fallback(),                "confidence", answer.confidence()        );        kafkaTemplate.send("agentic-rag-audit", context.request().requestId(), event);    }}

这段代码的价值不只是“把日志打出去”,而是为后续离线评估、问题回放、成本分析和 Prompt 版本对比沉淀证据。


八、多源检索策略设计:不是多加几个工具就叫生产级

8.1 常见工具类型

一个企业级 Agentic RAG 通常会接入以下工具:

    1. knowledge_search
      面向文档库、FAQ、制度手册。
    1. api_search
      面向接口定义、字段说明、错误码文档。
    1. graph_search
      面向实体关系、多跳路径、因果关联。
    1. sql_lookup
      面向结构化事实和维度过滤。
    1. ticket_search
      面向历史故障、工单处理与经验案例。
    1. change_log_search
      面向发布记录、配置变更、灰度说明。
    1. web_search
      面向公共信息补充,但企业内场景通常要默认受限。

8.2 不同问题应采用不同检索模式

问题类型 推荐模式
定义解释 直接回答或单轮 Hybrid RAG
步骤指南 文档检索 + FAQ 检索 + 引用生成
版本差异 文档版本过滤 + 结构化对比
故障诊断 工单 + 变更记录 + Runbook + 图谱
决策建议 检索 + 规则约束 + 置信度控制

8.3 GraphRAG 在 Agentic RAG 中的正确位置

GraphRAG 很强,但它不是所有问题的默认路径。

它更适合:

    1. 多实体关联问题
    1. 根因追踪问题
    1. 关系补全问题
    1. 多跳路径推理问题

不适合一上来就对所有问题强行走图谱,否则会导致:

    1. 建图成本高
    1. 延迟上升
    1. 命中收益不明显
    1. 关系噪声影响回答

正确做法是把 GraphRAG 作为可选检索策略,由 Planner 在满足条件时动态启用。


九、真实案例:从“客户支付失败”到“自主诊断检索”

9.1 业务背景

某支付 SaaS 平台服务 3000+ 商户,知识源包括:

    1. 产品使用文档
    1. API 接口手册
    1. 历史故障工单
    1. 发布变更记录
    1. 风控策略说明
    1. 运维 Runbook

用户提问:

某华东商户昨晚 22:00 后支付失败率突然升高,是通道故障、风控策略升级,还是网关限流导致?

如果用传统 RAG,大概率会召回“支付失败常见原因”“限流机制介绍”“风控系统说明”等泛化文档,最后输出一段正确但无用的套话。

9.2 Agentic RAG 的计划拆解

Planner 应把问题拆成三个子任务:

  1. 识别现象:支付失败率升高发生在什么时间段、影响哪些商户、主要错误码是什么。
  2. 定位变化:22:00 前后是否有通道、风控或网关相关变更。
  3. 给出处置:当前最可能原因是什么,推荐先执行哪一步排查或恢复动作。

9.3 工具路由方案

推荐调用:

  1. ticket_search
  2. change_log_search
  3. runbook_search
  4. knowledge_search

如果系统发现“商户、通道、风控规则、网关策略”之间存在复杂关联,再按需补调用 graph_search

9.4 评估标准

只有当以下问题都被回答时,证据才算充分:

  1. 有无异常现象证据
  2. 有无时间窗口证据
  3. 有无变更证据
  4. 有无恢复路径证据
  5. 最终结论是否可引用

9.5 最终输出示例

系统最终不应只回答“可能是限流”,而应给出结构化结果:

结论:当前更可能是网关租户级限流策略与新风控回调模式叠加导致,而不是支付通道整体故障。证据:1. 22:03 起,商户 tenant-hd-17 的 gateway_limit_exceeded 错误显著增加。2. 21:55 发布记录显示风控回调并发阈值由 200 提升为 800。3. Runbook 4.2 节明确指出,当风控回调并发抬升后,若租户级限流阈值未同步调整,会出现支付受理成功但后续确认失败。建议动作:1. 暂时提升该商户网关限流阈值。2. 回看 21:55 发布批次是否只影响华东通道。3. 若 5 分钟内失败率未回落,再切换到备用通道。

这才是企业真正要买单的价值:
不是“像人一样说话”,而是“像一个有经验的值班专家一样先检索、再判断、最后给行动建议”。


十、多租户、权限与安全治理

10.1 多租户隔离不能只靠索引名称

很多团队一开始做法是“每个租户一个索引”或者“查询时带个 tenantId filter”。
这还不够。

生产环境里建议至少做四层隔离:

  1. 请求鉴权隔离
    入口层校验租户、用户、角色、场景。
  2. 检索过滤隔离
    所有检索工具强制注入 tenant filter,不能信任模型自行传参。
  3. 引用输出隔离
    最终答案中不可引用越权证据,即使该证据在检索阶段被意外召回。
  4. 审计回放隔离
    运营人员回放执行链路时也要遵守权限约束。

10.2 工具层必须做最小权限控制

模型不应直接拥有任意查询数据库、任意访问外部系统的能力。

正确做法是:

  1. 为每个工具定义严格输入 Schema
  2. 对每个工具设置可访问范围
  3. 对高风险工具实行白名单和审批策略
  4. 对结构化查询生成结果做二次检查

例如 sql_lookup 不应让模型自由写 SQL,而应采用:

  1. 预定义查询模板
  2. 参数化占位符
  3. 列级脱敏
  4. 行级过滤

10.3 输出安全

输出前至少要做以下检查:

  1. 是否包含敏感信息
  2. 是否包含越权知识
  3. 是否缺少关键引用
  4. 是否在低置信度时误导性过强
  5. 是否给出了未被证据支持的操作建议

十一、可观测性:没有观测,就没有迭代

11.1 你必须监控的不只是响应时间

Agentic RAG 的观测对象比普通 API 多得多。

至少需要监控以下指标:

指标 含义
request_qps 请求吞吐
p95_latency_ms 端到端时延
planner_latency_ms 计划生成耗时
retrieval_latency_ms 检索耗时
llm_tokens_input 输入 Token
llm_tokens_output 输出 Token
reflection_rate 进入反思回路比例
fallback_rate 兜底回答比例
citation_coverage 答案关键结论引用覆盖率
tenant_isolation_violation 租户隔离违规次数

11.2 Trace 应该长什么样

一次请求的 Trace 最好能回放出以下信息:

  1. 用户问题
  2. 意图分类结果
  3. 检索计划
  4. 工具调用顺序
  5. 每个工具的耗时与结果量
  6. 证据评分
  7. 反思动作
  8. 最终答案与引用
  9. 成本统计

这样一来,当用户说“你这次为什么答错了”时,团队可以定位是:

  1. 问题路由错了
  2. 数据没召回
  3. 重排错了
  4. 反思没生效
  5. 生成时幻觉了
  6. 输出安全策略过度收缩

11.3 反馈闭环

真正成熟的系统,会把用户反馈转成后续优化数据:

  1. “答案不准” -> 检索召回评估样本
  2. “引用不全” -> 引用覆盖率问题样本
  3. “回答太慢” -> 延迟分析样本
  4. “该回答不该看到” -> 权限问题样本

这些样本可沉淀到离线评估平台,推动以下改进:

  1. Prompt 版本优化
  2. 检索策略优化
  3. rerank 模型替换
  4. 知识库清洗
  5. 工具路由策略更新

十二、缓存、熔断与降级策略

12.1 缓存设计

建议把缓存拆成四类:

  1. Query Rewrite Cache
    缓存标准化问法与检索计划。
  2. Evidence Cache
    缓存热点问题的证据集合。
  3. Citation Cache
    缓存高频问题对应的引用片段。
  4. Answer Cache
    只对低风险、低时效问题使用,且需要绑定知识版本。

12.2 熔断策略

不同下游要有不同熔断策略:

下游 熔断后动作
向量检索服务 切换到 BM25
Rerank 服务 使用基础融合排序
图谱服务 跳过图谱路径
工单系统 返回文档级证据并提示时效有限
外部 Web 检索 直接禁用

12.3 降级原则

降级不是“回答质量变差”这么简单,而是要保证:

  1. 系统仍可回答
  2. 风险不失控
  3. 用户知道当前答案的边界

因此低配模式下,宁可回答:

当前仅基于内部文档证据给出结论,外部变更与实时工单未纳入本次判断。

也不要假装自己看到了所有证据。


十三、评估体系:别只看主观体验

13.1 评估至少分三层

第一层:检索层

关注:

  1. Recall@K
  2. MRR
  3. NDCG
  4. 过滤命中率
  5. 新鲜度覆盖率
第二层:Agent 层

关注:

  1. 意图分类准确率
  2. 工具选择正确率
  3. 反思有效率
  4. 无效循环率
  5. 计划执行完成率
第三层:答案层

关注:

  1. 正确率
  2. 引用完整率
  3. 幻觉率
  4. 用户反馈满意度

13.2 线上评估不能只靠人工抽样

建议建设 A/B 评估能力,对比:

  1. 不同 Planner Prompt
  2. 不同检索路由策略
  3. 不同 rerank 模型
  4. 不同最大步数
  5. 是否启用 GraphRAG

关键是把实验结果落到指标,而不是只听“感觉更聪明了”。


十四、常见失败模式与反模式

14.1 反模式一:把 Agent 当成万能自动驾驶

让模型自由选择任意工具、无限循环、自主生成 SQL、自主访问外部系统,这种做法上线后大概率会变成事故放大器。

生产级 Agentic RAG 应是“有限自主”,而不是“无限自由”。

14.2 反模式二:所有问题都走最复杂链路

很多团队希望每个问题都经过规划、检索、反思、重排、引用、复核。
这会直接把时延和成本打爆。

正确做法是按问题复杂度分层:

  1. 简单问题直接回答
  2. 中等问题走单轮 Hybrid RAG
  3. 复杂问题再进入 Agent 回路

14.3 反模式三:把多智能体当成高级感

多智能体不是越多越好。
如果单 Agent 状态机就能解决问题,没必要拆成一堆“路由 Agent、分析 Agent、总结 Agent、批判 Agent”。

工程上更应该追求:

  1. 职责清晰
  2. 状态明确
  3. 成本可控
  4. 可回放
  5. 可降级

14.4 反模式四:忽视离线知识版本治理

很多团队只关注在线问答,忽视了:

  1. 哪份知识已生效
  2. 哪份知识已过期
  3. 哪份知识仍是草稿
  4. 答案引用的是哪个版本

没有版本治理,线上答案就无法审计。


十五、从 0 到 1 的落地路径

15.1 第一阶段:先做可控的 Hybrid RAG

目标不是一步到位做多智能体,而是先把下面这些基础能力打稳:

  1. 离线建库质量
  2. Hybrid Retrieval
  3. 元数据过滤
  4. 引用输出
  5. 基础监控

15.2 第二阶段:增加 Planner 和受控 Reflection

这一步开始引入 Agentic 能力,但不要贪大:

  1. 先限定工具集合
  2. 先限定最大步数
  3. 先限定问题范围
  4. 先限定租户范围

15.3 第三阶段:扩展为多源知识与复杂推理

当系统已经能稳定处理文档检索后,再逐步引入:

  1. 图谱检索
  2. 工单检索
  3. 结构化查询
  4. 变更记录分析
  5. 策略与规则系统

15.4 第四阶段:构建反馈驱动优化平台

只有走到这一步,系统才真正形成长期竞争力:

  1. 用户反馈沉淀
  2. 线上回放与标注
  3. 检索层评估
  4. Agent 决策评估
  5. 模型与 Prompt A/B 实验

十六、部署建议:一套更接近生产的基础设施组合

16.1 推荐组件组合

如果面向企业内部或 ToB 场景,比较稳妥的一组技术组合是:

推荐实现
接入层 API Gateway / Ingress
编排层 Spring Boot / 状态机编排服务
模型网关 统一 LLM Gateway
向量检索 OpenSearch / Milvus
全文检索 OpenSearch
图谱 Neo4j
缓存 Redis Cluster
事件流 Kafka
对象存储 OSS / S3
监控 Prometheus + Grafana + Tempo/Jaeger

16.2 为什么要有统一模型网关

不要让业务服务直接调用不同模型供应商。

统一模型网关至少有三个价值:

  1. 屏蔽模型差异
  2. 统一做限流、审计和成本核算
  3. 实现模型灰度切换和故障回退

16.3 为什么要有事件流

Kafka 这类事件流中间件对 Agentic RAG 的价值不只是“解耦”:

  1. 可以异步写审计事件
  2. 可以回放执行样本
  3. 可以构建离线评估数据集
  4. 可以触发缓存预热与知识修复任务

十七、什么时候该上 Agentic RAG,什么时候不要上

17.1 适合上的场景

  1. 企业知识源多且异构
  2. 问题复杂,需要多跳推理
  3. 对证据可追溯要求高
  4. 答案需要行动建议而非单纯解释
  5. 有持续优化和治理能力

17.2 不适合上的场景

  1. 知识规模很小
  2. 问题大多是固定 FAQ
  3. 时延预算极低
  4. 团队尚未把基础 RAG 做稳
  5. 没有评估、审计与治理资源

一句话总结:

如果你连“文档质量、切分质量、过滤质量、引用质量”都还没做稳,
那 Agentic RAG 往往不会帮你掩盖问题,只会把问题放大。


十八、架构师视角下的最终结论

Agentic RAG 的真正价值,不是让系统“更像人”,而是让系统在复杂知识环境中具备受控决策能力

它解决的不是“模型不知道答案”,而是:

  1. 系统不知道该去哪找答案
  2. 不知道当前证据够不够
  3. 不知道何时应该停
  4. 不知道怎样在成本、时延、准确率和安全之间做平衡

因此,建设 Agentic RAG 时,建议始终坚持四个原则:

  1. 把 Agent 看成控制系统,不是聊天外壳。
  2. 把检索看成数据工程,不是向量 API 调用。
  3. 把回答看成受控输出,不是自然语言表演。
  4. 把优化看成长期治理,不是一次性 Prompt 调优。

如果用一句最工程化的话收尾:

传统 RAG 解决的是“查得到”,Agentic RAG 解决的是“查得对、查得稳、查得可审计、查得能扩展”。

这也是它从实验室走向企业生产的分水岭。

学AI大模型的正确顺序,千万不要搞错了

🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!

有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!

就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

在这里插入图片描述

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇

学习路线:

✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经

以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!

我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐