docs(agent-engine): Use Agent Engine to decouple components - #438
docs(agent-engine): Use Agent Engine to decouple components#438xxx7xxxx wants to merge 10 commits into
Conversation
|
CSG channel 和 Feishu 不需要在接入、存储、鉴权上完全对称,只应在各自 Channel Adapter 完成事件校验、身份识别、去重、会话键构造和渲染之后,以统一的 turn 进入 Agent Engine,再由 Runtime Adapter 对接 Codex 等运行时。 对 Codex 而言,这条路可行,因为它已有直接的 Session/Prompt/Event/Cancel 能力;但 OpenClaw、PicoClaw 目前仍由 sandbox 内部的 Feishu channel 消费消息并注入 App 凭据,若后续再启用 host 侧 Feishu Adapter,必须通过 Binding 明确 runtime_owned 或 host_direct,保证一个机器人只有一个消费者,否则会出现重复和竞争消费。 三个契约需要考虑:
|
| ) | ||
|
|
||
| // AgentInterface manages persisted Agent resources and their Runtime lifecycle. | ||
| type AgentInterface interface { |
There was a problem hiding this comment.
Is AgentInterface intended to represent a single Agent or the Agent collection? It includes List, and every instance-level operation accepts an agentID, so it currently behaves as a collection-scoped interface; however, the singular name makes the scope unclear. Please make this explicit: if it represents the collection, consider AgentCollectionInterface or AgentsInterface; if it represents one Agent, select the Agent before returning the interface, remove the repeated agentID parameters, and keep List on a separate collection interface.
There was a problem hiding this comment.
Assuming AgentInterface is collection-scoped, which component is responsible for storing the Agent collection and serving Get/List? The document assigns Agent persistence to the existing Agent Service and states that Agent Engine must not copy durable Agent state, while agentengine.Interface exposes Agents(). How are these ownership boundaries intended to fit together?
There was a problem hiding this comment.
Thanks, this interface is collection-scoped, but I see naming and storage ownership as separate concerns.
The singular name intentionally follows the Kubernetes typed-client convention. For example, Pods(namespace) PodInterface returns a resource interface that supports both List and name-addressed operations such as Get and Delete. Likewise, Agents() AgentInterface returns the interface for Agent resources, while agentID selects an individual Agent. Splitting collection and instance operations into separate interfaces would diverge from that convention without making the API materially simpler.
Exposing Agents() also does not mean that Agent Engine stores or owns the Agent collection. It is the public resource-oriented facade, while the existing Agent Service remains the single source of truth for Agent persistence, desired state, Runtime lifecycle, and Get/List (Of course, we need to refactor it for the new architecture). The eventual AgentInterface implementation delegates those operations to the Agent Service. Agent Engine adds only the coordination needed where lifecycle operations interact with active Turns, such as ensuring that Recreate or Delete does not race with ongoing execution.
In short, Agent Engine owns the contract and execution coordination; Agent Service owns the durable Agent state behind that contract.
There was a problem hiding this comment.
6516072 has added the explanation and incremental solution; see if it has fixed your concern.
|
@GatewayJ 感谢。我们的目标也是只统一 Channel Adapter 之后的 Turn 边界,不要求 CSGClaw Channel 和 Feishu 在接入、存储或鉴权上对称。
关于 ExecutionID,你说得对。ConversationKey 只能标识 Conversation(各个 Channel Adapter自己觉得如何拼接,最佳实践是都在 ConversationKey 上加上自己 channel 的前缀,例如 feishu,csgclaw-im)。在存在 Running Turn 和 Queued Turn 时,无法精确表达 superseding 要取消哪一个 Turn。这里会恢复一个 Turn 级 ID,可能命名为 TurnID,用于 TurnRequest、Cancel 和 Engine 内部的 Turn Registry,不再把它扩散到不需要的类型。 已在 85f9be9 更新,看看是否能解决你的 concern。
同意。sender open_id、chat identity、Binding、用户/群聊授权和机器人身份校验都应由 Feishu Adapter 在进入 Agent Engine 前完成,@ 只作为触发条件,不能作为授权。Agent Engine 只接收已经验证的统一 Turn。
Codex Runtime 不需要获取 Feishu AppInfo。App ID 和 App Secret 由 Host Feishu Adapter 通过 Binding 持有;Adapter 负责收发 Feishu 消息,Codex 只处理规范化 Input 并返回 Event/Result,凭据不会进入 Runtime。 |
|
结合当前文档,我建议在结束 Phase 0 前再明确下面两个契约:
|
|
基于现有实现和这份新架构,我觉得 Phase 2 还需要补充 Channel consumer 生命周期如何迁移 的说明。 先澄清一下现状:当前 按照本文描述的目标边界,我的理解是:
但是目前 Phase 2 只写了“Move built-in IM execution behind Agent Engine”,还没有解释现有
如果缺少这部分迁移契约,最终可能只是包依赖上看起来完成了解耦,但 Agent 生命周期仍然在行为上与 Built-in IM 或 Feishu 的 Channel 生命周期绑定在一起。 |
|
3fe09f2 已补全生命周期和 Session Binding 契约。生命周期现在明确了 Admission 关闭、Queued Turn 取消、Running Turn Drain、超时处理及 Runtime Pinning。 |
|
结合当前的分阶段计划,我觉得这里需要区分两个不同的问题:一是 Agent Engine 最终的职责,二是这些职责在每个阶段如何接入现有实现。 按照当前接口,Agent Engine 最终同时通过 从当前实现看,
不过,这个较大的迁移问题不一定需要阻塞只改 Anonymous Session 的第一阶段。如果 Phase 1 的实际目标只是把 Session API 切到 这里仍有一个 Phase 1 必须处理的最小控制面交点:现有 Agent API 仍可能在 Session Turn 执行期间调用 Stop、Runtime-affecting Update、Recreate 或 Delete。即使这些 API 暂时继续调用 Agent Service,它们也必须与 Engine 共享同一个 Agent-scoped coordinator,才能关闭 admission、处理 queued Turn 并 drain running Turn。这个协调可以通过窄接口或 composition-root wiring 完成,不要求第一阶段就把整个控制面迁移到 Engine。 因此是否可以采用下面这种渐进结构: 在这个结构下:
当前文档的 Phase 1 同时写了“实现 所以这里想进一步确认:
我认为这里的核心不是 Engine 是否负责控制面,而是 控制面契约、底层状态 owner 和分阶段迁移不必在 Phase 1 同时完成。第一阶段只接入 Session 数据面并补上必要的 lifecycle coordination;在后续改造 Built-in IM、Feishu 或正式迁移 |
|
fe06c20 已补充相关迁移契约。 为避免误解,文档将 Channel Consumer 统一命名为 Channel Event Worker,指现有负责监听 Channel Event 的后台 bridge worker。Event Worker 由 Channel/Binding 层按稳定的 Binding ID 管理,不再由 Agent 生命周期或 RuntimeID/SessionID 驱动。Agent Stop、Recreate 和 Runtime restart 不影响 Worker、Binding 或 transcript;Agent Delete 由应用和 Binding 层完成清理。 |
|
@xxx7xxxx 感谢, 补充说明一下:你回复时,我后面那条评论还是旧版本;我刚刚已经更新了它:更新后的评论。 更新后的 concern 不再是 Channel Event Worker,而是分阶段迁移边界:
麻烦再基于更新后的版本看看这部分是否与你的实施计划一致。 |
|
可以让 Phrase 1 步幅更小一些,文档已按这个边界调整 8693547
因此 Phase 1 不会被完整控制面改造阻塞,但扩展现有 lifecycle gate(coordinator) 是该阶段必须完成的。 |
No description provided.