跳转到主要内容
当您想要的不止是单个搜索结果或一个快速的模型回答时,研究 agent 会很有用。一个好的研究 agent 可以将广泛的主题转换为搜索查询、收集来源、提取重要证据、跟进漏洞,并撰写您可以事后检查的带引用简报。 在本教程中,我们将使用 Python 和 Venice API 构建一个私有研究 agent。最后,您将拥有一个 CLI,它可以研究主题、将公开页面抓取为 Markdown、汇总源 chunk、运行漏洞感知的后续研究 pass,并生成带引用的报告及可选的本地 JSONL 工件。 对完整代码实现感兴趣?请查看 GitHub 仓库 在继续之前,您需要一个 Venice API 密钥:

我们要构建什么

参考实现是一个小型 Python 项目,包含几个明确的部分: 流程如下: 私有研究 agent 流水线
  1. 让 Venice 为该主题生成多样化的搜索查询。
  2. 使用一个或多个提供商搜索 web。
  3. 在读取之前对 URL 进行去重。
  4. 使用 Venice 的 scrape 端点将每个公共源页面转换为 Markdown。
  5. 将长页面拆分为 chunk。
  6. 让 Venice 从每个 chunk 中提取证据。
  7. 让 Venice 将 chunk 证据转换为源注释。
  8. 在生成后续查询之前识别研究漏洞和源平衡问题。
  9. 让 Venice 综合最终报告,并附带脚注式引用。
这是”私有”的实际意义在于 agent 将编排、源注释、工件和最终报告保留在您的机器上。Venice 通过其 API 处理模型调用和抓取。默认的参考实现仍然将搜索查询发送到 DuckDuckGo 或 arXiv,因此将提供商选择视为您的隐私设计的一部分。

设置项目

参考项目使用 Python 3.13 和 uv,但相同的代码也可以与普通的虚拟环境一起使用。 创建一个新项目:
安装依赖项:
如果您喜欢 pip,请创建虚拟环境并安装相同的包:
为本地开发创建 .env 文件:
我们使用 VENICE_MODEL 以便您可以在不编辑代码的情况下更改模型。参考实现当前默认为 openai-gpt-55,但您可以将其换为您的 Venice 账户可用的另一个聊天模型。

创建数据模型

在编写 agent 逻辑之前,我们将定义流经流水线的对象。这些模型使代码的其余部分更容易推理,因为每个源都携带来源信息:它来自哪里、哪个查询找到了它、什么时候获取的、以及它是如何被分块的。 创建 research_agent/models.py
这里重要的字段是 canonical_urlcontent_hashchunks canonical_url 让 agent 避免在搜索结果仅在跟踪参数或片段上不同的情况下重复读取相同的源。content_hash 帮助即使页面位于不同的 URL 也能捕获重复页面。chunks 让我们将长页面汇总为较小的部分,而不是因上下文限制而丢失有用的证据。 在数据类下方添加辅助函数:
此处的分块故意简单:固定大小的字符 chunk 加重叠。对于演示研究 agent 来说,这已经足够,因为 Venice 的 scrape 端点返回 Markdown,通常比原始 HTML 干净得多。对于长技术文档的生产研究,您可以通过按标题、段落或 token 数拆分来改进它。

构建 Venice 客户端

接下来,我们将创建一个小型 Venice 客户端。由于 Venice 与 OpenAI 兼容,您可以使用 OpenAI Python SDK 进行聊天补全,但参考实现直接使用 httpx,以便相同的客户端可以调用 Venice 的 POST /augment/scrape 端点。 创建 research_agent/venice.py
from_env() 辅助函数将密钥保持在源代码之外。它还使本地开发方便,因为 python-dotenv 可以从 .env 加载 VENICE_API_KEYVENICE_MODEL 现在添加聊天补全:
对于最终报告,我们希望使用流式传输,因为深度报告可能花费显著更长的时间(因为它会产生更多文本)。这可能会导致需要极长时间才能产生最终输出的请求出现超时问题。通过使用流式传输,我们可以消除此问题,并使请求更能抵抗超时失败:
然后添加抓取:
Venice 的 scrape 端点接受公开可访问的 URL 并将该页面作为 Markdown 返回。这意味着模型不需要解析原始 HTML,您的源提取 prompt 可以使用更干净的文本。 其余的辅助处理重试和响应解析:
完整仓库还包括一个健壮的 _post_chat_stream() 辅助函数,它从流式聊天补全读取服务器发送事件。您可以先不使用流式传输开始,然后在研究流程的其余部分工作后再添加它。

