首页 / 文章 / 如何在 Hermes Agent 中使用 DigitalOcean 知识库添加 RAG
← 返回
IT技术

如何在 Hermes Agent 中使用 DigitalOcean 知识库添加 RAG

✍️ zhirenhun 📅 2026/8/23 👁 201 阅读 ⏱ 57 分钟
如何在 Hermes Agent 中使用 DigitalOcean 知识库添加 RAG

Hermes 有记忆但没有外部语料检索。本指南建立这种连接——并证明它在模型无法伪造的语料库上有效。

这是 Hermes + DigitalOcean 系列的第 2 部分。第 1 部分:如何在 DigitalOcean 上运行 Hermes Agent


本教程内容:将 Hermes Agent 连接到 DigitalOcean 知识库

大多数 RAG 教程从未证明检索确实发生了。他们搭建一个管道,向模型提问,得到正确答案,然后宣布胜利——而未检查模型是否仅凭通用知识就能回答该问题。如果能够,那么演示毫无意义。

本教程采取相反的方法。我们先编写测试问题 首先,验证模型在未附加检索时会失败(该失败记录作为我们的负对照),随后才构建管道并重新运行这些问题。到最后,您将获得三项成果:一个可用的检索设置、证明其有效的证据,以及一种可在任何 RAG 堆栈上复用的验证方法——而不仅限于此示例。

在任何其他说明之前,先澄清这一点,因为这是关于 Hermes 最常见的误解:Hermes 并不缺少记忆。 它随附代理策划的记忆文件、对过去会话的 FTS5 全文搜索、跨会话回忆以及技能。如果您按照第 1 部分操作,您已经看到它能够跨对话记住事物。Hermes 并未随附的是 您自有外部语料的检索 —— 您的运行手册、内部文档、产品知识。记忆涉及对话。检索涉及您的文档。它们是不同的问题,而本教程解决了第二个问题。

我们将构建的具体用例:一个值班代理,能够从您实际的运行手册中回答“错误 FM-4419 的含义是什么,我应该呼叫谁?”,而不是仅根据错误的形状进行模式匹配。

您将获得:

  • 一个 DigitalOcean 知识库,索引自一个 对象存储
  • 大约60行的 Hermes 插件,用于调用 Knowledge Base Retrieve API
  • 一个经过验证的检索循环,其中每个答案都引用其源文件
  • 一个负控转录本,证明你的测试语料库确实无法猜测

一切 — 示例语料库、插件源代码和测试问题 — 都位于 配套仓库,因此您可以跟随学习或直接跳过。

范围说明:此设置旨在在可用语料库上实现检索的 correct。将其扩展到数百万份文档 — — 高吞吐嵌入、负载下的检索延迟 — — 是另一个架构问题,也是另一篇文章。

先决条件

  • 一个 DigitalOcean 账户
  • Hermes 已安装并连接到 DigitalOcean 推理(在 第 1 部分 中介绍)
  • 具有 genai:read 权限的 DigitalOcean 个人访问令牌
  • Spaces 访问密钥
  • doctl 已安装并经过身份验证
  • 熟悉使用 Python — 您将编辑四个简短的文件

本教程使用的版本: Hermes Agent v0.20.4 (2026.8.18),模型 slug 为 deepseek-v4-pro-0813qwen3.8-max 在 DigitalOcean 无服务器推理上。Hermes 更新很快,DigitalOcean 会淘汰旧的模型 slug,因此如果下面的命令失败,请先查看 故障排除部分,再假设教程有问题。


为什么 Hermes 需要外部检索(以及为什么其内存不够)

以下三种看似解决方案的方法,以及它们为什么不行:

上下文窗口无法解决此问题。 您不能在每轮对话中粘贴每份运行手册。即使语料库从技术上可以容纳,塞入上下文也会降低成本并削弱模型对重要部分的注意力。

网页搜索无法解决此问题。 您的运行手册不在网络上。如果它们在网络上,那么您就有其他问题。

Hermes 自身的记忆无法解决此问题。 记忆会回忆 您的对话。它会记得您上周二讨论了 FM-4419。如果没有人告诉它 FM-4419 的含义,它就不会知道。

