12 KiB
001 领域模型
本文命名 dsh-superagent 的各个概念,以及每个概念守住的规则。代码、agent 调用的工具、面板和其他文档都使用这些术语,不用别的。依照 000 理念,模型只保留工作需要的概念,一个业务需求会变成其中某个概念的属性。
流程
工作从 Trigger 到 Eval 再到执行。Source 产出 Triggers。一个 Trigger 作为上下文扇出给订阅了其 Threads 的各个 Tickets。以这种方式到达某个 Ticket 的人的消息就留在那个 Ticket 上;其余每个 Trigger 都进入 Eval,由它决定是否发起新的工作。Eval 接单时,创建一个带 Mandate 的 Ticket。Assignment 把 Ticket 交给一个 Executor,结果通过 Channel 回到 Source。
flowchart LR
S[Source] -->|Trigger| T[Thread]
T -->|batch| E[Eval]
T -->|Subscription fan-out| K[Ticket]
E -->|accept with Mandate / stop| K
E -->|ignore / unavailable| X[recorded]
P[Policy change] -->|notice| K
K -->|Assignment| W[Executor]
K -->|Channel| S
概念
Source、Thread、Eval、Mandate 与 Subscription 构成核心领域;其余概念支撑它们。
| 概念 | 定义 | 它守住的规则 | 为什么存在 |
|---|---|---|---|
| Source | 一条事件流。Source kind 是一个可复用的实现;Source instance 是某个 kind 的一份配置:是否运行、监听什么 | 保存自己的读取位置与自己的 bot 身份。定义什么算事件,包括只在时间上成立的条件。在进入领域之前,丢弃 bot 自己的消息和监听范围之外的 Triggers。把每个 Trigger 翻译成它所属的 Threads 与用于答复的 Channel 地址 | 重启不得重放事件 |
| Trigger | 一条不可变事件,带有 Source 自己的元数据,例如它的 actor | 属于一个或多个 Threads。同一个 Trigger 被接单两次只创建一个 Ticket | 审计与幂等的单位 |
| Thread | 按时间排列的 Triggers;由 Source 定义哪些 Triggers 构成一个 Thread | 同一时刻至多运行一个 Eval。它可以有多个 Tickets。它保存自己的 Subscriptions、正在运行的 Eval 和 Decision 历史 | 给 Eval 一个一致的视图和它的上下文 |
| Correlation | 同一个 Trigger 所属 Threads 之间的关系 | 只增加上下文;从不路由 | 让 Eval 能追踪相关证据 |
| Channel | Thread 的写入侧;地址由 Source 定义 | 一个 Thread 可以有多个 Channels;一个 Ticket 持有其中一个集合 | 认领回执、答复、pull requests 和 resolved reviews 都是写入 |
| Policy | 带版本的评估规则,加上每个 Source 自己的部分:谁可以通过该 Source 发起工作、用的是该 Source 自己的身份、名单如何同步、以及哪些 bots 可以产生候选 | Policy 版本覆盖规则、accept mode 和各 Source 部分。一个与某个活着的 Ticket 相关的变化——比如它接单所依据的规则变了——会投递给它的 Assignment;但不吊销 Mandates。不同 Source 上的身份彼此绝不映射 | 规则变了,正在运行的 Eval 必须作废 |
| Eval | 对一个 Trigger 批次的判断,分两层:gate 与 assessment | 输入:Trigger 批次、一个 Thread 快照、该 Thread 上的所有 Tickets(含 ended 的)、以及当前的 Policy。输出:一个 Decision。不持有状态 | 决定新的工作是开始、继续还是停止 |
| Mandate | 一次接单所授予的东西:Policy 版本、scope、强度、命中的规则、发送者在该 Source 上的身份、以及 Source 收到该 Trigger 的方式 | 不可变;更宽的 scope 意味着新的 Ticket。Ticket 的结束不等于它的结束:只有人的 stop 才撤回它,一个不再成立的 Policy 会吊销它。ended Ticket 恢复之前会按当前 Policy 重新校验 | 说明工作为什么可以做、能做到什么程度 |
| Ticket | 一份授权工作的单位 | 只能由 Eval 创建。引用它的 Mandate;持有它的 Channels 与 Inputs。走 open → ended,当有人在它某个 Thread 上发的消息让它复活时会回到 open,除非它是被人停掉的。它定义自己的状态,不跟随 Executor 的 | 拥有执行及其报告 |
| Assignment | 一个 Ticket 与某一个 Executor 实例的绑定 | 一个 Ticket 随时间会有多个。独立于 Ticket 走 pending → running → ended。在 Executor 一侧,只有它的最终报告能结束 Ticket | Executor 在领域之外,但"由谁执行"必须在领域之内 |
| Subscription | Ticket 与 Threads 之间的多对多关系,由 Thread 持有 | 命中的 Trigger 会扇出给每个订阅着的活 Ticket,不检查发送者是谁;以这种方式到达某个 Ticket 的人的消息不经过 Eval。Executor 为上下文关注的 Thread 同样会抵达它的 Ticket,但不给它带来任何授权、也不使它复活。ended Ticket 保留它的 Subscriptions,包括一个已关闭或已合并的 pull request 的订阅:人的消息会在同一个 Mandate 下让它复活,不经过 Eval 也不产生 Decision;来自 bots 和 CI 的消息到不了它,因为它的 Executor 恢复时会重新读取它的 Threads。被人停掉的 Ticket 什么都收不到。只有当一条消息明确指向某个既有 Ticket 时,main Session 才把 Thread attach 到它。人可以移除某个 Thread 上的所有 Subscriptions;Executor 只能移除自己的 Ticket 对一个它判定为无关的 Thread 的订阅,除了它的 Ticket 由此开始的那个 Thread、以及被 Source 要求关注的那个 Thread(例如它的 pull request) | 让后续讨论不需要任何人再次点名 agent,就能到达 Executor |
人不是领域概念。Trigger 带着 Source 给出的 actor 标识符。允许名单是 Source 在 Policy 里的那部分,用的是该 Source 的身份体系。同一个人拥有几个 Source 上的账号——这是 agent 需要时才会读的提示词文本。
Executor 连同它的 Sessions 和目录在领域之外,通过 Assignments 连接。
聚合:Source 持有它的读取位置。Thread 持有它的 Subscriptions、正在运行的 Eval——Eval 串行运行,期间到达的 Triggers 并入下一批——以及它的 Decision 历史。Ticket 持有它的 Assignments、Channels 与 Inputs,并按 id 引用 Decisions。
被拒绝的概念
| 被拒绝的 | 理由 |
|---|---|
| Principal 或 Membership 作为实体、或跨 Sources 映射身份 | 人是 Trigger 的元数据;允许名单是 Source 在 Policy 里的部分;跨 Source 的映射是提示词文本 |
| Eval 自己调度延迟的 Triggers | 一个需要等待的条件就是一个普通的 Source,这样 Eval 永远不反过来依赖 Sources |
| waiting 或 revoked 的 Eval 状态 | 等待属于 Sources;超时的 Eval 把它的 Triggers 重新排队,或以 unavailable 结束 |
| 每个 Thread 至多一个活 Ticket | 一个 Thread 可以承载多件相互独立的工作;评估靠 Ticket 列表来避免重复 |
| "append to Ticket" 这个 Decision | 追加是 Subscription 扇出,不需要 Eval |
| 对 Subscriptions 做准入检查 | Subscription 覆盖整个 Thread;Mandate 负责区分授权与上下文 |
| 会路由的 Correlation | 路由归 Subscriptions;Correlation 只增加上下文 |
| Ticket 状态跟随 DSH Session | Ticket 可以被移交、复活、或由多个 Executors 执行,所以它定义自己的状态 |
| 由 Executor 声称有人要求它停止 | 只有 Eval 决定结束的强度 |
| Policy 变更时取消正在运行的 Tickets | 被取消的改动会留下做了一半的工作;正确的做法是记录并投递这个变更 |
| Executor Session 失败或受阻时锁掉它的全部工具 | 那会连累直接在 Session 里工作的人、以及恢复之后的报告;暂停自动输入就够了 |
| 从报告的自由文本推断"在等人" | 分类需要结构化事实;Executor 用结构化字段来问 |
哪些是可复用的
上面的概念及其规则是可复用的。Source 实现、Policy 内容、Executor 与 Channel 实现属于具体的部署。每个 Source 在内部保留自己的说法,例如 message、chat 或 notification,并把它们翻译成 Triggers。
| 部分 | 在本仓库中 |
|---|---|
| Source | Feishu:实时事件、历史恢复、对订阅话题的轮询。GitHub:notifications、被订阅的 Issues 与 pull requests、CI 变化、pull requests 的合并或关闭,以及(开启时)仓库中每一个新的 Issue、pull request、评论和默认分支的 CI |
| Policy | 评估规则,以及每个 Source 的允许名单、它的同步、bot 名单与准入表 |
| Executor | 每个 Ticket 一个 DSH Executor Session 和一个目录,运行在一个由 Executor 与各 Sources 注册变量的执行环境里 |
| Channel | Feishu 话题回复、GitHub 评论与 review thread 回复 |
术语表
本及后续文档所用术语的对照表,附中文名;上面没有引入的术语由后续文档解释。面板一次只显示一种语言;它的 (?) 按钮打开这份双语术语表。agent 在 GitHub 与 Feishu 上发给人的消息使用平实的语言,不用这些术语。
| 术语 | 中文 | 含义 |
|---|---|---|
| Agent | 智能体 | dsh-superagent 运行的自动化团队成员,通过 GitHub 账号、Feishu bot 这样的身份在 Sources 上行动 |
| Main Session | 主 Session | 评估 Triggers、接单并派发 Tickets、以及答复的那个 DSH Session;代码与提示词里叫 orchestrator |
| Executor Session | 执行器 Session | Executor 为一个 Ticket 运行的 DSH Session;代码与提示词里叫 worker |
| Source | 来源 | 一条事件流:由插件提供的一个 kind,加上一份实例配置 |
| Trigger | 触发 | 来自 Source 的一条不可变事件 |
| Thread | 线程 | 按时间排列的 Triggers;Eval 在其上逐个运行的单位 |
| Correlation | 关联 | 共享一个 Trigger 的 Threads 之间的关系;只增加上下文 |
| Channel | 渠道 | Thread 的写入侧 |
| Policy | 策略 | 用于接受工作的带版本规则,以及每个 Source 在其中的部分,例如谁可以在那里请求工作 |
| Policy version | 策略版本 | 规则、Source 部分与 accept mode 的一种组合的标识;每个 Mandate 都记录它 |
| Eval | 评估 | 把一个 Trigger 批次变成一个 Decision 的过程 |
| Gate | 准入 | Eval 确定性的第一层:谁可以发起工作、以什么强度 |
| Assessment | 研判 | Eval 期间模型做出的判断 |
| Decision | 决定 | Eval 的结果 |
| Mandate | 授权 | 一次接单所授予的东西:强度、scope 和它所依据的 Policy 版本 |
| Ticket | 工单 | 一份授权工作的单位 |
| Assignment | 派工 | 一个 Ticket 与某一个 Executor 实例的绑定 |
| Subscription | 订阅 | 一个 Ticket 与一个 Thread 之间的关系 |
| Fan-out | 扇出 | 把被订阅 Thread 的 Triggers 作为上下文交付给一个 Ticket |
| Executor | 执行器 | 运行 Assignments 的端口:每个 Ticket 一个 Executor Session 和一个目录 |
| Execution environment | 执行环境 | Executor Sessions 与 prepare command 运行的地方:由 Executor 与各 Sources 注册的变量,每个都有 key、取值和说明 |
| Report | 报告 | 一个 Assignment 向它的 Ticket 报告的状态 |
| Attention | 待人工决定 | Ticket 上等待人答复的一个问题 |
| Disposition | 去向 | 一个 Trigger 被过滤、被评估或被扇出的记录 |
| Input | 输入 | 投递给一个 Ticket 的上下文:扇出的 Triggers、Policy 变更、resume 与文本 |
| Scope | 范围 | 一个 Mandate 所允许的工作 |
| Strength | 强度 | 一个 Mandate 或 Eval 的依据有多强 |
| 分组 | 取值 |
|---|---|
| Decision | accept 接单 · stop 停止 · ignore 忽略 · unavailable 未完成 |
| Strength | explicit 明确 · inferred 推断 |
| Assignment | pending 待启动 · running 执行中 · ended 已结束 |
| Report | progress 进展 · waiting 等待 · ready 就绪 · blocked 受阻 · failed 失败 · dropped 无需处理 |