mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
docs(action): add document for action
This commit is contained in:
@@ -141,10 +141,10 @@ Partner/
|
||||
- [配置中心](doc/config/configuration.md)
|
||||
- [模型提供商](doc/model/providers.md)
|
||||
- [ContextWorkspace](doc/context/context-workspace.md)
|
||||
- [行动系统](doc/action/action.md)
|
||||
|
||||
### 待完成
|
||||
|
||||
- [行动系统](doc/action/action.md)
|
||||
- [记忆存储与组织](doc/memory/memory.md)
|
||||
|
||||
---
|
||||
|
||||
131
doc/action/action-execution.md
Normal file
131
doc/action/action-execution.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# 行动执行
|
||||
|
||||
本文介绍 Partner 中行动系统的执行流程。
|
||||
|
||||
Partner 中的行动分为两类:`StateAction` 和 `ExecutableAction`
|
||||
。前者用于短路径的状态更新或逻辑触发;后者承载完整行动链,会经过阶段推进、参数提取、行动执行、结果记录和必要的纠偏过程。行动建模见 [MetaAction 与行动链建模](meta-action-and-action-chain.md)。
|
||||
|
||||
## 主流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["ActionExecutor.execute"] --> B{"行动类型"}
|
||||
B -- " StateAction " --> C["触发 StateAction.Trigger"]
|
||||
C --> D["写入 state_action_triggered"]
|
||||
D --> Z["结束"]
|
||||
B -- " ExecutableAction " --> E["登记行动并进入 EXECUTING"]
|
||||
E --> F["写入 executable_action_launched"]
|
||||
F --> G["选择当前 stage"]
|
||||
G --> H["为 stage 内 MetaAction 提取参数"]
|
||||
H --> I["提交 MetaAction 执行"]
|
||||
I --> J["记录 HistoryAction"]
|
||||
J --> K["写入 executable_action_stage_settled"]
|
||||
K --> L{"是否需要纠偏?"}
|
||||
L -- " 是 " --> M["应用 MetaIntervention"]
|
||||
M --> N["写入 executable_action_correction_triggered"]
|
||||
N --> O{"是否还有后续 stage?"}
|
||||
L -- " 否 " --> O
|
||||
O -- " 是 " --> G
|
||||
O -- " 否 " --> P["生成最终结果"]
|
||||
P --> Q["写入 executable_action_finished"]
|
||||
Q --> R["触发后续 cognition turn"]
|
||||
```
|
||||
|
||||
主流程中,`StateAction` 和 `ExecutableAction` 使用同一个执行入口,但运行路径不同。
|
||||
|
||||
- `StateAction` 直接触发内部逻辑,适合计时、轮询、状态更新等短任务。
|
||||
- `ExecutableAction` 按行动链推进,适合需要调用一个或多个 `MetaAction` 的任务。
|
||||
|
||||
## ExecutableAction 的阶段推进
|
||||
|
||||
`ExecutableAction` 的行动链按 stage 组织:
|
||||
|
||||
```text
|
||||
actionChain: Map<Int, List<MetaAction>>
|
||||
```
|
||||
|
||||
执行器每次选择当前 stage,并执行该 stage 下的 `MetaAction` 列表。同一个 stage 内的多个 `MetaAction` 可以并发执行;stage
|
||||
之间按顺序推进。
|
||||
|
||||
每个 stage 完成后,执行器会把该阶段的执行结果写入 history,并向 `ContextWorkspace` 发布阶段结算状态。后续
|
||||
stage、纠偏模块和沟通模块都可以基于这些结果继续工作。
|
||||
|
||||
## 参数提取
|
||||
|
||||
`MetaAction` 在行动链中只表示“要调用哪个行动能力”,并不提前固定所有参数。
|
||||
|
||||
执行器在真正执行某个 `MetaAction` 前,会根据当前行动描述、当前 stage 描述和已有执行上下文进行参数提取。提取出的参数写入
|
||||
`metaAction.params` 后,再交给 runner 执行。
|
||||
|
||||
这种设计把“行动规划”和“运行时参数确定”分开:规划阶段确定能力链,执行阶段结合当前上下文确定具体参数。
|
||||
|
||||
## 执行结果与历史
|
||||
|
||||
每个 `MetaAction` 执行后都会产生单步结果,并被记录为 `HistoryAction`。
|
||||
|
||||
`HistoryAction` 记录三类信息:
|
||||
|
||||
- action key
|
||||
- action description
|
||||
- action result
|
||||
|
||||
这些历史用于支撑后续参数提取、纠偏判断、最终结果生成和上下文反馈。
|
||||
|
||||
`ExecutableAction` 的最终结果通常来自行动链最后阶段的执行结果。如果没有明确结果,执行器会根据成功或失败状态生成兜底结果。
|
||||
|
||||
## 纠偏
|
||||
|
||||
执行器不会假设初始行动链一定能一次完成目标。阶段执行后,如果出现失败、偏离目标或最终检查不满足预期,执行器可以触发纠偏流程。
|
||||
|
||||
纠偏流程会生成 `MetaIntervention`,用于调整后续行动链。它可能插入新的行动、替换已有行动,或让行动进入失败状态。
|
||||
|
||||
纠偏事件会写入 `ContextWorkspace`,让后续模块知道行动链为什么发生变化,以及哪些 stage 受到了影响。
|
||||
|
||||
## 超时、失败与恢复
|
||||
|
||||
每个 action 都有 timeout。执行超时后,执行器会取消任务,将 action 标记为 `FAILED`,并写入失败结果。
|
||||
|
||||
失败可能来自:
|
||||
|
||||
- 行动链为空。
|
||||
- 参数提取失败。
|
||||
- `MetaAction` 执行失败。
|
||||
- 纠偏后仍无法继续推进。
|
||||
- 未捕获异常或超时。
|
||||
|
||||
执行器初始化时会恢复未完成行动:状态为 `EXECUTING` 的行动会重新提交;状态为 `INTERRUPTED` 的行动会恢复为 `EXECUTING`
|
||||
后继续执行。恢复事件会写入 `actions_recovered` 上下文块。
|
||||
|
||||
## ContextWorkspace 反馈
|
||||
|
||||
行动执行层会把关键运行事件写入 `ContextWorkspace` 的 `ACTION` 域。
|
||||
|
||||
| 事件 | 含义 |
|
||||
|------------------------------------------|-------------------------|
|
||||
| `actions_recovered` | 执行器恢复了未完成行动 |
|
||||
| `state_action_triggered` | `StateAction` 被触发 |
|
||||
| `executable_action_launched` | `ExecutableAction` 开始执行 |
|
||||
| `executable_action_stage_settled` | 某个 stage 已结算 |
|
||||
| `executable_action_correction_triggered` | 执行过程中触发纠偏 |
|
||||
| `executable_action_finished` | 行动执行完成 |
|
||||
|
||||
这些上下文块让行动状态可以被后续行动评估、认知模块和沟通模块读取。行动完成后,执行器还会触发新的 cognition
|
||||
turn,用于在合适时机向用户反馈结果。
|
||||
|
||||
## 相关组件
|
||||
|
||||
### ParamsExtractor
|
||||
|
||||
`ParamsExtractor` 负责在执行前为 `MetaAction` 生成参数。它根据行动描述、当前 stage 目标和运行时上下文,把抽象的行动能力转换成可提交执行的具体调用参数。
|
||||
|
||||
### RunnerClient
|
||||
|
||||
`RunnerClient` 是 `MetaAction` 的提交入口。执行器完成参数提取后,把 `MetaAction` 交给 `RunnerClient`,由后者根据行动类型路由到对应执行通道。
|
||||
|
||||
### ActionCorrectionRecognizer
|
||||
|
||||
`ActionCorrectionRecognizer` 负责判断行动过程或最终结果是否需要纠偏。它不是执行器本身,而是执行器在阶段结算或最终检查时使用的判断模块。
|
||||
|
||||
### ActionCorrector
|
||||
|
||||
`ActionCorrector` 负责生成纠偏方案。它输出的 `MetaIntervention` 会作用到行动链上,用于补充、替换或调整后续执行步骤。
|
||||
146
doc/action/action-extraction-and-evaluation.md
Normal file
146
doc/action/action-extraction-and-evaluation.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 行动提取与评估
|
||||
|
||||
本文介绍 Partner 行动系统的入口决策层:如何从输入中识别行动意图,并在进入执行或调度前完成可行性评估。
|
||||
|
||||
行动提取与评估位于 `ActionPlanner` 的前半段。它的输出不是执行结果,而是一个明确的运行时决策:本轮输入是否应进入行动系统;如果进入,应当成为即时行动、计划行动,还是等待用户确认。
|
||||
|
||||
## 入口决策层
|
||||
|
||||
行动系统面对的输入通常并不直接等同于行动。用户可能是在提问、讨论、确认、补充条件,也可能是在要求系统实际完成某件事。入口决策层负责把这些情况分开。
|
||||
|
||||
它需要在三个对象之间建立关系:
|
||||
|
||||
- **输入意图**:来自用户或认知模块的当前表达。
|
||||
- **上下文状态**:当前会话、感知、记忆、认知和已有行动状态。
|
||||
- **行动能力**:系统当前真实可用的 `MetaAction` 集合。
|
||||
|
||||
只有当一个意图能在当前上下文中被解释为可推进目标,并且存在可承接的行动能力时,它才会进入行动建模与执行链路。
|
||||
|
||||
## 分层职责
|
||||
|
||||
行动入口被拆成三个层次:
|
||||
|
||||
```text
|
||||
ActionExtractor
|
||||
负责发现候选行动倾向
|
||||
|
||||
ActionEvaluator
|
||||
负责判断候选倾向是否可推进
|
||||
|
||||
ActionPlanner
|
||||
负责把评估结果装配为运行时行动
|
||||
```
|
||||
|
||||
三者的边界如下:
|
||||
|
||||
| 层次 | 输入 | 输出 | 不负责 |
|
||||
|-------------------|--------------------------|--------------------------------------------------------------|----------------|
|
||||
| `ActionExtractor` | 当前输入 + 上下文 | `tendencies` | 生成行动链、判断能力是否足够 |
|
||||
| `ActionEvaluator` | 单条 tendency + 上下文 + 可用能力 | `EvaluatorResult` | 执行行动、持久化行动对象 |
|
||||
| `ActionPlanner` | `EvaluatorResult` | `ExecutableAction` / pending confirmation / refused tendency | 重新解释用户意图 |
|
||||
|
||||
这种分层使“发现可能要做的事”和“决定能不能做”相互独立。提取阶段保持轻量,评估阶段负责执行前的结构化判断,Planner 负责与行动运行时交接。
|
||||
|
||||
## 主流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input["当前输入"] --> Extractor["ActionExtractor"]
|
||||
ContextA["ContextWorkspace\n对话 / 感知 / 行动状态"] -.-> Extractor
|
||||
Extractor --> T{"tendencies"}
|
||||
T -- " empty " --> NoAction["不进入行动系统"]
|
||||
T -- " non-empty " --> IntentBlock["发布 pending_action_intentions"]
|
||||
IntentBlock --> Evaluator["ActionEvaluator"]
|
||||
ContextB["ContextWorkspace\n行动 / 对话 / 感知 / 记忆 / 认知"] -.-> Evaluator
|
||||
Capabilities["available_meta_actions"] -.-> Evaluator
|
||||
Evaluator --> Decision{"EvaluatorResult"}
|
||||
Decision -- " rejected " --> Refused["拒绝进入行动链"]
|
||||
Decision -- " need confirmation " --> Pending["actions_need_confirmation"]
|
||||
Decision -- " immediate " --> Immediate["ImmediateExecutableAction"]
|
||||
Decision -- " planned " --> Scheduled["SchedulableExecutableAction"]
|
||||
Pending --> ConfirmTurn["请求用户确认"]
|
||||
Immediate --> Executor["ActionExecutor"]
|
||||
Scheduled --> Scheduler["ActionScheduler"]
|
||||
```
|
||||
|
||||
## ActionExtractor:候选行动发现
|
||||
|
||||
`ActionExtractor` 的职责是从当前输入中提取行动倾向。倾向不是行动计划,而是一个待评估目标。
|
||||
|
||||
行动倾向应保持“目标语义”,而不是“执行语义”。例如用户说“先检查配置,再修复问题,最后告诉我结果”,提取结果应表达为一个整体目标,而不是拆成多个工具步骤。
|
||||
|
||||
提取阶段只决定是否存在候选目标,不承诺该目标一定能执行。它不会绑定 `MetaAction`,也不会判断是否需要确认。
|
||||
|
||||
### 提取边界
|
||||
|
||||
行动倾向通常对应以下语义:
|
||||
|
||||
- 用户要求系统代为完成某件事。
|
||||
- 当前目标需要访问能力、操作对象、修改状态或安排未来动作。
|
||||
- 该目标如果不进入行动链,就无法被实际推进。
|
||||
|
||||
普通交流不应进入行动系统。解释、分析、翻译、总结、评价、闲聊,以及可直接基于当前上下文回答的问题,都应留给沟通模块处理。
|
||||
|
||||
这条边界能避免行动系统过度激活。
|
||||
|
||||
## ActionEvaluator:执行前判断
|
||||
|
||||
`ActionEvaluator` 对单条 tendency 做执行前判断。它不重新提取意图,而是在当前上下文和能力集合下判断该倾向是否可推进。
|
||||
|
||||
评估阶段主要形成四类结论:
|
||||
|
||||
| 结论 | 含义 |
|
||||
|------|--------------------------------------|
|
||||
| 拒绝 | 倾向不应进入行动链,原因可能是无需行动、能力不足、目标不清或已有行动覆盖 |
|
||||
| 待确认 | 行动可以建模,但执行前需要用户确认 |
|
||||
| 即时行动 | 行动可以立即装配并交给执行器 |
|
||||
| 计划行动 | 行动应按未来时间、周期或条件进入调度器 |
|
||||
|
||||
评估结果通过 `EvaluatorResult` 表达。它是 planner 的输入契约,而不是给用户展示的回复文案。
|
||||
|
||||
## 确认与 pending action
|
||||
|
||||
某些行动虽然可以建模,但不能直接执行。典型原因包括副作用、权限风险、外部可见影响、长期运行影响或用户意图仍需最终确认。
|
||||
|
||||
这类行动会进入 pending confirmation:
|
||||
|
||||
```text
|
||||
EvaluatorResult(ok=true, needConfirm=true)
|
||||
↓
|
||||
ActionPlanner
|
||||
↓
|
||||
ContextWorkspace.register(actions_need_confirmation)
|
||||
↓
|
||||
后续输入承接确认 / 取消 / 修改
|
||||
```
|
||||
|
||||
> pending confirmation 将借助 CommunicationBlockContent 作为输入时的补充块,用来及时提醒交流模块生产合适响应。
|
||||
|
||||
## Planner 装配
|
||||
|
||||
`ActionPlanner` 异步接收评估结果后,把结构化决策转换为运行时对象。
|
||||
|
||||
主要装配动作包括:
|
||||
|
||||
- 将 `primaryActionChain` 转换为 `Map<Int, List<MetaAction>>`。
|
||||
- 加载每个 action key 对应的 `MetaAction`。
|
||||
- 根据 `MetaActionInfo` 修正顺序并补齐严格依赖。
|
||||
- 根据评估类型构造 `ImmediateExecutableAction` 或 `SchedulableExecutableAction`。
|
||||
- 对待确认行动注册 `actions_need_confirmation`。
|
||||
- 对已承接的 pending block 执行过期。
|
||||
- 将即时行动交给 `ActionExecutor`,将计划行动交给 `ActionScheduler`。
|
||||
|
||||
Planner 是入口决策层与运行时执行层之间的边界。评估器给出“应该怎样推进”,Planner 负责把这个结论变成可运行对象。
|
||||
|
||||
## 输出流向
|
||||
|
||||
评估和装配后的输出分为四种:
|
||||
|
||||
| 流向 | 说明 |
|
||||
|---------|-----------------------------------------|
|
||||
| 不进入行动系统 | 没有提取到 tendency,或评估认为无需行动 |
|
||||
| 等待确认 | 行动可建模,但先写入 pending confirmation |
|
||||
| 即时执行 | 构造 `ImmediateExecutableAction` 并交给执行器 |
|
||||
| 计划调度 | 构造 `SchedulableExecutableAction` 并交给调度器 |
|
||||
|
||||
这四种流向共同构成行动系统的入口安全阀。任何行动在进入执行器前,都必须先经过提取、评估和 planner 装配。
|
||||
36
doc/action/action-infrastructure.md
Normal file
36
doc/action/action-infrastructure.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 行动基础设施
|
||||
|
||||
本文是 Partner 行动基础设施文档索引。行动基础设施位于 `ActionExecutor / ActionCore` 之下,负责把已经完成参数填充的 `MetaAction` 路由到对应运行通道,并为本地 action、外部 MCP、命令执行和行动描述信息提供底层支撑。
|
||||
|
||||
```text
|
||||
ActionExecutor
|
||||
↓
|
||||
RunnerClient
|
||||
↓
|
||||
MCP / BUILTIN / ORIGIN
|
||||
↓
|
||||
ExecutionPolicy / CommandExecutionService / OriginExecutionService
|
||||
```
|
||||
|
||||
## 定位
|
||||
|
||||
行动基础设施负责把“结构化行动单元”转换成真实执行。
|
||||
|
||||
它主要处理:
|
||||
|
||||
- `MetaAction` 的提交与结果写回。
|
||||
- 不同 `MetaAction.Type` 的执行路由。
|
||||
- 内部能力与外部 MCP 能力的接入。
|
||||
- 命令执行前的策略包装。
|
||||
- 本地 action 文件的执行。
|
||||
- 临时行动与持久化行动的序列化入口。
|
||||
- `MetaActionInfo` 的描述生成与覆写。
|
||||
|
||||
本文只保留总览和目录;细节拆分到以下文档。
|
||||
|
||||
## 目录
|
||||
|
||||
- [`infra/runner-client.md`](infra/runner-client.md):说明 `RunnerClient`、`LocalRunnerClient` 以及 `MCP` / `BUILTIN` / `ORIGIN` 三类执行路由。
|
||||
- [`infra/execution-policy.md`](infra/execution-policy.md):说明执行策略如何把原始命令包装为 `WrappedLaunchSpec`,以及 `direct` / `bwrap` provider 的职责。
|
||||
- [`infra/command-execution-service.md`](infra/command-execution-service.md):说明一次性命令、持久命令、`CommandSession` 和 `Result` 的处理方式。
|
||||
- [`infra/meta-action-info.md`](infra/meta-action-info.md):说明 `MetaActionInfo` 的来源、描述覆写和在行动系统中的生效位置。
|
||||
192
doc/action/action-planning-and-action-scheduling.md
Normal file
192
doc/action/action-planning-and-action-scheduling.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# 行动计划与调度
|
||||
|
||||
本文介绍 Partner 行动系统中的计划行动与调度机制。
|
||||
|
||||
计划调度负责处理“不是立即执行”的行动,包括未来某一时间执行、周期性执行,以及用于状态更新或轮询的内部触发任务。它把行动从当前对话轮次中解耦出来,使行动可以在后续时间点重新进入执行流程。
|
||||
|
||||
## 调度对象
|
||||
|
||||
行动系统中可以被调度的对象都实现 `Schedulable`。
|
||||
|
||||
`Schedulable` 主要描述三个信息:
|
||||
|
||||
- `scheduleType`:调度类型,当前包括 `ONCE` 和 `CYCLE`。
|
||||
- `scheduleContent`:调度内容,按调度类型保存执行时间或 cron 表达式。
|
||||
- `enabled`:调度对象是否仍然有效。
|
||||
|
||||
当前主要有两类调度对象:
|
||||
|
||||
| 类型 | 作用 |
|
||||
|-------------------------------|-------------------------------------|
|
||||
| `SchedulableExecutableAction` | 带行动链的计划行动,到期后交给 `ActionExecutor` 执行 |
|
||||
| `StateAction` | 不带行动链的状态触发任务,到期后直接触发内部逻辑 |
|
||||
|
||||
`SchedulableExecutableAction` 用于用户可感知的未来行动;`StateAction` 更多用于系统内部,例如状态更新、轮询 watcher、一次性逻辑触发。
|
||||
|
||||
## 主流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["ActionPlanner / 系统模块"] --> B["构造 Schedulable"]
|
||||
B --> C["ActionScheduler.schedule"]
|
||||
C --> D["加入运行期调度集合"]
|
||||
C --> E["持久化计划行动\n仅 SchedulableExecutableAction"]
|
||||
C --> F["TimeWheel.schedule"]
|
||||
F --> G{"scheduleType"}
|
||||
G -- " ONCE " --> H["解析 ZonedDateTime"]
|
||||
G -- " CYCLE " --> I["解析 Quartz cron"]
|
||||
H --> J["计算下一次触发时间"]
|
||||
I --> J
|
||||
J --> K["放入小时分组 / 秒级时间轮"]
|
||||
K --> L["时间轮 tick"]
|
||||
L --> M["收集到期 Schedulable"]
|
||||
M --> N["ActionExecutor.execute"]
|
||||
N --> O{"周期任务且仍 enabled?"}
|
||||
O -- " 是 " --> P["等待本轮执行结束"]
|
||||
P --> F
|
||||
O -- " 否 " --> Q["结束"]
|
||||
```
|
||||
|
||||
调度器只决定“什么时候触发”,不决定“如何执行”。到期后的对象统一交给 `ActionExecutor`。
|
||||
|
||||
## 调度类型
|
||||
|
||||
### ONCE
|
||||
|
||||
`ONCE` 表示一次性调度。
|
||||
|
||||
`scheduleContent` 保存一个可解析为 `ZonedDateTime` 的时间字符串。调度器会在该时间点触发 action;触发后不会再次调度。
|
||||
|
||||
一次性调度适合:
|
||||
|
||||
- 指定时间执行某个行动。
|
||||
- 延迟执行一次任务。
|
||||
- 创建只触发一次的内部 watcher。
|
||||
|
||||
### CYCLE
|
||||
|
||||
`CYCLE` 表示周期性调度。
|
||||
|
||||
`scheduleContent` 保存 Quartz cron 表达式。每次触发后,如果调度对象仍然有效,并且本轮执行已经结束,调度器会计算下一次执行时间并重新放入时间轮。
|
||||
|
||||
周期性调度适合:
|
||||
|
||||
- 周期检查。
|
||||
- 周期同步。
|
||||
- 长期状态更新。
|
||||
- 按固定频率轮询某个 action 状态。
|
||||
|
||||
## 相关组件
|
||||
|
||||
### ActionScheduler
|
||||
|
||||
`ActionScheduler` 是调度入口。
|
||||
|
||||
它负责:
|
||||
|
||||
- 接收新的 `Schedulable`。
|
||||
- 保存运行期调度对象。
|
||||
- 持久化用户可感知的计划行动。
|
||||
- 把调度对象交给 `TimeWheel`。
|
||||
- 在触发时调用 `ActionExecutor.execute(action)`。
|
||||
- 取消指定 action id 对应的调度对象。
|
||||
|
||||
调度器本身不执行行动链,也不处理 `MetaAction` 参数。到期后的执行仍然统一交给 `ActionExecutor`。
|
||||
|
||||
### TimeWheel
|
||||
|
||||
`TimeWheel` 是调度器内部的时间轮实现。
|
||||
|
||||
它维护两层结构:
|
||||
|
||||
- 按小时分组的调度对象集合。
|
||||
- 当前小时内按秒排列的触发 bucket。
|
||||
|
||||
这种结构避免对所有计划任务进行高频全量扫描。调度器按天和小时刷新任务,再在当前小时内用秒级 tick 收集到期任务。
|
||||
|
||||
当某个 tick 到达时,时间轮会收集对应 bucket 中仍然 `enabled` 的对象,并调用触发回调。当前触发回调会把这些对象交给
|
||||
`ActionExecutor`。
|
||||
|
||||
## 周期任务重入
|
||||
|
||||
周期任务触发后不会立即无条件重新入轮。
|
||||
|
||||
对于 `CYCLE` 类型,时间轮会等待本轮 action 满足完成条件后再重新计算下一次执行时间。当前完成条件主要依据 action 状态:
|
||||
|
||||
- `SUCCESS`
|
||||
- `FAILED`
|
||||
|
||||
如果调度对象在等待期间被取消,或 `enabled=false`,则不会再次调度。
|
||||
|
||||
这可以避免周期任务在上一轮尚未结束时重复触发,导致同一个行动并发堆积。
|
||||
|
||||
## 持久化与运行期调度
|
||||
|
||||
调度对象有两类来源:
|
||||
|
||||
- **持久化行动**:主要是 `SchedulableExecutableAction`,通过 `ActionCapability` 保存和恢复。
|
||||
- **运行期调度**:例如 watcher 或内部 `StateAction`,只保存在当前运行期。
|
||||
|
||||
调度器初始化时会同时读取持久化计划行动和仍然有效的运行期调度对象,并装载到时间轮中。
|
||||
|
||||
这使用户可感知的计划行动能够跨运行周期保留,而内部临时调度可以只存在于当前运行期。
|
||||
|
||||
## 取消调度
|
||||
|
||||
`ActionScheduler.cancel(actionId)` 会从两个来源查找调度对象:
|
||||
|
||||
- 当前运行期调度集合。
|
||||
- 已持久化的 `SchedulableExecutableAction`。
|
||||
|
||||
找到后会将其 `enabled` 置为 `false`,并尝试从时间轮的 bucket 中移除。
|
||||
|
||||
取消并不依赖对象当前位于哪一层时间结构:无论对象还在小时分组中,还是已经进入当前小时的秒级时间轮,都会尝试移除。
|
||||
|
||||
## 与行动执行的关系
|
||||
|
||||
调度器只负责触发时机,执行过程仍由 `ActionExecutor` 负责。
|
||||
|
||||
```text
|
||||
ActionScheduler
|
||||
↓
|
||||
ActionExecutor.execute(action)
|
||||
```
|
||||
|
||||
因此:
|
||||
|
||||
- `SchedulableExecutableAction` 到期后按完整行动链执行。
|
||||
- `StateAction` 到期后触发内部逻辑。
|
||||
- 执行结果、失败、纠偏和上下文反馈仍然由执行器负责。
|
||||
|
||||
## 与计划行动的关系
|
||||
|
||||
计划行动通常来自行动评估结果中的规划流向:
|
||||
|
||||
```text
|
||||
ActionEvaluator
|
||||
↓
|
||||
EvaluatorResult(type = PLANNING, scheduleData)
|
||||
↓
|
||||
ActionPlanner
|
||||
↓
|
||||
SchedulableExecutableAction
|
||||
↓
|
||||
ActionScheduler.schedule
|
||||
```
|
||||
|
||||
`SchedulableExecutableAction` 保留普通 `ExecutableAction` 的行动链、原因、描述和来源,同时增加调度类型和调度内容。到期执行时,它与即时行动使用同一个执行器。
|
||||
|
||||
周期性计划行动执行结束后,会记录本轮执行历史并重置内部状态,以便下一次触发重新执行同一行动链。
|
||||
|
||||
## 时间表达
|
||||
|
||||
当前调度内容由 `scheduleType` 决定解析方式:
|
||||
|
||||
| `scheduleType` | `scheduleContent` |
|
||||
|----------------|---------------------|
|
||||
| `ONCE` | `ZonedDateTime` 字符串 |
|
||||
| `CYCLE` | Quartz cron 表达式 |
|
||||
|
||||
如果调度内容无法解析,调度器会跳过加载该调度对象并记录失败状态。
|
||||
|
||||
一次性任务如果已经过期,或不属于当前日期,也不会进入时间轮。
|
||||
@@ -0,0 +1,144 @@
|
||||
# 行动系统
|
||||
|
||||
本文说明 Partner 中行动系统的流程总览。行动系统负责把来自用户、认知模块或计划模块的“意图”转化为可建模、可评估、可执行、可调度、可追踪的行动对象。
|
||||
|
||||
在 Partner 中,行动不是一次简单的 tool call。一次行动可能包含多个阶段,每个阶段又可以包含多个可执行的 `MetaAction`;它可能被立即执行,也可能被登记为未来的一次性或周期性计划;它的执行过程还需要与 `ContextWorkspace` 交互,让行动提取、评估、执行和结果反馈都能进入统一的上下文循环。
|
||||
|
||||
## 目录
|
||||
|
||||
行动系统的细节设计拆分到以下专题文档中说明:
|
||||
|
||||
- [`meta-action-and-action-chain.md`](meta-action-and-action-chain.md):说明 `MetaAction`、`ExecutableAction` 与行动链的建模方式。
|
||||
- [`action-extraction-and-evaluation.md`](action-extraction-and-evaluation.md):说明如何从输入意图中提取候选行动,并判断行动是否完整、可执行、需要确认或需要调度。
|
||||
- [`action-execution.md`](action-execution.md):说明行动执行器如何按阶段执行行动链,并处理结果、失败、中断与恢复。
|
||||
- [`action-planning-and-action-scheduling.md`](action-planning-and-action-scheduling.md):说明未来行动、周期行动与状态触发行动如何被计划和调度。
|
||||
- [`action-infrastructure.md`](action-infrastructure.md):说明 `RunnerClient`、执行策略、命令执行和 Origin action 等底层行动基础设施。
|
||||
|
||||
本文只提供总览,不展开每个模块的内部实现细节。
|
||||
|
||||
## 核心对象
|
||||
|
||||
行动系统围绕三类对象组织:
|
||||
|
||||
- `MetaAction`:行动链中的单步可执行元素,用于描述具体要调用的行动程序。它包含行动名称、类型、位置、参数和执行结果。当前行动类型包括 `MCP`、`ORIGIN` 与 `BUILTIN`。
|
||||
- `ExecutableAction`:可执行行动的抽象基类,承载行动来源、原因、描述、倾向、状态、阶段化行动链、阶段描述、执行历史与最终结果。
|
||||
- `Schedulable`:可调度对象的统一接口,用于表达一次性或周期性触发。`SchedulableExecutableAction` 表示未来执行的行动链,`StateAction` 表示到期或周期性触发的状态更新 / 逻辑调用。
|
||||
|
||||
从粒度上看:
|
||||
|
||||
```text
|
||||
Action
|
||||
├─ ExecutableAction
|
||||
│ ├─ ImmediateExecutableAction
|
||||
│ └─ SchedulableExecutableAction
|
||||
└─ StateAction
|
||||
|
||||
ExecutableAction
|
||||
└─ actionChain: Map<Stage, List<MetaAction>>
|
||||
```
|
||||
|
||||
也就是说,`MetaAction` 是最小执行单元,`ExecutableAction` 是面向一次任务的行动容器,`Schedulable` 则给行动增加时间维度。
|
||||
|
||||
## 系统流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
|
||||
Context["ContextWorkspace<br/>上下文工作空间"]
|
||||
|
||||
A["输入意图"] --> B["行动提取"]
|
||||
B --> C["行动评估"]
|
||||
C --> D["MetaAction / ActionChain"]
|
||||
D --> E{"执行时机"}
|
||||
E -- "立即" --> F["行动执行"]
|
||||
E -- "未来 / 条件 / 周期" --> G["计划调度"]
|
||||
G --> F
|
||||
F --> H["结果反馈"]
|
||||
H --> I["结束"]
|
||||
|
||||
B -.-> Context
|
||||
C -.-> Context
|
||||
D -.-> Context
|
||||
F -.-> Context
|
||||
H -.-> Context
|
||||
```
|
||||
|
||||
虚线表示 `ContextWorkspace` 对行动系统的横切参与:行动提取与评估会读取当前上下文,行动建模、执行与反馈则可能发布新的上下文块,供后续认知、记忆和沟通模块继续使用。
|
||||
|
||||
## 主链路说明
|
||||
|
||||
一次行动通常经历以下阶段:
|
||||
|
||||
1. **输入意图**
|
||||
|
||||
输入意图可以来自用户显式指令,也可以来自认知模块推导出的内部目标,或来自已经登记的计划行动。行动系统首先要判断这段意图是否包含“需要系统实际去做”的部分。
|
||||
|
||||
2. **行动提取**
|
||||
|
||||
行动提取负责把自然语言意图或内部决策结果转化为候选行动。此阶段关注“是否存在可执行目标”,而不是立即执行。提取结果可能只是一个候选行动,也可能是一组带有阶段关系的行动链。
|
||||
|
||||
3. **行动评估**
|
||||
|
||||
行动评估负责判断候选行动是否可以进入后续流程。评估内容包括参数是否完整、能力是否存在、风险是否可接受、是否需要用户确认、是否应该立即执行,以及是否应该转化为未来计划。
|
||||
|
||||
4. **行动建模**
|
||||
|
||||
通过评估的行动会被建模为 `ExecutableAction`。如果一个任务需要多个步骤,它会被拆成按阶段组织的 `actionChain`:外层 stage 表示阶段顺序,内层 `MetaAction` 列表表示同一阶段下的一组行动单元。
|
||||
|
||||
5. **执行时机判断**
|
||||
|
||||
行动建模后会进入执行时机分流:
|
||||
|
||||
- 即时行动会成为 `ImmediateExecutableAction`,交给执行器运行。
|
||||
- 未来行动会成为 `SchedulableExecutableAction`,由调度器在指定时间、周期或条件满足后触发。
|
||||
- 状态类触发任务会成为 `StateAction`,用于周期性状态更新或普通逻辑调用。
|
||||
|
||||
6. **行动执行**
|
||||
|
||||
行动执行器按阶段执行行动链。每个 `MetaAction` 会根据类型映射到不同执行通道:`MCP` 调用 MCP tool,`BUILTIN` 调用内置行动注册表,`ORIGIN` 对应临时生成或持久化的行动程序。执行过程中会更新行动状态、阶段、单步结果与历史记录。
|
||||
|
||||
7. **结果反馈**
|
||||
|
||||
执行结束后,行动结果会回写到行动对象,并通过上下文、trace 或用户可见响应进入反馈闭环。成功结果可以成为后续认知和记忆的输入;失败、中断或修正信息也会被保留,以便后续恢复、纠错或重新规划。
|
||||
|
||||
## 状态与生命周期
|
||||
|
||||
`Action` 使用统一状态描述生命周期:
|
||||
|
||||
- `PREPARE`:行动已创建,等待执行或调度。
|
||||
- `EXECUTING`:行动正在执行。
|
||||
- `INTERRUPTED`:行动被暂时中断,等待恢复或超时退出。
|
||||
- `SUCCESS`:行动执行成功。
|
||||
- `FAILED`:行动执行失败。
|
||||
|
||||
`MetaAction` 自身也维护单步结果状态:
|
||||
|
||||
- `WAITING`:单步行动尚未执行或已经重置。
|
||||
- `SUCCESS`:单步行动执行成功。
|
||||
- `FAILED`:单步行动执行失败。
|
||||
|
||||
这种双层状态设计区分了“整个行动任务的生命周期”和“行动链中单个执行单元的结果”。
|
||||
|
||||
## 与 ContextWorkspace 的关系
|
||||
|
||||
行动系统与 `ContextWorkspace` 不是上下级关系,而是运行时协作关系。
|
||||
|
||||
- 行动提取需要读取上下文,以判断用户当前意图与历史状态之间的关系。
|
||||
- 行动评估需要读取上下文,以判断参数、权限、风险和依赖是否满足。
|
||||
- 行动建模可以把待执行行动、阶段目标或计划信息发布为上下文块。
|
||||
- 行动执行可以把执行中的状态、当前阶段、失败原因或等待条件暴露给上下文系统。
|
||||
- 结果反馈可以把最终结果、历史记录或修正信息写回上下文,供后续认知模块继续使用。
|
||||
|
||||
因此,`ContextWorkspace` 是行动系统的上下文协调层:它不直接决定行动如何执行,但影响行动如何被理解、评估、观察和回收反馈。
|
||||
|
||||
## 设计取向
|
||||
|
||||
行动系统的目标是让 Partner 的“行动能力”具备以下特征:
|
||||
|
||||
- **可建模**:行动不是临时字符串,而是带有来源、原因、描述、阶段、状态和结果的结构化对象。
|
||||
- **可组合**:复杂任务可以拆成阶段化行动链,每个阶段包含一个或多个 `MetaAction`。
|
||||
- **可评估**:行动在执行前经过提取与评估,避免把所有意图都直接转成工具调用。
|
||||
- **可调度**:未来行动、周期行动和状态触发行动可以脱离当前对话轮次继续存在。
|
||||
- **可观察**:行动状态、执行历史、结果和上下文反馈可以被追踪,便于调试与后续推理。
|
||||
|
||||
总览页只描述行动系统的主干。后续专题文档会分别展开行动建模、提取评估、执行机制、计划调度和基础设施。
|
||||
77
doc/action/infra/command-execution-service.md
Normal file
77
doc/action/infra/command-execution-service.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# CommandExecutionService
|
||||
|
||||
`CommandExecutionService` 是命令执行的最低层服务。它接收 `WrappedLaunchSpec` 或原始命令数组,负责启动进程、传入环境变量与工作目录、收集输出,并把进程执行结果转换为结构化 `Result`。
|
||||
|
||||
它有两类执行路径:
|
||||
|
||||
- 一次性命令:调用 `exec(...)`,等待进程结束后返回完整结果。
|
||||
- 持久命令:调用 `createSessionTask(...)`,返回 `CommandSession`,由调用方持有进程与输出缓冲区。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["CommandExecutionService"] --> B{"调用入口"}
|
||||
B -- " exec(List/String...) " --> C["defaultLaunchSpec<br/>command + args + System.getenv"]
|
||||
B -- " exec(WrappedLaunchSpec) " --> D["WrappedLaunchSpec"]
|
||||
C --> D
|
||||
D --> E["startProcess"]
|
||||
E --> F["ProcessBuilder"]
|
||||
F --> G["command + args"]
|
||||
F --> H["workingDirectory"]
|
||||
F --> I["environment<br/>clear + putAll"]
|
||||
G --> J["process.start"]
|
||||
H --> J
|
||||
I --> J
|
||||
J --> K["stdout virtual thread<br/>collect lines"]
|
||||
J --> L["stderr virtual thread<br/>collect lines"]
|
||||
J --> M["process.waitFor"]
|
||||
K --> N["join"]
|
||||
L --> N
|
||||
M --> N
|
||||
N --> O["Result"]
|
||||
O --> P["ok = exitCode == 0"]
|
||||
O --> Q["stdoutLines / stderrLines"]
|
||||
O --> R["resultList<br/>stdout 优先,否则 stderr"]
|
||||
O --> S["total<br/>stdout + stderr 展示文本"]
|
||||
```
|
||||
|
||||
一次性命令会同步等待进程结束,并用两个虚拟线程分别读取 stdout 和 stderr。进程退出后,执行服务等待输出读取线程结束,再构造 `Result`。
|
||||
|
||||
`Result` 中几个字段的语义如下:
|
||||
|
||||
| 字段 | 语义 |
|
||||
|---|---|
|
||||
| `ok` | 进程退出码是否为 0,或异常路径下为 false |
|
||||
| `stdoutLines` | 标准输出逐行结果 |
|
||||
| `stderrLines` | 标准错误逐行结果 |
|
||||
| `resultList` | 优先使用 stdout;如果 stdout 为空,则使用 stderr |
|
||||
| `total` | 合并后的展示文本,stdout 和 stderr 都存在时按顺序拼接 |
|
||||
|
||||
异常会被转换为失败结果:`ok=false`,`stderrLines` 与 `resultList` 保存异常信息,`total` 保存异常 message。
|
||||
|
||||
持久命令路径用于需要保留进程句柄和持续读取输出的场景:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["createSessionTask(List/String...)"] --> B["defaultLaunchSpec"]
|
||||
A2["createSessionTask(WrappedLaunchSpec)"] --> C["startProcess"]
|
||||
B --> C
|
||||
C --> D["Process"]
|
||||
D --> E["CommandSession"]
|
||||
E --> F["process"]
|
||||
E --> G["stdoutBuffer"]
|
||||
E --> H["stderrBuffer"]
|
||||
D --> I["stdout virtual thread<br/>readToBuffer"]
|
||||
D --> J["stderr virtual thread<br/>readToBuffer"]
|
||||
I --> G
|
||||
J --> H
|
||||
```
|
||||
|
||||
`CommandSession` 不等待进程结束,也不生成 `Result`。它保留 `Process`、`stdoutBuffer` 和 `stderrBuffer`,让上层可以在长生命周期命令运行期间自行读取输出、判断状态或终止进程。
|
||||
|
||||
`buildFileExecutionCommands` 是 `ORIGIN` 路由使用的命令构造辅助方法。它把 action 文件执行转换为:
|
||||
|
||||
```text
|
||||
launcher absolutePath --param=value ...
|
||||
```
|
||||
|
||||
随后这组 commands 会先进入 `ExecutionPolicyRegistry.prepare`,再由 `CommandExecutionService.exec(WrappedLaunchSpec)` 真正启动进程。
|
||||
49
doc/action/infra/execution-policy.md
Normal file
49
doc/action/infra/execution-policy.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 执行策略
|
||||
|
||||
执行策略用于把原始命令包装成具体可启动的 `WrappedLaunchSpec`。它位于 `OriginExecutionService`、动态 action MCP 执行和底层 `CommandExecutionService` 之间,负责在命令真正交给进程执行前应用运行策略。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph ConfigPlane["配置平面"]
|
||||
A1["action/runner_policy.json"] --> A2["ExecutionPolicy"]
|
||||
A2 --> A3["ExecutionPolicyRegistry.currentPolicy"]
|
||||
A4["LocalRunnerClient"] --> A5["registerPolicyProviders"]
|
||||
A5 --> A6["direct provider<br/>默认已注册"]
|
||||
A5 --> A70["Linux"] --> A80["bwrap"]
|
||||
A5 --> A71["Windows/MacOS"] --> A81["暂无"]
|
||||
end
|
||||
|
||||
subgraph PreparePlane["命令包装"]
|
||||
B1["commands: List<String>"] --> B2["ExecutionPolicyRegistry.prepare"]
|
||||
A3 --> B2
|
||||
B2 --> B3{"policy.provider"}
|
||||
B3 -- " 已注册 " --> B4["selected PolicyProvider"]
|
||||
B3 -- " 未注册 " --> B5["fallback: direct"]
|
||||
B4 --> B6["provider.prepare(policy, commands)"]
|
||||
B5 --> B6
|
||||
B6 --> B7["WrappedLaunchSpec<br/>command + args + workingDirectory + environment"]
|
||||
end
|
||||
|
||||
subgraph ExecutePlane["命令执行"]
|
||||
B7 --> C1["CommandExecutionService.exec"]
|
||||
end
|
||||
|
||||
subgraph ListenerPlane["策略变更监听"]
|
||||
A2 --> D1["updatePolicy"]
|
||||
D1 --> D2["RunnerExecutionPolicyListener"]
|
||||
D2 --> D3["McpConfigWatcher<br/>reload MCP clients"]
|
||||
end
|
||||
```
|
||||
|
||||
`ExecutionPolicyRegistry` 本身不执行命令,只选择 provider 并返回包装后的启动规格。当前默认 provider 是 `direct`;在 Linux 环境下,`LocalRunnerClient` 会额外注册 `bwrap` provider。
|
||||
|
||||
`ExecutionPolicy` 描述运行策略,包括 provider、运行模式、网络开关、是否继承环境变量、额外环境变量、工作目录,以及只读 / 可写路径集合。provider 会根据这些信息生成最终的 `WrappedLaunchSpec`:
|
||||
|
||||
| provider | 生效方式 |
|
||||
|---|---|
|
||||
| `direct` | 直接使用原始命令和参数,附加工作目录与环境变量 |
|
||||
| `bwrap` | 使用 `bwrap` 包装原始命令,按策略加入网络隔离、路径绑定和工作目录设置 |
|
||||
|
||||
`ExecutionPolicyRegistry` 也维护策略变更 listener。`McpConfigWatcher` 会注册为 listener;当 policy 被更新时,它可以重新加载 MCP client,使 MCP server 的启动规格与最新执行策略保持一致。
|
||||
|
||||
> 当前沙箱仅支持 Linux,其他平台暂未提供沙箱支持,后续会进行补充。
|
||||
56
doc/action/infra/meta-action-info.md
Normal file
56
doc/action/infra/meta-action-info.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# MetaActionInfo 行动描述覆写
|
||||
|
||||
`MetaActionInfo` 是行动能力进入 planner / evaluator / executor 前的描述层。它不负责执行行动,而是描述一个 action 可以被如何理解、选择、组装和调用。
|
||||
|
||||
不同来源的行动能力都会汇入 `ActionCore.existedMetaActions`,但它们生成描述信息的方式不同:
|
||||
|
||||
- 外部 MCP tool 会先生成基础 `MetaActionInfo`,再允许通过 `mcp/desc/*.desc.json` 进行覆盖。
|
||||
- BUILTIN action 在注册时直接提供 `MetaActionInfo`。
|
||||
- dynamic action 使用 `dynamic/<name>/desc.json` 作为描述来源。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Sources["MetaActionInfo 来源"]
|
||||
A["外部 MCP tool"]
|
||||
B["mcp/desc/*.desc.json<br/>描述覆盖"]
|
||||
C["BuiltinActionDefinition"]
|
||||
D["dynamic/name/desc.json"]
|
||||
end
|
||||
|
||||
subgraph Build["描述构建 / 合并"]
|
||||
A --> E["McpMetaRegistry<br/>基础描述"]
|
||||
B --> E
|
||||
C --> F["BuiltinActionRegistry"]
|
||||
D --> G["DynamicActionMcpManager"]
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
E --> H["existedMetaActions"]
|
||||
F --> H
|
||||
G --> H
|
||||
end
|
||||
|
||||
subgraph Usage["使用位置"]
|
||||
H --> I["listAvailableMetaActions"]
|
||||
H --> J["loadMetaActionInfo"]
|
||||
H --> K["loadMetaAction(actionKey)"]
|
||||
K --> L["MetaAction"]
|
||||
L --> M["ExecutableAction.actionChain"]
|
||||
end
|
||||
```
|
||||
|
||||
描述覆写主要服务于外部 MCP tool。外部 tool 自带的信息通常只够完成调用,但不一定足够支撑 Partner 的行动规划。`mcp/desc/*.desc.json` 可以补充或覆盖这些信息,让外部 tool 进入行动系统后具备更完整的行动语义。
|
||||
|
||||
当前描述信息会影响:
|
||||
|
||||
| 信息 | 作用 |
|
||||
|---|---|
|
||||
| `description` | 让评估器和规划器理解 action 的用途 |
|
||||
| `params` | 描述行动参数,用于参数提取和调用装配 |
|
||||
| `launcher` | 为 ORIGIN / dynamic action 提供启动器信息 |
|
||||
| `io` | 描述输入输出形态,辅助行动组合 |
|
||||
| `preActions` / `postActions` | 描述行动前后依赖关系 |
|
||||
| `strictDependencies` | 表达必须满足的行动依赖 |
|
||||
| `tags` | 为 action 分类和筛选提供辅助信息 |
|
||||
|
||||
`ActionCore.loadMetaAction(actionKey)` 会根据 action key 前缀构造 `MetaAction`,但 `MetaActionInfo` 本身不会直接执行。它的价值在于让系统在执行前能够知道:有哪些 action 可用、它们需要什么参数、适合什么场景,以及是否存在依赖或补充约束。
|
||||
277
doc/action/infra/runner-client.md
Normal file
277
doc/action/infra/runner-client.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# RunnerClient 与本地路由
|
||||
|
||||
本文说明 `RunnerClient`、`LocalRunnerClient` 以及 `MCP` / `BUILTIN` / `ORIGIN` 三类执行路由。
|
||||
|
||||
## MetaAction 执行通道
|
||||
|
||||
`MetaAction` 当前有三类执行通道:
|
||||
|
||||
| 类型 | 说明 |
|
||||
|---|---|
|
||||
| `MCP` | 通过 MCP client 调用外部工具能力 |
|
||||
| `BUILTIN` | 调用 JVM 内部注册的内置行动 |
|
||||
| `ORIGIN` | 执行本地或持久化的 action 文件 |
|
||||
|
||||
行动链只保存 `MetaAction`,不直接关心底层通道。执行时,`RunnerClient` 根据 `MetaAction` 的类型和位置信息把调用交给对应实现。
|
||||
|
||||
这种设计把“行动链结构”和“行动运行方式”分开:上层只需要知道 action key,底层负责解释这个 key 如何运行。
|
||||
|
||||
## RunnerClient
|
||||
|
||||
`RunnerClient` 是执行器与底层行动运行环境之间的边界。`ActionExecutor` 完成参数提取后,只需要把 `MetaAction` 提交给 runner;runner 负责确认该行动是否仍处于可执行状态、调用具体执行通道,并把结果统一写回 `MetaAction.Result`。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["ActionExecutor<br/>已完成参数提取"] --> B["MetaAction"]
|
||||
B --> C["RunnerClient.submit"]
|
||||
C --> D{"Result 是否仍为 WAITING?"}
|
||||
D -- " 否 " --> E["跳过重复执行"]
|
||||
D -- " 是 " --> F["doRun(metaAction)"]
|
||||
F --> G["RunnerResponse"]
|
||||
G --> H["写回 MetaAction.Result"]
|
||||
H --> I{"ok?"}
|
||||
I -- " true " --> J["SUCCESS"]
|
||||
I -- " false " --> K["FAILED"]
|
||||
```
|
||||
|
||||
这个边界让 `ActionExecutor` 不需要感知 MCP、ORIGIN、BUILTIN 的差异。对执行器来说,单个 `MetaAction` 只有“提交、等待结果、记录历史”这一种语义。
|
||||
|
||||
`RunnerClient` 还抽象了 action 文件序列化能力。临时 action 和持久化 action 的落盘位置、命名和文件结构由 runner 管理,上层只通过 runner 暴露的接口生成临时路径或持久化 action。
|
||||
|
||||
## LocalRunnerClient
|
||||
|
||||
`LocalRunnerClient` 是当前主要的 runner 实现。它不只是本地执行器,还负责初始化本地 action 基础设施。
|
||||
|
||||
启动时,它会建立本地 action 目录结构:
|
||||
|
||||
```text
|
||||
action/
|
||||
tmp/
|
||||
dynamic/
|
||||
mcp/
|
||||
desc/
|
||||
```
|
||||
|
||||
这些目录分别承担不同角色:
|
||||
|
||||
| 目录 | 作用 |
|
||||
|---|---|
|
||||
| `tmp/` | 临时 action 文件目录,不进入长期能力注册 |
|
||||
| `dynamic/` | 持久化动态 action 目录,由 watcher 转换为本地 MCP tool |
|
||||
| `mcp/` | 外部 MCP 配置目录 |
|
||||
| `mcp/desc/` | MCP tool 描述覆盖目录 |
|
||||
|
||||
`LocalRunnerClient` 初始化时还会启动三组基础设施:
|
||||
|
||||
- `McpMetaRegistry` + `McpDescWatcher`:维护 MCP tool 的描述资源和描述覆盖。
|
||||
- `DynamicActionMcpManager`:把 `dynamic/` 下的本地 action 包装成本进程 MCP tool。
|
||||
- `McpConfigWatcher`:监听外部 MCP 配置,动态注册或移除 MCP client。
|
||||
|
||||
因此,`LocalRunnerClient` 同时承担两个角色:一方面是 `MetaAction` 的本地执行入口,另一方面是本地行动能力集合的维护入口。
|
||||
|
||||
三类路由共享同一组平面:能力注册 / 描述平面负责让能力进入系统,`ActionCore` 维护可用能力表,行动链装配平面把 action key 加载成 `MetaAction`,执行平面再按 `MetaAction.type` 分流到具体 route。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph CapabilityPlane["能力注册 / 描述平面"]
|
||||
A1["MCP config"] --> A2["McpClientRegistry"]
|
||||
B1["BuiltinActionRegistry"]
|
||||
C1["dynamic action"] --> A2
|
||||
C1
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
A1 --> A3["existedMetaActions"]
|
||||
B1 --> A3
|
||||
C1 --> A3
|
||||
end
|
||||
|
||||
subgraph PlanningPlane["行动链装配平面"]
|
||||
A3 -- " loadMetaActions " --> D2["MetaAction"]
|
||||
D2 --> D3["ExecutableAction.actionChain"]
|
||||
end
|
||||
|
||||
subgraph ExecutionPlane["执行平面"]
|
||||
D3 --> E1["ActionExecutor stage"]
|
||||
E1 --> E2["RunnerClient.submit"]
|
||||
E2 --> E3{"MetaAction.type"}
|
||||
E3 --> F1["MCP route"]
|
||||
E3 --> F2["BUILTIN route"]
|
||||
E3 --> F3["ORIGIN route"]
|
||||
end
|
||||
|
||||
F1 -. " uses location " .-> A2
|
||||
```
|
||||
|
||||
## 执行路由:MCP
|
||||
|
||||
`MCP` 路由用于调用外部 MCP server 暴露的工具能力。外部 MCP 配置被加载为 MCP client,tool 元信息被写入 `ActionCore.existedMetaActions`,随后 planner / evaluator 可以把这些 tool 作为 `MetaAction(type=MCP)` 放入行动链。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph CapabilityPlane["能力注册 / 描述平面"]
|
||||
A1["mcp/*.json<br/>外部 MCP 配置"] --> A2["McpConfigWatcher"]
|
||||
A2 --> A3["McpTransportFactory"]
|
||||
A3 --> A4["McpSyncClient"]
|
||||
A4 --> A5["McpClientRegistry<br/>clientId -> client"]
|
||||
A4 --> A6["listTools"]
|
||||
A6 --> A7["McpMetaRegistry<br/>buildMetaActionInfo"]
|
||||
A8["mcp/desc/*.desc.json<br/>描述覆盖"] --> A7
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
A7 --> B1["existedMetaActions<br/>clientId::toolName"]
|
||||
end
|
||||
|
||||
subgraph PlanningPlane["行动链装配平面"]
|
||||
B1 -- " loadMetaAction " --> C1["MetaAction<br/>type=MCP<br/>location=clientId<br/>name=toolName"]
|
||||
C1 --> C2["ExecutableAction.actionChain"]
|
||||
end
|
||||
|
||||
subgraph ExecutionPlane["执行平面"]
|
||||
C2 --> D1["ActionExecutor stage"]
|
||||
D1 --> D2["RunnerClient.submit"]
|
||||
D2 --> D3["LocalRunnerClient.doRun"]
|
||||
D3 --> D4["McpActionExecutor.run"]
|
||||
D4 --> D5["McpClientRegistry.get(location)"]
|
||||
D5 --> D6["CallToolRequest<br/>name + params"]
|
||||
D6 --> D7["McpSyncClient.callTool"]
|
||||
D7 --> D8["RunnerResponse"]
|
||||
D8 --> D9["MetaAction.Result"]
|
||||
end
|
||||
|
||||
D5 -. " uses clientId " .-> A5
|
||||
```
|
||||
|
||||
在这个通道中,`MetaAction.location` 表示 MCP client id,`MetaAction.name` 表示 tool name。执行阶段通过 `location` 回到 `McpClientRegistry` 找到已注册 client,再用 `name` 和参数调用 tool。
|
||||
|
||||
## 执行路由:BUILTIN
|
||||
|
||||
`BUILTIN` 路由用于承载智能体内部向行动系统暴露的能力集合。系统内部能力可以通过 `BuiltinActionProvider` 或 `BuiltinActionRegistry` 注册为 `MetaActionInfo`,从而进入 planner / evaluator / executor 共享的行动链模型。
|
||||
|
||||
这些内部能力覆盖的范围可以很宽,包括命令执行、capability layer 操作、临时 meta action 创建与持久化、主动 turn等。`BUILTIN` 的意义是把这些内部能力也组织成 `MetaAction`,让它们与外部 MCP tool、临时 action 一样接受行动提取、评估、编排和执行。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph CapabilityPlane["能力注册 / 描述平面"]
|
||||
A1["BuiltinActionProvider"] --> A3["BuiltinActionDefinition<br/>actionKey + MetaActionInfo + invoker"]
|
||||
A2["BuiltinActionRegistry.defineBuiltinAction"] --> A3
|
||||
A3 --> A4["BuiltinActionRegistry.definitions"]
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
A3 --> B1["ActionCapability.registerMetaActions"]
|
||||
B1 --> B2["existedMetaActions<br/>builtin::name"]
|
||||
end
|
||||
|
||||
subgraph PlanningPlane["行动链装配平面"]
|
||||
B2 -- " loadMetaAction " --> C1["MetaAction<br/>type=BUILTIN<br/>location=builtin<br/>name=name"]
|
||||
C1 --> C2["ExecutableAction.actionChain"]
|
||||
end
|
||||
|
||||
subgraph ExecutionPlane["执行平面"]
|
||||
C2 --> D1["ActionExecutor stage"]
|
||||
D1 --> D2["RunnerClient.submit"]
|
||||
D2 --> D3["LocalRunnerClient.doRun"]
|
||||
D3 --> D4["doRunWithBuiltin"]
|
||||
D4 --> D5["BuiltinActionRegistry.call"]
|
||||
D5 --> D6["BuiltinActionDefinition.invoker"]
|
||||
D6 --> D7["RunnerResponse"]
|
||||
D7 --> D8["MetaAction.Result"]
|
||||
end
|
||||
|
||||
D5 -. " uses actionKey " .-> A4
|
||||
```
|
||||
|
||||
`BuiltinActionRegistry` 是当前的承载实现。它负责保存 builtin action 定义,把对应 `MetaActionInfo` 注册到 action capability,并在执行时根据 action key 和参数调用具体能力。
|
||||
|
||||
## 执行路由:ORIGIN
|
||||
|
||||
`ORIGIN` 路由用于执行临时 meta action 的本地文件形态。临时 meta action 通常由某个 builtin meta-action 创建:系统内部能力先生成 action 文件和对应 `MetaAction`,随后通过 `ORIGIN` 路由运行该文件。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph CreationPlane["临时行动生成平面"]
|
||||
A1["BUILTIN meta-action"] --> A2["生成临时 action code"]
|
||||
A1 --> A3["RunnerClient.buildTmpPath"]
|
||||
A2 --> A4["RunnerClient.tmpSerialize"]
|
||||
A3 --> A4
|
||||
A4 --> A5["tmp action file"]
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
A5 --> B1["origin::tmpActionPath"]
|
||||
B1 --> B2["existedMetaActions"]
|
||||
end
|
||||
|
||||
subgraph PlanningPlane["行动链装配平面"]
|
||||
B2 -- " loadMetaAction " --> C1["MetaAction<br/>type=ORIGIN<br/>location=origin<br/>name=tmpActionPath"]
|
||||
C1 --> C2["ExecutableAction.actionChain"]
|
||||
end
|
||||
|
||||
subgraph ExecutionPlane["执行平面"]
|
||||
C2 --> D1["ActionExecutor stage"]
|
||||
D1 --> D2["RunnerClient.submit"]
|
||||
D2 --> D3["LocalRunnerClient.doRun"]
|
||||
D3 --> D4["OriginExecutionService.run"]
|
||||
D4 --> D5["resolveOriginPath"]
|
||||
D5 --> D6["CommandExecutionService<br/>buildFileExecutionCommands"]
|
||||
D6 --> D7["ExecutionPolicyRegistry.prepare"]
|
||||
D7 --> D8["WrappedLaunchSpec"]
|
||||
D8 --> D9["CommandExecutionService.exec"]
|
||||
D9 --> D10["RunnerResponse"]
|
||||
D10 --> D11["MetaAction.Result"]
|
||||
end
|
||||
|
||||
D5 -. " resolves to " .-> A5
|
||||
```
|
||||
|
||||
`ORIGIN` 表示临时能力的执行阶段:action 文件有本地路径,执行时由 `OriginExecutionService` 解析文件位置、组合 launcher 与参数,并交给执行策略和命令执行服务处理。
|
||||
|
||||
临时 meta action 可以过期,也可以被主动持久化。持久化后,它会进入 dynamic action 流程,成为可长期复用的动态行动能力。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph OriginPlane["临时 ORIGIN 阶段"]
|
||||
A1["tmp action file"]
|
||||
A2["临时 MetaAction<br/>type=ORIGIN"]
|
||||
A1 --> A2
|
||||
end
|
||||
|
||||
subgraph PersistencePlane["持久化阶段"]
|
||||
A2 --> B1["BUILTIN persist meta-action"]
|
||||
B1 --> B2["RunnerClient.persistSerialize"]
|
||||
B2 --> B3["ActionSerializer.persistSerialize"]
|
||||
B3 --> B4["dynamic/name/run.ext.tmp"]
|
||||
B3 --> B5["dynamic/name/desc.json.tmp"]
|
||||
B4 --> B6["ATOMIC_MOVE<br/>run.ext"]
|
||||
B5 --> B7["ATOMIC_MOVE<br/>desc.json"]
|
||||
end
|
||||
|
||||
subgraph CapabilityPlane["能力注册 / 描述平面"]
|
||||
B6 --> C1["DynamicActionMcpManager"]
|
||||
B7 --> C1
|
||||
C1 --> C2["normalPath"]
|
||||
C2 --> C3["system MCP server<br/>addTool"]
|
||||
C2 --> C4["dynamic action MetaActionInfo"]
|
||||
end
|
||||
|
||||
subgraph ActionCore["行动核心: ActionCore"]
|
||||
C4 --> D1["existedMetaActions<br/>local::name"]
|
||||
end
|
||||
|
||||
subgraph PlanningPlane["行动链装配平面"]
|
||||
D1 -- " loadMetaAction " --> E1["MetaAction<br/>type=MCP<br/>location=local<br/>name=name"]
|
||||
E1 --> E2["ExecutableAction.actionChain"]
|
||||
end
|
||||
|
||||
subgraph ExecutionPlane["执行平面"]
|
||||
E2 --> F1["ActionExecutor stage"]
|
||||
F1 --> F2["RunnerClient.submit"]
|
||||
F2 --> F3["MCP route"]
|
||||
end
|
||||
|
||||
F3 -. " uses location=local " .-> C3
|
||||
```
|
||||
|
||||
因此,`ORIGIN`、dynamic action 和 `MCP` 可以组成一条能力生命周期:内部能力创建临时 meta action,临时阶段通过 `ORIGIN` 执行,持久化后转为 dynamic action,并由系统创建的 MCP server 统一管理。
|
||||
178
doc/action/meta-action-and-action-chain.md
Normal file
178
doc/action/meta-action-and-action-chain.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# MetaAction 与行动链建模
|
||||
|
||||
本文说明 Partner 行动系统中 `MetaAction`、`ExecutableAction` 与行动链的建模方式。
|
||||
|
||||
行动系统把“要做什么”拆成两个层级:
|
||||
|
||||
- `ExecutableAction` 表示一次完整任务,包含来源、原因、描述、状态、阶段、结果与执行历史。
|
||||
- `MetaAction` 表示行动链中的最小可执行单元,描述一个具体行动程序及其调用参数、执行结果。
|
||||
|
||||
因此,一次行动不是一个扁平 tool call,而是一个可阶段化、可恢复、可纠偏、可调度的任务对象。
|
||||
|
||||
## 对象层级
|
||||
|
||||
```text
|
||||
Action
|
||||
├─ ExecutableAction
|
||||
│ ├─ ImmediateExecutableAction
|
||||
│ └─ SchedulableExecutableAction
|
||||
└─ StateAction
|
||||
|
||||
ExecutableAction
|
||||
├─ tendency
|
||||
├─ actionChain: Map<Int, List<MetaAction>>
|
||||
├─ stageDescriptions: Map<Int, String>
|
||||
├─ executingStage
|
||||
├─ history
|
||||
└─ result
|
||||
```
|
||||
|
||||
`Action` 是最上层的抽象,负责承载通用生命周期字段。`ExecutableAction` 表示真正包含行动链的任务。`StateAction` 不包含 `MetaAction` 链,而是用于定时或周期性触发状态更新、普通逻辑调用等系统行为。
|
||||
|
||||
## MetaAction
|
||||
|
||||
`MetaAction` 是行动链中的单一元素,封装调用外部行动程序所需的基本信息。
|
||||
|
||||
核心字段包括:
|
||||
|
||||
- `name`:行动名称,用于标识行动程序。
|
||||
- `type`:行动程序类型,目前包括 `MCP`、`ORIGIN` 和 `BUILTIN`。
|
||||
- `location`:行动所在位置。对 MCP 来说通常是 MCP client id;对 ORIGIN 来说是磁盘路径;对 BUILTIN 来说固定为 `builtin`。
|
||||
- `launcher`:启动器或解释器,主要用于 `ORIGIN` 类型。
|
||||
- `io`:是否偏 IO 密集,用于执行器选择线程池。
|
||||
- `params`:执行前由参数提取器填充的调用参数。
|
||||
- `result`:单个 `MetaAction` 的执行结果,包含 `WAITING`、`SUCCESS`、`FAILED` 三种状态。
|
||||
|
||||
`MetaAction` 的 `key` 由 `location::name` 组成。行动规划阶段只会引用真实存在的 action key,执行阶段再通过该 key 加载行动元信息、提取参数并提交给 runner。
|
||||
|
||||
## MetaAction 类型
|
||||
|
||||
当前实现中 `MetaAction.Type` 分三类:
|
||||
|
||||
- `MCP`:调用已注册的 MCP 工具,可以对应本地或远程服务。
|
||||
- `ORIGIN`:临时生成或持久化的行动程序,通常需要启动器或解释器。
|
||||
- `BUILTIN`:由本地内置注册表直接执行的行动。
|
||||
|
||||
这个类型划分让行动链只关心“要调用哪个行动单元”,不把执行通道细节硬编码进 planner。真正的路由由 action runner / capability 层负责。
|
||||
|
||||
## ExecutableAction
|
||||
|
||||
`ExecutableAction` 是一次完整行动任务的结构化容器。
|
||||
|
||||
核心字段包括:
|
||||
|
||||
- `uuid`:行动实例 id。
|
||||
- `source`:行动来源,通常关联用户或触发源。
|
||||
- `reason`:为什么要执行该行动。
|
||||
- `description`:行动描述,用于人类可读说明和上下文反馈。
|
||||
- `tendency`:上游 `ActionExtractor` 提取出的行动倾向。
|
||||
- `status`:任务级状态。
|
||||
- `actionChain`:阶段化行动链。
|
||||
- `stageDescriptions`:每个阶段的执行目标说明。
|
||||
- `executingStage`:当前执行阶段。
|
||||
- `history`:每个阶段已经执行过的 `HistoryAction`。
|
||||
- `result`:最终行动结果。
|
||||
|
||||
它把“行动为什么存在”“当前执行到哪里”“每一步做了什么”“最终结果是什么”放在同一个对象里,便于调度、恢复、纠偏和上下文反馈。
|
||||
|
||||
## 行动链结构
|
||||
|
||||
行动链使用 `Map<Int, List<MetaAction>>` 表示。
|
||||
|
||||
- `Int` 是阶段序号。
|
||||
- 同一阶段中的 `List<MetaAction>` 表示该阶段下的一组行动单元。
|
||||
- 阶段之间按序推进。
|
||||
- 同一阶段内的多个 `MetaAction` 可以被执行器并发提交。
|
||||
|
||||
示意:
|
||||
|
||||
```text
|
||||
Stage 1
|
||||
├─ MetaAction A
|
||||
└─ MetaAction B
|
||||
|
||||
Stage 2
|
||||
└─ MetaAction C
|
||||
|
||||
Stage 3
|
||||
├─ MetaAction D
|
||||
└─ MetaAction E
|
||||
```
|
||||
|
||||
这种结构适合表达“先收集事实,再执行修改,最后验证结果”一类任务。规划阶段只负责形成阶段与行动单元,执行阶段负责提取参数、提交行动、收集结果并推进阶段。
|
||||
|
||||
## 阶段描述
|
||||
|
||||
`stageDescriptions` 为每个 stage 提供自然语言目标说明。它不是用户回复文案,也不是完整执行计划,而是给执行器和参数提取器使用的阶段目标。
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
1 -> "读取当前配置文件"
|
||||
2 -> "根据目标修改配置项"
|
||||
3 -> "运行检查命令确认修改有效"
|
||||
```
|
||||
|
||||
执行器在为某个 `MetaAction` 提取参数时,会把当前行动描述和当前阶段描述一起传给参数提取模块,帮助它生成更贴近阶段目标的参数。
|
||||
|
||||
## 依赖修正
|
||||
|
||||
行动评估结果中的 `primaryActionChain` 并不直接成为最终行动链。`ActionPlanner` 会在装配阶段做两类修正:
|
||||
|
||||
1. **顺序归一化**:把阶段序号修正为从 1 开始依次递增。
|
||||
2. **前置依赖补齐**:读取 `MetaActionInfo.preActions` 和 `strictDependencies`,必要时把严格依赖的前置 action key 插入到当前阶段之前。
|
||||
|
||||
这意味着 LLM 评估结果可以表达主要行动链,但最终进入执行器的链会经过系统能力层校验和依赖补齐。若 action key 不存在、元信息加载失败或依赖无法处理,planner 会放弃构造该行动。
|
||||
|
||||
## 即时行动与计划行动
|
||||
|
||||
`ExecutableAction` 有两个主要实现:
|
||||
|
||||
- `ImmediateExecutableAction`:立即进入执行器。
|
||||
- `SchedulableExecutableAction`:实现 `Schedulable`,先进入调度器,到期后再交给执行器。
|
||||
|
||||
二者共享同一套行动链结构。差异只在执行时机和生命周期维护:计划行动执行后会记录 `ScheduleHistory`,并重置阶段、参数和单步结果,以便周期性任务下次继续运行。
|
||||
|
||||
## StateAction
|
||||
|
||||
`StateAction` 也是 `Action`,同时实现 `Schedulable`,但它不包含 `MetaAction` 链。
|
||||
|
||||
它的作用是承载定时或周期性触发的系统逻辑,例如:
|
||||
|
||||
- 周期性状态更新。
|
||||
- 一次性逻辑调用。
|
||||
- 即时行动 watcher。
|
||||
|
||||
`StateAction.Trigger` 目前包括:
|
||||
|
||||
- `Update<T>`:对某个状态源执行更新函数。
|
||||
- `Call`:执行普通逻辑调用。
|
||||
|
||||
执行器收到 `StateAction` 时不会走行动链,而是直接触发 `trigger.onTrigger()`,并把触发事件写入上下文。
|
||||
|
||||
## 建模边界
|
||||
|
||||
行动建模层只负责把行动表示清楚,不负责完成所有执行细节。
|
||||
|
||||
它不负责:
|
||||
|
||||
- 判断用户意图是否应该行动化。
|
||||
- 选择所有参数值。
|
||||
- 执行 MCP / BUILTIN / ORIGIN 行动。
|
||||
- 判断执行是否成功。
|
||||
- 生成最终用户回复。
|
||||
|
||||
这些分别属于行动提取、行动评估、参数提取、行动执行、反馈闭环和沟通模块。
|
||||
|
||||
## 设计取向
|
||||
|
||||
`MetaAction` 与行动链建模的核心目标是把“系统要做的事”变成结构化任务,而不是把一次行动压缩成不可追踪的工具调用。
|
||||
|
||||
这种设计带来几个好处:
|
||||
|
||||
- 可以表达多阶段任务。
|
||||
- 可以为每个阶段保留目标描述。
|
||||
- 可以记录每个阶段的执行历史。
|
||||
- 可以在失败或结果不满足目标时插入纠偏行动。
|
||||
- 可以把同一行动链用于立即执行或未来调度。
|
||||
- 可以把行动状态写入 `ContextWorkspace`,供后续认知和沟通使用。
|
||||
Reference in New Issue
Block a user