您真正需要的是一个能够自行判断何时需要调用语料库的代理,仅获取相关的文本块,并准确告知您这些块来源于哪些文档。这就是我们正在构建的循环。


Hermes、知识库和无服务器推理如何协同工作

完整的链条如下所示:

Spaces (source docs)
  → Knowledge Base (chunk + embed + index)
    → Retrieve API (kbaas.do-ai.run)  ← the Hermes plugin calls this
      → Hermes agent loop
        → DigitalOcean Serverless Inference (the model)

演示中 Hermes 与 Knowledge Base RAG 使用的操作顺序的 SVG

在你构建任何东西之前值得做的修正:无服务器推理并不知道知识库的存在。 检索和完成是两个完全独立的 API 调用。如果你的心智模型是“我会把我的推理端点指向我的知识库”,你就会撞墙,因为根本没有东西可指向。本教程中我们构建的插件就是将它们连接起来的东西:它从 Retrieve API 获取数据块,并将其放入代理的上下文中,随后代理的下一次推理调用会基于这些数据块进行推理。

何时使用此方法——以及何时不使用

在以下情况下使用 Retrieve API + 插件方法(本教程):

  • 您希望返回原始数据块,并在代理的控制之下——而不是第二个模型的完成被包装在您的工具调用内部
  • 您需要将检索参数(alpha、结果数量、格式)在代码中确定性地固定
  • 您正在运行自己的代理循环,并且关心每次检索每轮注入多少个标记

改用托管的替代方案 — 将知识库附加到代理并使用 include_retrieval_info 调用其端点 — 何时:

  • 您希望有一个单一的托管端点,返回检索增强的完成结果
  • 您不在运行自己的代理循环,也不需要 chunk 级别的控制

