☰
IronClaw 自动化任务接线契约:从 AutomationTask 事件模型到持久化 Suggestions 契约的设计演进
2026/9/25 13:48:47 网站建设 项目流程
  • 人工智能
  • AI 应用
  • 交互助手
  • AI Agent

【免费下载链接】ironclaw

IronClaw is an Agent OS focused on privacy, security and extensibility

项目地址:https://gitcode.com/gh_mirrors/iro/ironclaw
点击查看免费下载

导读

本文围绕 IronClaw 仓库中 docs/internal/design/oobe/AUTOMATION-TASKS-CONTRACT.md 展开,完整梳理了"自动化任务"这一 OOBE(首次运行体验)核心概念的后端接线契约:它最初以AutomationTask领域模型(五个持久事件 + 投影 + 五条 HTTP 路由 + 五个 facade 方法)被提出,后因 PR #7694 落地的持久化 Suggestions 契约而部分被取代。读完本文,你将掌握:原始设计契约的完整骨架(领域模型、事件表、投影规则、传输帧、路由表、Agent 模式门控语义)、实际落地契约的四路由细节(suggestions.list/suggestions.generate/suggestion.start/suggestion.dismiss),以及从ironclaw_product_contracts到ironclaw_webui再到前端 SPA 的源码级实现证据。


一、文档定位:一份被记录在案、部分被取代的设计参考

AUTOMATION-TASKS-CONTRACT.md自身在开头就明确标注了状态:design reference (proposed wiring),即"设计参考(提议的接线方案)"。它的历史角色是为 WebChat v2 的两个 OOBE 概念提供"可评审的接线路径"——包括持久事件、投影、传输帧、HTTP 表面和 facade 方法,供后续 PR 落地实现:

  1. 已完成的自动化轮播(Completed-automations carousel):位于落地页 composer 上方(原设计中的components/automation-carousel.tsx);
  2. 线程内联日历改期富预览(Inline calendar reschedule rich-preview):位于线程内部的日历改期卡片(原设计中的components/calendar-reschedule-card.tsx)。

两者共享同一个动作模型:suggested(已建议)状态支持 Approve / Modify / Cancel 三种操作;automated(已自动化)状态支持 Modify / Revert 两种操作。

需要特别强调文档顶部那条醒目的警示:§1–§3 已被 PR #7694 取代。实际落地的后端改用"基于ScopedFilesystem的 typed store"(带边界 CAS、无 SQL 迁移)和四条 product-surface 路由契约,而非原始的五个事件 + 投影方案。因此阅读本文时应把原契约视为"设计意图的记录",把 docs/internal/design/oobe/VISION-RECONCILIATION.md §1.1 视为"真实契约",两者对照阅读才能获得完整认知。

