agent-Specialization/cache_research/gateway/gateway_work_plan.md
JOJO 6e043389b9 docs(research): Gateway 化改造工作计划与范围复杂度评估
Co-authored-by: Astrion powered by Kimi-K3 <astrion-agent@users.noreply.github.com>
Co-authored-by: Codex powered by ChatGPT-6-Astra <codex@example.com>
2026-09-07 12:27:59 +08:00

149 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Astrion 运行时边界整理与定时任务工作清单
> 更新日期2026-09-07
> 状态:修订后的设计与实施建议,尚未实施。
> 原题为「Astrion Gateway 化工作清单」,保留文件名便于既有引用。
> 依据:用户对目标的澄清、三份研究报告,以及本轮三个 Luna 子智能体的只读核查。
> 本轮核查为静态代码分析,没有运行并发、断线或进程重启复现;下文区分已有机制、待核验风险与拟新增能力。
## 0. 改造目标与范围
本次目标是让 Astrion 的内部职责、状态修改规则和任务入口更清晰,使后续功能沿稳定边界扩展。当前明确的新功能场景是**定时任务**。
Gateway 是长期架构方向;近期交付是在现有进程中整理运行时服务边界,让 Web、CLI 和定时触发器复用任务受理、执行与控制逻辑。验收依据是新增入口所需理解和修改的范围,以及现有行为是否得到保留。
近期主线:
1. 固定已有状态边界和正确性保障。
2. 抽出接收显式运行上下文的公共任务入口。
3. 通过定时任务验证该入口,并补齐调度所需的持久记录和生命周期。
事件持久化、传输替换、公开 SDK、设备配对和远程执行分别评估不构成本次完成的前置条件。
### 参考资料的使用边界
- 原始愿景:`.astrion/user_upload/Astrion_Architecture_Product_Roadmap_Review_1.md`,重点 §1014、§35、§3738。
- 原始盘点:`astrion_audit/astrion_gateway_gap.md`。其中“无唯一 owner”“补丁不是投影”“恢复机制脆弱”等结论须结合下表理解不能直接当作未修复故障。
- 外部研究:`opencode_study/opencode_architecture.md`、`openclaw_study/openclaw_gateway.md`。参考其职责分离、契约、幂等和恢复设计,不要求复制其部署、存储或认证方案;外部实现未在本轮重新核验。
## 1. 现状:已有保障、剩余耦合与待核验事项
以下路径相对仓库根目录;行号是本轮读取时的定位锚点,后续以符号为准。
| 类别 | 当前证据 | 对改造的含义 |
|---|---|---|
| 已有:对话级资源隔离 | `server/context.py:58` 按 username/workspace/conversation 生成终端 key | 保留每对话资源边界;多端共享同一对话资源本身合理 |
| 已有:主任务门闸 | `server/chat_flow.py:139` 获取门闸,`:258` 在 finally 释放;实现位于 `server/main_task_gate.py` | 公共入口复用同一执行裁决,不新增平行门闸 |
| 已有:对话保存保护 | `utils/conversation_manager/crud_mixin.py:335` 按 message_id 合并、防止意外缩减;`:194` 使用 I/O 锁;`index_mixin.py:104` 原子替换文件 | 将保存语义纳入契约;不能因存在内存和文件副本就断言写入竞争未解决 |
| 已有:客户端恢复 | `static/src/stores/task.ts:156` 偏移轮询;`static/src/app/methods/taskPolling/probe.ts:73` 对账恢复;同目录 `lifecycle.ts:137` 按 task_id/idx 去重 | 保留恢复闭环和过期响应过滤;当前轮询是增量事件读取,不是每轮全量读取 |
| 已有:审批单次决定 | `modules/tool_approval_manager.py:65` 在锁内裁决 pending已决定时返回现状 | 复用决定规则;记录谁决定与提前认领审批是不同需求 |
| 耦合:执行依赖 Web 环境 | `server/tasks/models.py:785` 后台任务建立 test_request_context、回填 session 后获取资源和执行 | 优先抽出显式上下文,减少隐式请求状态依赖 |
| 耦合:写入口分散 | 任务 manager、terminal、conversation manager 各有职责和直接调用方 | 按状态明确权威、写入口和缓存更新;新增门面不是完成所有权收敛的证明 |
| 边界:过程记录为内存态 | `server/tasks/models.py:75` 有界事件 deque`:108` 清理旧任务,`:762` 分配任务级 idx审批 manager 为进程内实例 | 不能承诺服务重启续跑或无限期回放;也不能说任务一结束事件就立即丢失 |
| 优化候选:用户级广播 | `server/context.py:75` 给事件补 conversation_id由客户端筛选 | 当前也是投影方案;对话级订阅用于减少无关流量和明确受众,不是 Owner 正确性的必选前提 |
| 新能力:用户定时任务 | 本轮搜索只发现任务清理、idle reaper 等维护定时器,未发现用户 Schedule 实体与到期派发链路 | 单独设计调度状态与记录,复用现有任务执行 |
保留两个有界核验项,避免将静态推断直接升级为故障结论:
- `server/tasks/models.py:157` 的“检查运行中任务→创建记录”与最终执行门闸分属两处。核验并发请求是否产生重复任务记录;不能据此断言会并发修改同一对话。
- 原报告提到 `server/chat/terminal.py::issue_socket_token` 发放竞争。本轮未重新复现;若仍存在,作为独立的小范围缺陷处理,不捆绑整个运行时改造。
## 2. 阶段一:固定契约与已有不变量
**目标:明确状态属于谁、新入口应该调用哪里。**
- [ ] 产出 `docs/runtime_contract.md`,先描述内部服务契约;暂不强制公开网络协议、握手或实体全面改名。
- [ ] 建立状态责任表:每项列明权威来源、允许的修改方、持久化入口、缓存刷新、并发裁决和失效条件。内存/文件/客户端缓存可以共存,权威关系必须明确。
- [ ] 对齐概念Session 对应现有 conversationRun 对应一轮主任务Schedule 表示计划Occurrence 表示某次到期触发Event 表示变化通知。子 agent、后台命令与主任务的关系单独说明不直接把所有现有 task 等同主 Run。
- [ ] 审批与用户提问保留不同语义。可共享 ID、关联、等待和回答的基础设施但提问答案不能被当作工具执行授权。
- [ ] 明确公共入口所需的 principal、workspace、conversation、模型/运行配置与事件输出接口;默认值由明确的解析步骤产生。
- [ ] 建立调用方迁移表Web/CLI 对应 API、Workflow 激活、通知派发、定时触发。每项标注上下文来源、门闸获取/释放和取消传播。
- [ ] 围绕修改范围保留或补充回归用例:同对话并发、不同对话隔离、保存不丢消息、取消、审批重复回答、偏移恢复和过期响应过滤。
**完成标准:**目标入口的状态修改路径和执行裁决可定位;已有保障成为明确约束,未验证风险有独立记录。
## 3. 阶段二:抽出显式上下文与公共任务入口
**目标:无浏览器也能通过受控入口启动、观察和停止一轮任务。**
```text
Web / CLI 适配层 定时触发器 Workflow / 通知派发
\ | /
公共任务受理与控制入口
|
现有执行门闸与 Agent 执行
|
现有保存、审批、事件和取消链路
```
- [ ]`TaskManager.create_chat_task` 及执行链路为基础抽出服务接口。`RuntimeService` 可作为名称候选;文件位置按真实职责确定,避免把所有 manager 和状态塞入一个新类。
- [ ] 服务接口至少覆盖提交、查询和取消;审批回答复用现有 manager通过明确关联接入服务层。会话创建按需要复用现有服务第一条验证链路可使用已有会话。
- [ ] HTTP 参数解析、Cookie/CSRF/Bearer 验证放在适配层,向服务层传入可信 principal 和经校验的资源范围。不能接受客户端或 Schedule payload 自报的 role 作为授权依据。
- [ ] 消除目标执行链路对隐式 Flask session 的读取。迁移期可在明确的兼容适配层保留旧上下文,但须列出剩余依赖;只把 test_request_context 包进新方法不算完成解耦。
- [ ] 复用门闸、取消、审批和保存规则;分别定义任务受理去重与实际执行互斥,防止重复启动或门闸泄漏。
- [ ] Web 任务入口先接入服务,再逐条迁移 Workflow/通知等调用方;每次明确哪些旧写路径已封闭。不能把“门面转发成功”写成“全部状态唯一 Owner 已完成”。
- [ ] 内部错误采用稳定状态/错误码HTTP 状态码由适配层映射;事件先适配现有 idx/offset 和 sender不同时重写客户端。
**完成标准:**不创建浏览器会话、不伪造 HTTP 请求,测试能通过显式身份和资源上下文启动一次受控任务、读取结果并取消;同对话重入仍受门闸保护;现有 Web/CLI 行为兼容。执行可使用可控模型/工具替身验证,无需真实外部副作用。
## 4. 阶段三:定时任务纵向落地
**目标:时钟成为公共任务入口的另一个调用方。**
本节是待实施设计,不代表当前已有调度器。具体 UI 和默认行为在实现前确认,下列保守默认作为讨论起点。
### 4.1 计划与触发记录
- [ ] Schedule 至少保存ID、所属 principal、目标 workspace、会话策略、提示词/任务配置、时间规则、时区、启用状态和配置版本。明确夏令时重复/不存在时刻的处理。
- [ ] 支持创建、暂停、恢复和删除计划。建议暂停/删除只影响未来触发,已受理的 Run 另行取消;最终行为须明确。
- [ ] 明确使用已有会话还是每次新建会话;首版可只实现一种,须说明上下文累积、目标删除和工作区失效时的行为。
- [ ] 模型配置确定“创建时固定”还是“触发时解析”;权限在触发时按当前有效授权重新校验,不保存可绕过权限变更的长期授权快照。
- [ ] 每个 Occurrence 有稳定触发标识,例如 schedule_id + 计划时间点保存所用配置版本、受理状态、run_id 和终态。计划编辑后的未来触发身份规则须明确。
### 4.2 重复、重叠与停机
- [ ] 为 Occurrence 登记和 Run 受理定义持久幂等规则:同一触发重试返回同一受理结果,相同键不同参数拒绝;记录保留期覆盖允许的重试窗口。
- [ ] 明确“登记后未启动”“已启动但关联未写完”等崩溃窗口。登记/受理应原子提交或具备可验证的恢复对账;不能只用内存 TTL 承诺跨重启不重复执行。
- [ ] 选定单个活动调度器的启动与所有权规则,防止重载器或多个服务进程重复派发;不要求因此引入分布式基础设施。
- [ ] 同会话已有任务或上一轮仍在运行时,定义 skip/queue/parallel 策略。建议首版跳过并记录原因,沿用会话门闸,不默默增加无限队列。
- [ ] 定义服务关闭期间错过触发的策略。建议首版记录错过并等待下个未来时点,不集中补发;服务需运行才会触发,本阶段不包含操作系统唤醒/开机自启。
- [ ] 重启后恢复 Schedule 和触发记录;上一进程未确认完成的 Run 标为中断或结果待核验,不显示仍在正常运行,不自动重做可能已产生副作用的动作。
- [ ] 触发去重仅保证任务受理规则,不承诺任意外部工具副作用 exactly-once。无法确认的执行结果进入显式待核验状态。
### 4.3 审批、存储与验收
- [ ] 无人在线时仍保持原有权限限制:遇到审批/提问按明确超时等待,超时结束或中断本轮并记录原因,不自动扩大权限。等待中的任务也遵守重叠策略。
- [ ] 审批关联当前 Run/会话,复用现有 pending/answer 链路。重启后旧请求不能被当作仍有执行现场的有效审批;持久审批记录与继续执行分别设计。
- [ ] 为 Schedule/Occurrence/必要的运行摘要选择持久化方案,先列出原子更新、唯一性、查询和恢复要求,再决定文件或 SQLite。数据走运行态路径解析不写源码树不强制迁移全部对话历史。
- [ ] 沿用当前任务事件读取;补充触发记录查询,使旧内存任务被清理或重启后仍能解释触发结果。日志记录 schedule_id/occurrence_id/run_id复用既有设施。
**完成标准:**可控时钟与执行替身验证一次触发、重复触发、任务重叠、计划暂停/恢复、目标失效、无人审批超时、停机错过触发及重启对账;真实入口验证不依赖浏览器。明确只恢复计划与记录,不承诺从任意执行位置续跑。
## 5. 后续独立决策:有明确需求再启动
| 决策 | 启动条件 | 决策前必须补充的内容 |
|---|---|---|
| SSE / WebSocket / 保持轮询 | 现有延迟、连接数或带宽不能满足目标,或新增双向交互需求 | 部署 worker 模型、代理缓冲、重连、慢消费者处理;传输更换不等于状态正确性提升 |
| 对话级订阅 | 需要减少无关广播、精确控制受众 | 订阅与资源授权分别校验,区分任务/会话事件与用户级通知 |
| durable 事件与快照恢复 | 需要超出内存保留窗口的过程追溯、跨重启生命周期查询 | 提交时机、序号作用域、快照边界、日志裁剪、缺口处理与 schema 版本 |
| 审批持久化与可恢复执行 | 需要重启后继续等待并执行原动作 | 重建上下文和待执行动作、权限重校验、过期请求处理及结果不确定性;恢复 pending 记录不能实现续跑 |
| TS 类型 / SDK 生成 | 对外契约或多客户端类型维护成为实际成本 | 选一个 schema 权威源、兼容策略与生成检查;不强制新网络握手 |
| 身份体系整理 | 跨凭证访问同一资源或统一授权成为需求 | 统一 principal/resource/authorization各适配层可保留不同凭证机制不能直接合并 Web/API 用户数据空间 |
| 设备配对 / Remote Worker | 明确需要多设备接入或远程执行 | 执行契约、连接身份、所有权转移及故障语义,独立设计验收 |
若进入事件恢复改造,必须遵守以下边界:
1. 快照注明覆盖的事件水位 N并与同一状态版本一致续传从 N 之后开始。快照生成、订阅和历史读取之间不得有漏事件窗口,重复投递仍需幂等应用。
2. durable 事件只承诺已提交记录可恢复live delta 是否恢复单独定义。运行中未完成的 Item 需要当前内容快照、覆盖式更新或不完整标记,不能只依赖最终 completed 事件。
3. 状态和事件写入需要一致的提交/恢复规则。对话 JSON、事件 JSONL、审批 JSONL 不天然构成一致快照与日志。
4. 连接序号、任务偏移和持久会话序号不能混用;连接重建、历史裁剪或会话重置时给出明确的重新同步规则。
5. JSONL 与 SQLite 都需定义恢复、保留和迁移方案。选择依据是事务与查询边界,不承诺以后可低成本平移。
## 6. 改造完成的判断
- 新增任务来源只需构造显式上下文、校验目标并调用公共入口,无需复制 Web 聊天启动流程。
- 已迁移路径有明确状态权威与写入规则,现有门闸、保存和客户端恢复保护得到保留。
- 定时任务有可查询的计划、触发记录和终态;重复、重叠、权限变化与停机行为可解释。
- 各阶段可独立验证;只为当前边界迁移做必要的接口调整,不同时重写 Agent loop、前端状态管理或部署拓扑。
- 每阶段说明实际迁移的入口、剩余兼容依赖和验证结果。完成门面、生成架构图或更换传输本身不算完成改造。