在 MCP 服务器处停止(选项 A,下面何时:

  • 您希望零代码,且默认的检索行为已经足够好。此路径确实可行,一些读者应该采用它。

值得提出本教程跳过的前置问题:使用托管的 Knowledge Base,还是自建向量存储?自己运行 Qdrant、pgvector 或 Pinecone 可以获得对索引的控制——你可以选择嵌入模型、自行分块和实现混合搜索、跨云迁移——以此换取对摄取管道及其背后数据库的所有权;而 DigitalOcean Knowledge Base 会自动处理分块、嵌入、混合检索以及可选的重排序,并在你未提供时自动配置和调整底层 OpenSearch 数据库的规模。我们在这里采用托管方案,因为目标是验证检索的正确性,而不是运营向量数据库——但下面的插件模式并不关心 HTTP 调用的另一端是什么,因此如果你已经在 DO 托管的 Postgres 上运行 pgvector,只需替换 URL,其余保持不变。


如何构建能证明检索正常工作的测试语料库

这是大多数 RAG 教程会跳过的部分,也是大多数 RAG 演示无法证明任何东西的原因。

设计原则:如果一个称职的模型能够凭借通用知识回答你的测试问题,那么你的测试就是毫无价值的。 向前沿模型提问 “HTTP 503 是什么意思,我应该检查什么?” 它会在不进行任何检索的情况下给出一个好答案。即使你的管道完全损坏,演示也仍然看起来完美。

因此,语料库必须是 不可猜测的。工作流程,按顺序如下:

  1. 先编写测试问题。 在创建任何文档之前。
  2. 在不附加任何检索的情况下,用模型跑这些问题并保存转录。 模型应该会失败——拒绝回答、含糊其辞或询问信息所在。这个失败的转录是你的负面对照。如果模型在这里正确回答了,说明你的问题可以被猜到,需要重新编写。
  3. 只有在这之后 才构建语料库和管道,然后重新运行相同的问题。

这个三步法并不特定于 Hermes 或 DigitalOcean。它适用于任何 RAG 堆栈,并且是“我的演示产生了一个合理答案”与“我有证据表明检索确实发生了”之间的区别。

示例语料库

伴随仓库包含八个 Markdown 文件,记录了一个虚构的支付平台,该平台有四个服务:atlas-ingestledger-corepayouts-servicewebhook-relay。语料库故意植入了模型无法知道的内容:

  • 虚构的服务名称和自定义错误码。 FM-4419 在此语料库中具有特定含义,而在其他地方则没有。
  • 一个有名称的值班轮换 (Riverbend) 具有明确的范围——它覆盖哪些服务以及更重要的是它不覆盖哪些服务。
  • 特定版本的行为变更。 变更日志条目记录了租约超时从 30s 改为 90s,以及此变更为何改变了持续错误爆发的含义。
  • 故意的交叉引用,因此至少有一个测试问题只能通过结合两到三份文档才能回答。
  • 一个故意的范围陷阱。 payouts-service 的错误在整个语料库中出现,但升级文档明确指出它们不属于 Riverbend 值班轮换的责任。这用于测试模型是读取其检索到的内容,还是仅凭错误前缀进行自由联想。

我们不会在这里粘贴所有八个文件——从 仓库 获取。结构比内容更重要:当你为自己的验证构建语料库时,窃取其中的 模式(不可猜测的细节、交叉引用、范围陷阱),而不是文件本身。

八份文档的语料库故意保持较小。这里的目标是证明正确性。将摄取和检索扩展到数百万份文档会引入不同的限制,并且值得单独处理。


如何从 Spaces 存储桶创建 DigitalOcean 知识库

将语料库上传到 Spaces

Spaces 存储桶创建页面

创建一个 Spaces 存储桶并在文件夹前缀下上传语料库。类似 runbooks/ 的前缀效果很好。

前缀不仅仅是装饰。知识库会将每个文档的 file_id 存储为 {bucket}/{object_key} 的形式,因此统一的前缀(runbooks/)使你以后能够通过元数据过滤将检索范围限定在存储桶的子集上。如果你预计同一个存储桶中会有多个团队或文档类型,请现在就确定好前缀。

创建知识库并开始索引

知识库创建页面

在控制面板:数据服务 → 知识库 → 创建。选择一个地区和嵌入模型,将你的 Spaces 存储桶作为数据源添加,然后开始索引。

在点击创建之前,你需要了解以下两点:

索引是一个会消耗 token 的计费作业。 每个文档都会被分块并嵌入,你需要为嵌入的 token 付费。对八个 Markdown 文件来说这微不足道;但对于真实的语料库,在索引之前你需要清楚自己要索引什么。这也不是即时的——即使是小型语料库,索引作业也可能需要几分钟。

分块策略是按数据源设置的,更改它意味着删除然后重新添加该数据源——这会触发完整的重新索引并产生新的嵌入费用。请提前慎重选择。基于章节的分块(按 Markdown 标题分割)和固定长度分块是可预测成本的选项,而基于章节的分块更适合已经具有有意义标题结构的运行手册式文档。

如何查找你的知识库 UUID

你在每次 API 调用时都需要知识库的 UUID,而在 GUI 中它并不显眼。可以在以下三个位置获取:

  1. 知识库详情页的浏览器 URL —— UUID 是路径段
  2. doctl gradient knowledge-base list
  3. 检索端点 选项卡中的自动生成代码片段,其中内联包含了它

现在导出它,以及你的令牌:

export DO_API_TOKEN="dop_v1_..."   # PAT with genai:read — not a Spaces key
export KB_UUID="your-kb-uuid"

如何直接测试知识库 Retrieve API

在使用 Hermes 前,先单独验证检索功能是否正常。若检索出现问题,最好现在就发现,而不是在调试包含其他四个移动部件的代理循环时才察觉。

最快的检查方法是打开知识库控制面板的 Retrieve 选项卡:使用 num_results: 5alpha: 0.5 运行测试查询,即可看到返回的文本块及其相关性得分和来源元数据。

知识库 RAG 的有效测试

The Retrieve Endpoint 选项卡更进一步:它会根据当前知识库和设置生成一个可直接使用的 curl 命令。原始调用示例如下:

curl -X POST "https://kbaas.do-ai.run/v1/$KB_UUID/retrieve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $DO_API_TOKEN" \
  -d '{
    "query": "what does FM-4419 mean",
    "num_results": 5,
    "alpha": 0.5
  }'

理解响应的结构很重要,因为插件依赖于它:

{
  "results": [
    {
      "text_content": "FM-4419 indicates a single-shard lease expiry in ledger-core...",
      "metadata": {
        "item_name": "ledger-core-errors.md",
        "page_number": 1
      },
      "score": 0.87
    }
  ],
  "total_results": 5
}

