概述
Agentic RAG 是将 AI Agent 与 RAG(检索增强生成)结合的新范式。Agent 不仅被动回答问题,还能主动规划检索策略、自适应查询优化、多步推理、调用工具。本文介绍如何基于腾讯云 PostgreSQL 构建完整的 Agentic RAG 应用。
架构设计
┌──────────────────────────────────────────────────────┐│ Agentic RAG 系统 │├──────────────────────────────────────────────────────┤│ ││ ┌──────────┐ ┌───────────┐ ┌──────────────┐ ││ │ Planner │ │ Retriever │ │ Generator │ ││ │ Agent │ → │ Agent │ → │ Agent │ ││ │ (规划) │ │ (检索) │ │ (生成) │ ││ └──────────┘ └───────────┘ └──────────────┘ ││ ↓ ↓ ↓ ││ ┌─────────────────────────────────────────────┐ ││ │ PostgreSQL(统一存储层) │ ││ ├─────────────────────────────────────────────┤ ││ │ pgvector │ Apache AGE │ 记忆表 │ ││ │ (向量检索) │ (知识图谱) │ (Agent 记忆) │ ││ │ │ │ │ ││ │ tencentdb_ai(大模型调用) │ ││ └─────────────────────────────────────────────┘ │└──────────────────────────────────────────────────────┘
与传统 RAG 的区别
维度 | 传统 RAG | Agentic RAG |
检索策略 | 固定(单次向量搜索) | 自适应(多次 / 多策略) |
查询改写 | 无或简单改写 | Agent 动态改写和分解 |
结果评估 | 直接使用 | Agent 评估后决定是否需要更多检索 |
工具调用 | 无 | 可调用 SQL API 外部服务 |
多跳推理 | 不支持 | 支持多步推理链 |
记忆 | 无状态 | 有长期记忆 |
数据库准备
-- 知识库表CREATE TABLE rag.documents (id BIGSERIAL PRIMARY KEY,title TEXT,content TEXT NOT NULL,chunk_index INT DEFAULT 0,embedding vector(1024),source TEXT,metadata JSONB DEFAULT '{}',created_at TIMESTAMPTZ DEFAULT NOW());CREATE INDEX idx_docs_embedding ON rag.documentsUSING hnsw (embedding vector_cosine_ops);-- Agent 检索记忆CREATE TABLE rag.search_memory (id UUID PRIMARY KEY DEFAULT gen_random_uuid(),session_id UUID NOT NULL,query TEXT NOT NULL,strategy TEXT,results_summary TEXT,satisfaction FLOAT, -- Agent 对结果的满意度created_at TIMESTAMPTZ DEFAULT NOW());-- 查询改写日志CREATE TABLE rag.query_rewrites (id BIGSERIAL PRIMARY KEY,original_query TEXT,rewritten_query TEXT,rewrite_reason TEXT,session_id UUID,created_at TIMESTAMPTZ DEFAULT NOW());
核心实现
说明:
向量维度需与您使用的大模型输出维度一致。常见模型维度:OpenAI text-embedding-3-small(1536维)、BGE-M3(1024维)、ChatGLM Embedding(1024维)。
智能查询改写
-- Agent 使用大模型改写查询CREATE OR REPLACE FUNCTION rag.rewrite_query(original TEXT)RETURNS TEXT AS $$DECLARErewritten TEXT;BEGINSELECT tencentdb_ai.chat_completions('<your-llm-model>','你是一个查询优化专家。请将以下用户问题改写为更适合向量检索的查询。只输出改写后的查询,不要解释。' ||E'\\n\\n原始问题:' || original) INTO rewritten;-- 记录改写INSERT INTO rag.query_rewrites (original_query, rewritten_query)VALUES (original, rewritten);RETURN rewritten;END;$$ LANGUAGE plpgsql;
多策略检索
-- 向量检索CREATE OR REPLACE FUNCTION rag.vector_search(query TEXT, top_k INT DEFAULT 5)RETURNS TABLE(id BIGINT, content TEXT, score FLOAT) AS $$BEGINRETURN QUERYSELECT d.id, d.content,1 - (d.embedding <=> tencentdb_ai.get_embedding('bge-m3', query)) AS scoreFROM rag.documents dORDER BY d.embedding <=> tencentdb_ai.get_embedding('bge-m3', query)LIMIT top_k;END;$$ LANGUAGE plpgsql;-- 关键词检索(BM25 风格)CREATE OR REPLACE FUNCTION rag.keyword_search(keywords TEXT, top_k INT DEFAULT 5)RETURNS TABLE(id BIGINT, content TEXT, score FLOAT) AS $$BEGINRETURN QUERYSELECT d.id, d.content,ts_rank(to_tsvector('chinese', d.content), plainto_tsquery('chinese', keywords))::FLOAT AS scoreFROM rag.documents dWHERE to_tsvector('chinese', d.content) @@ plainto_tsquery('chinese', keywords)ORDER BY score DESCLIMIT top_k;END;$$ LANGUAGE plpgsql;-- 混合检索(RRF 融合)CREATE OR REPLACE FUNCTION rag.hybrid_search(query TEXT, top_k INT DEFAULT 5)RETURNS TABLE(id BIGINT, content TEXT, rrf_score FLOAT) AS $$BEGINRETURN QUERYWITH vec AS (SELECT id, content, ROW_NUMBER() OVER (ORDER BY score DESC) AS rankFROM rag.vector_search(query, 20)),kw AS (SELECT id, content, ROW_NUMBER() OVER (ORDER BY score DESC) AS rankFROM rag.keyword_search(query, 20))SELECT COALESCE(v.id, k.id),COALESCE(v.content, k.content),(COALESCE(1.0/(60+v.rank), 0) + COALESCE(1.0/(60+k.rank), 0))::FLOAT AS rrf_scoreFROM vec v FULL OUTER JOIN kw k ON v.id = k.idORDER BY rrf_score DESCLIMIT top_k;END;$$ LANGUAGE plpgsql;
Agent 自适应 RAG 流程
-- 完整的 Agentic RAG 函数CREATE OR REPLACE FUNCTION rag.agentic_answer(question TEXT)RETURNS TEXT AS $$DECLARErewritten_query TEXT;context TEXT;answer TEXT;BEGIN-- Step 1: 查询改写rewritten_query := rag.rewrite_query(question);-- Step 2: 混合检索SELECT string_agg(content, E'\\n---\\n') INTO contextFROM rag.hybrid_search(rewritten_query, 5);-- Step 3: 生成回答SELECT tencentdb_ai.chat_completions('<your-llm-model>','基于以下参考资料回答用户问题。如果资料不足,明确说明。' ||E'\\n\\n参考资料:\\n' || COALESCE(context, '无相关资料') ||E'\\n\\n用户问题:' || question) INTO answer;RETURN answer;END;$$ LANGUAGE plpgsql;
实践
1. 文档切片:建议 chunk_size = 500–1000 字符,overlap = 100。
2. 混合检索:向量 + 关键词 RRF 融合效果优于单一方式。
3. 查询改写:对复杂问题进行分解和改写。
4. 结果重排:使用 Rerank 模型对检索结果二次排序。
5. 记忆复用:对相似问题复用历史检索结果。