跳到主要内容

第 5 章:Issue、Comment 与工作触发

5.1 “创建 Issue”与“让 Agent 工作”不是同一动作

Issue 是长期工作对象,Task 是一次执行尝试。两者分离后,同一个 Issue 可以:

  • 先由人处理,后来分配给 Agent;
  • 因多轮评论触发多次 Agent Task;
  • 切换 Assignee,而保留旧执行历史;
  • 由 Squad Leader 先路由,再由 Worker 执行;
  • 在失败后重试或由人类 rerun;
  • 被 Autopilot 创建,但继续由普通协作流程推进。

因此,源码里没有简单的 issue.save(); agent.run()。它先确定工作对象,再经过触发计算、权限、归因、合并策略,最后进入 TaskService

5.2 Canonical Issue Create

IssueService.Create 是多入口共享的创建事务。它负责的不只是 INSERT:

  1. 验证 Workspace 与父 Issue/Project 等范围;
  2. 检查重复候选和调用方选项;
  3. 分配 Workspace 内 Issue Number;
  4. 创建 Issue 与附件/Label 关联;
  5. 提交事务;
  6. 发布 issue:created
  7. 记录分析事件;
  8. 若 Assignee 已是可运行 Agent/Squad,决定是否入队。

HTTP Handler、Channel /issue、Autopilot Create-Issue 等入口复用该 Service,避免出现“从 Slack 创建的 Issue 不触发 Agent”或“Autopilot 创建没有附件事件”之类分叉。

5.3 触发来源全景

当前 Task 主要可以来自:

来源典型入口目标
Issue Assignment创建/更新 Issue 的 AssigneeAgent 或 Squad Leader
显式 @Mention新 Comment 内容被提及 Agent / Squad
Thread Parent回复 Agent 发起的线程线程所属 Agent
Assignee Fallback普通评论没有更明确目标当前 Agent Assignee / Squad Leader
Direct ChatWeb、Slack、Feishu ChatChat Session 的 Agent
Quick Create用户只给一段意图Builder Agent 创建一个 Issue
AutopilotManual / Schedule / Webhook配置的 Agent 或 Squad
Rerun / Retry人工或系统恢复原执行角色
Squad DelegationLeader 评论 @WorkerSquad 成员 Agent

这些来源最终都创建 agent_task_queue,但 Prompt、权限、归因、输出位置与重试策略不同。

5.4 Assignment 触发

Issue 创建或更新为 assignee_type=agent 时,系统检查:

  • Agent 在同一 Workspace;
  • 未归档且配置完整;
  • Runtime 可用于该 Agent;
  • 当前动作不是 Agent 自己在执行中造成的自触发;
  • 调用者有权调用该 Agent。

通过后调用 EnqueueTaskForIssue 一类路径。若 Assignee 是 Squad,则解析 Leader,创建 is_leader_task=true 的协调 Task,而不是随机选一个成员。

分配触发的 Prompt 以整个 Issue 为中心;Comment 触发则以“新输入”与线程回复为中心,二者不能混为一种模板。

5.5 Comment 是最复杂的触发入口

handler/comment.go 之所以庞大,是因为一条评论可能同时具备:

  • 多个 @Mention;
  • 父 Comment 与 Root Thread;
  • 作者是 Member、Agent 或系统;
  • 作者 Agent 背后有 source_task_id
  • Issue 当前 Assignee 是 Agent 或 Squad;
  • 目标 Agent 已有 queued/dispatched/running Task;
  • 相同评论可能被预览、创建、编辑或删除;
  • 某个目标对当前 Originator 是 private。

触发计算大体遵循“越明确优先级越高”:

实际代码还叠加去重、自触发、权限和延迟升级,图只表示主要选择顺序。

5.6 显式 Mention 为什么不等于字符串匹配

触发一个 Mention Task 至少要确认:

  • Mention 引用真实、同 Workspace 的 Agent/Squad;
  • 目标没有归档;
  • 当前人或委派链的 Originator 允许调用目标;
  • Agent-to-Agent Mention 保留来源 Task 和人类 Originator;
  • 同一目标在一条评论中只触发一次;
  • Leader/Worker 自触发规则不会形成协作回环。

权限来源是 Agent 的 permission_mode 和 Invocation Targets,而不是“能在编辑器里搜到这个 Agent”。私有 Agent 可以被展示,但调用仍被服务端阻止。

5.7 Thread Parent 路由

当人回复一条 Agent Comment 时,最自然的目标往往是该 Comment 的作者,而不是 Issue 当前 Assignee。

系统通过 Parent/Root Thread 找到对话归属,并判断 Agent 是否已在该线程回复过。这样即使 Issue 后来换了 Assignee,回复仍可回到原对话参与者。

这个规则让 Comment Thread 成为“局部会话路由”,同时不把它和 Provider Session 强绑定:Task 仍会根据可恢复记录决定是否 Resume。

5.8 Assignee Fallback 与延迟升级

没有显式 Mention 和明确线程目标时,普通评论可以触发当前 Agent Assignee。

但在多 Agent 场景,Leader/Worker 可能正在处理同一轮输入。立即同时唤醒 Assignee 和被 Mention Worker 会制造重复劳动。Multica 因此支持 Deferred Assignee Fallback:

  1. 先触发更明确的 Worker Task;
  2. 创建 status=deferred、带 fire_atescalation_for_task_id 的 fallback;
  3. 若 Worker 正常启动/解决,取消 Deferred;
  4. 若 Worker 未能推进,到期后提升为 queued,由 Assignee/Leader 接管。

这是一个小型升级策略,而不是简单的延时队列。

5.9 Comment Coalescing:为什么要合并 queued Task

用户可能连续发送:

“请修复登录错误”
“补充:只发生在 Safari”
“还有截图里的 401”