最重要的字段是 metadata.item_name。它是源文件名,也是后续引用的全部基础:如果它能从此响应通过插件进入模型的上下文,模型就能标注其来源。如果在格式化过程中丢失了该字段,无论怎样提示,引用都不可能实现。

关于 alpha,这个最不明显的参数:它设置了词法检索和语义检索之间的平衡。0 表示仅关键词,1 表示仅语义,介于之间的值会执行混合搜索。DigitalOcean 的指导是将混合作为默认,并从 0.5–0.7 范围开始 — 纯语义会偏离精确查询,纯关键词会错过同义词。

我们的测试问题涵盖了两个方向,这正是有趣的地方。“FM-4419 代表什么”是一个精确的标记查找,正是 DO 文档建议将 alpha 调整到 0 的产品代码查询类型。“如果它一直发生,我应该叫谁”是一种对话式查询,需要基于语义的匹配。一个能够同时处理这两种查询形式的语料库正是混合搜索所针对的场景,因此我们在插件中固定 alpha 为 0.5,而不是让模型在每次调用时自行选择。如果你的语料库主要由标识符组成 — 如错误码、SKU、工单号 — 请调低 alpha;如果是散文文本,则调高 alpha。

关键点:直接在此端点上运行测试问题。如果这里返回的不是正确的块,无论怎样进行 Agent 工程都无法解决 — 先修复语料库或分块策略,然后再继续。


将 Hermes 连接到知识库的两种方式:MCP 服务器 vs. 自定义插件

有一条零代码路径和一条约 60 行代码的路径。先说老实话:零代码路径是可行的,有些读者应该直接采用它并停止。

选项 A:DigitalOcean MCP 服务器(无代码)DigitalOcean 提供了一个 MCP 服务器 它包含 Knowledge Base 工具,而 Hermes 直接从 config.yaml 注册 MCP 工具,无需涉及 Python。如果你在第 1 部分设置了 MCP 服务器,这一步骤与此相同:

# ~/.hermes/config.yaml
mcp_servers:
  digitalocean:
    command: npx
    args: ["-y", "@digitalocean/mcp"]
    env:
      DIGITALOCEAN_ACCESS_TOKEN: ${DO_API_TOKEN}

重启 Hermes,知识库工具将与其内置工具一起出现。这确实有效。如果您只需要默认的检索行为,且不需要控制参数或格式,请在此停止。

选项 B:自定义 Hermes 插件(约 60 行)

您为何仍要编写此插件 — — 以及为何它是您在生产环境中所需要的:

  • 对模型可见内容的控制。 您编写工具描述,而该描述决定了模型何时调用该工具。使用 MCP 时,您将获得服务器提供的任何描述。
  • 固定的检索参数。 alphanum_results 在您的代码中保持为常量,而不是每次调用由 LLM 选择。允许模型调节检索会导致运行不具确定性 — — 同一问题在不同运行中可能检索到不同的结果。
  • 对结果格式的控制。 您决定块如何进入提示,这是每轮 token 成本的主要杠杆。
  • 无需额外进程。 插件是进程内 HTTP 调用,而非您必须保持运行的服务器。
  • 它是一个模板。 任何 HTTP API 都可通过相同的四文件模式成为 Hermes 工具。

MCP 是快速路径。插件是生产路径。本教程的其余部分将构建该插件。


如何为知识库检索构建 Hermes 插件

插件由四个文件组成:

~/.hermes/plugins/do-knowledge-base/
├── plugin.yaml      # manifest — what this is, what it needs
├── __init__.py      # register() — wires schema to handler
├── schemas.py       # what the LLM sees
└── tools.py         # what runs

plugin.yaml:声明清单文件及所需凭据

name: do-knowledge-base
version: 1.0.0
description: Retrieval over a DigitalOcean Knowledge Base
provides_tools: true
requires_env:
  - name: DO_API_TOKEN
    description: DigitalOcean personal access token with the genai:read scope
    url: https://cloud.digitalocean.com/account/api/tokens
    secret: true
  - name: KB_UUID
    description: UUID of the target Knowledge Base
    url: https://cloud.digitalocean.com/gen-ai/knowledge-bases

