- 人工智能
- AI Agent
- Agent 框架
- 后端
- 多智能体
- RAG
- 工具调用
- Agent 记忆
【免费下载链接】voltagent
AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework
导读
本文基于 VoltAgent 官方示例 examples/with-pinecone,完整讲解如何在 VoltAgent 中接入 Pinecone 向量数据库,为 Agent 赋予基于语义相似度的知识检索(RAG,Retrieval-Augmented Generation)能力。示例同时提供了两种检索模式:自动检索(每次交互自动执行语义搜索)与工具化检索(由 LLM 自行决定何时查询知识库),并自动完成索引创建、文档嵌入与向量检索的完整链路。读完本文,你将掌握如何在 VoltAgent 中编写自定义 Retriever、配置 Pinecone 索引、注入环境变量,以及理解BaseRetriever与retriever.tool两种接入方式背后的源码原理。
示例概述:Pinecone + VoltAgent 能做什么
本示例演示 VoltAgent 与 Pinecone 向量数据库的集成,面向需要"高级知识管理与检索"的场景。示例内置了一个预加载的知识库——包含 VoltAgent、Pinecone、RAG、向量数据库、TypeScript 等主题的样例文档——并在运行时自动完成以下工作:
- 自动创建索引:检测 Pinecone 中是否已存在目标索引,不存在则自动创建;
- 自动填充知识库:使用 OpenAI
text-embedding-3-small模型为样例文档生成向量并写入索引; - 语义向量检索:对用户查询做同样嵌入后,在 Pinecone 中执行高相似度向量搜索;
- 来源追踪:将命中文档的 ID 与相似度分数写入上下文(
context.get('references')),便于 Agent 与用户追溯答案出处。
整个示例的核心文件只有两个:
- 入口文件:组装 Memory、两个 Agent 与 Hono 服务器;
- 检索器实现:Pinecone 客户端初始化、索引管理、嵌入生成与
PineconeRetriever类。
前置条件
在开始之前,需要准备以下资源:
- Pinecone 账号:注册并登录 Pinecone 控制台;
- Pinecone API Key:从控制台获取,用于初始化
@pinecone-database/pinecone客户端; - OpenAI API Key:示例使用 OpenAI 同时承担两类职责——生成文档/查询的嵌入向量(
text-embedding-3-small),以及作为 Agent 的语言模型(openai/gpt-4o-mini)。
示例依赖清单可参见 package.json,其中与 VoltAgent 相关的核心依赖为@voltagent/core(Agent 与 Retriever 框架)、@voltagent/libsql(持久化记忆)、@voltagent/logger(日志)、@voltagent/server-hono(HTTP 服务),以及@pinecone-database/pinecone、openai两个第三方客户端。
快速开始
第 1 步:创建项目。使用 VoltAgent 官方脚手架,指定with-pinecone示例模板:
npm create voltagent-app@latest -- --example with-pinecone cd with-pinecone第 2 步:配置环境变量。复制环境变量模板并填入密钥:
cp .env.example .env编辑.env,添加 API Key:
PINECONE_API_KEY=your_pinecone_api_key_here OPENAI_API_KEY=your_openai_api_key_here说明:入口依赖
.env文件读取密钥。项目的dev脚本为tsx watch --env-file=.env ./src,即运行时会显式加载.env中的变量;若没有正确配置上述两个 Key,Pinecone 客户端与 OpenAI 客户端将拿到空字符串,索引初始化和检索都会失败。
第 3 步:安装依赖并运行:
npm install npm run dev启动成功后,控制台会输出提示,说明两个 Agent 已就绪,并建议尝试类似这样的提问:
- "What is VoltAgent?"
- "Tell me about vector databases"
- "How does Pinecone work?"
- "What is RAG?"
环境变量一览
| 变量 | 是否必填 | 说明 |
|---|---|---|
PINECONE_API_KEY | 是 | 你的 Pinecone API Key |
OPENAI_API_KEY | 是 | 你的 OpenAI API Key(用于生成嵌入向量与 LLM 调用) |
从源码看,两个 Key 的使用位置分别为 retriever/index.ts(Pinecone 客户端,并额外设置了sourceTag: "voltagent")和同一文件中的 OpenAI 客户端初始化(apiKey: process.env.OPENAI_API_KEY || "")。
入口代码剖析:如何组装一个带检索能力的 Agent
src/index.ts 展示了 VoltAgent 的核心装配流程,依次包含四步:日志、记忆、Agent、服务器。
1. 日志:使用createPinoLogger创建名为with-pinecone的 Pino 日志实例,级别为info。
2. 持久化记忆:示例使用 LibSQL 作为记忆存储,且两个 Agent 共享同一份记忆:
import { Agent, Memory, VoltAgent } from "@voltagent/core"; import { LibSQLMemoryAdapter } from "@voltagent/libsql"; const memory = new Memory({ storage: new LibSQLMemoryAdapter({ url: "file:./.voltagent/memory.db", }), });记忆数据被持久化到本地 SQLite 文件.voltagent/memory.db,这保证了 Agent 重启后仍能保留跨会话的上下文。
3. 两个 Agent:分别演示"直接挂载 Retriever"与"把 Retriever 包装成 Tool"两种模式:
// Agent 1: 自动检索 —— 每次交互自动搜索知识库 const agentWithRetriever = new Agent({ name: "Assistant with Retriever", instructions: "A helpful assistant that can retrieve information from the Pinecone knowledge base using semantic search to provide better answers. I automatically search for relevant information when needed.", model: "openai/gpt-4o-mini", retriever: retriever, memory, }); // Agent 2: 工具化检索 —— LLM 自行决定何时搜索 const agentWithTools = new Agent({ name: "Assistant with Tools", instructions: "A helpful assistant that can search the Pinecone knowledge base using tools. The agent will decide when to search for information based on user questions.", model: "openai/gpt-4o-mini", tools: [retriever.tool], memory, });关键差异在于:
agentWithRetriever通过retriever字段挂载,对应 README 中说的"Automatic retrieval":Agent 在生成回答前会自动把检索结果注入系统消息;agentWithTools通过tools: [retriever.tool]挂载,对应"tool-based retrieval":检索行为变成 LLM 可自主调用的工具函数。
4. HTTP 服务:通过honoServer暴露 Agent 能力,监听3141端口:
new VoltAgent({ agents: { agentWithRetriever, agentWithTools }, logger, server: honoServer({ port: 3141 }), });两种检索模式背后的源码原理
理解两种模式的差异,需要看 VoltAgent 核心包中BaseRetriever的实现。
BaseRetriever:一个实例,两种用法
抽象基类 BaseRetriever 定义在packages/core/src/retriever/retriever.ts。它要求子类实现retrieve(input, options): Promise<string>,并在构造函数中自动完成两件事:
- 自动创建工具:调用
createRetrieverTool生成this.tool,工具名称默认search_knowledge,描述默认为 "Searches for relevant information in the knowledge base based on the query."。因此retriever.tool无需任何额外配置即可直接塞进tools数组; - 绑定方法上下文:将
retrieve显式bind到实例上,保证从对象解构或作为回调传入时this不丢失。
RetrieverOptions(见 types.ts)支持自定义toolName、toolDescription与logger,同时允许子类扩展任意自有配置项。
createRetrieverTool:Retriever 到 AgentTool 的桥接
createRetrieverTool 位于packages/core/src/retriever/tools/index.ts,是"工具化检索"的核心。它用zod定义工具参数query(字符串,描述为 "The search query to find relevant information"),并在execute内调用retriever.retrieve(query, options),同时:
- 把调用方的
options(含logger、userId、conversationId等)完整透传给retriever; - 记录
RETRIEVER_SEARCH_STARTED / COMPLETED / FAILED日志事件; - 支持通过
getObservabilityAttributes()为 OpenTelemetry span 附加检索器属性。
这也解释了为什么tools: [retriever.tool]能直接工作——createRetrieverTool返回的就是标准的AgentTool。
Agent 侧的自动检索流程
当通过retriever字段挂载时,Agent 在生成系统消息阶段调用getRetrieverContext(见 agent.ts):
- 将用户输入(字符串、
UIMessage[]或BaseMessage[])统一归一化为检索器可处理的输入; - 创建名为
retriever.search的 OpenTelemetry 子 span; - 调用
retriever.retrieve(...),并把结果拼装进系统提示词,格式为Relevant Context:\n<检索内容>(见 enrichInstructions)。
因此"自动检索"本质上是在每次生成前,把语义检索结果作为上下文固定注入;而"工具化检索"则由 LLM 根据问题相关性决定是否触发搜索。前者答案更稳定地基于知识库,后者更省 Token、更灵活。
Pinecone 检索器实现详解
retriever/index.ts 是本示例的核心,包含索引管理、文档填充与检索器实现三个部分。
1. 自动索引管理
initializeIndex()函数实现"不存在即创建"的幂等逻辑:
const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY || "", sourceTag: "voltagent", }); const indexName = "voltagent-knowledge-base"; // 先探测索引是否存在 try { await pc.describeIndex(indexName); indexExists = true; } catch (_error) { // 不存在则创建 } if (!indexExists) { await pc.createIndex({ name: indexName, dimension: 1536, // OpenAI text-embedding-3-small dimension metric: "cosine", spec: { serverless: { cloud: "aws", region: "us-east-1", }, }, waitUntilReady: true, }); }随后通过describeIndexStats()检查totalRecordCount:若索引为空,则为样例文档生成嵌入并upsert;若已有数据则直接跳过填充,避免重复写入。
2. 嵌入与写入
文档向量化使用 OpenAI 官方 SDK:
const embeddingResponse = await openai.embeddings.create({ model: "text-embedding-3-small", input: record.metadata.text, });每个文档的metadata包含text(原始内容)、category(分类)、topic(主题)三个字段,这些元数据后续可用于过滤与展示。写入使用index.upsert(recordsWithEmbeddings)批量提交。
3. 语义检索
retrieveDocuments(query, topK = 3)完成查询向量化与相似度搜索:
const searchResults = await index.query({ vector: queryVector, topK, includeMetadata: true, includeValues: false, }); return ( searchResults.matches?.map((match) => ({ content: match.metadata?.text || "", metadata: match.metadata || {}, score: match.score || 0, id: match.id, })) || [] );检索结果被格式化为{ content, metadata, score, id }结构,供后续注入上下文。
4. PineconeRetriever:自定义 Retriever 的标准范式
PineconeRetriever继承自BaseRetriever,其retrieve方法展示了自定义检索器的标准写法:
- 输入归一化:兼容
string与BaseMessage[]两种输入——对消息数组取最后一条,提取其中type === "text"的内容片段拼接为搜索文本; - 执行检索:调用
retrieveDocuments(searchText, 3)取 Top-3 结果; - 写入引用:若调用方传入
options.context(一个Map),则将结果映射为{ id, title, source, score, category }并context.set("references", references)——这正是 README 中"Source Tracking"的来源,运行时可通过context.get('references')查看使用了哪些文档及对应分数; - 格式化输出:将每条结果拼接为带
Document N (ID: ..., Score: ..., Category: ...)前缀的文本块,交给 LLM 作为Relevant Context;无结果时返回 "No relevant documents found in the knowledge base."。
文件末尾export const retriever = new PineconeRetriever();导出单例,供入口文件直接引用。
自定义:接入你自己的知识库
添加自有文档
修改 retriever/index.ts 中的sampleRecords数组即可替换/扩充知识库:
const sampleRecords = [ { id: "your_doc_1", metadata: { text: "Your document content here...", category: "your_category", topic: "your_topic", }, }, // Add more documents... ];values字段无需手填(源码中置空),示例会在初始化阶段用 OpenAI 嵌入模型自动生成。注意:由于索引在totalRecordCount === 0时才填充,若要强制重新灌入新文档,需要先清空或更换索引。
调整索引配置
索引创建参数同样在 retriever/index.ts 中配置:
await pc.createIndex({ name: indexName, dimension: 1536, // OpenAI text-embedding-3-small dimension metric: "cosine", // or 'euclidean', 'dotproduct' spec: { serverless: { cloud: "aws", // or 'gcp', 'azure' region: "us-east-1", // choose your preferred region }, }, waitUntilReady: true, });关键参数说明:
dimension:向量维度,必须与嵌入模型输出维度一致。示例使用 OpenAItext-embedding-3-small(1536 维),更换嵌入模型时必须同步修改;metric:相似度度量方式,cosine(余弦相似度,对向量模长不敏感,语义检索最常用)、euclidean(欧氏距离)、dotproduct(点积)三选一;spec.serverless:Serverless 部署配置,cloud可选aws/gcp/azure,region选择就近区域以降低延迟;waitUntilReady:设为true时阻塞等待索引就绪后再继续写入。
检索行为调优
retrieveDocuments的topK参数(默认3)控制每次注入上下文的文档条数,可根据知识库规模与 Token 预算调整;index.query还可补充filter条件利用metadata做元数据过滤(如按category限定检索范围),README 中提到的"Metadata filtering capabilities"即指此能力。
运行验证与排查建议
启动npm run dev后:
- 首次运行时控制台应依次出现索引探测、索引创建/填充日志(
Creating new index...、Populating index with sample documents...、Successfully upserted N documents); - 向两个 Agent 提问 "What is RAG?" 这类与知识库强相关的问题,对比自动检索与工具化检索的行为差异;
- 通过
context.get('references')查看命中文档的id与score,验证来源追踪是否生效。
常见问题排查方向:
- 索引初始化失败:检查
PINECONE_API_KEY与OPENAI_API_KEY是否正确写入.env,并确认 Pinecone 控制台已开通对应云与区域; - 检索结果为空:确认索引中
totalRecordCount大于 0;若使用新索引,可删除索引后重启示例触发重新填充; - 维度不匹配:更换嵌入模型后需同步修改
createIndex的dimension,否则upsert会报错。
延伸阅读
- 核心抽象 BaseRetriever 与 Retriever 类型定义;
- 工具化桥接 createRetrieverTool;
- Agent 侧自动注入检索上下文 getRetrieverContext;
- 核心框架测试 retriever.spec.ts(验证默认工具名
search_knowledge、自定义toolName/toolDescription及tool属性暴露); - 同仓库内还有更多检索相关示例可供对照,例如 examples/with-retrieval、examples/with-voltops-retrieval,以及独立封装的 packages/rag。
总结
本示例用最小的代码量呈现了 VoltAgent × Pinecone 的完整 RAG 落地路径:一条PineconeRetriever(继承BaseRetriever)同时支撑"自动检索"与"工具化检索"两种模式,配合自动索引管理、OpenAI 嵌入、引用追踪与持久化记忆,构成了一个可直接扩展为生产级知识问答 Agent 的骨架。理解BaseRetriever/createRetrieverTool/ Agent 系统消息注入这三层机制后,你可以轻松将其替换为任意向量数据库或检索后端。
- 人工智能
- AI Agent
- Agent 框架
- 后端
- 多智能体
- RAG
- 工具调用
- Agent 记忆
【免费下载链接】voltagent
AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework
相关推荐
LangChain.js 集成 Pinecone 向量数据库:@langchain/pinecone 完整实战指南
LangChain.js 集成 Pinecone 向量数据库:@langchain/pinecone 完整实战指南 @langchain/pinecone 是
人工智能大模型AI AgentAI 应用RAG工具调用Wallaby错误处理终极指南:如何快速调试和优化测试用例
Wallaby错误处理终极指南:如何快速调试和优化测试用例 Wallaby是Elixir生态系统中强大的并发浏览器测试框架,专为Web应用测试设计。然而,即使是
测试质量保障向量数据库双雄对决:Pinecone与Weaviate Java集成实战指南
向量数据库双雄对决:Pinecone与Weaviate Java集成实战指南 你是否在Java项目中纠结于向量数据库 Vector Database 的选型?面
文档知识库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考