如果 Agent 还没领取第一条,每条都启动独立 Task 会浪费上下文和并发。系统尝试把新评论合并到同一 (issue, agent) 的 queued Task:

  • trigger_comment_id 进入 coalesced_comment_ids
  • 最新评论成为新的 Trigger;
  • trigger_summary 更新;
  • 完整归因快照重新计算;
  • MCP Overlay 与 Connected Apps 快照一起重算;
  • 交付评论集合保持可追踪。

注意:合并不是只 append 一个 ID。新评论可能来自另一个人,因而授权、归因和可用外部应用都可能变化。相关辅助函数在 TaskService.AttributionForMergedCommentBuildRuntimeMCPOverlayForMerge

5.10 为什么不合并已 dispatched/running Task

一旦 Task 被 Claim:

  • Claim Payload 已经冻结;
  • Daemon 可能已写 Brief;
  • Agent 可能已读取 Trigger Comment;
  • 中途修改数据库 Context 不保证执行进程能看见;
  • 重新归因却不重发能力会产生安全错位。

所以 Coalescing 只发生在仍 queued、尚未交付的 Task。

若评论在运行期间到达,Completion Reconciliation 会检查未覆盖的新评论,并在当前 Task 结束后创建 Follow-up,防止输入静默丢失。

5.11 预览与真实触发要共用计算

Issue UI 可以在提交评论前预览“这条消息会触发哪些 Agent”。如果预览使用一套简化规则,而真实创建使用另一套规则,就会出现严重体验和权限偏差。

源码把预览尽量接到相同的 Trigger Compute 逻辑:

  • 相同 Mention 解析;
  • 相同 Thread/Assignee Fallback;
  • 相同 Permission/Readiness;
  • 预览只不执行写入。

设计原则是:解释系统行为的接口,必须复用实际决策源,而不是重新描述它。

5.12 Agent Comment 与 source_task_id

Agent 通过任务内 CLI 发布 Comment 时,Comment 会记录来源 Task。这条链有多个用途:

  • 找到最初的人类 Originator;
  • 判断 Agent-to-Agent Mention 是否属于被授权委派;
  • 抑制 Agent 自己评论后再次触发自己;
  • 让 Squad Leader 知道 Worker 结果来自哪次执行;
  • 完成后 Reconciliation 判断哪些输入已处理。

管理者后来手动编辑 Agent Comment 时,旧 Task Lineage 不能继续被盲目信任;对应路径会清理或重算陈旧来源。

5.13 Chat 触发与 Issue 触发的差异

Direct Chat 通过 TaskService.SendDirectChatMessage 完成:

  • 先写用户 Chat Message;
  • 决定是否续用已有 Chat Task/Session;
  • 创建 Task 并关联 chat_session_id
  • 外部渠道还带 Channel Type 与回复目标;
  • 完成输出写回 chat_message,而不是默认创建 Issue Comment。

Chat 是对话型工作,不一定有 Issue。但其 Task 仍使用相同 Claim、Daemon、Agent Adapter、消息与用量管道。

5.14 Quick Create 为什么要求“恰好创建一次”

Quick Create 让 Agent 根据自由文本创建结构化 Issue。它的特殊风险是重复:如果 Agent 第一次已经创建成功,但输出解析或网络回报失败,自动重试可能再创建一个 Issue。

因此 Prompt 要求只调用一次 Issue Create,系统对某些失败不采用普通自动重试,并通过 Task Context/Inbox 把成功或失败反馈给请求者。

这体现了副作用任务的一条原则:可重试的技术操作不一定意味着业务动作可重放。

5.15 删除、编辑与取消的联动

触发评论被删除或编辑时,系统不能只改 Comment:

  • queued/deferred Task 可能应取消或重算;
  • Coalesced 列表要提升仍存在的最新评论;
  • 已运行 Task 不能抹去历史,但后续 Follow-up 要基于新事实;
  • Thread Resolution 可能因新回复自动取消;
  • Realtime 需要广播 Comment 与 Task 两类变化。

这也是 Comment 流程必须与 TaskService 协作,而不能只做 CRUD 的原因。

5.16 触发路径的五个不变量

  1. Workspace scoped:所有引用对象必须属于同一 Workspace。
  2. Permission checked:每个目标独立校验调用权,不能因一个目标可用就放行全部。
  3. Attribution complete:Originator、Accountable、Evidence 与 Delegation 一起移动。
  4. Delivery explicit:进入 Claim Payload 的 Comment 要能标记已交付,后续输入才能判断是否遗漏。
  5. No invisible mutation:已 Claim Task 的上下文不能在数据库里悄悄改写后假装 Agent 已收到。

5.17 调试入口

当“评论没有触发 Agent”时,按顺序查:

  1. Comment 是否成功创建,Actor Type/ID 是否正确;
  2. Trigger Preview 返回了哪些候选和阻止原因;
  3. Mention、Thread Parent、Assignee Fallback 哪条分支命中;
  4. canInvokeAgent、Agent Status、Runtime Readiness 是否阻止;
  5. 是否被合并进已有 queued Task;
  6. 是否创建为 deferred;
  7. 是否因为自触发/已运行任务而等待 Reconciliation。

主要源码:

5.18 本章结论

Multica 不把“用户输入”直接等同于“启动 Agent”。中间有一层明确的工作路由:

  • Issue 负责长期目标;
  • Comment/Chat/Rule 提供新意图;
  • Trigger Compute 选择目标;
  • Permission 与 Attribution 决定这次调用是否合法、代表谁;
  • Coalescing、Deferred 与 Reconciliation 决定何时执行以及输入是否已交付。

下一章进入这些入口的共同落点:agent_task_queue 状态机。


上一章:服务启动、路由、中间件与认证
下一章:任务队列与状态机