值得突出的部分是 requires_env。在此声明您的凭据 — — 包含描述和 URL — — Hermes 会在安装时提示您输入这些凭据,并将其写入 ~/.hermes/.env。将令牌标记为 secret: true 会掩盖输入。这比在处理器中手动编写环境检查要好得多,也是使插件能够由非你本人的人安装的原因。

schemas.py: 为什么工具描述决定了模型何时调用您的工具

description 字段是产品。它是模型在决定您的工具是否与当前问题相关时唯一读取的内容。描述模糊,工具未被使用。

KNOWLEDGE_BASE_RETRIEVAL_SCHEMA = {
    "name": "knowledge_base_retrieval",
    "description": (
        "Search the team's operational runbooks for the payments platform "          # names the domain
        "(atlas-ingest, ledger-core, payouts-service, webhook-relay). "
        "Use this for questions about error codes in the FM-#### format, "            # names the query format
        "escalation and on-call routing, service configuration, and "
        "version-specific behavior changes. "
        "Do NOT use this for general programming questions or anything "               # negative space
        "unrelated to the payments platform. "
        "Always cite the source file names returned in the results."                   # citation instruction
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "The question or search terms to retrieve documents for",
            }
        },
        "required": ["query"],
    },
}

每个条款都有其存在的理由:第一个命名领域,第二个命名错误代码格式(这样模型就能识别 FM-4419 为在范围内),第三个划定负空间(即 应该用该工具做什么),最后一个指示引用。

注意模式刻意不暴露的内容:alphanum_results、过滤器。这些是 tools.py 中的常量,而不是模型可控参数。这是选项 B 中的确定性论点的具体体现。

tools.py: 处理程序以及如何为代理循环格式化检索结果

来自 Hermes 插件文档的四条规则,每条都值得内化:

  1. 处理程序签名是 (args: dict, **kwargs) -> str
  2. 始终返回 JSON 字符串, 永远不要返回字典
  3. 永不抛出 — 捕获所有异常并返回错误 JSON 而不是
  4. 接受 **kwargs 以保持与未来 Hermes 版本的前向兼容性
import json
import os
import urllib.request

_ALPHA = 0.5
_NUM_RESULTS = 5
_MAX_CHUNK_CHARS = 1200
_RETRIEVE_URL = "https://kbaas.do-ai.run/v1/{kb_uuid}/retrieve"


def knowledge_base_retrieval(args: dict, **kwargs) -> str:
    try:
        query = args["query"]
        url = _RETRIEVE_URL.format(kb_uuid=os.environ["KB_UUID"])
        payload = json.dumps({
            "query": query,
            "num_results": _NUM_RESULTS,
            "alpha": _ALPHA,
        }).encode()
        req = urllib.request.Request(
            url,
            data=payload,
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {os.environ['DO_API_TOKEN']}",
            },
        )
        with urllib.request.urlopen(req, timeout=15) as resp:
            data = json.load(resp)

        results = data.get("results", [])
        if not results:
            return json.dumps({
                "results": [],
                "note": "No relevant documents found in the knowledge base "
                        "for this query. Do not answer from general knowledge; "
                        "say the runbooks do not cover it."
            })

        formatted = [
            {
                "source": r.get("metadata", {}).get("item_name", "unknown"),
                "page_number": r.get("metadata", {}).get("page_number"),
                "excerpt": r.get("text_content", "")[:_MAX_CHUNK_CHARS],
            }
            for r in results
        ]
        return json.dumps({"results": formatted})

    except Exception as e:
        return json.dumps({"error": f"knowledge base retrieval failed: {e}"})

有趣的部分不是 HTTP 调用 —— 而是格式化,因为格式化决定了引用的成败:

  • 每个块映射到 {source, page_number, excerpt}, 所以 item_name 会在模型的上下文中以一个无歧义的键存活下来。这是使“始终引用你的来源”变得可实现而非仅仅是愿望的管道。
  • 摘录将在 _MAX_CHUNK_CHARS 处截断。 这里的每个字符都会在每次检索轮次中被注入到提示中;这个常量是你的 token 成本杠杆。
  • 空结果返回显式消息,而不是空结构。 空的 [] 会邀请模型用通用知识填补空白。明确的 “runbooks 不涵盖此内容” 指令则给它提供了可以说的话。

