AI Agent 框架探秘:拆解 OpenHands(14)--- Microagents
目录
- [AI Agent 框架探秘:拆解 OpenHands(14)--- Microagents](#AI Agent 框架探秘:拆解 OpenHands(14)--- Microagents)
- [0x00 概要](#0x00 概要)
- [0x01 需求](#0x01 需求)
- [1.1 当前问题](#1.1 当前问题)
- [1.2 多智能体系统](#1.2 多智能体系统)
- [1.3 Multi-Agent](#1.3 Multi-Agent)
- [1.4 连接机制](#1.4 连接机制)
- [1.5 Sub-agent](#1.5 Sub-agent)
- [1.6 microagents](#1.6 microagents)
- [1.7 原理](#1.7 原理)
- [1.7.1 协作](#1.7.1 协作)
- [1.7.2 token](#1.7.2 token)
- [1.7.3 成本](#1.7.3 成本)
- [0x02 基本梳理](#0x02 基本梳理)
- [2.1 基类定义](#2.1 基类定义)
- [2.2 微型代理的类型](#2.2 微型代理的类型)
- [2.3 微型代理的来源](#2.3 微型代理的来源)
- [2.3.1. 可共享微型代理(公共)](#2.3.1. 可共享微型代理(公共))
- [2.3.2. 仓库指令(私有)](#2.3.2. 仓库指令(私有))
- [2.4 升级为 Skill](#2.4 升级为 Skill)
- [0x03 实现](#0x03 实现)
- [3.1 处理流程](#3.1 处理流程)
- [3.2 触发条件](#3.2 触发条件)
- [3.3 AgentDelegateAction](#3.3 AgentDelegateAction)
- [3.4 特色](#3.4 特色)
- [3.4.1 委托代理(Delegate)机制](#3.4.1 委托代理(Delegate)机制)
- [3.4.2 功能概述](#3.4.2 功能概述)
- [3.4.3 层级型的合作模式](#3.4.3 层级型的合作模式)
- [3.4.4 流程图](#3.4.4 流程图)
- [主 - 子 Agent 交互与控制流程图](#主 - 子 Agent 交互与控制流程图)
- [AgentController 核心事件处理流程图](#AgentController 核心事件处理流程图)
- [3.4.5 容错和隔离机制](#3.4.5 容错和隔离机制)
- [3.5 代码](#3.5 代码)
- [0xFF 参考](#0xFF 参考)
0x00 概要
很多Agent系统会采用多智能体(multi-agent)架构,划分不同子模块/子Agent各司其职,由中央调度Agent统筹管理整个生命周期。这种模块化架构能将复杂任务拆解给最擅长该子任务的模块,发挥各模型所长,避免单一模型在某些任务上的弱点。
在OpenHands 中,Microagents 本质上是一组量身定制的指令模块,核心作用是给 OpenHands 工具注入更聚焦的能力 ------ 不管是某个细分领域的专业知识,还是特定任务的标准化流程,都能通过它们来落地。对开发者来说,这些小模块就像身边的专项助手:遇到 Git 操作、代码审查这类具体场景时,不用再手动梳理步骤,微型代理会提供现成的专业指引;重复任务能直接交给它们自动化处理,还能保证不同项目里的操作逻辑保持一致,省不少事。
Microagents 允许我们为 Agent "外挂"领域知识,而无需修改 Agent 的核心代码或 Prompt。Memory 组件会在任务开始时或在对话中检测到特定关键词时,自动加载相应的 Microagent 文件内容,并将其作为上下文信息提供给 LLM。这使得 Agent 能够:
- 快速适应特定项目: 通过加载项目专属的
repo.md,Agent 可以了解项目的架构、编码规范和测试方法。 - 利用领域知识: 比如,当用户提到 "Python" 时,可以自动注入一份关于 Python 最佳实践的
knowledge.md。
0x01 需求
1.1 当前问题
既然单智能体能搞定不少复杂事,为啥还要费劲搞多智能体协作?核心原因很简单:任务一超出一定复杂度,单个智能体就容易 "力不从心"。
就像一个人要同时处理项目规划、数据调研、数学计算、文案撰写一堆事,不仅容易顾此失彼,还会被海量信息淹没 ------ 智能体也一样,面对太多工具要选、太多上下文要记,它的 "思考空间" 根本不够用,决策效率和准确性都会明显下滑。更别说有些任务横跨好几个专业领域,指望一个智能体精通所有,就像让一个医生同时当好工程师,显然不现实。
单一的、庞大的"万能"Agent在处理多任务时,其上下文窗口和工具集会变得异常臃肿,导致"上下文污染)"(Context Pollution),最终影响系统的可靠性和效率。
LangChain的benchmark研究显示,当distractor domains(干扰域)从0增加到8个时,单agent架构的性能从0.67暴跌至0.34,下降50%。
1.2 多智能体系统
而多智能体系统恰好能解决这些痛点。它的核心逻辑是 "分工协作":遇到复杂任务时,先把大任务拆成一个个小模块,比如把 "完成一份市场分析报告" 拆成 "数据收集""统计计算""报告撰写""合规审核" 几个子任务,再给每个子任务分配专门的 "专家" 智能体 ------ 有的专攻数据爬取,有的擅长数学建模,有的精通文案润色。
这些智能体不用包揽所有事,只需要把自己领域的工作做精。它们之间还能随时沟通:数据爬取的智能体拿到素材后,会同步给统计计算的智能体;计算结果出来后,再传给文案智能体,过程中如果遇到问题,还能互相协调调整。这种模式不仅让每个智能体都能发挥专长,还可能产生意外的 "协作红利"------ 就像一个高效的团队,整体能完成的事,远超出单个成员的能力总和。
从实际开发来看,多智能体也更实用:每个智能体都是独立模块,开发时不用考虑全盘,测试和维护起来更简单;如果某个领域需要升级,直接替换对应的智能体就行,不用动整个系统;而且智能体之间怎么沟通、传递信息,都能提前设定好规则,比单个智能体混乱调用工具要可控得多。
1.3 Multi-Agent
多智能体(Multi-Agent)系统与单一智能体工作流的核心区别,在于突破了 "顺序接力" 的任务执行模式,实现了智能体间的高效并行协作。也有研究人员认为,Sub-Agents 是 Multi-Agent 体系下的一种架构模式,而不是一个与 Multi-Agent 对立的总体范式。
Multi-Agent(多智能体系统) ,本质上是一群相对独立的智能体在协作。它们可能有各自的目标、各自的状态、各自的上下文,通过通信协议来协调。就像一个真正的工程团队,产品经理、架构师、工程师、测试,大家有明确的分工,各自负责自己的部分,通过会议、文档、工具来协同。
而Sub-Agent(子代理) ,本质上是集中式架构下的分工。一个主智能体掌控全局,把任务委派给几个专用的子代理。这些子代理更像工具,无状态,只处理被分配的子任务。就像一个项目经理手下有几个专精的执行者。项目经理知道全局目标和上下文,根据需要把任务分发给不同的人,拿到结果后再整合。
关键区别在于:控制权和上下文。
Multi-Agent模式下,控制权是分布式的,每个Agent都有一定的自主权,上下文是隔离的。而Sub-Agent模式下,控制权是集中式的,主智能体说了算,上下文是共享的。
1.4 连接机制
多智能体(Multi-Agent) 的核心思想是:让专业的人干专业的事 。我们创建多个拥有独立 Prompt 和独立工具的 Agent,然后通过某种机制把它们连起来。最基础的连接机制有两种:Handoffs(交接/路由) 和 Orchestrator-Workers(指挥/分发)。
handoffs 指的是一个智能体将其执行上下文和执行权交接给另一个智能体。handoffs需要包含两个最基本的要素:
- 目的地:下一个智能体
- State:传递给下一个智能体的信息
其实,工具调用也是一种连接机制,比如一个智能体(如主管)将另一个智能体作为工具进行调用。移交更适用于自主协作的场景,而工具调用则提供了更明确的层级控制和接口约束。
1.5 Sub-agent
Sub-agent 这一架构的核心设计是引入 "协调者智能体(Orchestrator Agent)" 作为全局管控核心,其核心职责是先深度理解整体任务目标,再通过合理的任务拆解策略,将复杂任务分解为多个可独立执行的子任务,进而委派给多个并行工作的 "子智能体(Sub-agent)"。
该系统的实现逻辑为:
- 在协调者智能体的视角中,调用子智能体与调用普通工具(Tool)的交互模式完全一致。协调者通过工具调用(tool calling)机制,以提示词(prompt)形式向子智能体下达明确指令;
- 子智能体接收指令后,在独立的执行环境中自主完成分配的任务,无需与其他子智能体交互,最终仅将完成结果反馈给协调者。
子智能体架构能够高效落地的本质,是上下文工程(Context Engineering)的成功实践。其核心思想在于精准把控 "信息供给的时机与内容"------ 为每个子智能体创建专注且隔离的执行环境,确保其在处理对应子任务时,能获得最匹配的信息与工具支持。这种设计不仅能大幅提升整个系统的任务处理性能,还能通过职责拆分与环境隔离,降低复杂目标的实现成本,成为应对大规模、多维度复杂任务的高效架构方案。
1.6 microagents
Google Cloud的一篇文章给出了**Agent as Tool(工具式子代理)**的说法。。
Agent as Tool就像一个专家顾问,主智能体调用它时,给出明确的输入,拿到明确的输出,就像调用一个API。这个专家顾问有自己的逻辑,但主智能体不需要知道细节。
而Sub-Agent(委派式子代理)更像一个项目经理的分身,它在主智能体的全局上下文中工作,处理复杂的多步骤流程,可以访问主智能体的对话历史和状态。
结合 Google Cloud 对两种代理模式的核心界定,以及 OpenHands 的 microagents(微代理)的功能特性来看,microagents 本质上属于 Agent as Tool(工具式子代理) 。下面结合两者的核心差异与 microagents 的具体表现展开分析:
-
从核心定义与控制逻辑来看
- Google Cloud 明确,Agent as Tool 是封装好的特定任务专家,主代理调用它时只需传递清晰输入并获取直接输出,类似事务性 API,无需关注其内部逻辑;而 Sub - Agent 是接受委派的角色,需自主处理复杂多步骤流程,和主代理是层级协作关系,拥有一定自主决策与流程管理权。
- OpenHands 的 microagents 不管是知识代理、任务代理还是仓库代理,均是响应特定触发条件来执行固定功能。比如知识代理靠 "docker""container" 等关键词触发,提供对应领域的标准化支持;任务代理按预设交互式模板,接收参数后完成 PR 描述生成等操作。它们不会自主规划复杂任务流程,完全由系统或主流程触发调用,契合 Agent as Tool 的 "被动响应、执行特定功能" 的控制逻辑。
-
从上下文与状态特性来看
- Agent as Tool 具有上下文隔离、无状态的特点,运行在自身独立会话中,无法获取调用方的对话历史和状态,且每次交互的信息都靠单次请求传递。而 Sub - Agent 能共享主代理的上下文,处于同一会话,适合需要多步骤推进的有状态流程。
- microagents 是独立封装的模块,不同代理间相互隔离。例如仓库代理仅加载所在项目的专属规范,知识代理只聚焦单一领域的知识输出。它们的运行不依赖其他代理的历史状态,每次触发都是基于当前的输入信息执行任务并返回结果,不存在与主流程或其他代理共享上下文来推进多步骤任务的情况,符合 Agent as Tool 的无状态、上下文隔离特征。
-
从复用性与耦合度来看
- Agent as Tool 的一大优势是复用性强,可在不同代理或系统中被重复调用,与调用方的耦合度低;而 Sub - Agent 和主代理耦合紧密,是特定流程的一部分,复用性较弱,多适配所属的层级协作体系。
- microagents 的设计着重于高复用性。公共微代理库中的代理可跨不同项目使用,私有仓库代理虽为团队专属,但也是在项目内部的固定场景中重复发挥作用。并且它们可通过简单配置接入系统,无需和主流程进行深度的层级绑定开发,这种高复用、低耦合的特性,和 Sub - Agent 的强耦合特征不符,反而匹配 Agent as Tool 的核心特点。
1.7 原理
1.7.1 协作
多智能体不仅仅是把任务拆分,它引入了一个全新的优化维度,协作(Collaboration)。
有研究者问了一个深刻的问题:为什么两个Agent配合往往比一个超级Agent更好? 答案在于一个新的概率项--- 协作概率:\(P(C_L|a_L)\)。
在多智能体系统中,Agent A(比如产品经理)执行动作\(a_L\)后,不仅仅是产生一个结果,它通过动作产生了一个上下文(Context)\(C_L\),并把这个\(C_L\)传递给Agent B(比如程序员)。
这听起来很抽象,但请这样理解: 协作和协商(Negotiation),本质上是在搜索最优的通信上下文。
- 单体Agent:只能自己闷头干,必须在给定的S下硬解a。
- 多智能体 :Agent A的任务变成了"寻找一种最好的说法(\(C_L\))",使得Agent B成功的概率最大化。
研究者指出,这种"通过对话来动态调整上下文"的能力,实际上是在运行时(Runtime)动态微调系统的参数,而不需要重新训练模型。这就是多智能体系统强大的数学根源,它增加了一个巨大的、可优化的参数空间。
1.7.2 token
Anthropic 在其博客中指出,多智能体系统之所以有效,主要是因为它们投入足够的token来解决问题 。在分析中,三个因素解释了BrowseComp评估中95%的性能差异(该评估测试浏览智能体定位难以找到信息的能力)。研究发现,仅token使用量 就解释了80%的差异,工具调用次数 和模型选择是另外两个解释因素。
1.7.3 成本
虽然多智能体协作听起来很美,但研究者非常冷静地泼了一盆冷水:协作是有成本的(Collaboration Costs)。
用户增加的每一个Agent,每一次交互,都会带来:
- 延迟(Latency):网络请求和生成的耗时。
- 算力消耗(Tokens):真金白银的成本。
- 复杂性(Complexity):系统越复杂,越容易出错。
0x02 基本梳理
2.1 基类定义
Microagents 是 Openhands 中一种模块化的知识注入机制。它们通常是一些 Markdown 文件,包含了针对特定领域、特定仓库或特定任务的知识、指南或代码片段。
从系统架构的角度看,微型代理(Microagents)本质是轻量化的 "专项执行者"------ 它们不负责整体任务的统筹规划,只聚焦某一类特定工作,比如专门处理代码格式化、数据校验这类单一职责。和主智能体(主 Agent)的 "总指挥" 角色不同,它们更像随时待命的 "专业小分队",平时不占用过多系统资源,一旦主智能体需要,要么被直接召唤上场,要么接手主智能体拆分出来的细分任务,灵活又高效。
这些专项执行者并不是孤立的 "散兵",系统早就设计好了一套统一的集成逻辑,核心就是get_microagents_from_selected_repo这个核心方法。具体用起来很简单:用户或者团队可以在自己的代码仓库里,单独建一个文件夹专门存放微型代理,不管是自己开发的,还是适配好的专项工具,都可以放在这里统一管理。等系统将这个仓库设为当前的工作仓库时,会自动扫描这个专属文件夹,把里面所有的微型代理一次性加载进来,相当于为系统搭建了一个 "专项工具储备库"。之后主智能体在处理复杂任务时,比如遇到需要专门做日志分析或者接口调试的环节,就能直接从这个储备库里调取对应的微型代理,一起协同完成工作。
python
class BaseMicroagent(BaseModel):
"""Base class for all microagents."""
name: str
content: str
metadata: MicroagentMetadata
source: str # path to the file
type: MicroagentType
PATH_TO_THIRD_PARTY_MICROAGENT_NAME: ClassVar[dict[str, str]] = {
'.cursorrules': 'cursorrules',
'agents.md': 'agents',
'agent.md': 'agents',
}
2.2 微型代理的类型
大多数微型代理使用带有YAML前导的Markdown文件。对于仓库代理(repo.md),前导是可选的 - 如果未提供,文件将使用默认设置作为仓库代理加载。
KnowledgeMicroagent 和 RepoMicroagent 都是 BaseMicroagent 的子类,但它们有不同的用途和激活机制。这两种微代理类型共同构成了 OpenHands 系统中灵活而强大的知识管理机制,允许同时拥有按需访问的专业知识和持续可用的仓库特定知识。
2.2.1. KnowledgeMicroagent
知识代理提供由对话中的关键词触发的专业技能。它们帮助:
- 语言最佳实践
- 框架指南
- 常见模式
- 工具使用
基本特征
- 类型:MicroagentType.KNOWLEDGE 或 MicroagentType.TASK
- 激活方式:关键词触发,当消息中包含特定触发词时激活
主要功能
- 提供专业领域知识和特定技能指导
- 用于语言最佳实践、框架指南、常见模式和工具使用
- 通过 match_trigger 方法匹配消息中的触发词
激活机制
- 需要在 frontmatter 中定义 triggers 数组
- 仅在用户输入包含触发词时才会被激活
适用场景
- 特定技术栈的使用指南
- 框架或工具的最佳实践
- 特定领域的专业知识
- 需要用户输入的任务型微代理(TaskMicroagent)
python
class KnowledgeMicroagent(BaseMicroagent):
"""Knowledge micro-agents provide specialized expertise that's triggered by keywords in conversations.
They help with:
- Language best practices
- Framework guidelines
- Common patterns
- Tool usage
"""
def __init__(self, **data):
super().__init__(**data)
if self.type not in [MicroagentType.KNOWLEDGE, MicroagentType.TASK]:
raise ValueError('KnowledgeMicroagent must have type KNOWLEDGE or TASK')
def match_trigger(self, message: str) -> str | None:
"""Match a trigger in the message.
It returns the first trigger that matches the message.
"""
message = message.lower()
for trigger in self.triggers:
if trigger.lower() in message:
return trigger
return None
@property
def triggers(self) -> list[str]:
return self.metadata.triggers
可以在OpenHands的GitHub微型代理中看到一个基于知识的代理示例。
2.2.2. RepoMicroagent
仓库代理提供仓库特定的知识和指导方针。它们是:
- 从
.openhands/microagents/repo.md加载 - 特定于个别仓库
- 为其仓库自动激活
- 非常适合团队实践和项目惯例
基本特征
- 类型为
MicroagentType.REPO_KNOWLEDGE - 激活方式:始终激活,与特定仓库关联
主要功能
- 提供仓库特定的知识和指南
- 包含私有的、仓库特定的指令
- 自动加载并与当前仓库关联
激活机制
- 自动激活,不需要触发词
- 在处理相关仓库时始终可用
- 通常来自
.openhands/microagents/repo.md文件
适用场景
- 仓库特定的开发规范
- 团队实践和约定
- 项目特定的工作流程
- 自定义文档引流
- 通用仓库指南
python
class RepoMicroagent(BaseMicroagent):
"""Microagent specialized for repository-specific knowledge and guidelines.
RepoMicroagents are loaded from `.openhands/microagents/repo.md` files within repositories
and contain private, repository-specific instructions that are automatically loaded when
working with that repository. They are ideal for:
- Repository-specific guidelines
- Team practices and conventions
- Project-specific workflows
- Custom documentation references
"""
def __init__(self, **data):
super().__init__(**data)
if self.type != MicroagentType.REPO_KNOWLEDGE:
raise ValueError(
f'RepoMicroagent initialized with incorrect type: {self.type}'
)
可以在OpenHands仓库本身的代理中看到一个仓库代理的示例。
2.2.3 对比
以下是两类微代理的全维度对比,涵盖激活、功能、适用场景等核心差异,便于快速区分与选型:
| 对比维度 | KnowledgeMicroagent(知识型微代理) | RepoMicroagent(仓库型微代理) |
|---|---|---|
| 核心类型 | 通用知识 / 技能载体 | 特定仓库专属知识载体 |
| 激活方式 | 关键词触发(需匹配预设触发词) | 自动激活(与仓库关联后始终生效) |
| 作用范围 | 跨仓库通用(所有场景均可调用) | 绑定特定仓库(仅当前仓库可用) |
| 触发条件 | 需用户输入包含触发词的内容 | 无需额外操作,关联仓库即生效 |
| 使用频率 | 按需使用(用户主动触发) | 持续可用(处理仓库任务时自动调用) |
| 配置要求 | 必须定义 triggers(触发词列表) |
无需配置 triggers |
| 内容来源 | 通用技术文档 / 公共知识库 | 仓库内 .openhands/microagents/repo.md 文件 |
| 典型内容 | 技术栈指南、框架使用方法、领域专业知识 | 仓库开发规范、团队协作约定、项目工作流程 |
| 适用场景 | 解答通用技术问题、工具 / 框架使用指导 | 仓库内开发约束、团队实践对齐、项目流程指引 |
2.2.4 TaskMicroagent
TaskMicroagent 是 KnowledgeMicroagent 的子类,具有特殊的任务导向特性,需要用户输入才能执行。
核心特点
需要用户输入
- 变量提取:通过
extract_variables方法从内容中提取变量(格式为${variable_name}) - 输入检测:
requires_user_input方法检查内容中是否包含变量 - 输入定义:通过
inputs属性获取预定义的输入元数据
特殊触发方式
- 命令式触发:通过格式
/agent_name触发,例如/test_task - 自动触发词添加:如果 frontmatter 中没有定义触发词,系统会自动添加
/前缀的触发词
适用场景
TaskMicroagent 适用于以下场景:
- 需要参数的任务:需要用户提供特定参数才能执行的任务
- 交互式操作:需要与用户进行交互以获取必要信息的操作
- 模板化任务:可以通过填充变量来执行的标准化任务
2.3 微型代理的来源
OpenHands从两个来源加载微型代理:
2.3.1. 可共享微型代理(公共)
此目录(OpenHands/microagents)包含所有OpenHands用户都可以使用的可共享微型代理:
- 由OpenHands仓库维护
- 非常适合重用知识和常见工作流程
目录结构:
python
OpenHands/microagents/
├── # 关键词触发的专业技能
│ ├── git.md # Git 操作
│ ├── testing.md # 测试实践
│ └── docker.md # Docker 指南
├── # 这些微型代理总是加载
├── pr_review.md # PR 审查流程
├── bug_fix.md # Bug 修复工作流程
└── feature.md # 功能实现
2.3.2. 仓库指令(私有)
每个仓库可以在.openhands/microagents/repo.md中拥有自己的指令。这些指令是:
- 私有于该仓库
- 在使用该仓库时自动加载
- 非常适合仓库特定的指导方针和团队实践
示例仓库结构:
python
your-repository/
├── .openhands/
├── microagents/
├── repo.md # 仓库特定的指令
├── ... # 仅在此仓库内可用的私有微型代理
当OpenHands与仓库协作时,它将:
- 如果存在,从
.openhands/microagents/repo.md加载仓库特定的指令 - 根据对话中的关键词加载相关的知识代理
2.4 升级为 Skill
注意:在OpenHands最新代码中,对MicroAgent升级为Skills,我们会在其他系列中进行Skill的相关学习和分析。
https://github.com/OpenHands/extensions
https://docs.openhands.dev/overview/skills
https://docs.openhands.dev/sdk/arch/skill
KnowledgeMicroagent
KnowledgeMicroagent is the legacy name for what is now called a Knowledge Skill (keyword-triggered skill).
Knowledge Skills are keyword-triggered skills that activate when specific keywords are detected in user messages. They use a KeywordTrigger with regex patterns to match against user input, and when matched, inject domain-specific knowledge into the agent's context.
RepoMicroagent
RepoMicroagent is the legacy term for what is now called a Repository Skill (or "General Skill" / "Permanent Context"). These are always-active, repository-specific guidelines that are automatically loaded into the agent's context at conversation start.
The recommended approach is to create an AGENTS.md file at your repository root. This file contains project purpose, setup instructions, repo structure, and development guidelines. It has no trigger --- it's always injected into the system prompt.
You can also use model-specific variants like GEMINI.md or CLAUDE.md. Legacy paths (.openhands/microagents/) are still supported but deprecated in favor of .agents/skills/
0x03 实现
3.1 处理流程
- CodeActAgent 通过
response_to_actions将工具调用转换为AgentDelegateAction。 - AgentController 接收到
AgentDelegateAction并调用start_delegate方法创建新的代理控制器处理委托任务。
3.2 触发条件
AgentDelegateAction 会在以下条件下生成:
- LLM 决定将任务委托给另一个专门的代理,比如LLM 调用名为 delegate_to_browsing_agent 的工具函数
该工具函数需要以下参数:
- agent:要委托给的代理名称
- task:委托的具体任务描述
- 可选的 inputs:传递给委托代理的额外输入参数
python
# ================================================
# AgentDelegateAction (Delegation to another agent)
# ================================================
elif tool_call.function.name == 'delegate_to_browsing_agent':
action = AgentDelegateAction(
agent='BrowsingAgent',
inputs=arguments,
)
3.3 AgentDelegateAction
AgentDelegateAction 是由 LLM 决定委托任务时通过调用相应工具函数生成的,然后通过 response_to_actions 方法处理并添加到待处理动作队列中,最终在 step 方法中返回执行。
python
@dataclass
class AgentDelegateAction(Action):
agent: str
inputs: dict
thought: str = ''
action: str = ActionType.DELEGATE
@property
def message(self) -> str:
return f"I'm asking {self.agent} for help with this task."
3.4 特色
3.4.1 委托代理(Delegate)机制
AgentController 通过 "委托代理(Delegate)机制" 实现对 microAgent(子智能体)的全生命周期控制,核心流程如下:
- 触发启动 :主 Agent 生成
AgentDelegateAction动作(含子智能体名称、任务参数),主控制器通过start_delegate()方法初始化子智能体控制器(AgentController实例,标记is_delegate=True)。 - 资源与状态隔离 :子智能体控制器继承主控制器的事件流、文件存储等资源,但拥有独立的状态(
State),包括独立的迭代次数、预算限制、任务上下文,避免与主 Agent 相互干扰。 - 事件转发与独立执行:子智能体运行期间,主控制器将所有事件(如用户消息、工具反馈)转发给子控制器处理,子智能体独立执行任务(无需主 Agent 干预)。
- 状态监控与终止 :主控制器实时检查子智能体状态,当子智能体完成(
FINISHED)、拒绝(REJECTED)或出错(ERROR)时,通过end_delegate()方法终止子智能体,回收资源并接收其执行结果。 - 结果整合 :子智能体终止后,主控制器将其输出结果封装为
AgentDelegateObservation事件,回传给主 Agent,主 Agent 基于该结果继续执行后续任务。
3.4.2 功能概述
AgentController 是 OpenHands 框架中智能体(Agent)的核心控制组件,负责管理 Agent 的生命周期(启动、运行、终止)、事件处理(动作 / 观察结果)、状态维护、资源调度,以及子智能体(microAgent)的委托与协同,是多智能体系统中实现 "主 - 子 Agent 协作" 与 "任务拆分执行" 的核心枢纽。
- 主 - 子 Agent 交互机制 :
- 主 Agent 以
AgentDelegateAction为 "调用接口",将子任务委托给 microAgent,类比工具调用(Tool Calling)的简洁模式。 - 子智能体拥有独立的控制器实例,运行期间事件全转发、状态全隔离,执行完成后通过
AgentDelegateObservation回传结果,实现 "委托 - 执行 - 回调" 的闭环。
- 主 Agent 以
- 事件处理机制 :
- 事件分流:根据是否存在活跃子智能体,决定事件是转发给子控制器还是主控制器自行处理。
- 类型适配:区分
Action(动作)和Observation(观察结果)事件,分别调用对应处理方法,支持AgentDelegateAction、用户消息、工具反馈等多类事件。 - 步骤触发:通过
should_step()方法判断是否触发 Agent 下一步执行(如用户消息、子智能体结果回调时自动触发)。
- 全生命周期状态管理 :
- 支持
RUNNING、AWAITING_USER_INPUT、AWAITING_USER_CONFIRMATION、FINISHED等多状态切换,状态变更时自动同步到事件流并持久化。 - 子智能体状态实时监控,异常时自动终止并回收资源,确保系统稳定性。
- 支持
- 鲁棒性设计 :
- 防卡死机制:内置
StuckDetector检测 Agent 循环卡死,触发异常处理。 - 预算与迭代限制:通过
State中的iteration_flag和budget_flag控制最大迭代次数与任务预算,避免资源耗尽。 - 异常容错:对 LLM 错误(如上下文窗口溢出、API 超时)、子智能体执行错误等场景,提供降级策略(如历史截断、重试)。
- 防卡死机制:内置
- 多智能体协同支持 :
- 子智能体继承主 Agent 的资源(事件流、文件存储、安全分析器),但状态独立,支持多层级委托(子智能体可再委托其他 microAgent)。
- 主控制器统一汇总所有智能体的执行 metrics(成本、Token 消耗),便于全局监控。
3.4.3 层级型的合作模式
Agent和Microagent之间是层级型的合作模式。:
层级结构:
- Agent是主要的决策者,负责整体任务的执行
- Microagent是小型的、专门化的代理,用于处理特定子任务或提供特定功能
- Agent可以调用Microagent,但Microagent不能直接调用Agent
具体实现方式:
- Microagent被加载到Memory中,作为工具提供给Agent使用
- 在openhands/server/session/agent_session.py中,可以看到microagents通过get_microagents_from_selected_repo方法加载,并通过memory.load_user_workspace_microagents添加到内存中
- Agent可以在需要时调用这些microagents来执行特定任务
两种类型的Microagents:
- Repo Agents:针对特定仓库的代理,处理与该仓库相关的任务
- KnowledgeAgents:提供特定领域知识的代理
工作流程:
- Agent在执行任务时,可以决定是否需要调用某个Microagent
- Microagent执行完任务后,将结果返回给Agent
- Agent根据Microagent的输出继续执行后续操作
加载机制:
- Microagents可以从多个位置加载: 从选定的仓库中的.openhands/microagents目录
- 从组织/用户级别的仓库(例如github.com/acme-co/.openhands/microagents)
因此,这是一种层级型的合作模式,其中Agent作为主控制器,Microagent作为专门的助手提供特定功能,两者之间不是对等的网络型关系,也不是监督者与被监督者的关系。
3.4.4 流程图
主 - 子 Agent 交互与控制流程图
AgentController 核心事件处理流程图
3.4.5 容错和隔离机制
独立的Controller实例
当父Agent创建委托时,会为子Agent创建一个AgentController实例,这确保了子Agent有自己独立的状态管理和事件流程。
python
# Create the delegate with is_delegate=True so it does NOT subscribe directly
self.delegate = AgentController(
sid=self.id + '-delegate',
file_store=self.file_store,
user_id=self.user_id,
agent=delegate_agent,
event_stream=self.event_stream,
conversation_stats=self.conversation_stats,
iteration_delta=self._initial_max_iterations,
budget_per_task_delta=self._initial_max_budget_per_task,
agent_to_llm_config=self.agent_to_llm_config,
agent_configs=self.agent_configs,
initial_state=state,
is_delegate=True,
headless_mode=self.headless_mode,
security_analyzer=self.security_analyzer,
)
独立的状态管理
每个AgentController都有自己的State实例,子Agent的状态变化不会影响父Agent。
事件流隔离
虽然父子Agent共享一个Event Stream,但是通过不同的ID和事件源来区分。
异常处理
系统通过_step_with_exception_handling方法处理异常,子Agent的异常不会中断父Agent的运行。
错误状态传播
当子Agent出错时,会通过AgentDelegateObservation将错误信息发给父Agent。
状态恢复机制
_react_to_exception函数让系统可以从错误状态中恢复。
3.5 代码
具体工作流程如下:
3.5.1 调用大模型
python
class CodeActAgent(Agent):
def step(self, state: State) -> 'Action':
"""Performs one step using the CodeAct Agent."""
initial_user_message = self._get_initial_user_message(state.history)
messages = self._get_messages(condensed_history, initial_user_message)
params: dict = {
'messages': messages,
}
params['tools'] = check_tools(self.tools, self.llm.config)
params['extra_body'] = {
'metadata': state.to_llm_metadata(
model_name=self.llm.config.model, agent_name=self.name
)
}
response = self.llm.completion(**params)
actions = self.response_to_actions(response) # 在这里处理返回值
for action in actions:
self.pending_actions.append(action)
return self.pending_actions.popleft()
3.5.2 解析
如果发现需要调用 delegate_to_browsing_agent,则生成一个 AgentDelegateAction。
python
def response_to_actions(
response: ModelResponse, mcp_tool_names: list[str] | None = None
) -> list[Action]:
# ================================================
# AgentDelegateAction (Delegation to another agent)
# ================================================
elif tool_call.function.name == 'delegate_to_browsing_agent':
action = AgentDelegateAction(
agent='BrowsingAgent',
inputs=arguments,
)
3.5.3 执行
AgentController 中会处理AgentDelegateAction,执行microAgent。
python
async def start_delegate(self, action: AgentDelegateAction) -> None:
"""启动委托智能体以处理子任务。
OpenHands 是多智能体系统:
- 「任务(task)」:系统与用户之间的完整对话,始于用户初始输入(通常是任务描述),
终于智能体发起的完成动作、用户停止操作或错误触发。
- 「子任务(subtask)」:智能体与用户或其他智能体之间的对话。
若单个智能体即可完成任务,则任务与子任务合一;否则任务由多个子任务组成,每个子任务由独立智能体处理。
参数:
action (AgentDelegateAction):包含待启动委托智能体信息的动作对象
"""
# 根据动作中指定的智能体名称,获取对应的智能体类
agent_cls: Type[Agent] = Agent.get_cls(action.agent)
# 获取智能体配置:优先使用动作指定的配置,未指定则复用当前智能体的配置
agent_config = self.agent_configs.get(action.agent, self.agent.config)
# 创建委托智能体实例(确保父子智能体共享LLM注册信息)
# 注:父子智能体共享指标,实现全局指标累积
delegate_agent = agent_cls(
config=agent_config, llm_registry=self.agent.llm_registry
)
# 启动委托智能体前,创建初始状态(继承父智能体关键配置)
state = State(
session_id=self.id.removesuffix('-delegate'), # 会话ID:移除父智能体的委托后缀
user_id=self.user_id, # 继承用户ID,保持用户关联
inputs=action.inputs or {}, # 子任务输入参数(默认为空字典)
iteration_flag=self.state.iteration_flag, # 继承迭代控制标志(限制迭代次数)
budget_flag=self.state.budget_flag, # 继承预算控制标志(限制资源使用)
delegate_level=self.state.delegate_level + 1, # 委托层级+1(标识子智能体层级)
metrics=self.state.metrics, # 共享全局指标(父子智能体指标统一累积)
start_id=self.event_stream.get_latest_event_id() + 1, # 事件起始ID:从最新事件后开始记录
parent_metrics_snapshot=self.state_tracker.get_metrics_snapshot(), # 父智能体指标快照(用于后续对比)
parent_iteration=self.state.iteration_flag.current_value, # 父智能体当前迭代次数
)
# 输出调试日志:记录委托智能体启动信息
self.log(
'debug',
f'start delegate, creating agent {delegate_agent.name}',
)
# 创建委托智能体的控制器(核心:标记is_delegate=True,避免直接订阅事件流)
self.delegate = AgentController(
sid=self.id + '-delegate', # 会话ID:在父ID后添加委托后缀,唯一标识
file_store=self.file_store, # 继承文件存储对象(用于状态持久化)
user_id=self.user_id, # 继承用户ID
agent=delegate_agent, # 待管理的委托智能体实例
event_stream=self.event_stream, # 共享事件流(父子智能体事件互通)
conversation_stats=self.conversation_stats, # 继承对话统计信息
iteration_delta=self._initial_max_iterations, # 迭代次数增量(子任务的最大迭代限制)
budget_per_task_delta=self._initial_max_budget_per_task, # 单任务预算增量(子任务的资源限制)
agent_to_llm_config=self.agent_to_llm_config, # 继承LLM配置映射
agent_configs=self.agent_configs, # 继承智能体配置字典
initial_state=state, # 初始状态(继承父智能体配置后的状态)
is_delegate=True, # 标记为委托智能体(关键:避免重复订阅事件流)
headless_mode=self.headless_mode, # 继承无头模式(无交互界面)配置
security_analyzer=self.security_analyzer, # 继承安全分析器(用于安全校验)
)
3.5.4 微代理记忆提示模板
openhands/microagent/prompts/generate_remember_prompt.j2 是一个 Jinja2 模板文件,其主要作用是生成用于更新特殊参考文件的提示语。这个特殊文件存储着重要的信息和学习成果,用于执行特定任务,并且可以在时间推移过程中扩展以纳入新知识和经验。
核心功能
- 事件分析:分析提供给它的新事件子集
- 更新决策:确定是否需要对特殊参考文件进行更新
- 提示生成:生成指导另一个 AI 正确高效地进行这些更新的提示语
处理流程
- 接收事件数据:通过 {{ events }} 变量接收待分析的事件
- 内容分析:分析这些事件以确定需要更新文件的哪些部分
- 生成更新指令:创建一个结构化的提示,包含具体的更新说明
指导原则
根据模板内容,生成的提示必须遵循以下准则:
内容要求
- 清晰指定:明确指出文件的哪些部分需要更新或添加新章节
- 提供上下文:解释基于新事件为什么需要这些更新
- 具体信息:精确说明应添加或修改的信息内容
格式要求
- 保持结构:维持文件现有的结构和格式
- 保持一致性:确保更新与现有内容一致,不产生矛盾
技术实现
模板结构
jinja2 <update_prompt></update_prompt>
数据流
- 输入:通过 events 变量传入的新事件数据
- 处理:模板逻辑分析事件并生成更新提示
- 输出:包含在 <update_prompt> 标签内的生成提示
应用场景
这个模板主要用于:
记忆维护
- 在 AI 执行任务过程中,持续更新重要知识库
- 确保系统能从新经验中学习并改进
自动化更新
- 使 AI 能够自动维护自己的知识基础
- 减少手动更新参考文件的需求
与其他组件的关系
与微代理系统集成
- 作为 Microagent 系统的一部分,支持知识的动态更新
- 与 KnowledgeMicroagent 和 RepoMicroagent 配合工作
与事件系统连接
- 利用事件系统(EventStream)收集需要学习的事件
- 与 AgentController 协同工作以维护状态和历史记录
这个模板是 OpenHands 系统中实现持续学习和知识管理的关键组件,允许 AI 系统从交互中学习并将这些知识持久化到参考文件中。
3.5.5 get_prompt 函数
get_prompt 函数是一个 FastAPI 路由处理器,位于 /openhands/server/routes/manage_conversations.py 文件中,路径为 /conversations/{conversation_id}/remember-prompt。其主要作用是基于特定事件生成一个提示模板,用于更新特殊参考文件。这个函数是 OpenHands 系统中实现持续学习和知识管理的关键组件之一,它使得 AI 系统能够从对话历史中的特定事件生成更新提示,从而维护和扩展其知识库。
参数处理
conversation_id:通过依赖注入验证的对话 IDevent_id:查询参数,指定要从中获取上下文文件的事件 ID- 其他依赖:用户设置存储、对话元数据等
核心处理步骤
获取事件存储
event_store = EventStore(
sid=conversation_id, file_store=file_store, user_id=metadata.user_id
)
创建一个事件存储实例来访问特定对话的事件历史。
提取上下文事件
调用 _get_contextual_events(event_store, event_id) 方法:
- 获取目标事件前后各 4 个事件(总共约 9 个事件)
- 过滤掉无意义的事件类型(如 NullAction、NullObservation 等)
- 返回格式化的事件字符串
生成提示模板
- 从用户设置中加载 LLM 配置
- 使用
generate_prompt_template函数基于事件内容生成提示模板 - 模板使用
generate_remember_prompt.j2Jinja2 模板
生成最终提示
- 通过
generate_prompt函数调用 LLM 生成最终提示 - 从 LLM 响应中提取 `` 标签之间的内容
与系统其他组件的关系
模板系统
- 使用
generate_remember_prompt.j2模板(位于/openhands/microagent/prompts/目录) - 该模板专门用于生成更新特殊参考文件的提示
事件系统
-
与 EventStore 和事件过滤系统集成 利用 EventFilter 来筛选相关事件
-
与 conversation_manager 集成以请求 LLM 完成
应用场景
这个函数主要用于:
- 记忆维护:生成用于更新 AI 记忆文件的提示
- 知识积累:基于特定事件序列构建知识更新
- 自动化学习:允许 AI 从交互中学习并更新其参考知识
返回值
返回一个 JSON 响应,包含:
- status:请求状态(成功/失败)
- prompt:生成的提示内容,可用于更新特殊参考文件
0xFF 参考
大模型总 "健忘"?Dify 记忆工程架构实践:让 AI 真正记住该记的事
Multi-Agent全面爆发!一文详解多智能体核心架构及LangGraph框架