文档还交代了代码归属背景(#6918 家族文件夹重组后的命名):事件日志在ironclaw_event_log、持久存储在ironclaw_event_store(均位于crates/events/);facadeRebornServicesApi在crates/product/ironclaw_assistant;路由在crates/product/ironclaw_webui/src/webui_v2/。这些与当前仓库结构完全吻合,是理解整个接线路径的地图。


二、原始设计意图 §1:领域模型

原契约规定,一个自动化任务是"持久的、可投影的记录",其 Rust 类型形状是前端 TSAutomationTask的镜像(标识符使用crates/ironclaw_common的 newtype):

pub struct AutomationTaskId(String); // newtype, validated (types.md) pub enum AutomationApp { // #[serde(rename_all = "snake_case")] Gmail, GoogleCalendar, GoogleDocs, Slack, Notion, } pub enum AutomationTaskKind { // snake_case EmailTriage, CalendarAccept, CalendarReschedule, DocInsights, } pub enum AutomationTaskState { // snake_case Suggested, InProgress, Automated, Reverted, Cancelled, }

三个设计要点值得展开:

  • 标识符 newtype 化:AutomationTaskId不是裸String,而是经过校验的 newtype,这与 Reborn 的types.md规则一致——所有跨层标识符都要有类型边界,避免字符串散落各处。
  • 枚举统一 snake_case:AutomationApp、AutomationTaskKind、AutomationTaskState均通过 serderename_all = "snake_case"序列化,保证 wire 层与 TS 前端命名一致。
  • Payload 默认敏感:Kind 相关的 payload(如CalendarReschedule、TriagedEmail)与 TS 接口lib/automation-tasks.ts逐字段对齐,且默认视为敏感数据——邮件正文、与会者列表都属于敏感信息,在任何日志写入、持久化追加、投影输出、传输帧、模型可见结果之前,调用方都背负脱敏义务(对应.claude/rules/safety-and-sandbox.md)。

仓库提示:原文档引用的lib/automation-tasks.ts、hooks/useAutomationTasks.ts等 mock seam 代码已按文档记载被回滚("rolled back so the branch stays code-free"),当前仓库中对应的是下文第七、八节介绍的 suggestions 系列文件,两者不要混淆。


三、持久事件(源真相)与投影

3.1 五个持久事件

原契约要求在 Reborn 运行时事件枚举上新增五个 typed 变体(属主 crate 为ironclaw_event_log,经由ironclaw_event_store的 durable sink 追加,绝不通过 handler 直接广播):

EventWhen(触发时机)Carries(携带内容)
AutomationTaskProposedagent 提出任务(Suggest/Plan 模式,或 Auto 模式实际运行之前)完整任务,状态为suggested
AutomationTaskModified用户编辑一个 suggested 或 automated 任务任务 id + 校验过的 patch
AutomationTaskAutomated任务运行完成(已批准,或 Auto/Bypass 模式直接运行)任务 id + provider 出具的 evidence(见 §6)
AutomationTaskReverted用户撤销一个 automated 任务任务 id + revert evidence
AutomationTaskCancelled用户关闭一条建议任务 id

每个变体都必须满足:脱敏(redacted)、可重放(replayable),并针对持久化 / 重放 / 投影可见性 / 脱敏 / 排序 / 传输序列化分别编写测试。这体现了 Reborn 事件系统的硬性纪律:事件是唯一的源真相,任何 UI 状态都不是权威。

3.2 投影(Projection)

在ironclaw_event_projections中扩展AutomationTaskProjection,核心约束有四条:

  1. 按(tenant, user)作用域过滤——调用方只能看到自己的任务;
  2. 携带投影游标(projection cursor)——支持重放与断点续传(replay/resume);
  3. 把上述事件折叠成当前的AutomationTaskState;
  4. 读取语义分两种:轮播读取state ∈ {automated, reverted}的任务集合;内联卡片按 id 读取单个任务。

同时要求一个专门的跨用户隔离回归测试:为用户 A 提出的任务,绝不能出现在用户 B 的投影里。这条"最小隔离断言"在后续落地契约中同样以(tenant, user)scope 的形式保留了下来(见第七节)。


四、传输层:内联富预览的帧设计

内联日历卡片是"持久的 UI 状态",因此它复用已有的projection → EventStreamManager → SSE/WebSocket通道(events.md规则),不引入定制消息。具体做法是新增一个脱敏的WebChatV2EventFrame变体:

capability_display_preview{ kind: "automation_task", task: <redacted AutomationTask> }

前端MessageList已经具备渲染非消息子元素的能力(例如 gates、onboarding),因此它把这一帧映射到<CalendarRescheduleCard>(以及未来的其他 kind)即可。重连时从重放恢复,绝不依赖前端乐观状态——这是 Reborn 事件驱动 UI 的一条总原则:浏览器可以乐观渲染,但持久事实只能来自服务端投影。


五、HTTP 表面与 facade 方法(§5–§6)

5.1 五条路由

原契约规定在ironclaw_webui的src/webui_v2/增加五条路由,同时必须在webui_v2_routes()描述符表中登记对应行——否则tests/webui_v2_descriptors_contract.rs会失败(这正是当前仓库对路由描述符表的强制锁定机制):

Route IDMethodPatternEffect path
webui.v2.list_automation_tasksGET/api/webchat/v2/automations/tasksProjectionOnly
webui.v2.approve_automation_taskPOST/api/webchat/v2/automations/tasks/{id}/approveTurnCoordinator
webui.v2.modify_automation_taskPATCH/api/webchat/v2/automations/tasks/{id}ProductWorkflow
webui.v2.cancel_automation_taskPOST/api/webchat/v2/automations/tasks/{id}/cancelProductWorkflow
webui.v2.revert_automation_taskPOST/api/webchat/v2/automations/tasks/{id}/revertTurnCoordinator

这些是认证调用方路由(tenant/user 作用域),而非 operator 门控路由;handler 只消费RebornServicesApi,错误统一走WebUiV2HttpError(当前仓库中该错误类型正是 handler 返回错误的唯一通道,见 crates/product/ironclaw_webui/CONTRACT.md)。

5.2 五个 facade 方法与真实第三方效应

RebornServicesApi(位于ironclaw_assistant)新增五个方法,每个都返回服务端确认的任务记录,绝不返回乐观回显(events.md规则):

  • list_automation_tasks(caller) -> Vec<AutomationTask>
  • approve_automation_task(caller, id) -> AutomationTask
  • modify_automation_task(caller, id, patch) -> AutomationTask
  • cancel_automation_task(caller, id) -> AutomationTask
  • revert_automation_task(caller, id) -> AutomationTask

两个要点决定了这套接线的安全性:

  1. Approve/Revert 是真实的第三方效应(Gmail 发送/归档、Calendar 移动/恢复),必须经由 mediated capability host + product adapters(ironclaw_host_api中的ProductAdapter表面,通过 composition 接线)执行——绝不允许第二条 outbound HTTP 路径。成功只能由 provider 出具的 evidence(message id / event id / revision)加上一次最小化 read-back 来确认,AutomationTaskAutomated/AutomationTaskReverted事件携带这份 evidence。
  2. Modify 语义按状态分叉:修改suggested任务 = 原地编辑提案(不执行任何效应,直到 Approve);修改已 automated任务 =携带修改重新执行(对 provider 的真实再执行),返回带新 evidence 的全新 automated 记录。前端将后者建模为rerunModified:刷新完成状态 + 重算派生字段(如邮件发送计数)。因此modify_automation_task必须在服务端按当前状态分支。

六、Agent 模式(composer pill,§7)

原契约第七节定义了落地页 composer 上的模式选择器(原设计中的components/mode-selector.tsx+ storelib/agent-mode.ts,当前持久化到 scoped localStorage),并规划了持久化 home:

  • GET /api/webchat/v2/settings/agent-mode→{ mode: AgentMode }
  • POST /api/webchat/v2/settings/agent-mode{ mode }→ 确认后的{ mode }
  • 在 session(session.features/settings)上暴露agent_mode,使 pill 在加载时水合——与既有的global_auto_approve特性完全一致。

语义接入现有审批系统(ApprovalCard、resolve_gate路径、global_auto_approve特性),四种模式的 gate 行为如下:

ModeGate behavior
suggest每个动作都触发审批 gate(今天的默认行为)
planagent 输出一揽子待办任务计划,一次审批解决整个集合
auto已获批的任务类型(邮件整理、邀请接受、文档洞察)跳过逐动作 gate;其他任务仍走 gate。这是global_auto_approve的 typed 泛化
bypass完全不触发 gate(全自动化)

auto/bypass是特权升级模式,需要为 gate 抑制路径编写显式测试,并为每个自动运行的动作留下审计轨迹。


七、实际落地契约:持久化 Suggestions 四路由

正如第一节所述,原契约 §1–§3 已被 PR #7694 取代。真实的契约冻结在ironclaw_product_contracts中,由 docs/internal/design/oobe/VISION-RECONCILIATION.md §1.1 记录,并与当前仓库源码完全一致:

MethodWebUI 路由Product 操作
GET/api/webchat/v2/suggestionssuggestions.list
POST/api/webchat/v2/suggestions/generatesuggestions.generate
POST/api/webchat/v2/suggestions/{id}/startsuggestion.start
DELETE/api/webchat/v2/suggestions/{id}suggestion.dismiss

其 wire 类型定义在 crates/contracts/ironclaw_product_contracts/src/product_wire.rs,产品面描述符在 crates/contracts/ironclaw_product_contracts/src/suggestions.rs:

RebornSuggestionsResponse { status: "empty" | "generating" | "ready" | "failed", generation_id?: string, retry_after_seconds?: number, suggestions: RebornSuggestion[], } RebornSuggestion { id, title, description, suggested_prompt, icon, sources, thread_id?, run_id? } RebornSuggestionStartResponse { suggestion_id, thread_id, run_id } RebornSuggestionDismissResponse { suggestion_id, dismissed }

关键行为(均有源码佐证):

  • 生成是异步的:POST generate(携带client_action_id作为幂等键)返回202、status: generating和retry_after_seconds提示;客户端轮询GET suggestions直到终态。
  • 每 (tenant, user) 只有一套卡片;新一次生成会清除上一套(replace-only)。
  • 卡片数量有界 1–5,且字段有硬约束。需要指出的是:产品侧校验(crates/product/ironclaw_assistant/src/suggestions.rs)实际执行的是title ≤ 48、description ≤ 240、suggested_prompt ≤ 2000、sources 1–5 条且每条 ≤128、icon ≤128,并拒绝控制字符与重复 source;提示词文件prompts/suggestion_generation.md同样写明 "titleis 48 characters or fewer"。
  • 存储模型:正如契约警示所述,落地实现不是事件投影,而是基于ScopedFilesystem的 typed store + 边界 CAS + 无 SQL 迁移;生成以 30 秒租约(GENERATION_LEASE_DURATION_SECONDS = 30)保护,崩溃可安全恢复(源码 crates/product/ironclaw_assistant/src/suggestions.rs)。

八、源码级验证:路由描述符与 handler

当前仓库中这四条路由是真实存在且始终开启的。在 crates/product/ironclaw_webui/src/webui_v2/descriptors.rs 中,每个路由都在webui_v2_routes()描述符表登记了精确的 method、pattern、body 限制、限速与 Effect path:

  • suggestions_list_descriptor():GET,走read_policy,Effect path 为ProductSurface,无流式;
  • suggestions_generate_descriptor():POST,body_limit_kib(4),限速rate_limit_per_caller(10, 60)(每 60 秒 10 次)——注释明确指出"每个被接受的请求都可能启动一次 provider 支撑的 agent 运行",因此需要限速;
  • suggestion_start_descriptor():POST,NoBody,走通用 mutation 限速;
  • suggestion_dismiss_descriptor():DELETE,NoBody,同样 mutation 限速。

handler 实现位于 crates/product/ironclaw_webui/src/webui_v2/handlers.rs,统一经由query_product_view/invoke_product_command走ProductSurface,错误收敛到WebUiV2HttpError:

  • list_suggestions调用SUGGESTIONS_LIST_VIEW.descriptor()投影视图;
  • generate_suggestions解析client_action_id后调用SUGGESTIONS_GENERATE_COMMAND;当响应status == Generating时返回202 并设置 HTTPRetry-After头,否则返回 200;retry_after_seconds会被钳制到SUGGESTIONS_MAX_RETRY_AFTER_SECONDS之内;
  • start_suggestion调用SUGGESTION_START_COMMAND,返回{suggestion_id, thread_id, run_id};
  • dismiss_suggestion调用SUGGESTION_DISMISS_COMMAND,返回{suggestion_id, dismissed: true}。

产品面编排在 crates/product/ironclaw_assistant/src/suggestions.rs 中,几个实现细节与契约精神高度吻合:

  • scope 按用户:suggestion_scope以tenant_id + user_id为主键(注释明确 "/suggestionsis a per-user mount by design"),agent/project 仍携带用于文件系统授权与审计上下文;
  • start不在浏览器注入 prompt:start_suggestion服务端先create_thread,再以suggested_prompt作为消息submit_turn——线程和运行都由服务端通过正常 ProductSurface 路径创建;thread_action_id/turn_action_id由tenant:user:suggestion_id的 UUIDv5 派生,保证幂等;
  • 生成冲突返回 409:GenerationInProgress映射为409 Conflict,前端据此收敛到胜出的生成(见第九节);
  • 生成器本身是"零工具推断":submit_suggestion_generation声明tools: Vec::new()、require_no_approval: true,以suggestion_generation.md作为 system prompt,按schemas/suggestions.output.json结构化输出——即一个只读的、边界内的 canonical run。

九、生产者的行为约束:suggestion_generation.md

生成器的提示词 crates/product/ironclaw_assistant/prompts/suggestion_generation.md 是这条契约的灵魂,它定义了"好建议"的标准,值得逐条理解:

  • 先看再建议(Look before you suggest):模型被允许读用户已连接的工具与记忆;"一条基于真实读取的建议胜过五条凭空想象";如果读完无事可做,宁少勿滥。
  • 具体优于泛泛:"Reply to Dana about the contract question from Tuesday"优于"Check your email."。
  • 只读是绝对的:生成期间只 read / list,绝不 draft / modify / send / post;"Suggesting an action is your job; taking it is not"——建议可以写进suggested_prompt,但执行必须由用户触发。
  • 只主张亲眼所见:不得声称某个账号/扩展/能力可用,除非有证据(扩展以installation_phase == active为唯一可用判据);suggested_prompt以用户第一人称书写、可独立成立(接收方看不到这张列表)。

这些约束解释了为什么卡片天然"工具无关":模型能看到扩展,但输出 schema 不声明扩展身份——这直接导向第十一节"connect 去耦"的决策。


十、icon / sources 语义契约

卡片上两个必填字段的契约由 docs/internal/design/oobe/SUGGESTION-ICONS.md 单独定义:

  • icon:必填、provider 中立的任务类别语义枚举(email/calendar/document/storage/spreadsheet/presentation/code/messaging/notes/web/memory/generic),只控制卡片字形,不是扩展/厂商/能力身份;generic是保证兜底——未知、缺失、遗留值一律映射到generic,保证卡片永远可渲染。
  • sources:1–5 条简洁的人类可读来源标签(如"Gmail"、"GitHub"),是展示字符串而非扩展 ID,前端绝不从它们推导 icon 或 setup 路由。

Rust wire 层刻意把icon存为普通String,使 schema 演进无需持久化迁移;前端渲染由pages/chat/lib/suggestion-icons.tsx负责,位于懒加载的 suggestion surface chunk 中(避免给/chat的 eager bundle 增重)。


十一、前端消费层:API 客户端、数据 hook 与落地表面

落地契约的前端一半在crates/product/ironclaw_webui/frontend/src/pages/chat/下,与第七、八节的服务端一一对应:

  • lib/suggestions-api.ts:四个 typed 调用(fetchSuggestions/generateSuggestions/startSuggestion/dismissSuggestion),文件头注释明确"后端拥有生成、持久化与 suggestion → thread/run 绑定;浏览器从不臆造卡片状态"。pollDelayMs把retry_after_seconds钳制在 1–30 秒,防止缺失值造成热循环、敌意值造成无限挂起。
  • hooks/useSuggestions.ts:浏览器中 suggestion 状态的唯一 owner。轮询只在status === "generating"时按后端节奏运行,终态即停;生成冲突(另一 tab/设备先 claim 的 409)触发invalidateQueries重新读取权威状态;start成功后同时刷新 suggestions 与 threads 两个查询键;dismiss成功后本地立即移除卡片(服务端已提交)。
  • components/suggested-task-surface.tsx:四种生成状态驱动四种呈现——empty→ 生成 CTA(生成消耗真实模型运行,必须用户主动请求);generating→ 品牌化进行指示器 + 静态 skeleton 瓦片(尊重 motion policy);ready→ 横向滚动卡片条;failed→ 重试按钮。start成功后通过返回的thread_id导航到对应线程。

前端细节中还包含两个超出基础契约的入口(issue #7815 F1/F2):抽屉头部的refresh(重新执行 generate,因后端是 replace-only,所以"刷新即诚实刷新")与connect(链接到既有/extensions表面,作为连接流程的第一段,而非卡片内的 connect 状态)。


十二、从设计冲突中沉淀的三个关键决策

VISION-RECONCILIATION.md 记录了契约落地过程中三个已拍板的关键决策,它们直接塑造了当前实现:

  1. 卡片并行运行——单一活动锁被移除。原PROPOSAL.md §2A.3以submit_turn返回DeferredBusy/RejectedBusy为据限制"同一时刻一个活动任务";但suggestion.start为每条建议创建独立线程,后端不存在并行约束,Vision 的多卡片抽屉天然并行。
  2. "+Automation" 被移除。卡片 schema 没有automation_prompt字段、也没有 automation 路由——与其保留一个无持久化背书的客户端合成 prompt 注入入口,不如直接从卡片上去掉该动作(未来若要做循环自动化,需要单独的契约,而不是在此打补丁)。
  3. 事件/投影契约被取代。即本文 §1–§3 的AutomationTask领域模型,被 typed store overScopedFilesystem取代,作为"原始设计意图的记录"保留。

另一个值得注意的取舍是connect 与卡片的解耦:卡片不携带工具身份、不设 connect 状态、不依赖连接即可启动;如果启动的运行需要未连接的工具,agent 会发出既有的AuthRequiredgate 帧,线程内渲染AuthOauthCard——这是一条已上线且无需改动的路径。连接面板独立为"由扩展目录驱动的落地表面"(Connect panel ← extensions catalog),与建议抽屉成为两个并列表面。


十三、当前状态:什么是 stub、什么是 shipped

最后,厘清本文两个契约的"实现真相"(避免把设计文档误当实现文档):

  • AutomationTask契约(§2–§7)全部未实现:原文档明确 "Everything in §§2–7 isnot implemented"。当时前端基于 mock 数据 + localStorage 模式 store 运行;Rust 事件/投影/路由桩有意不写——零警告 clippy gate 会把未使用的脚手架类型判为告警,必须与首次实现 + 测试一起落地。
  • Suggestions 契约已 shipped 且始终开启:服务端(ironclaw_product_contracts描述符、ironclaw_assistant编排、ironclaw_webui四路由)与前端(suggestions-api.ts、useSuggestions.ts、suggested-task-surface.tsx)均已上线,路由无 feature flag 保护。前端表面保持懒加载,其卡片与图标代码不进入/chat的 eager bundle。
  • 尚未构建的 Vision 后续项(见 VISION-RECONCILIATION §5.1):live card status(订阅绑定run_id反映运行中/完成/失败)、V1 冷启动连接面板(批量 OAuth)、Agent 模式选择器(Suggest / Plan / Auto / Bypass,仍无持久化 home)、输入时卡片折叠为 pill 行、命名问候(V5)等。

结语:一份契约的两种命运

AUTOMATION-TASKS-CONTRACT.md的价值不在于它是最终实现,而在于它是一份完整、可评审的接线蓝图:它把"自动化任务"拆解为领域模型、持久事件、投影、传输帧、HTTP 表面、facade 方法与 Agent 门控语义,任何一步都有明确的属主 crate 与纪律约束(脱敏、可重放、服务端确认、无第二 outbound 路径)。而 PR #7694 的 Suggestions 契约则证明了同一套纪律在更轻量的存储模型上同样成立——typed store + CAS 取代事件投影,四条路由取代五条,但"浏览器从不臆造状态、成功只由 provider evidence 确认、卡片不携带工具身份"这些原则原封不动地延续了下来。对于要在 IronClaw 上新增产品表面的开发者,这份文档连同 VISION-RECONCILIATION.md、SUGGESTION-ICONS.md 以及上文列出的源码,构成了从设计到实现最完整的参照系。

  • 人工智能
  • AI 应用
  • 交互助手
  • AI Agent

【免费下载链接】ironclaw

IronClaw is an Agent OS focused on privacy, security and extensibility

项目地址:https://gitcode.com/gh_mirrors/iro/ironclaw
点击查看免费下载

相关推荐

上一篇:Wand-Enhancer 本地补丁完整指南:不下载 exe、3 步打好 Wand 客户端并解锁手机远程面板
下一篇:炉石传说HsMod终极指南:免费解锁32倍速和200+皮肤定制

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询