摘要
传统检索增强生成(RAG)系统在简单问答场景中表现出色,但在处理复杂、多步骤查询时存在显著局限。本文介绍Agentic RAG——一种智能体驱动的动态检索系统,通过查询重写、动态路由、工具调用和自我反思等能力,将传统RAG升级为具备自主决策能力的智能工作流。我们将使用Golang实现完整的Agentic RAG系统,代码占比超过60%,包含可运行的企业级示例。
1. 传统RAG的局限与Agentic RAG的诞生
1.1 传统RAG的技术瓶颈
传统RAG系统遵循"检索-生成"的固定范式:
- 用户查询 → 2. 向量检索 → 3. 上下文拼接 → 4. LLM生成答案
这种范式存在以下问题:
- 查询理解单一:无法处理隐含意图、多轮对话、复杂逻辑
- 检索源固定:通常只使用向量数据库,忽略结构化数据(SQL)、API服务和实时网络信息
- 缺乏验证机制:生成答案后无自我检查,可能导致"幻觉"传播
- 无工具调用能力:无法执行计算、代码运行、文件操作等任务
1.2 Agentic RAG的核心优势
Agentic RAG引入智能体范式,赋予系统自主决策能力:
- 查询理解与重写:使用LLM分析用户真实意图,生成优化后的检索查询
- 动态路由引擎:根据查询类型智能选择检索源(向量库、SQL、API、网络搜索)
- 工具调用层:集成计算器、代码执行器、API客户端等工具扩展能力边界
- 反思循环机制:验证答案准确性,必要时触发重新检索或查询改写
- 工作流集成:支持LangGraph式状态机,实现复杂业务逻辑编排
2. Agentic RAG系统架构设计
2.1 整体架构图
图1:Agentic RAG系统架构,包含查询重写、动态路由、多源检索、工具调用、反思循环等核心模块
2.2 核心模块说明
| 模块名称 | 功能描述 | 技术实现 |
|---|---|---|
| 查询重写模块 | 理解用户真实意图,优化检索查询 | GPT-4o + 提示工程 |
| 动态路由引擎 | 根据查询类型选择最佳检索源 | 决策树 + 机器学习分类 |
| 多源检索器 | 并行查询向量库、SQL、API、网络 | 协程池 + 超时控制 |
| 工具调用层 | 执行计算、代码、API等工具操作 | Plugin架构 + 沙箱环境 |
| 反思循环 | 验证答案质量,触发重试机制 | 置信度评分 + 规则引擎 |
| 工作流引擎 | 编排复杂业务逻辑 | 状态机 + 有向图 |
2.3 数据流设计
用户查询 → 查询重写 → 动态路由 → 多源检索 → 工具调用 → 反思循环 → 答案生成 ↑ ↓ └──────────────────────────────────────┘ 反思循环触发重新检索3. Golang实现:核心代码解析
3.1 项目结构
agentic-rag/ ├── cmd/ │ ├── main.go # 入口文件 │ └── server/ # HTTP服务器 ├── internal/ │ ├── query_rewriter/ # 查询重写模块 │ ├── router/ # 动态路由引擎 │ ├── retrievers/ # 多源检索器 │ │ ├── vector_retriever.go │ │ ├── sql_retriever.go │ │ ├── api_retriever.go │ │ └── web_retriever.go │ ├── tools/ # 工具调用层 │ ├── reflection/ # 反思循环 │ └── workflow/ # 工作流引擎 ├── pkg/ │ ├── llm/ # LLM客户端封装 │ ├── vectorstore/ # 向量数据库操作 │ └── config/ # 配置管理 └── go.mod3.2 查询重写模块实现
// internal/query_rewriter/rewriter.go package query_rewriter import ( "context" "encoding/json" "fmt" "strings" "github.com/ai-tech-blog/agentic-rag-golang/pkg/llm" ) // QueryAnalysis 查询分析结果 type QueryAnalysis struct { Intent string `json:"intent"` Entities []string `json:"entities"` RequiresTool bool `json:"requires_tool"` ToolType string `json:"tool_type,omitempty"` Complexity string `json:"complexity"` // simple, medium, complex } // RewrittenQuery 重写后的查询 type RewrittenQuery struct { Original string `json:"original"` Rewritten string `json:"rewritten"` Analysis QueryAnalysis `json:"analysis"` SearchQueries []string `json:"search_queries"` // 用于检索的查询列表 } // Rewriter 查询重写器 type Rewriter struct { llmClient *llm.Client } // NewRewriter 创建重写器实例 func NewRewriter(apiKey string) (*Rewriter, error) { client, err := llm.NewClient(apiKey, llm.WithModel("gpt-4o")) if err != nil { return nil, fmt.Errorf("创建LLM客户端失败: %w", err) } return &Rewriter{llmClient: client}, nil } // Rewrite 重写用户查询 func (r *Rewriter) Rewrite(ctx context.Context, query string) (*RewrittenQuery, error) { // 构建提示词 prompt := fmt.Sprintf(`请分析以下用户查询并生成优化后的检索查询。 用户查询: "%s" 请按以下格式返回JSON: { "intent": "查询的主要意图,如'技术问题解答'、'数据查询'、'代码生成'等", "entities": ["查询中提到的关键实体"], "requiresTool": true/false, "toolType": "如果需要工具,指定工具类型,如'calculator'、'code_executor'、'api_call'", "complexity": "simple/medium/complex", "rewrittenQuery": "优化后的检索查询语句", "searchQueries": ["用于向量检索的查询1", "用于关键词检索的查询2"] } 注意: 1. 如果查询涉及计算,requiresTool设为true,toolType为"calculator" 2. 如果查询需要最新信息,searchQueries应包含网络搜索关键词 3. 复杂查询应拆分为多个searchQueries`, query) // 调用LLM response, err := r.llmClient.Completion(ctx, prompt, llm.WithTemperature(0.3)) if err != nil { return nil, fmt.Errorf("LLM调用失败: %w", err) } // 解析响应 var result struct { Intent string `json:"intent"` Entities []string `json:"entities"` RequiresTool bool `json:"requiresTool"` ToolType string `json:"toolType"` Complexity string `json:"complexity"` RewrittenQuery string `json:"rewrittenQuery"` SearchQueries []string `json:"searchQueries"` } if err := json.Unmarshal([]byte(response), &result); err != nil { return nil, fmt.Errorf("解析LLM响应失败: %w", err) } // 构建返回结果 analysis := QueryAnalysis{ Intent: result.Intent, Entities: result.Entities, RequiresTool: result.RequiresTool, ToolType: result.ToolType, Complexity: result.Complexity, } return &RewrittenQuery{ Original: query, Rewritten: result.RewrittenQuery, Analysis: analysis, SearchQueries: result.SearchQueries, }, nil } // BatchRewrite 批量重写查询 func (r *Rewriter) BatchRewrite(ctx context.Context, queries []string) ([]*RewrittenQuery, error) { results := make([]*RewrittenQuery, 0, len(queries)) for _, query := range queries { rewritten, err := r.Rewrite(ctx, query) if err != nil { return nil, fmt.Errorf("重写查询'%s'失败: %w", query, err) } results = append(results, rewritten) } return results, nil }3.3 动态路由引擎实现
// internal/router/router.go package router import ( "context" "fmt" "sync" "github.com/ai-tech-blog/agentic-rag-golang/internal/query_rewriter" ) // RouteDecision 路由决策 type RouteDecision struct { Query string `json:"qu