ag-ui-rag-agent:基于 Pydantic AI 与 PostgreSQL PGVector 构建语义 + 混合搜索的 RAG 智能体
【免费下载链接】ottomator-agentsAll the open source AI Agents hosted on the oTTomator Live Agent Studio platform!项目地址: https://gitcode.com/GitHub_Trending/ot/ottomator-agents
本文以 ag-ui-rag-agent/agent/README.md 中定义的 Semantic Search Agent 为核心,完整讲解如何用 Pydantic AI 与 PostgreSQL(PGVector 扩展)搭建一个具备语义搜索与混合搜索双策略、可自动选择检索方式并总结结果的知识库问答智能体。读完后,你可以掌握从建库、配置环境变量、文档摄取到 CLI 交互与 AG-UI 前端状态同步的完整实战链路。
一、这个智能体解决了什么问题
该智能体是一个"智能知识库搜索系统",官方定位为:powered by Pydantic AI and PostgreSQL with PGVector。它提供两种检索能力,并由 Agent 自动选择策略、对检索结果做总结:
- Semantic Search(语义搜索):纯向量相似度检索,基于 embedding;
- Hybrid Search(混合搜索):语义向量与关键词匹配结合,适合精确命中;
- Intelligent Strategy Selection:Agent 根据查询类型自动选择检索方式;
- Result Summarization:从检索到的分块生成连贯的洞察性回答;
- Interactive CLI:基于 Rich 的交互式命令行,支持实时流式输出;
- Multi-Provider Support:兼容任意 OpenAI 协议 API(OpenAI、Gemini、Ollama 等)。
从源码结构看,该目录实际上处于两条形态的叠加状态:README.md 描述的是独立的 Semantic Search Agent 形态,而 agent.py 已升级为 AG-UI(Agent User Interaction Protocol)支持版本——检索结果会写入共享状态RAGState并通过状态快照事件推送给前端,这一点本文会在第六节结合源码展开。
二、前置条件与安装步骤
README 给出的运行前提:
- Python 3.10+;
- 带 PGVector 扩展的 PostgreSQL;
- 一个 LLM API Key(OpenAI、Gemini、Ollama、Groq 或任意 OpenAI 兼容服务);
- 已建好、且包含 documents 与 chunks 数据的数据库(仓库提供 schema)。
安装流程(对应 README.md 的 Installation 一节):
- 进入 agent 目录;
- 安装依赖:
pip install -r requirements.txt- 初始化数据库 schema。仓库提供了现成的建库脚本 sql/schema.sql,README 给出两种执行方式:在 Supabase/Postgres 平台里直接执行 SQL,或用 psql:
psql -d your_database -f sql/schema.sql- 配置环境变量文件
.env(README 建议从.env.example复制后编辑;该目录当前未包含此示例文件,可参照下文配置项自行创建); - 摄取文档。README 明确说明这一步必须先于运行 Agent 完成:
python -m ingestion.ingest --documents documents/仓库的 requirements.txt 也印证了技术栈:核心为pydantic-ai[agui]、ag-ui、openai;数据库层为asyncpg、psycopg2-binary、pgvector;Web 层为fastapi、uvicorn、python-multipart;数据与文档处理依赖pandas、numpy、scikit-learn、tiktoken、pypdf、python-docx、beautifulsoup4等;开发工具为pytest、pytest-asyncio、black、ruff、mypy。
三、配置体系:环境变量与默认值
README 列出的必需环境变量如下:
| 变量 | 说明 |
|---|---|
DATABASE_URL | 带 PGVector 的 PostgreSQL 连接串 |
LLM_PROVIDER | 提供商名称(openai、anthropic、ollama 等) |
LLM_API_KEY | LLM 提供商 API Key |
LLM_MODEL | 使用的模型(如 gpt-4.1-mini、gemini-2.5-flash) |
LLM_BASE_URL | API 基础地址(默认https://api.openai.com/v1) |
EMBEDDING_MODEL | 向量模型(如 text-embedding-3-small、text-embedding-3-large) |
这些变量在 settings.py 中通过pydantic_settings.BaseSettings统一解析,load_settings()在缺少DATABASE_URL或 API Key 时会抛出带明确提示的错误。结合源码,完整的配置项与默认值如下(Settings类,settings.py):
| 配置项 | 默认值 | 含义 |
|---|---|---|
database_url | 必填 | PostgreSQL 连接 URL |
llm_provider | openai | LLM 提供商名称 |
llm_api_key | 必填 | LLM API Key |
llm_model | gpt-4o-mini | 检索与总结使用的对话模型 |
llm_base_url | https://api.openai.com/v1 | OpenAI 兼容服务的 Base URL |
default_match_count | 10 | 搜索默认返回条数 |
max_match_count | 50 | 允许的最大返回条数 |
default_text_weight | 0.3 | 混合搜索中关键词匹配权重(0–1) |
db_pool_min_size | 10 | 连接池最小连接数 |
db_pool_max_size | 20 | 连接池最大连接数 |
embedding_model | text-embedding-3-small | 向量模型 |
embedding_dimension | 1536 | 向量维度 |
多提供商支持的关键在于 providers.py:get_llm_model()用配置里的base_url与api_key构造OpenAIProvider,再包一层OpenAIModel——因此只要把LLM_BASE_URL指向 Ollama、Groq 等 OpenAI 兼容端点即可切换模型;get_model_info()则供info命令展示当前配置。
四、数据库 Schema 与两个核心搜索函数
README 的 Database Setup 一节概括了 schema 的四要素:documents表(存全文与元数据)、chunks表(存分块与向量)、match_chunks()(语义搜索函数)、hybrid_search()(混合搜索函数)。sql/schema.sql 给出了完整实现:
4.1 表结构与索引
documents:id UUID主键、title、source、content、metadata JSONB、时间戳字段,并对metadata建 GIN 索引、对created_at建降序索引;chunks:id UUID主键、外键document_id(级联删除)、content、embedding vector(1536)、chunk_index、metadata JSONB、token_count;- 关键索引包括
idx_chunks_embedding(IVFFlat 余弦索引,WITH (lists = 1))、idx_chunks_document_id、以及供关键词路径使用的idx_chunks_content_trgm(gin_trgm_ops三元组 GIN 索引)。
schema 同时启用vector、uuid-ossp、pg_trgm三个扩展,并附有一个自动更新documents.updated_at的触发器update_documents_updated_at。
4.2 match_chunks:纯语义搜索
函数签名match_chunks(query_embedding vector(1536), match_count INT DEFAULT 10),实现逻辑(schema.sql):
SELECT c.id AS chunk_id, c.document_id, c.content, 1 - (c.embedding <=> query_embedding) AS similarity, -- 余弦距离转相似度 c.metadata, d.title AS document_title, d.source AS document_source FROM chunks c JOIN documents d ON c.document_id = d.id WHERE c.embedding IS NOT NULL ORDER BY c.embedding <=> query_embedding LIMIT match_count;要点:用 PGVector 的<=>(余弦距离)操作符排序,并把相似度表达为1 - 距离,因此返回值范围在 -1 到 1 之间、越接近 1 越相似。
4.3 hybrid_search:混合搜索
函数签名hybrid_search(query_embedding vector(1536), query_text TEXT, match_count INT DEFAULT 10, text_weight FLOAT DEFAULT 0.3)。它用两个 CTE 分别产出向量路径结果(vector_results,同上按余弦距离取全部候选)与文本路径结果(text_results,基于to_tsvector('english', ...)与plainto_tsquery做全文匹配、以ts_rank_cd计算文本分),再做FULL OUTER JOIN,合并公式为(schema.sql):
(COALESCE(v.vector_sim, 0) * (1 - text_weight) + COALESCE(t.text_sim, 0) * text_weight)::float8 AS combined_score即combined_score = 向量相似度 × (1 − text_weight) + 文本相似度 × text_weight,按combined_score DESC取前match_count条,并同时返回vector_similarity与text_similarity两个子分,方便上层解释排序原因。默认text_weight = 0.3意味着混合检索以语义为主、关键词为辅;查专有名词时调高该权重可以让精确词命中占更大比重。
此外 schema 还提供了get_document_chunks(doc_id)函数,按chunk_index顺序返回某文档的全部分块,用于"从分块回溯全文结构"的场景。
五、两种搜索策略:适用场景与工具层实现
README 对策略选择给出了清晰的经验法则:
语义搜索(Semantic Search)
适合概念性、主题性查询,例如:
- "concepts similar to machine learning"
- "ideas about artificial intelligence"
- "related to neural networks"
混合搜索(Hybrid Search)
适合特定事实与技术术语,例如:
- "OpenAI GPT-4 specifications"
- "NASDAQ:NVDA stock price"
- "specific quote from Sam Altman"
README 说明:Agent 会根据查询自动选择合适的策略,也可以在 prompt 中显式指定检索类型。
工具层实现在 tools.py。semantic_search()与hybrid_search()两个协程的关键行为(均以RunContext[AgentDependencies]注入依赖):
- 参数兜底与钳制:
match_count缺省时取settings.default_match_count(10),并统一min(match_count, max_match_count)(上限 50);text_weight缺省时优先读会话级user_preferences['text_weight'],再落到settings.default_text_weight(0.3),且被钳制在[0.0, 1.0]; - 查询向量化:通过
deps.get_embedding(query)生成查询向量——dependencies.py 中该函数调用 OpenAI 兼容 API 的embeddings.create(model=embedding_model, input=text)并返回浮点列表; - 向量字符串格式:Python 侧手工拼成 PGVector 接受的
'[0.1,0.2,...]'(逗号后无空格)格式再执行SELECT * FROM match_chunks($1::vector, $2)/SELECT * FROM hybrid_search($1::vector, $2, $3, $4); - 结果模型:
semantic_search返回SearchResult列表(chunk_id、document_id、content、similarity、metadata、document_title、document_source);hybrid_search返回附带combined_score、vector_similarity、text_similarity的字典列表; - 错误处理:数据库异常时打印并返回空列表,保证 Agent 不会因检索失败而崩溃。
tests/test_tools.py 用 mock 数据库验证了这些行为:自定义match_count是否正确下传(test_semantic_search_with_custom_count断言第 3 个参数为 5)、超过上限时被钳制到max_match_count(50)、embedding 生成参数正确、空结果与异常路径等。
六、AG-UI 形态:共享状态与工具事件(源码级补充)
在 agent.py 中,Agent 已升级为 AG-UI 支持版本,这是理解本目录名的关键:
- 共享状态
RAGState(agent.py):包含retrieved_chunks(RetrievedChunk列表,含 chunk_id、content、similarity、document_title、highlight 等字段)、current_query、search_history(只保留最近 10 条)、selected_chunk_id、total_chunks_in_kb、knowledge_base_status; - Agent 定义:
rag_agent = Agent(get_llm_model(), deps_type=StateDeps[RAGState], system_prompt=MAIN_SYSTEM_PROMPT); - 工具集:
search_knowledge_base(query, match_count, search_type):执行语义或混合检索,把结果写入state.retrieved_chunks并返回StateSnapshotEvent(前端据此刷新列表);出错时清空 chunks 并把knowledge_base_status置为error: ...;clear_search_results():清空当前结果与选中项;select_chunk(chunk_id):高亮指定分块;get_knowledge_base_stats():执行SELECT COUNT(*) FROM chunks更新知识库统计;display_search_results():发出名为DisplaySearchResults的CustomEvent,携带 chunks、query、total_results 触发 UI 展示;
- 动态指令:
@rag_agent.instructions装饰的rag_instructions每次运行时根据状态拼装系统指令——有检索结果时会把 Top 5 分块的得分、来源与前 200 字符摘要注入 prompt,让模型基于真实检索内容作答; - 应用导出:文件末尾
app = rag_agent.to_ag_ui(deps=StateDeps(RAGState())),__main__下用uvicorn在0.0.0.0:8000启动(agent.py)。仓库上层还有一个 Next.js/CopilotKit 前端(见 README_AGUI_SETUP.md 与 src/app/api/copilotkit/route.ts)与之对接。
系统提示词基础版在 prompts.py:要求"仅在用户明确需要知识库信息时才搜索"、问候语直接回复不触发检索、优先小match_count(5–10)聚焦结果,并给出了text_weight的调节建议;get_dynamic_prompt()还会把会话 ID、用户偏好(search_type、text_weight、result_count)与最近 3 次搜索历史拼入上下文。
七、文档摄取管道(Ingestion Pipeline)
README 中"必须先摄取文档"一步的实现在 ingestion/ingest.py。完整支持的命令行参数(argparse定义见 ingest.py):
python -m ingestion.ingest \ --documents documents/ \ --chunk-size 1000 \ --chunk-overlap 200 \ --no-semantic \ --clean \ --verbose| 参数 | 默认值 | 说明 |
|---|---|---|
--documents/-d | documents | 文档目录(递归查找*.md、*.markdown、*.txt) |
--chunk-size | 1000 | 目标分块字符数 |
--chunk-overlap | 200 | 分块重叠字符数 |
--no-semantic | 关闭 | 禁用语义分块,改用纯规则分块 |
--clean/-c | 关闭 | 摄取前清空chunks与documents表 |
--verbose/-v | 关闭 | 日志级别提升至 DEBUG |
管道DocumentIngestionPipeline处理单个文档的流程:读取文件(UTF-8 失败回退 latin-1)→ 从 Markdown 首行#提取标题(否则用文件名)→ 抽取元数据(文件路径、大小、行数、词数,若文档带 YAML frontmatter 则一并解析合并)→ 分块 → 生成 embedding → 在单个事务中先插documents再逐条插chunks(embedding 同样拼为[...]字符串写入vector列)。结束后输出总结:处理文档数、总分块数、错误数与总耗时。
分块策略在 ingestion/chunker.py:ChunkingConfig默认chunk_size=1000、chunk_overlap=200、max_chunk_size=2000、min_chunk_size=100,并校验 overlap 必须小于 chunk size。SemanticChunker先按 Markdown 结构边界(标题、段落、列表、代码块、表格)切段,再把段聚合到chunk_size以内;超长段落会调用 LLM(通过 Pydantic AI 临时 Agent,以---CHUNK---分隔)做语义切分,失败时回退到句界优先的规则切分(_simple_split在目标位置附近回溯.!?\n作为切点)。SimpleChunker则完全按段落做规则分块、支持 overlap。仓库的 documents/ 目录自带 21 篇大科技 AI 行业 Markdown 样本(如 doc1_openai_funding.md),可直接用于端到端验证——这也解释了 README 中 "NASDAQ:NVDA"、"Sam Altman" 等示例查询的出处。
八、交互式 CLI 使用
README 给出的运行命令:
python -m cliCLI 提供的能力与命令表(README.md 与 cli.py 的display_help一致):
| 命令 | 作用 |
|---|---|
help | 显示可用命令 |
info | 展示系统配置(LLM Provider/Model、Embedding Model、默认 match count 与 text weight) |
clear | 清屏并重新显示欢迎面板 |
set <key>=<value> | 设置会话偏好,如set text_weight=0.5(自动尝试 int/float 转换) |
exit/quit/q | 退出 |
从 cli.py 源码看,会话流程为:main()初始化AgentDependencies(建立 asyncpg 连接池、创建会话 UUID)→ 循环读取输入 → 将最近 6 轮对话拼成 "Previous conversation" 上下文 → 通过search_agent.iter(prompt, deps=deps)流式执行,逐节点处理:模型请求节点中把PartDeltaEvent的content_delta实时打印为 Assistant 输出,工具调用节点打印 "🔹 Calling tool: ..." 及参数预览(长值截断)、工具结果打印 "✅ Tool result: ..."(截断至 100 字符)。set命令写入的user_preferences会被hybrid_search在计算text_weight时优先读取,实现"会话内在线调参"。需注意:当前 agent.py 导出的 Agent 实例名为rag_agent(AG-UI 形态),而 cli.py 与测试仍从模块导入search_agent,从源码结构看,CLI 入口与 Agent 模块之间尚存在命名未完全对齐的情况,实际以仓库当前版本为准。
九、测试与开发
README 的 Development 一节给出的命令:
pytest tests/ black . ruff check .仓库 tests/ 目录包含test_agent.py、test_cli.py、test_dependencies.py、test_tools.py、test_integration.py、test_requirements.py等测试文件与共享 fixture 的conftest.py,以及一份 VALIDATION_REPORT.md。以 test_tools.py 为例,测试用AsyncMock模拟 asyncpg 连接,覆盖基础检索、自定义条数、上限钳制、embedding 调用参数、数据库异常与空结果等路径,是验证第五节所述工具行为的直接证据。
README 描述的目录结构与实际文件一一对应:
semantic_search_agent/ ├── agent.py # 主 Agent 实现 ├── cli.py # 命令行界面 ├── dependencies.py # Agent 依赖 ├── providers.py # 模型提供商 ├── prompts.py # 系统提示词 ├── settings.py # 配置 ├── tools.py # 搜索工具 ├── ingestion/ # 文档摄取管道 ├── sql/ # 数据库 schema └── documents/ # 示例文档对应本仓库路径为 ag-ui-rag-agent/agent/,其中ingestion/下另有embedder.py(embedding 客户端工厂)、utils/下另有db_utils.py、models.py(IngestionConfig、IngestionResult等数据模型)、providers.py。
十、小结
这套 Semantic Search Agent 的完整闭环是:schema.sql用 PGVector 的余弦距离与 Postgres 全文检索实现match_chunks/hybrid_search两个数据库函数;settings.py+.env统一管理与 OpenAI 兼容端点的连接;ingestion/管道负责把 Markdown 文档切成带 embedding 的分块入库;tools.py把两个 SQL 函数包装为带参数钳制的 Pydantic AI 工具;agent.py再将其扩展为 AG-UI 共享状态 Agent,检索结果以状态快照事件驱动前端展示。配置时重点关注LLM_BASE_URL(切换提供商)、EMBEDDING_MODEL(须与库内 1536 维向量匹配)、default_text_weight(混合检索的关键词占比)三个参数,即可在不同知识库场景下快速调整检索行为。
【免费下载链接】ottomator-agentsAll the open source AI Agents hosted on the oTTomator Live Agent Studio platform!项目地址: https://gitcode.com/GitHub_Trending/ot/ottomator-agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考