08 - 扩展 Harness 智能体
2026/7/30 21:29:45 · 更新于 2026/7/30 22:47:42
AI 翻译于 2026/7/30 23:28:26 · 使用 Qwen3.6 35B (fast, default)
通过五大内置扩展点(工具、MCP 服务器、技能、钩子和模型)扩展 Harness Agent 系统的实战指南,以及接下来值得构建的结构改进
本文档作为扩展 apps/agent-server 架构的实践实现指南。该系统围绕累加式扩展模式设计,允许通过小型配置文件和注册代码片段集成新功能,而无需修改核心请求或执行循环。以下是现有扩展路径的参考,随后是旨在长期强化系统的架构改进建议。
通过本地工具进行扩展
添加本地工具是最直接的扩展路径。创建 tools/your_tool.py 并导出两个组件:
- 一个
SCHEMA字典,用于定义 OpenAI 函数调用模式(参数描述、数据类型和约束条件)。 - 一个
async def实现函数,用于执行该工具的业务逻辑。
在 tools/registry.py 中注册该工具:导入模块,并分别在 SCHEMAS 列表和 _FUNCTIONS 名称到可调用对象的字典中添加一项。由于 agent_loop.py 和 graph.py 仅通过注册表与工具交互,因此无需进行任何源代码修改。
注入可信上下文: 如果工具需要服务器提供的上下文(例如已认证的用户身份或会话状态),请声明一个与预期名称匹配的参数(遵循现有具备记忆感知功能的工具所使用的 partition_key 模式)。call_tool() 方法会检查函数的真实签名并自动注入这些值。具有相同名称的模型提供参数无法覆盖此设置;服务器提供的上下文始终优先,从而保持针对大语言模型(LLM)欺骗的关键安全边界。
通过 MCP 服务器进行扩展
当某项功能已作为 Model Context Protocol (MCP) 服务器存在时(范围涵盖从网络搜索到特定领域的数据源),将其接入通常比手动编写自定义工具所需的工作量更少。向 apps/agent-server/mcp_servers.json 添加一项(请勿修改 .example.json 配套文件,该文件仅作静态参考)。每项遵循以下结构:
{
"name": "...",
"command": "...",
"args": [...],
"env": {...}
}
重启服务器,并通过 /health 端点验证连接情况(查看 mcp_tools_registered 计数)。
预飞检查清单: 在将其接入项目之前,务必先在本地进行冒烟测试(根据软件包分发渠道运行
npx <package>或uvx <package>)。两台预配置服务器(fetch、secedgar)在已正确安装的情况下,仍因导入时报错无法使用;测试可避免因假设而导致的部署失败。在从头研究新选项之前,请先查阅MCP_SERVERS_CATALOG.md中整理的关于网络搜索、PDF 处理、结构化规划及财务数据的调研备选列表。
已知限制:
- 仅支持 stdio 传输。无法直接连接仅支持 HTTP 或 SSE 的 MCP 服务器;必须先自行构建自定义传输层(如果特定工具需要,这可作为一个独立项目进行开发)。
- 隔离性是内置功能:失败的服务不会注册任何工具,仅记录警告日志,并允许其他服务正常启动。引入此容错机制的风险确实极低。
通过技能进行扩展
技能代表了架构中阻力最小的扩展方式。只需在 apps/agent-server/skills/ 目录下放置一个 Markdown 文件,文件需包含 YAML frontmatter(description: ...)以及匹配时想要注入系统提示语的指导文本即可。无需编写任何注册代码——文件的存在即代表完成配置。
技能通过关键词重叠与用户消息进行匹配,每个请求精确注入一个技能。如果您的新技能与现有技能共享词汇表,请将指导内容合并到原始文件中,而不是创建互相竞争且都无法赢得注入位置的重叠文件。
注意: 注册新技能文件或修改现有技能文件需要重启服务器。与每次请求时重新读取的
AGENT.md不同,技能在进程启动时被缓存。
通过钩子进行扩展
钩子在生命周期中的两个精确时刻执行:工具立即执行之前,以及真实(非阻塞)工具调用返回之后。创建 hooks/your_hook.py、定义相应的签名,并通过 register_pre_tool() 或 register_post_tool() 注册它。在 hooks/__init__.py 中添加单行导入以激活它。
钩子签名:
- 前置钩子:
fn(tool_name, arguments) -> dict | None。返回dict会阻止实际工具调用,并将该字典作为结果返回(非常适合策略执行,类似于现有的写入路径守卫)。 - 后置钩子:
fn(tool_name, arguments, result) -> None。专用于副作用场景(例如,在成功写入后触发后台重新索引,可以参考知能库自动同步钩子作为模板)。
所有钩子均以防御方式运行:单个钩子抛出异常会被捕获、记录并跳过。错误仅隔离到钩子本身,而不会导致活跃请求或服务崩溃。
使用或添加新模型
模型路由和切换完全通过配置传递管理 Ollama,无需更改源代码。有关详细的配置和选择策略,请参阅 07_Switching_LLM_Models_Guide.md。
值得优先实施的架构改进
除了上述增量式扩展点之外,还有几个能够显著增强系统的结构改进。以下按影响力大致排序:
| 功能增强 | 描述与实现路径 |
|---|---|
| 服务端模型路由 | 目前需要调用方显式指定。在 agent_loop.run() / run_streaming() 早期(_prepare_upstream_body() 之前)实现一个轻量级路由器,按能力(例如,当存在图像时自动选择视觉模型)或延迟配置文件进行路由。该逻辑已有清晰的接口点可在此处插入。 |
| 统一流式/非流式路径 | 两种执行模式目前都重复实现了“先规划器后工具”的控制流,导致未来对工具预算、钩子或记忆记录的修改需要双重维护。重构 graph.py 的波次执行器以支持增量/原生流输出。实施成本较高,但长期收益显著。 |
| 技能匹配器路由优化 | 将第 5.9/10 步的“正则先行、嵌入相似度校验”机制直接应用于 skills/loader.py。嵌入基础设施已存在并在其他地方使用;应用此模式可彻底杜绝重复的假阳性匹配问题。这是理解代码库路由层体系的极佳实操练习。 |
| 人工审批写入关卡 | 当前防护机制仅限制了写入发生的 位置,但缺少暂停等待确认的机制。最小化实现方案(记录预期操作 → 要求调用方再次显式调用工具以确认执行)即可在无需外部 UI 或通知系统的情况下弥补工作流漏洞。 |
| 共享缓存/限流层 | 目前属于前瞻性设计,但对横向扩展必不可少。内存限流器、MCP 会话池及多个缓存均需迁移至共享后端(Redis 是标准选择;正确加锁的 SQLite 在此规模下也能胜任)。在突破单进程进行水平扩展前,需先完成架构规划。 |
系统扩展的核心原则
每一个扩展点的存在,都源于原始架构有意将**“经常变更的部分”(工具、技能、钩子、MCP 服务器、模型)与“保持不变的部分”**(请求流水线、图执行器、授权层)进行了分离。
在构建扩展时,验证你方案正确性的最强依据是:它仅需编写一个全新的文件以及寥寥数行注册代码即可实现。若某项扩展要求修改 agent_loop.py 的核心控制流或 graph.py 的执行器本身,请暂停并评估是否可以通过扩展现有的注册表/钩子/技能模式来实现。正是这种有意的分离设计,使得代码库能够在四天内的八个构建阶段中始终保持紧凑、可审计且稳定。
关键要点
- **设计即支持可扩展:**新功能通过小型隔离文件和注册绑定进行集成,无需改动核心编排循环。
- **服务端强制安全:**受信任的上下文注入使用真实的 Python 函数签名,从结构上杜绝了大模型伪造参数的可能。
- **存在硬性限制:**MCP 完全依赖 stdio 传输方式;Skills 于启动时完成缓存,并基于关键词重叠情况,每次仅注入一个匹配项。
- **钩子优雅降级:**前置与后置钩子均采取防御性运行机制,将异常隔离于主执行流之外。
- **优先利用架构杠杆:**在服务端进行模型路由,在预算变更前统一流式传输路径,并尽早规划共享缓存架构,以避免扩展性债务。
学习地图
扩展 Harness Agent — 学习路线图
第一阶段 — 基础(理解架构)
- 理解核心设计原则:将"变化的部分"与"不变的部分"分离
- 探索
agent_loop.py和graph.py,了解核心循环处理的内容 - 研究
tools/registry.py——所有扩展遵循的中心模式
第二阶段 — 加法扩展(无需修改核心代码)
- 添加本地工具 —— 编写
tools/your_tool.py,在注册表中注册,通过/health端点测试 - 添加 MCP 服务器 —— 编辑
mcp_servers.json,在终端中进行冒烟测试,确认工具注册 - 添加技能(Skill) —— 将带有 YAML 前端的 Markdown 文件放入
skills/目录(无需编写代码) - 添加钩子(Hook) —— 编写工具前或工具后的逻辑,通过
hooks/__init__.py注册
第三阶段 — 模型集成
- 配置 Ollama 的模型透传(无需修改代码)
- 详见
07_Switching_LLM_Models_Guide.md
第四阶段 — 结构增强(大型项目)
- 实现服务端模型路由 (
agent_loop.run()) - 统一
graph.py中的流式和非流式请求路径 - 复用正则表达到嵌入的匹配模式用于技能(Skill)匹配
- 为文件写入添加人工审批关卡
- 在水平扩展之前,规划共享缓存/速率限制层
动手实践——分步指南
I can see the knowledge bank's structure, but apps/agent-server does not exist within the allowed directory /Volumes/AI_DATA/knowledge-bank. The accessible directories are:
/Volumes/AI_DATA/knowledge-bank/DOD-FM-Knowledge-Bank/(DOD-FM regulations PDFs)/Volumes/AI_DATA/knowledge-bank/K12-Knowledge-Bank/(K12 curricula PDFs)/Volumes/AI_DATA/knowledge-bank/Wiki/(markdown reference docs)/Volumes/AI_DATA/knowledge-bank/_inbox/and/_index/
These are read-only knowledge files — they don't contain agent-server source code, tools, skills configuration, or the project directories you referenced.
To proceed with steps 1–11, I would need access to the actual agent-server project directory. Could you:
- Place the agent-server repo inside the allowed path (e.g., create
/tmp/agent-serverif/tmpis accessible, or confirm a different allowed path), or - Paste the contents of the key files (
tools/registry.py,mcp_servers.json.example, an existing tool file, an existing skill file) so I can write new ones for you locally?
Without filesystem access to apps/agent-server/ or its sibling directories, I cannot explore it or create any extension files.
三大推荐资源
- 1OpenAI function calling documentation
The canonical reference for OpenAI function-calling schemas — defines what a SCHEMA dict should contain and how the model decides when to call tools.
https://platform.openai.com/docs/guides/function-calling
- 2Model Context Protocol (MCP) Specification
The official MCP specification — explains server transport types (stdio vs HTTP/SSE), tool protocols, and the standard server format used in mcp_servers.json.
https://github.com/modelcontextprotocol/specification
- 3LangChain Agent Patterns
A well-known agent framework demonstrating tool integration patterns, hook-like callbacks, and extensibility architectures similar to those in Harness Agent.
https://python.langchain.com/docs/modules/agents/
链接由 AI 推荐——使用前建议快速核实。