第一章:Harness工程概论
要理解当前系统工程能力比模型能力本身更为关键
理解驾驭这个词,是约束、可验证性、层次化的信任机制
Harness的五大核心子系统和两大基础保障。(另外一本书中也提到了harness拆分为5个部分)
1.1 从大语言模型到智能体的快速过渡
LLM从实验室走向生产环境,遇到的问题:
- 可靠性问题:同样的问题,同一个模型的多次回答可能差异巨大,
- 外部交互
- 成本控制:大规模调用API的成本快速增长,需要更精细的控制和优化
- 安全性问题:让LLM任意调用系统权限和外部接口存在严重的安全风险
一个Agent需要的三个核心能力:
- 思维能力
- 行动能力
- 学习能力:从结果中反馈、调整策略、积累经验,涉及记忆机制、强化学习信号、迭代优化等
为什么还需要构建复杂的系统工程层:在于可靠性和可控性的鸿沟。
Harness要让智能体更可靠、更可控、更安全。
Harness工程:将一个理论上的智能体概念转化为生产级别的系统
1.2 Harness的定义和职责边界
Harness是在大语言模型与真实执行环境之间构建的一套系统工程框架,通过标准化的消息协议、分层的权限管理、多维的执行可见性和完善的错误处理机制,使得LLM能够在受控约束下安全、可靠地感知环境、执行任务、学习反馈。
核心要素:
- 中间层角色:harness本质上是一个适配器和控制层,在LLM和执行环境之间,我觉得这句话很直白:不替代LLM产生业务推理,也不直接承担工具内部逻辑,而是把LLM的意图转换为可验证、可授权、可执行的操作,并将结果正确反馈给LLM。
- 约束和可控性:Harness主要目标不是扩展智能体的能力,而是限制智能体的风险。将智能体行为控制在安全边界内。
- 完整的生命周期管理:在各个过程需要在整个智能体执行周期中保持可见性和控制力
所以说harness通常指包裹在模型外面,负责把模型能力安全可靠地接到真实世界的那一层,harness承接意图,转成可验证、可授权、可执行的操作,并把结果回传。
harness是LLM和外部世界的唯一通道。
harness与传统中间件的区别:
在分布式系统中,中间件Middleware也提供适配,转发,可见性等功能。
- 传统中间件的假设:参与交互的各方都是确定的,行为可预测,中间件的工作是高效转发消息、处理协议转换、管理连接
- harness的假设:Agent的决策可能包含错误,理解偏差,甚至不当意图,harness需要对agent的每一个决策都进行验证、授权、隔离执行
传统中间件通常不会:
- 在转发请求前,拦截和验证请求的合法性
- 对某些危险操作进行权限检查或人工审批
- 在执行失败时自动进行智能重试或降级
- 为每个操作维护完整的审计日志
- 而这些恰好是 Harness 的核心职责
Harness做的事:
- 工具集成:负责将各种外部工具和系统集成进来,无论是API调用,数据库查询,文件操作还是系统命令。维护一个统一的工具注册表,确保每个工具都有清晰的接口定义、权限配置和使用说明。
- 权限和授权:实现梯度化权限管理,对于高危操作,Harness可以拦截请求,记录意图,等待人工审批。
- **执行跟踪和验证:**每一个工具调用都被记录、追踪和验证,如果工具返回了意外的结果,harness需要识别这些异常并决策是否重试,降级或报告
- 状态管理:harness维护智能体执行过程中的完整上下文状态,当智能体被中断或故障后,可以恢复到一致的状态
- 可观测性和审计:通过日志、分布式追踪、性能指标等多个维度,记录Agent的每个行为和决策,不仅用于故障排查,更是合规性和安全审计的基础
上面五个部分就和之前看到的harnese五个部分类似,
harness不做的事:
- 业务推理和任务分解
- 工具的实际执行
- 模型的优化和训练
- 业务逻辑
通过harness层实现了:
- 安全性:权限层确保不会有非法操作
- 可靠性:追踪和验证层确保即时出错也能快速定位
- 可用性:harness的重试和降级机制确保系统韧性
DeerFlow的后端中间件
DeeFlow的中间件层,正式前面讨论的harness思想的一个典型工程实现,并非传统意义上的消息转发中间件,而是一套包裹在agent循环外部的可插拔、有状态的治理链。
agent运行时基于langgraph和langchain构建,核心是lead agent,所有中间件都实现langchain的agentMiddleware接口,在模型调用前后before_model/after_model,或工具调用前后wrap_tool_call执行,以读写agent状态
这些中间件按严格顺序组成一条洋葱责任链,请求从最外层向内穿透,逐层完成清晰、准备和验证,响应则从核心向外回溯,逐层完成审计、整理和反馈。
中间件按职责分层:
最外层:输入清洗与安全边界
| 中间件 | 核心职责 | 生命周期 |
|---|---|---|
| InputSanitizationMiddleware | 最外层清洗,中和用户消息中的提示注入标签,如,防止恶意指令进入LLM | before_model |
| ToolOutputBudgetMiddleware | 截断过长的工具输出,防止上下文被撑爆 | wrap_tool_call |
| ToolResultSanitizationMiddleware | 清晰远程内容工具,如web_fetch的结果,防止攻击者控制的网页伪造框指令 | wrap_tool_call |
运行时:状态与资源准备
| 中间件 | 核心职责 | 生命周期 |
|---|---|---|
| ThreadDataMiddleware | 为每个会话创建隔离的工作目录 | before_agent |
| UploadsMiddleware | 将新上传的文件注入对话上下文,并去重 | before_agent |
| SandboxMiddleware | 获取并分配沙箱实例,隔离代码执行环境 | before_agent |
| DanglingToolCallMiddleware | 为被中断的工具调用注入占位响应,保持消息历史完整 | before_agent |
安全与授权:工具调用的安检员
| 中间件 | 核心职责 | 生命周期 |
|---|---|---|
| GuardrailMiddleware | 工具调用的授权检查点,每次工具执行前,通过AuthorizationProvider验证权限,采用失败即阻断策略 | wrap_tool_call |
| SandboxAuditMiddleware | 对bash命令进行专门的安全审计和正则阻断 | wrap_tool_call |
| ReadBeforeWriteMiddleware | 文件修改前强制校验SHA256哈希,防止基于过期内容的写入 | wrap_tool_call |
流程管控:防失控与资源管理
| 中间件 | 核心职责 | 生命周期 |
|---|---|---|
| LoopDetectionMiddleware | 检测并打破Agent的重复工具调用死循环 | after_model |
| SubagentLimitMiddleware | 限制单轮中并行子Agent的数量,防止资源耗尽 | before_model |
| TokenUsageMiddleware | 追踪每次模型调用的Token消耗,用于成本控制 | after_model |
| SummarizationMiddleware | 上下文接近token上限时自动压缩历史消息 | before_model |
| MemoryMiddleware | 跨会话记忆的注入与队列更新 | before_model/ after_agent |
| TodoListMiddleware | 在计划模式下维护可见的任务列表 | after_model |
| ClarificationMiddleware | 必须最后执行,拦截澄清请求并转化为面向用户的提问 | after_model |
| TitleMiddleware | 首次交互后自动生成会话标题 | after_model |
| ViewImageMiddleware | 为支持视觉的模型注入图像数据 | before_model |
Deerflow允许在config.yaml中注册自定义中间件,按业务需求注入领域护栏,工具治理或可观测性钩子,无需修改核心代码。
1.3 五大核心子系统总览
这里也是将harness系统分为了五个核心子系统和两大基础保障。
五大核心子系统
运行时引擎
职责:实现智能体的执行循环,协调各个子系统的交互
维护智能体执行的主循环,通常遵循:
核心职责:
- 状态机管理
- 消息编排
- 异步执行:在I/O等待期间不阻塞,并发处理多个智能体任务
- 超时和中断:设置合理的执行超时,支持优雅的任务中断和清理
工具层
职责:抽象和管理各种外部工具的访问,提供统一的接口调用
核心职责:
- 工具注册,
- 工具发现,
- 参数验证
- 权限检查
- 执行隔离
- 结果保准化
记忆子系统
职责:管理智能体的各种记忆,支持上下文理解和学习
记忆子系统让智能体能够学习、改进和个性化响应
模型集成与输出治理
职责:管理与LLM的交互,控制和验证模型的输出
解决一个关键问题:LLM的输出不一定总是可靠的,需要在充分利用LLM能力的同时,防止其产生幻觉,矛盾或危险的决策
核心职责:
- 模型选择
- 提示词管理
- 输出解析
- 输出验证
- 降级策略:当输出质量不满足要求时,进行重试、提示词调整、或切换到更强大的模型
编排引擎
职责:支持复杂的多步任务和多智能体协作
编排引擎提供了一套机制,让多个智能体能够协同完成任务
核心职责:
- 工作流定义
- 依赖管理
- 智能体分配
- 结果聚合
- 故障恢复
两大基础保障
安全层
职责:在整个智能体生命周期中,防止安全威胁和风险
包括:
- 权限管理
- 沙箱隔离
- 输入验证
- 输出过滤
- 审计日志
可观测性
职责:提供对harness系统运行的完整可见性
为什么需要:因为一个无法观测的系统,一旦出现问题,就很难诊断和修复
可观测性提供了三个维度的视图:
- 日志Logs:记录详细的事件序列,支持文本搜索和过滤
- 追踪Traces:跟踪单个请求或任务的完整执行路径,显示各个组件的耗时
- 指标Metrics:收集系统级别的性能指标(如吞吐量、延迟、错误率),支持告警和趋势分析
子系统间的交互
典型的Agent执行流程:
1.4 Harness为什么比模型更重要
公开案例:OpenAI Codex,提示词优化,Anthropic Constitution AI等
用了Harness层之后,许多错误会在执行前或执行后被发现并修正,能够改变最终可用率
理解Harness比模型更关键,需要回到Agent系统的本质。
大语言模型的本质是概率机器,就是概率生成模型,给定上下文,生成最可能的下一个token。导致:
- 不可能完全消除错误
- 确定性需求无法满足
- 实时学习困难
系统级别的可靠性无法仅通过提升单个LLM调用的准确率来实现,必须在系统工程层面进行干预。
以claude为例,harness组件占据了核心地位
实战建议:
- 不要过度优化模型:模型准确率高了,有边际效应,重点转向系统工程
- 优先投入高效益的harness组件
- 把模型的最后一英里交给harness
- 提前规划故障处理:想到这一步失败了怎么办
Harness不只是比模型更重要,设计空间本身已经大到值得用自动化方法去搜索
1.5 MiniHarness项目介绍
目标是完成构建一个完整,可运行的harness系统原型
先快速搭建和运行miniharness,
克隆并下载项目:git clone https://github.com/yeasy/harness_engineering_guide
进入项目:cd harness_engineering_guide/lab
创建虚拟环境:python3 -m venv venv
windows下激活虚拟环境:venv \Scripts\activate
安装依赖:pip install -e ".[dev]"
配置示例模型密钥:在 .env中进行配置,cp .env.example .env
然后可以运行基本测试,pytest .\tests\unit\test_core.py,
核心依赖:
- pydantic,数据验证和序列化,
- httpx,http客户端
- asyncio,异步编程
- python-dotenv,环境变量管理
- anthropic,claude api
- oepnai,apenAPI
- pyyaml,yaml解析
- pytest,单元测试
- pytest-asyncio,异步测试
- black,代码格式化
- pylint,代码质量检查
- mypy,类型检查
miniharness的项目结构:
- lab/
- project.toml,项目配置和依赖
- READEMD.md,项目说明
- .env.example,环境变量说明
- mini_harness/,源代码目录
- init.py
- core/ 核心接口定义
- agent.py,智能体基础类
- message.py,消息类型定义
- tool.py,工具接口
- event.py,事件定义
- runtiem/,运行时引擎
- init.py
- engine.py,智能体执行循环
- events.py,运行时时间,
- models.py,运行时数据模型
- tools/,工具实现
- init.py
- registry.py,工具注册表,
- builtin.py,内置工具示例
- memory/,记忆子系统
- init.py
- storage.py,记忆存储
- context.py,上下文组装
- consolidation,记忆整合
- models/ 模型集成
- init.py
- provider.py,模型提供者
- parser.py,响应解析
- quality.py,输出质量门控
- orchestration/,任务编排
- init.py
- engine.py,编排引擎与状态机
- mcp/,mcp协议集成
- init.py,
- integration.py,mcp工具注册与适配
- reliability/,可靠性与可观测性
- init.py
- tracing.py,链路追踪
- monitoring.py,监控指标收集
- logging.py,日志记录
- resiliience.py,容错与重试机制
- security/,安全防护
- init.py
- permission.py,权限决策引擎
- path-validator.py,路径校验与标准化
- guardrails.py,危险操作检测
- secure_executor.py,安全执行器
- utils/ ,工具函数
- init.py
- cofing.py
miniharness支持自定义和扩展,
importasynciofrommini_harness.runtime.engineimportRuntimeEnginefrommini_harness.tools.registryimportToolRegistryfrommini_harness.tools.builtinimportBashTool,FileReadTool,FileWriteToolasyncdefmain():# 构建工具注册表并注册内置工具registry=ToolRegistry()registry.register(BashTool())registry.register(FileReadTool())registry.register(FileWriteTool())# 创建运行时引擎,就把注册工具给到engine=RuntimeEngine(tool_registry=registry)# 运行并消费事件流asyncforeventinengine.run("帮我运行bash打印hello from miniharness"):safe_keys={"turn_number","tool_name","is_error","content_length"}safe_metadata={k:vfork,vinevent.metadata.items()ifkinsafe_keys}print(f"[{event.__class__.__name__}]{safe_metadata}")if__name__=="__main__":asyncio.run(main())第二章:Harness架构全景
这里书中概括为三层架构 + 横切关注点。
三层架构:接入层、编排层、智能体核心层
两个横切关注点:安全性和可观测性
2.1 通用参考架构
了解了五大核心和两大基础保障。
还需要理解这些子系统如何组织,如何交互的。因此书中给出了一个架构模型,基于行业的最佳实践的抽象,
简单系统可以裁剪,强实时、强合规或嵌入式场景可能需要变体。
三层详细:
- 接入层Access Layer,系统与外部世界的边界,负责接收用户请求、协议转换和响应格式化,CLI, Web API, SDK都属于接入层的实现形式
- 编排层Orchestration Layer,负责复杂任务的分解、多智能体的协调和工作流管理。负责分配子任务、任务依赖和聚合结果
- 智能体核心层Agent Core Layer,harness核心执行引擎,包含运行时引擎,工具层,记忆子系统和模型集成与输出治理四个子系统,在运行时引擎的同一循环中交替协作,而非分层调用
横切点:安全,可观测性,存储
接入层
是Harness系统的交互入口,
- 协议适配:将不同来源请求统一转换为内部任务格式
- 身份认证
- 响应格式化,
接入层做格式转换和路由分发
编排层
负责任务级别的调度
- 任务分解
- 依赖管理:识别子任务间的依赖关系,构建执行DAG
- 多智能体协调
- 结果聚合
智能体核心层
模型调用、工具执行、记忆更新并非各自独立的层级,而是在运行时引擎的同一个循环中交替发生
运行时引擎:
- 管理生命周期
- 维护消息类型系统和执行状态
- 协调其他三个子系统的调用顺序
- 处理错误恢复和漂移检测
模型集成与输出治理:
模型集成负责与LLM的交互,
- 支持多种LLM
- 管理提示词和系统消息
- 处理token限制和成本优化
- 支持流式和批量调用
输出治理负责处理LLM输出中最不确定的部分:
- 格式校正:如果LLM没有返回期望的json格式,尝试修复
- 语义验证:检查工具调用的参数是否合理,是否调用了不存在的工具
- 安全检查:检查输出是否包含有害内容或违反约束
- 置性度评估:评估LLM对其输出的置信度,低置信度时触发重试或人工审批
要理解为什么需要输出治理:因为LLM的输出是不可预测的,要应对这种不确定性的防线
例如一个示例:先将输出文本解析为json,然后校验格式,解析失败让LLM自己修复,然后进行语义验证,进行安全检查
层间通信设计原则
了解了每一层的职责,还要定义层间的通信方式
向下的调用
上层调用下层,应该使用明确的、类型安全的接口,
类型安全:指每个方法的输入参数和返回值都有明确的类型声明,这样在开发阶段就能发现参数传错的问题,而不是等到运行时报错。
向上的反馈
下层向上层报告时,应该使用事件或回调机制,而不是异常
事件更像日常的工作汇报,对于监控和调试系统至关重要
例如通过事件机制进行汇报:
@dataclassclassExecutionEvent:""" 表示系统中发生的一个事件 所有层都可以发出事件,上层可以订阅这些事件监控系统运行状态 """timestamp:datatime# 事件发生时间level:str# 严重程度source:str# 事件来源message:str# 可读描述context:dict# 附加信息classAgentCore:def__init__(self,event_handler:Callable[[ExecutionEvent],None]):""" 构造方法接收一个事件处理函数,回调函数 Callable[[ExecutionEvent], None]是: 接收ExecutionEvent参数,不返回值的函数, 这个函数由上层提供,上层决定接收事件后做什么,例如写日志、告警等 下层只负责发出事件,不关心上层如何处理 """self.event_handler=event_handlerasyncdefrun(self,task:Task):# 发出任务开始事件self.event_handler(ExecutionEvent(timestamp=datetime.now(),level="info",source="AgentCore",message="Task started",context={"task_id":task_id}))# 后续逻辑))