__init__.py: 注册工具

from .schemas import KNOWLEDGE_BASE_RETRIEVAL_SCHEMA
from .tools import knowledge_base_retrieval


def register(ctx):
    ctx.register_tool(
        "knowledge_base_retrieval",
        "do-knowledge-base",
        KNOWLEDGE_BASE_RETRIEVAL_SCHEMA,
        knowledge_base_retrieval,
    )

就这样。ctx.register_tool(name, toolset, schema, handler) 它把模型看到的 schema 绑定到实际运行的处理程序。

调节旋钮:_ALPHA_MAX_CHUNK_CHARSnum_results

这三个常量被刻意放置在 tools.py 文件的顶部。它们共同控制答案质量以及每轮注入的 token 数量:更多的结果和更长的摘录会为模型提供更多素材,同时每次检索的成本也更高;alpha 参数在精确匹配和语义查询之间调节精度。它们值得根据语料库进行调优——但完整的优化矩阵超出了本文的讨论范围。对于运行手册式的语料库,上面的默认值已经很合理。


如何安装并排查 Hermes 插件问题

安装并启用

mkdir -p ~/.hermes/plugins/do-knowledge-base
# copy the four files into it
hermes plugins doctor ~/.hermes/plugins/do-knowledge-base --ci
hermes plugins enable do-knowledge-base

在启用之前运行 plugins doctor —— 它会实际演练发现、解析和注册路径,因而能在问题仍然易于修复时捕获结构性问题。随后 plugins enable 会触发清单中的 requires_env 提示,并将您的凭据写入 ~/.hermes/.env

确认插件已加载

启动 Hermes 并检查:

hermes
/plugins

您应该看到:

✓ do-knowledge-base v1.0.0 (1 tools, 0 hooks)

Hermes 代理插件已启用

常见错误及修复(来自实际构建)

此表格中的每一行都是我们在构建本教程时实际遇到的问题。

Symptom Cause Fix
插件未出现 插件需要手动启用 hermes plugins enable ; 运行 HERMES_PLUGINS_DEBUG=1 hermes plugins list 以查看详情
插件未出现 __init__.py 在下载过程中被重命名为 (init.py) 将其改回原名 — Python 要求文件名必须完全匹配
KB 返回 HTTP 401 令牌缺少 genai:read,或者您使用了 Spaces 密钥而非个人访问令牌(PAT) 在控制台中检查令牌范围;Spaces 密钥和 PAT 是不同的凭据
KB 返回 HTTP 401 凭据存储在 shell export 中,而不是 ~/.hermes/.env 重新运行 plugins enable,或者直接将它们添加到 .env 并重启
no endpoints available: request rejected pre-queue 模型 slug 错误或已被退役 — 不是插件问题 doctl serverless-inference models list,然后使用 hermes model
凭据已修改但未生效 .env 仅在启动时读取 重启 Hermes

其中一项值得单独说明。此 no endpoints available: request rejected pre-queue 错误看起来很令人担忧,且似乎与工具相关 — 您在安装插件后立即会看到它,并会认为插件导致了问题。表明这是提供商层面的问题,而非工具层面:它会在每次 every API 调用时触发,包括诸如会话标题生成之类的辅助操作。原因几乎总是已退役的模型 slug。DigitalOcean 会主动退役较旧的 slug,因此上个月仍然有效的配置可能会失效 — 每当出现此情况时,请使用 doctl serverless-inference models list 重新验证。


验证 Hermes RAG:三个测试问题,有检索和无检索

这是收获,也是我们一开始提出的方法的完成:先提问题,再做负面对照,最后运行管道。

三个测试问题

  1. 两文档连接: “错误 FM-4419 的含义是什么?如果它一直发生,我应该呼叫谁?” — 只有将错误参考文档和升级文档结合起来才能回答。
  2. 三文档链: “为什么现在持续爆发的 FM-4419 被视为真实问题,而之前不是?当前的租约超时是多少?” — 需要变更日志、服务文档和错误参考文档。
  3. 范围陷阱: “FM-3350 现在频繁触发 — 应该让 Riverbend 值班人员处理它吗?” — 正确答案是 。FM-3350 是一个 payouts-service 错误,且升级文档明确将 payouts 放置在 Riverbend 的范围之外。依赖错误代码格式进行自由联想的模型会自信地将其路由错误。

