TL;DR
- Hermes Agent 是 Nous Research 开源、MIT 许可的自托管 AI 代理 - 一个具有持久内存、自我改进技能系统,并支持 Telegram、Discord、Slack、WhatsApp、Signal 和 CLI 的终端和桌面应用程序。
- 它提供 70 多个内置工具,包括
web_search和web_extract,并可以通过两种方式扩展:连接 MCP 服务器或编写通过ctx.register_tool()注册自定义工具的插件。 - Hermes 内置的
web_extract工具返回 Markdown,但应用了严格的字符预算截断,并且未记录渲染 JavaScript;其browser_navigate/browser_snapshot路径能够添加代理和反机器人处理,但仅通过付费的 Browserbase 风格的云浏览器后端和多步骤可访问性树交互循环。 - 将 Nstdata Crawl 作为 Hermes 插件接入,为代理提供了一个工具调用,可以渲染 JavaScript,通过 Nstdata 自己的代理池进行解析,并返回干净的 Markdown 或 JSON —— 不需要单独的浏览器自动化订阅。
- 集成是一个大约 40 行的 Hermes 插件:一个
plugin.yaml清单,一个register(ctx)函数,以及一个向 Nstdata Crawl 的/api/v1/crawl/scrape端点发送 POST 请求并返回解析 Markdown 的处理程序。 - Nstdata Crawl 按每次成功获取计费(包括 404/403 响应),而不是按尝试计费,网站级抓取需要明确的
maxDepth/maxPages边界——一旦代理独立调用工具而不是人类一次只进行一个请求,这两者都很重要。
What Hermes Agent is
Hermes Agent 是由 Nous Research 构建的开源、MIT 许可的 AI 代理,作为终端应用程序和 macOS 和 Windows 的原生桌面应用程序发布。它通过一个脚本安装(curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash 在 Linux/macOS/WSL2/Termux,或 Windows 上的一行 PowerShell),安装程序捆绑了 Python 3.11、Node.js、ripgrep 和 ffmpeg,以便代理可以即刻拥有一个工作运行时。它的定义特性是学习循环:该项目自己的 README 描述了一个 "从经验中创建技能,在使用过程中改进它们的" 代理,并搜索它自己过去的对话历史,而不是在每个会话开始时都冷启动。
模型访问是提供者无关的—— hermes model 可以在 Nous Research 自己的 Nous Portal、OpenRouter、OpenAI 或自定义端点之间切换,而无需接触代码——且代理能够通过 Telegram、Discord、Slack、WhatsApp、Signal、电子邮件或普通 CLI 会话触及用户。在底层,Hermes 提供 70 多个内置工具,按工具集(浏览器自动化、文件和终端访问、网络搜索和提取、视觉、图像生成、家庭自动化等等)进行分组,按平台可配置使用 hermes tools 命令。
Why extend it with Nstdata Crawl
Hermes 的网络工具在常见情况下表现良好,但在围绕它们构建工作流程之前有一些文档化的边缘情况值得了解。内置的 web_extract 工具 获取一个 URL 并返回 Markdown,但 Nous Research 自己的文档描述了结果的固定字符预算——默认大约 15,000 个字符,分为“~75% 头 / 25% 尾”,并且一旦页面超出这个限制,会显式添加 [TRUNCATED] 页脚——相同的文档将返回内容较少的重度 JS 页面指向 browser_navigate 加 browser_snapshot。该 浏览器路径 可行,但它完全是不同形状的工具:它打开一个真实的浏览器会话,返回带有编号交互元素的可访问性树快照,需要一个能够支持 CDP 的后端(本地 Chrome,或像 Browserbase 这样的云选项)来运行——并且在该路径上的代理或反机器人处理是付费云浏览器层的功能,而不是 web_extract 本身所提供的。
Nstdata Crawl 在这两个工具之间填补了一个缺口,提供一种第三种形状:一个 API 调用可以渲染 JavaScript,路由通过 Nstdata 自己的代理池并具有浏览器指纹处理,返回干净的 Markdown、HTML 或 JSON——更接近于 web_extract 已经返回给模型的内容,但没有截断默认行为或 JS 渲染缺口,也不需要为代理覆盖单独的云浏览器订阅。
Take a Quick Look
当代理需要读取的页面是重JS或在机器人检测后,Nstdata Crawl 将其在单个代理支持的 API 调用中转换为干净的 Markdown 或 JSON — 不需要单独的浏览器自动化后端。
前提条件
- 安装了 Hermes 代理,且通过
hermes可以访问,已经通过hermes model配置了模型后端。 - 拥有 Nstdata 账户和 Crawl 的 API 密钥,保存在环境变量中,而不是硬编码到任何文件中。
- Python 3.11,Hermes 自己的安装程序已经捆绑了它。
- 熟悉编辑 YAML 和 Python — 下面的插件很简单,但你需要根据自己的工作流程调整工具的描述和参数。
安装
Hermes 插件存放在 ~/.hermes/plugins/<plugin-name>/ 下。创建该目录和插件所需的三个文件:
mkdir -p ~/.hermes/plugins/nstdata-crawl cd ~/.hermes/plugins/nstdata-crawl touch plugin.yaml __init__.py
配置
在 plugin.yaml 中声明插件的元数据:
name: nstdata-crawl version: "1.0" description: 通过 Nstdata Crawl 获取一个 URL 的干净 Markdown,支持 JS 渲染和代理支持的访问。
将 Nstdata API 密钥设置为环境变量,而不是写入任何插件文件中 — Hermes 插件以常规 Python 运行,因此 os.environ 对它们同样可用,就像任何脚本一样:
export NSTDATA_API_KEY="your-api-key-here"
真正的握手
在 __init__.py 中注册工具,遵循 Hermes 的插件文档模式 — 一个 register(ctx) 函数,调用 ctx.register_tool() 并提供架构和处理程序:
import json import os import requests NSTDATA_API_BASE = "https://api.nstdata.io" def register(ctx): schema = { "name": "nstdata_crawl_scrape", "description": ( "获取一个 URL 并返回干净的 Markdown,渲染 JavaScript 和 " "通过 Nstdata 的代理池路由。用于 web_extract 返回较少内容的页面,或需要代理支持的访问。" ), "parameters": { "type": "object", "properties": { "url": { "type": "string", "description": "要获取并转换为 Markdown 的 URL。", } }, "required": ["url"], }, } def handle_crawl_scrape(params, **kwargs): del kwargs target_url = (params or {}).get("url", "").strip() if not target_url: return json.dumps({"success": False, "error": "url 是必需的"}) api_key = os.environ.get("NSTDATA_API_KEY") if not api_key: return json.dumps({"success": False, "error": "NSTDATA_API_KEY 未设置"}) response = requests.post( f"{NSTDATA_API_BASE}/api/v1/crawl/scrape", headers={"x-api-key": api_key, "Content-Type": "application/json"}, json={"url": target_url, "formats": ["markdown"], "onlyMainContent": True}, timeout=60, ) response.raise_for_status() envelope = response.json() if envelope.get("err"): return json.dumps({"success": False, "error": envelope.get("msg", "请求失败")}) result = envelope.get("data", {}) if not result.get("success"): return json.dumps({"success": False, "error": result.get("status", "爬取未完成")}) page = result.get("data", {}) return json.dumps({ "success": True, "title": page.get("metadata", {}).get("title", ""), "markdown": page.get("markdown", ""), }) ctx.register_tool( name="nstdata_crawl_scrape", toolset="nstdata_crawl", schema=schema, handler=handle_crawl_scrape, )
这里有两个细节很重要,均来自 Nstdata 自身的 API 文档和剧本:响应是嵌套的 — 顶级的 err/code/msg 信封包裹着内部的 success/status 对象,后者又包裹着实际的 data.markdown 有效载荷 — HTTP 200 只确认请求已被接收,而不是爬取成功,因此处理程序明确检查 err 和 success,而不是仅仅信任状态码。
附加工具
以与启用任何内置工具相同的方式启用新工具集:
hermes tools
选择 nstdata_crawl 从交互列表中(或者直接通过 hermes chat --toolsets "web,nstdata_crawl" 传递它),模型获得对 nstdata_crawl_scrape 的访问权限,以及其现有工具,可以在整个会话中使用。
工作示例
这是处理程序的解析逻辑,经过 Nstdata Crawl 的文档响应封装验证 — 在一个本地组件上运行,提供该确切的 JSON 形状,因为此环境没有可以调用真实端点的实时 Nstdata API 密钥:
import json import requests def crawl_scrape(api_base): resp = requests.get(f"{api_base}/scrape_response.json", timeout=10) # 代表真实的POST resp.raise_for_status() envelope = resp.json() if envelope.get("err"): return {"success": False, "error": envelope.get("msg", "unknown error")} inner = envelope.get("data", {}) if not inner.get("success"): return {"success": False, "error": inner.get("status", "not completed")} page = inner.get("data", {}) return {"success": True, "title": page.get("metadata", {}).get("title", ""), "markdown": page.get("markdown", "")} print(json.dumps(crawl_scrape("http://127.0.0.1:8100"), indent=2))
输出:
{ "success": true, "title": "Careers at Example Corp", "markdown": "# Example Careers Page\n\nWe are hiring a Senior Backend Engineer in Remote - US.\n\n[Apply here](https://example.com/jobs/8077887)" }
这与 Nstdata 实时 API 文档描述的相同嵌套 err / data.success / data.data.markdown 结构相匹配,确认了插件处理程序中的解包逻辑,然后再指向一个真实的 API 密钥。
返回输出
一旦工具启用,自然语言请求,如“读取 nstdata.io 的职业页面并总结开放的工程职位”,将使模型进行 nstdata_crawl_scrape 调用,并且工具将返回一个 JSON 字符串,其中包含 success、title 和 markdown 字段 — 与上面所示的相同形状。Hermes 自己的工具调用循环会以与读取 web_extract 输出相同的方式读取这些 Markdown,因此无需更改代理对结果的推理;不同之处完全在于基础提取可以处理的内容。
限制
上述插件每次调用获取单个页面 — 它不设置 maxDepth 或 maxPages,因为这些参数仅适用于 Nstdata Crawl 的站点级爬取端点(POST /api/v1/crawl),而不是在这里使用的单页面 scrape 端点。如果您扩展此插件以调用站点级端点,请明确设置这些限制:由代理自主启动的无界站点爬取可能会进入搜索结果、分页或登录页面,而没有人进行监控。
Nstdata Crawl 按照成功获取计费,而不是尝试一次 — 返回具有真实状态代码的响应,包括 404 或 403,在一次计费中算作一次,因为尽管页面没有成功,但获取本身仍然成功。这个区别在代理自行安排调用工具时比对于一次性手动请求更为重要,因为在循环中提供给代理的错误 URL仍会计入使用量。在依赖具体数值之前,请重新验证 Crawl 的当前订阅定价,因为它可能会变动,并查看 Crawl API 参考,以了解此插件构建的完整请求/响应架构,包括站点级爬取端点及其 maxDepth/maxPages 参数。如果该集成超出单页面提取,Nstdata 的 Crawl 启动公告 涵盖了更完整的功能集。
处理程序还假定 Crawl API 在其 60 秒的超时内作出响应;一个慢速或 JS 密集的页面可能会超过这个时间,在这种情况下,该工具应返回明确的失败,而不是让请求挂起 — 该插件的生产版本应明确捕获 requests.Timeout,而不是让它作为未处理的异常传播,因为 Hermes 期待工具处理程序始终返回 JSON 字符串。
结论
Hermes Agent 的内置网络工具覆盖大多数情况,但截断的 web_extract 结果或需要付费云浏览器后端以支持代理的 browser_navigate 会话都是您最终会遇到的真实边缘。一个小插件 —— 一个 plugin.yaml 清单和一个包裹对 Nstdata Crawl 的 scrape 端点单个 HTTP 调用的 register(ctx) 函数 —— 通过一个代理支持的、JavaScript 渲染的工具调用来填补那个空白,返回相同类型的 Markdown,代理已知如何读取。将 API 密钥保持在环境变量中,明确绑定您稍后添加的任何站点级爬取的页面和深度限制,并且该集成可以无缝融合到 Hermes 现有的工具调用循环中,而不改变代理对其结果的推理。
常见问题
问:什么是 Hermes Agent? Hermes Agent 是一个开源的、MIT 许可的、自托管的 AI 代理,由 Nous Research 构建。它作为终端应用和本地桌面应用运行,支持 Telegram、Discord、Slack、WhatsApp 和 Signal,同时也可以使用普通的 CLI 使用,并围绕自我改善的技能系统构建,具备跨会话的持久记忆。
Q: 我需要使用 Nous Portal 来运行 Hermes Agent 吗?
不需要。hermes model 可以在 Nous Portal、OpenRouter、OpenAI 或自定义端点之间切换,而无需更改任何代码,因此 Nous Portal 只是众多选项之一,而不是必需品。
Q: 为什么不直接使用 Hermes 内置的 web_extract 工具?
web_extract 对普通页面效果很好,但 Nous Research 自己的文档描述了其输出的默认约 15,000 字符截断,并指出,对于内容较少的 JS 重页面,应改为使用 browser_navigate/browser_snapshot。Nstdata Crawl 插件为代理提供了一个工具调用,可以直接处理 JavaScript 渲染和代理支持的访问,而无需截断或单独的浏览器自动化后端。
Q: 这个集成使用 Nstdata Crawl 的站点级爬取,还是仅单页面?
本指南中的插件调用了 Crawl 的单页面 scrape 端点。站点级爬取是一个单独的端点(POST /api/v1/crawl),需要显式的 maxDepth 和 maxPages 界限——如果您扩展插件以爬取整个站点,请故意添加这些界限,因为自主代理没有内置的感觉来判断爬取是否足够。
Q: 我该把 Nstdata API 密钥放在哪里?
在环境变量中(本指南示例中的 NSTDATA_API_KEY),在运行时通过 os.environ.get() 读取。切勿直接写入 plugin.yaml、__init__.py 或任何可能进入版本控制或共享 Hermes 配置的文件中。
Q: Hermes Agent 可以通过 MCP 调用 Nstdata Crawl 吗,而不是使用插件?
Hermes 支持通过 ~/.hermes/config.yaml 连接任何 MCP 服务器,但 Nstdata Crawl 是以 REST API 的形式暴露的,而不是一个已发布的 MCP 服务器,因此通过注册自定义工具的插件——本指南中的方法——是今天的直接集成路径。如果您更愿意在多个代理上标准化使用 MCP,同样的 REST API 也可以用 MCP 包装。
Q: 如果爬取请求失败或超时会发生什么?
本指南中的处理程序明确检查响应信封的 err 和 success 字段,并返回一个 JSON 失败对象,而不是引发异常,因为 Hermes 期待每个工具处理程序返回一个 JSON 字符串。生产版本也应该以相同方式捕获网络级错误,如 requests.Timeout 和 requests.ConnectionError,以便单个失败的获取不会崩溃代理的工具调用循环。
Q: 这是否完全取代了 Hermes 的浏览器自动化工具?
不。browser_navigate 和 browser_snapshot 仍然是需要点击、滚动、填写表单或以其他方式与页面交互等任务的正确选择,而不仅仅是阅读页面。Nstdata Crawl 插件是专门用于“读取此页面内容”的更快、更便宜的路径,这涵盖了代理的网页工具在实践中的大部分使用。





