执行设计
面向开发者。从开发手册选择 API 开发、App 发布或项目修改。
本页说明统一执行设计。旧数据在维护窗口中通过带校验的迁移完成转换。最后一节链接功能和发布检查。
ChatGPT 使用普通模型提供方,凭证类型为 OAuth。OAuth 通过浏览器授权访问账户。加密令牌归提供方所有。所有语言模型调用都经过
ModelRegistry,包括定时工作、规则判断和 Bot 记忆。API 调用方接收本机浏览器回调,把结果发送给提供方 API。
OpenBot 保持 headless 和 API first。保留 Bot、Chat、工具、委派、审批、记忆、Code
Mode、App、索引、Computer、Channel、触发器、实时会话、组织、SSO、文件、Vault、模型和计费。core
和 protocol 仍可独立使用。
必需的外部服务只有 PostgreSQL 和对象存储。OpenBot 节点通过经过认证的私有 HTTP RPC 通信。RPC 指调用另一个进程中的代码。单节点和多节点使用同一个执行引擎。
OpenBot 提供模型用量记录、价格、点数、额度、预算和限流功能。这些功能通过公开 API 提供。服务器检查并执行相关规则。
| 包 | 职责 |
|---|---|
core |
模型步骤、工具步骤、上下文大小和 Chat 基础数据 |
domain |
产品数据、品牌 ID、状态、错误和纯规则 |
api |
HTTP 输入输出、鉴权和错误映射 |
protocol |
MCP、OAuth、App Card、发现和连接 |
openbot |
应用用例、工作流、PostgreSQL 和外部适配器 |
client 和 CLI |
通过公开契约调用 |
存储不从 HTTP 模型取得数据模型。业务代码返回领域失败,HTTP 处理器将其映射为公开错误。模型、Computer、Vault 和存储适配器仍可替换。普通查询和纯规则不需要只做转发的服务。
服务在构建 Layer 时取得依赖。入口组合 Layer 并持有作用域;业务模块不能导入入口目录。导入模块不启动 I/O。
服务器不为每个成员构建 Chat runtime。每个 Turn 或子阶段持有自己的模型和工具。账户连接可以按连接 ID 和版本共享缓存,但缓存不负责 Chat 状态或执行。
执行是具有稳定 ID 的持久工作。业务资源保留自己的 ID,并关联执行。命令键标识请求及其输入。重试复用同一个键;使用同一键改变输入会失败。
代次是执行当前持有者的编号。接管会增加编号。状态写入在数据库锁内检查代次。旧持有者可以保存迟到的外部结果,但不能推进新持有者的状态。
Effect 的 Workflow、Activity、DurableDeferred、DurableClock 和
ClusterWorkflowEngine
提供持久步骤、信号、定时器、消息和节点分配。各功能注册自己的工作流,引擎没有 Bot、审批或 Computer 分支。独立使用的 core 提供内存宿主,不保证进程停止后的恢复。
Program 用同一个循环处理恢复、持有者检查、取消和阶段失败。各功能提供 next
选择下一阶段,用 end 保存自己的结束状态。阶段的 after
操作只在资源关闭后运行。Turn、Tool、App、Delegation 和简单工作流都使用这个循环。各功能保留自己的子工作、时限和业务事务。Runner
负责接管和存储重试; RunScope 负责阶段资源和信号。
每个工作阶段在 Activity
中运行,名称由已保存的阶段和代次决定。引擎只保存安全的阶段结果,例如错误 ID 或暂停引用。输入、外部结果和完整失败保留在加密记录中。新持有者使用新的阶段名称,通过共同调用日志复用结果。取消及其他信号先关闭阶段入口,再等待本地 fiber 和资源停止。入口关闭后,迟到的 Activity 请求不能开始工作。
执行状态包括
queued、running、waiting、cancelling、outcome_unknown、completed、failed、cancelled
和 interrupted。最后四种是终态。outcome_unknown
等待已保存的结果或决策。取消请求进入 cancelling,不表示外部动作已经停止。
执行归属和来源身份相互独立。父执行等待所拥有的子工作及通知结束。分离的工具可以在 Turn 结束后继续。子工作结束与创建通知在同一事务中提交,父执行不会在两次读取之间漏掉工作。每个取消阶段有自己的编号和持久信号;重复发送旧信号不能取消后续阶段。
状态、结果、事件和待发送命令在同一事务中提交。发件箱保留命令,直到引擎将其持久保存。稳定 ID 保证重复投递安全。事务内不运行模型、网络或其他外部调用。
每个 Chat 同时只允许一个 Turn 推进。Chat 行锁确定持久输入命令的顺序。相同身份的输入可以引导运行中的 Turn;排队输入保留自己的身份和位置。资源释放后,当前 Turn 结束与下一个输入获准执行在同一事务中提交。
每个 Chat 检查点同时检查 Turn 和执行代次。检查点失败时复用已保存的模型和工具结果。模型结果先保存,再运行本地工具。工具结果先保存,再发布。保存的工具定义保留参数、路由、App 来源和 Vault 引用。发现结果改变不能改变已保存的调用;新调用仍须检查当前权限。钩子在明确的步骤运行,重放时去重写入。插件不能改变执行生命周期。
Effect 4.0.7 的工具结果使用 modelOutput
保存发给模型的文本和文件。OpenBot 保存并重放这个字段。旧补丁的 content
字段不会被读取或转为新字段。
执行存储按 ID、步骤或最新尝试查询调用。完整调用历史仍可用于审计。恢复和重试代码使用这些共同查询选择尝试。
工具发现构建供模型使用的工具列表。已保存的工具调用按其保存的定义查找处理器。恢复 Computer 或宿主工具不运行 App 发现;它仍检查当前权限,并在阶段作用域内持有资源。
打开 App 连接不会读取能力列表。已保存的 App 调用只查找所选类别和名称,再校验完整的已保存声明及连接版本。返回的调用对象在同一阶段内持有这份声明,用于发送和后续输入。发送时不再读取列表。恢复后的阶段会重新查找并校验声明。需要完整列表的调用方显式执行完整发现。
记忆先读取标签、来源关系和访问规则,再打开内容。只有选中且可见的内容才会解密。读取持有该 Bot 记忆的共享锁,修改独占同一个锁,使权限和内容来自同一个读取状态。模型调用在锁外执行。记忆模块负责上下文选择和学习变更,调用方不读取或替换整池数据。修改标签或状态时保留原有内容密文。
外部调用先保存意图,再标记发送,最后保存结果。安全重试复用供应商请求键。没有可靠重试规则的写入丢失结果后,进入
outcome_unknown
等待。已保存的结果会复用。模型请求重试产生新的 attempt 和开始事件,不能将新文本接到旧 attempt 的部分文本后面。
调用日志保存迟到结果,包括已确认用量。只有当前代次可以发布结果或改变业务状态。数据库失败在最近的安全边界重试。已知结果在保存重试期间保留,不再次调用供应商。损坏的存储数据作为失败报告,不当作临时数据库故障。
阶段结束或等待决策前先释放资源。释放失败保存完整 Cause 和安全错误 ID,并阻止新发送。未知写入仍需结果或停止决策。观察者关闭流不会取消阶段。
Chat 停止和删除都是执行命令。删除先关闭接纳,再取消关联工作并等待释放。Live 提供商会话属于一个作用域,在连接提供商之前注册。实际持有连接的节点关闭它并确认释放。数据库保存每个物理租约的关闭结果。回复丢失后,调用方重读该结果。关闭失败的错误 ID 在持有节点退出后仍然保留。缺少路由或运行器不健康不能证明资源已释放。持有者失去租约或无法检查租约时,会关闭连接。未知交互写入和音频不会重放。
一个 App 连接可在多个节点持有物理作用域。每个作用域都有独立租约。连接清理会等待所有作用域,包括路由已被替换的旧作用域。调用方结束作用域和远端清理都读取同一关闭结果。
App 授权交换和刷新会在保存每个令牌结果时,同事务写入清理命令,迟到结果也一样。服务为来源组织执行清理,所以成员失去权限不会停止清理。安装令牌时,调用 ID、凭据和结束事件在同一事务中提交。未使用结果的清理会等待来源执行结束。删除或替换已安装授权时,同一事务会将授权交给清理;此类清理可以在来源执行结束前运行。只要任何连接或开发测试仍使用同一签发方和 OAuth 客户端,其他清理就保持待处理。OAuth 服务器可能一起撤销相关令牌,即使令牌字符串不同。使用者释放这些授权后,持久计时器会再次检查。检查涵盖所有 OpenBot 所有者。
共同的授权占用表记录哪个连接或开发测试持有授权,也记录待处理的授权发送和撤销。占用记录与对应业务数据在同一事务中改变。清理按授权范围查询此表,无需打开所有连接或测试的秘密。
App 连接设置共用一套准备规则。每个宿主只刷新一次 App card,检查所选端点和输入值,然后探测并保存同一份准备好的连接。
授权发送和安装与清理共用数据库锁。已保存的撤销意图在重试和节点接管期间持续占用授权范围。新授权发送会等待该撤销结束。清理在提交结束事件时释放连接和授权范围。远端撤销失败时使用共同的重试计时器。服务器没有撤销端点时,会保存
LocalOnly 结果。
ExecutionIdentity
区分资源所有者、当前操作人、原始 token、Bot、委派和来源执行。子工作保留来源的全部限制。其他调用人可以增加限制,但不能移除先前限制。Channel 工作还保留绑定的发言人。身份不能从字符串前缀、Turn 作者或插件顺序推断。
HTTP、工具、Code Mode 和恢复工作共用应用用例及权限规则。所有者和操作人必须仍有效。恢复时、外部发送前,以及等待模型限额或密钥后,都会检查权限。已保存的审批不能授予平台、成员或原始 token 拒绝的权限。列表在数据库查询中应用资源规则。API key 和同源 session cookie 进入相同用例。
ServiceIdentity
标识归平台或某个组织所有的服务,不使用成员 ID。它共用执行存储、命令日志、发件箱、事件和恢复循环。命令键属于成员或服务范围。服务不能进入成员工作流;成员或 token 不能通过服务工作流发送。每个服务有自己的当前权限检查。组织公开接口不能读取平台执行。
| 功能 | 规则 |
|---|---|
| Chat、Turn、工具 | Chat 排序输入,Turn 持有模型步骤,后台工具有独立执行。 |
| 审批、委派、计划、限额 | 保存决策和信号。审批等待时来源 Turn 可以继续。任务通知去重。 |
| 记忆 | 学习和辅助调用使用共同调用日志。游标提交不能覆盖较新的进度。 |
| Code Mode | 保留沙箱隔离、固定时间和随机种子、调用顺序检查。重放使用共同调用记录。 |
| App 调用、OAuth、开发测试、索引 | 长任务使用工作流。App 管理连接和授权记录。实例重启不重置其他节点工作或有效 View。 |
| Channel、触发器、提醒 | 收件去重,持久定时器,记录发送和重试。已确认拒绝与未知发送结果采用不同重试规则。 |
| Computer、Connector、Live | 使用共同资源目录和 RPC,物理连接仍由实际节点持有。仅驱动支持时恢复;不重放音频或结果未知的交互写入。 |
| 文件、Vault、模型 | 保留专用适配器。跨存储文件删除使用可重试工作。仅在调用边界解封密钥。 |
| 账户、组织、SSO | 保持独立业务模块,所有入口使用共同身份和权限规则。 |
任务状态保留时间、步骤和费用限额,恢复不会重置限额。暂停阻止新发送并释放阶段资源。评审可处理自己的暂停,不能绕过父任务暂停。只在任务及其父任务都可运行时计时。关闭工作收到通知后只增加历史,不启动新 Turn。
用量、失败和秘密
Section titled “用量、失败和秘密”每个模型调用有唯一计费身份。已确认用量、结果和事件一起提交。重复结果不重复扣款。估算仅用于预算预留和限流。缺少的用量保持未知,并保留估算和调用 ID,不能记录为成功且零消耗。调用结束后释放预留。接管时也会释放旧尝试的预留。估算保存在单独字段中,缺失的旧估算保持为空。未知用量不会阻止新调用。迟到的已确认用量仍然只结算一次。这些估算不是经过证明的提供方最终扣费上限。
完整失败 Cause 保存在加密错误记录中。HTTP、Channel、父 Bot 和工作流结果只携带安全分类及错误 ID。复用安全失败时保留原错误 ID。
HTTP API 的服务端故障返回 RequestFailed,包含 category 和 errorId。响应头
openbot-error-id
使用同一 ID。契约中声明的输入、访问和冲突错误保留原状态码和类型。API
key 和 session
cookie 使用同一错误边界。类型化错误映射会保留完整的私有 Cause,直到该边界保存它;不会将 Cause 打印到普通日志。
事件流开始后,服务端故障会在失败事件中发送相同的 RequestFailed
字段。事件编码也在这个边界内进行。如果保存或编码失败记录时再次出错,连接会关闭。读取方可从最后保存的游标继续读取;内部错误不会作为事件数据发送。
后续释放资源失败会保存自己的加密 Cause 和错误 ID,并关联同一执行。它不会覆盖最初的失败,也不会使执行提前结束。释放失败的重试间隔从 100 毫秒逐步增加到 5 秒。
引擎消息只携带记录引用。敏感输入、结果、身份和事件保存在加密领域记录中。Vault 调用保存条目 ID 和允许的字段,不保存密钥值。同名新条目不能替换已保存的引用。发送前再次检查当前权限和字段。
observe(executionId, afterSequence)
读取有序事件日志。每个执行内的事件有序号;文本还标识步骤和 attempt。文本按小批次持久保存后再发布。
PostgreSQL 通知唤醒每个节点的共享读取器。通知丢失时,事件日志仍是真实依据。有界缓冲限制内存。慢读者和重新连接的读者从游标补读,终态可以再次读取。HTTP、SDK、Channel 和父 Bot 使用相同路径。关闭 SSE 或长轮询只关闭观察;取消必须发送独立命令。
迁移前停止旧节点和新输入,备份数据库及对象引用。先运行新的 Drizzle migration,再按批次转换数据并保存进度。不能修改基线迁移。运行时存储 Layer 不执行 DDL,也就是改变表结构的 SQL。
授权占用迁移也覆盖已使用持久执行的数据库。迁移按批次读取当前授权和待处理的授权调用。授权占用记录完整前,服务器不能启动。令牌仍保存在原有加密记录中。
保留资源 ID、所有者、历史、审批、连接、秘密和待办。旧加密记录使用专用迁移 codec。为未完成工作关联新执行。未知外部结果保持未知。无法重建的工作标记为中断并保留原因。损坏记录使迁移停止,不能跳过。开发者邀请的所有者必须与其所有权证明一致。
读取导入的文件和索引请求历史时,每次都检查当前权限。索引请求还检查当前开发者证明和 App 访问权。证明撤销后,不能再凭它读取旧结果。导入记录不会重启外部动作,也不会取得读取者的执行身份。
新节点启动前检查数量、引用、解密和状态。转换必须能中断后续跑,也能安全重复运行。只在恢复流量前从备份回退。恢复外部动作后采用向前修复,避免回滚重复写入。
能力覆盖、源码测试用例、已删除路径及发布证据见 执行设计验收。
功能基线为 581 项测试。新增测试覆盖持久命令、代次检查、取消、迟到结果、慢读者、权限改变和用量。真实双进程测试共享 PostgreSQL,杀死持有节点并检查从已保存步骤恢复。成员任务和服务任务共用测试路径。
已删除成员 runtime 包装:BackendLayers、Backend、ManagedChats、Chats、Coordinator
和旧 LiveSessions
map。引用计数、keepOpen、current、逐方法代理和 Chat 心跳扫描随之删除。旧记忆任务队列和辅助模型调用路径也已删除。Turn、工具、审批、委派、App、Code
Mode、Channel、触发器、提醒和记忆工作流通过共同应用 Layer 注册。
旧 App Operations、Invocations 服务和 Code
Mode 租约执行器已删除。App 发现只返回描述,不再持有动作回调。调用和程序只通过共同工作流执行。服务器启动不再重置旧 App 调用、扫描旧委派或写入 Chat 执行器心跳。旧 PostgreSQL
Operation 监听器、取消通知和过期扫描已删除。core
中的内存执行器仍可独立使用。旧 App 和 Code 存储记录保留迁移编解码,不再作为执行路径。
旧的全局 Gate、工具审批、工作工具层和 Bot 信号总线也已删除。每个执行在自己的作用域中构建 Gate 和审批。提醒工具提交持久命令。请求工作流逐条关闭到期请求,不再全局扫描请求。请求、保存的工具调用和通知在同一事务中提交。写入失败不会留下缺少调用内容的待审批请求。
旧的委派 Limits 服务也已删除。限额检查使用已保存的用量和委派工作流。读取用量或设置失败时,动作停止,不采用旧费用或默认时限。审批检查中断后,任务保持暂停,等待恢复。
开发者测试运行和登录准备已使用共同工作流、调用记录和事件日志。命令保存原始调用者和稳定的执行 ID。进度提交会检查执行代次。清理会等待运行关闭连接。旧运行 FiberMap 和结果回调已删除。
连接检查、更新、手动更改和重新验证已使用共同执行命令。已保存的读取结果提出状态更改。最终事务在应用更改前检查当前调用者、版本和执行代次。检查点指向已保存的结果,所以删除连接后仍可读取结果。被替换的授权在同一事务中交给服务清理。
授权准备、授权码交换、刷新、撤销、删除、重置和文件维护使用共同执行记录。开发者测试清理使用已保存的调用、信号和计时器,不再有独立 worker 或本地协调路径。
记忆辅助调用使用 PostgreSQL 共享许可,每个许可占用一个共享调用名额。模型 token 限额使用数据库共享桶,也就是共享计数器。发送前,当前执行必须仍持有许可,因此节点接管不会重置共享限额。
数据转换器保存进度并检查旧记录,启动门禁随后才允许新服务器运行。测试覆盖加密数据、中断工作、重复运行和部分迁移后的恢复。表结构迁移与数据转换是同一维护命令中的两个步骤。
完整服务器和双进程测试覆盖持有者退出、缓慢关闭、回复丢失、数据库网络断开和替换后的迟到关闭。未保存关闭结果的物理租约保持未知。检查还覆盖权限、秘密、用量和模块依赖。
发布时,查看该提交的
CI 运行。构建、类型检查、包测试、Compose 联调、镜像层检查和漏洞扫描必须通过。推送到
main
还会发布经过检查的镜像。Docs 工作流
为同一提交构建并部署文档。测试数量和其他提交的成功运行不能替代这些发布检查。