负面对照:未使用插件的 Hermes

禁用插件后,我们提出了问题 3。代理搜索了其可用的表面 — — 包括本地文件系统 — — 在 FM-3350 或 Riverbend 值班轮换上未找到任何信息,并拒绝猜测:

“在事故中对‘谁应该被呼叫’做出错误猜测,比没有猜测还要糟糕。”

然后它询问知识库实际上位于哪里。

未使用 RAG 实现的问题运行失败

此记录演示了两点。首先,语料库设计有效:这些问题确实无法猜出,因此之后任何正确答案只能来自检索。其次 — — 值得单独指出 — — Hermes 诚实降级。面对它无法依据的问题,它拒绝回答并请求来源,而不是编造。这就是在随时待命的代理中所期望的失败模式。

启用插件后的结果

启用插件后,同样的问题会产生可见的工具序列 — tool_searchtool_describeknowledge_base_retrieval — 然后得到有依据的答案:

  • Q1:正确地区分 FM-4419(单分片租约到期,自恢复)与其邻居 FM-4420(仲裁丢失,Sev-1),并将页面路由到 payments-riverbend Pageloop 键,而不是命名某个个体 — — 这正是 escalation 文档所规定的。
  • Q2:正确解释了 30s→90s 租约超时变更的原因及其导致持续 FM-4419 爆发被重新分类的原因,并通过两份独立文件确认了当前值。
  • Q3 通过陷阱测试:代理拒绝将付款错误路由到 Riverbend,而是将 Payouts Eng 指定为负责轮值。它读取了它检索到的内容;它没有根据错误前缀进行模式匹配。

成功回答问题 2 的工具调用序列

在这些答案中需要注意的细节是:每个都会命名其源文件。这就是 metadata.item_name 从 Retrieve API 幸存下来,经过插件的 {source, ...} 格式化,进入模型上下文。这里的引用不是提示技巧 — — 而是管道结果。如果插件在格式化过程中丢掉了 item_name,则 schema 中的任何指令都无法恢复它。

“验证”对 RAG 流程实际上意味着什么

不是 “它回答正确了。” 通过条件有三部分:

  1. 工具禁用 → 模型拒绝或失败。(这证明这些问题确实无法猜出。)
  2. 工具已启用 → 模型能够正确回答 并且 引用其来源。 (证明检索发生且来源得以保留。)
  3. 陷阱问题路由正确。 (证明模型读取的是检索到的内容,而不是自由联想。)

该定义可适用于任何 RAG 堆栈。如果你的验证无法区分工作流程和知识丰富的模型,那就不是验证。


为什么不应在智能体循环中途切换模型

检索正常工作时,会出现一个诱人的优化想法:“检索到的块现在在承担主要工作——我会把这些轮次路由到一个更便宜的模型。”

DigitalOcean 自己的 推理路由器文档 建议不要在循环中途切换模型,原因有三,且这些原因会相互叠加:

  • 工具调用格式在不同模型之间有所不同。 智能体循环会按照其模型发出的格式解析工具调用;在循环中途切换模型是在这最糟糕的时刻破坏该解析的好方法。
  • 基于前缀的 KV 缓存在模型切换时会失效,迫使您重新计算到目前为止累积的全部上下文。
  • 缓存的输入标记比新鲜标记更便宜 — 所以失去缓存会增加您本来想要优化的那部分成本。

正确的成本杠杆就是您插件中已有的检索参数。更少、更精准的块(_NUM_RESULTS_MAX_CHUNK_CHARS、一个经过良好选择的 _ALPHA)会在每个轮次上缩小提示,且不会引入任何失败模式。为循环选择一个模型;调整您喂给它的内容。


扩展设置:过滤、重新排序和多个知识库