添加搜索提供商

搜索层有两个工作:找到源 URL 并通过 Venice scraper 获取这些 URL。参考实现使用 DuckDuckGo 的 HTML 端点进行一般网页搜索,使用 arXiv 的 Atom API 进行论文搜索。 创建 research_agent/web.py
现在添加 DuckDuckGo:
以及 arXiv:
WebSearch 类协调提供商和获取页面:
完整的参考实现添加了重试、主机级请求延迟和更友好的错误。这些值得保留,因为研究 agent 花费大量时间处理阻止自动化的页面、意外重定向或返回临时错误。 在底部添加小型提供商辅助函数:

写入本地工件

对于研究工作流,可审计性很重要。如果最终报告说了一些令人惊讶的事情,您应该能够检查哪个源导致了它。 创建 research_agent/artifacts.py
这每行写入一个 JSON 对象,这使得工件易于附加、检查并稍后使用命令行工具处理。

构建研究 Agent

现在我们有了 Venice、搜索、模型和工件,我们可以构建实际的 agent。 创建 research_agent/agent.py
系统 prompt 是核心行为护栏。我们不希望模型从记忆中产生听起来令人印象深刻的报告。我们希望它使用源材料,并在证据不足时指出不确定性。 如果尚未添加,我们还需要在 models.py 中添加两个最终数据类:
接下来,定义 ResearchAgent
run() 方法协调研究 pass:
两个 seen_* 集合是防止 agent 在重复源上浪费时间的方式。URL 去重捕获重复链接。内容哈希去重捕获镜像、联合发布的帖子和重定向到相同最终内容的页面。

规划初始和后续搜索

第一个模型调用将主题转换为搜索查询:
在每次研究 pass 之后,更新的 agent 进行更深思熟虑的漏洞分析步骤。它查看当前注释、按域名计算源集群、询问 Venice 缺少什么覆盖、将这些漏洞写入工件,然后使用结果查询进行下一次 pass。 漏洞分析循环 从跟踪源平衡开始:
这为 agent 提供了一种简单的方法来注意到源集群占领。如果每个源都来自一家公司、一个框架或一个域名,后续查询应该有意扩展源集,而不是收集更多相同的内容。 现在在创建后续搜索时使用该平衡信息:
更新的参考实现将其包装在 _gap_follow_up_queries() 中,它要求 Venice 同时返回漏洞记录和查询:
启用 --artifacts 时,这些记录被写入 research_gaps.jsonl。这为您提供了 agent 为何搜索特定第二 pass 查询的有用审计线索。 解析器应宽容。如果模型返回格式错误的 JSON,agent 会回退到原始主题:
这种模式值得贯穿 agent 代码使用:要求结构化输出、解析它,并在输出不可用时提供简单的回退。

读取和汇总源

现在我们收集源注释。agent 搜索每个查询、通过 Venice scrape 获取每个结果、对 Markdown 进行分块,并汇总有用的证据。
个别搜索和获取失败不应停止整个运行。公共 web 是混乱的。某些页面阻止抓取、某些返回 PDF、某些已关闭,而某些重定向到意外的地方。研究 agent 应该继续移动并记录失败的内容。 以下是源读取方法:
对于每个源 chunk,向 Venice 请求简短的证据摘要和精确引用:
然后将 chunk 摘要折叠为源注释:
这两步汇总是使 agent 比基本的”汇总这些 URL”脚本感觉更可靠的部分。模型首先读取源 chunk,然后从那些提取的证据片段中编写源级注释。

撰写最终报告

一旦 agent 有了源注释,就可以撰写报告。从单 pass 报告编写器开始:
参考实现针对深度报告做了更多:它向 Venice 请求大纲、分别起草每个报告部分,然后请求最终编辑 pass 组装完成的报告并将内部源 ID 转换为脚注式引用。 当您希望长篇研究输出时,那种分阶段方法很有用,因为一个巨大的 prompt 通常会压缩太多。更新的 prompt 还将报告推向广泛的、源支持的调查而不是薄薄的决策指南。如果源基础偏向一个集群,编辑器 prompt 会告诉 Venice 承认这种偏差并避免将其呈现为整个领域的代表。 添加摘要辅助函数:
最后,添加错误记录:
此时,核心研究循环已就位。