在基础循环得到验证后,可以采取以下方向:

  • 元数据过滤。 Retrieve API 支持对 item_name 使用 starts_with,对 file_id 使用 wildcard — 这就是之前提到的 Spaces 文件夹前缀发挥作用的地方。将查询范围限定为共享语料库中某个团队的文件夹。
  • 重新排序。 在 KB 的 Settings 选项卡启用它,以获得更好的检索块排序。请注意它的计费与查询向量化分开。
  • 定期重新索引。 运行手册会变化;一次索引的 KB 会变得过时。安排重新索引以保持检索的时效性。
  • 插件作为模板。 四文件模式 — 清单、模式、处理程序、注册 — 将 任意 HTTP API 转变为 Hermes 工具。KB 检索插件是一个示例实现,而非特殊情况。
  • 多个知识库。 可以为每个 KB 注册一个工具(不同的描述让模型进行选择),或者在处理程序中添加映射到 UUID 的 kb_name 参数。

超出小型语料库的规模 — 百万级文档、高吞吐嵌入、负载下的检索延迟 — 这是一个不同的架构问题,超出本文讨论范围。


摘要:Hermes + DigitalOcean RAG 堆栈

链条:Spaces → Knowledge Base → Retrieve API → Hermes 插件 → DigitalOcean 无服务器推理。 实际工作:四个短文件,一个清单,一个 HTTP 调用。

此方法值得前沿推广:先编写不可猜测的测试问题,捕获负面对照,然后构建 — — 并且不要在工具禁用运行失败、工具启用运行带有引用的回答以及陷阱问题正确路由之前,称 RAG 管道已验证。

对 Hermes 用户而言,更广泛的观点是插件界面是连接 Hermes 与 X 的通用答案。“如何将 Hermes 连接到 X” — 而今天的 X 是知识库。

资源:

常见问题

Hermes Agent 是否开箱即用支持 RAG?

不。Hermes 自带记忆功能 — — 包含代理 curated 的记忆文件、过去会话的全文搜索、跨会话回忆 — — 但记忆仅覆盖您的对话,而不包括您拥有的外部文档语料库。在您自己的文档上进行检索需要将 Hermes 连接到向量存储或托管的知识库,无论是通过 DigitalOcean MCP 服务器,还是通过类似上面构建的自定义插件。

我需要自行运行向量数据库才能在 Hermes 中使用 RAG 吗?

不。DigitalOcean 知识库负责分块、嵌入、混合检索以及可选的重排序,并为您准备好底层的 OpenSearch 数据库。自行运行 Qdrant、pgvector 或 Pinecone 可以获得对索引的更多控制权,但需要您自己管理摄取管道和数据库 — — 本教程中的插件模式适用于任意一种,因为它仅仅是一个 HTTP 调用。

我如何知道我的 RAG 管道实际上在检索任何内容?

针对模型凭借通用知识无法回答的语料库进行测试。先编写问题,在检索功能关闭的情况下运行并确认模型无法回答,然后打开检索功能重新运行。当工具禁用的运行被拒答、工具启用的运行不仅正确回答 并且 引用其来源,且一个精心设计的陷阱问题能被正确路由而非仅依赖模式匹配时,该管道即被视为有效。

我应该在从知识库检索时将 alpha 设置为多少?

The alpha parameter sets the balance between keyword and semantic retrieval: 0 is keyword-only, 1 is semantic-only. DigitalOcean 建议 hybrid as the default, starting in the 0.5–0.7 range. Tune lower for corpora dominated by exact identifiers like error codes or SKUs, and higher for conversational prose.

一旦检索开始工作,我应该切换到更便宜的模型吗?

不要在循环中途切换。不同模型的工具调用格式不同,切换可能会导致你的代理解析工具调用出错,而且模型切换会使基于前缀的 KV 缓存失效——这会增加你本来想优化的事物的成本。相反,应减少检索参数:使用更少的块和更短的摘录可以在每轮中缩小提示,且不会引入故障模式。

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

← 上一篇
神经网络详解:它们是什么以及如何用 Python 构建一个
下一篇 →
为AI代理选择推理服务提供商:延迟、工具调用和每任务成本

📌 相关推荐

GraphRAG 是推理问题,而非数据库问题
2026/8/30
构建市场时光机:使用 Python 和 WebSocket 重放交易会话
2026/8/30
如何自行基准测试LLM推理:值得信赖的数字设计标准
2026/8/30
← 返回文章列表