添加 CLI

现在我们需要一个命令行入口点。创建 main.py
CLI 暴露您在研究期间实际会调整的旋钮: 现在将所有内容连接起来:
这为我们提供了一个工作的本地研究 CLI。

运行 Agent

运行快速研究 pass:
将报告写入 Markdown 文件:
使用更多源和多个提供商:
选择最终报告样式:
使用 brief 获得简洁的源支持简报,standard 获得更完整的调查,deep 获得分阶段大纲/部分/编辑器工作流。 保存可审计的工件:
启用工件时,您将看到如下文件:
当您想了解 agent 如何得出结论时,这些文件很有用。例如,source_notes.jsonl 显示汇总的源证据,research_gaps.jsonl 显示生成后续搜索的原因,errors.jsonl 显示在搜索、抓取或汇总期间失败的页面。

隐私和可靠性注意事项

研究 agent 涉及多个系统,因此精确说明数据流向是有帮助的: 私有研究 agent 数据边界 如果您希望将更多搜索路径保留在 Venice 内,您可以调整提供商层以调用 Venice 的 POST /augment/search 端点,而不是直接查询 DuckDuckGo。参考实现使用轻量级公共提供商,使演示易于运行和理解。 为了可靠性,保持这些默认值保守:
  • 对 Venice 调用和 web 请求使用重试。
  • 如果您从同一主机读取许多页面,添加小的 --request-delay
  • 限制 --max-sources,使广泛的主题不会无限期运行。
  • 为重要报告保存 --artifacts,以便您可以审计最终输出。
  • 将报告视为简报,而不是地面真相。当准确性重要时,沿着引用追溯到原始源。

测试各部分

您不需要实时 web 请求或 Venice 调用即可测试大多数系统。参考仓库使用假 Venice 和假 web 类来测试研究循环、去重行为、工件和报告 prompt。 有用的第一个测试是 URL 规范化:
然后测试重复内容被跳过:
假实现使 agent 测试更快、不易出错。您可以验证编排逻辑,而不依赖实时搜索结果、网络条件或模型输出。

基准测试

许多 AI 提供商现在都有自己的深度研究工作流,因此参考仓库包括针对 Perplexity 的 Deep Research 工具的简单基准测试。两个 agent 都被要求撰写关于 AI agent 框架架构的报告,然后将生成的报告检入 GitHub 仓库 这不是一个正式的基准测试。它是一种实用的方式来检查报告结构、源覆盖、引用质量,以及 agent 是否过度关注一个源集群。这也是为什么更新的实现在后续搜索之前跟踪 research_gaps.jsonl 和源平衡的原因。

扩展此示例

一旦基线 agent 工作,以下是改进它的实用方法:
  • 使用 POST /augment/search 添加 Venice 搜索提供商。
  • 将报告和工件存储在小型 SQLite 数据库中,而不是 JSONL 文件中。
  • 为受信任的研究域名添加源允许列表或阻止列表。
  • 通过将 Venice scrape 与文档解析相结合,为不公开干净 HTML 的源添加 PDF 支持。
  • 添加主题和预期源类型的评估集,以便您可以比较 prompt 更改后的研究质量。
  • 添加一个审查步骤,要求 Venice 在保存之前在最终报告中查找未支持的声明。
最大的升级通常是更好的源选择。查询生成有帮助,但您也可以通过优先选择主要来源、标准文档、官方文档、论文、变更日志和数据集页面而不是低信号摘要来提高质量。

收尾

感谢阅读!希望这能帮助您使用 Python 和 Venice API 构建一个实用的私有研究 agent。 这里有用的模式不仅是”让模型研究某事”。它是将研究分解为可审计的步骤:规划搜索、收集源、提取证据、撰写源注释、跟进漏洞,并附带引用进行综合。通过保持这些步骤明确,我们获得了一个更容易随时间检查、测试和改进的研究工作流。