Repository navigation
Open
Labels
area/agent-coreAgent Loop、Runner、预算、取消与运行时生命周期Agent Loop、Runner、预算、取消与运行时生命周期area/harness编码 Agent 的任务生命周期、工作区事务、验证反馈与评测闭环编码 Agent 的任务生命周期、工作区事务、验证反馈与评测闭环challenge/C4-collaboration人机及多智能体协作人机及多智能体协作priority/P0必须优先解决必须优先解决type/epic大型课题大型课题
Description
这条 Issue 要解决什么
现在,SztuCode 的任务主要靠 daemon 进程里的内存状态运行。任务记录虽然会分别写入几个文件,但没有一个统一的“进度存档”。
这意味着客户端断线通常还能重新看日志,但 daemon 一旦在关键时刻退出,系统就不一定知道任务究竟执行到了哪里,更不知道某个工具是否已经产生了效果。
用一个实际场景说明
假设 Agent 正在执行下面的步骤:
- 模型决定修改
config.py; - 文件已经成功写入;
- daemon 还没来得及保存“文件修改成功”的工具结果;
- daemon 崩溃并重启。
重启后,系统只能看到“模型准备修改文件”,却看不到结果。此时有两个危险选择:
- 再执行一次:可能重复修改、重复安装或重复发布;
- 当作没事发生:会留下已经变化、但没有记录来源的工作区。
当前代码还存在几个类似问题:
- 任务状态保存在
CoreApp的内存字典中,重启后run.get可能得到unknown; - 用户消息、session 状态、运行事件和最终回复分别写入不同文件,不是一起保存;
- daemon 重启时,遗留的 active session 会直接变成“等待输入”,未说明之前的任务是完成、失败还是做到一半;
- session 正在运行时,新消息只得到
session busy,不能选择“排在后面”“立即改变方向”或“先停止再继续”。
主要入口:
src/sztu_code/core/app.pysrc/sztu_code/core/session/manager.pysrc/sztu_code/core/session/store.pysrc/sztu_code/core/runner.pysrc/sztu_code/core/loop.py
修改后应该是什么样
1. 每个任务有一份统一、可靠的进度记录
任务的输入、当前步骤、模型回复、准备调用的工具、工具结果和最终状态都按顺序保存。内存中的 asyncio Task 只是执行者,不再是唯一事实来源。
客户端看到“任务已接收”之前,这条任务必须已经保存成功。客户端重复发送同一个请求,也只能创建一个任务。
2. 每个关键步骤都有明确的恢复点
至少在以下位置保存:
- 收到用户消息后;
- 收到完整模型回复后;
- 工具开始前;
- 工具结束后;
- 权限等待时;
- 子 Agent 交接时;
- 任务正式结束时。
这里不是要求保存每一个流式 token,而是保存足够恢复的完整步骤。
3. 重启后根据真实情况处理
重启扫描未完成任务,并区分:
- 尚未开始,可以直接继续;
- 只读操作中断,可以安全重做;
- 文件操作结果不确定,需要先检查文件;
- 外部命令结果不确定,需要用户确认;
- 任务已经完成,只是界面状态没有及时写好,此时只修复状态,不重新执行。
不能再把所有情况统一改成“等待输入”。
4. 支持运行中追加和改变要求
用户应能选择:
- 排队:当前任务结束后再处理新消息;
- 转向:在当前安全步骤结束后采用新要求;
- 中断:停止当前任务并清理子任务和进程;
- 恢复:从最近一次安全记录继续。
界面需要显示“消息已收到”“等待生效”“已经生效”或“无法应用”,不能只返回一个模糊的成功响应。
5. 主 Agent、子 Agent 和后台任务统一管理
所有子 Agent、压缩任务和工具进程都要知道自己属于哪个父任务。父任务取消或 daemon 关闭时,它们必须一起停止并完成清理。
必须保证的规则
- 同一个用户请求无论重发多少次,只产生一个 run。
- 一个 run 只能有一个最终结果。
- 已经产生副作用但结果不确定的工具,不能自动再执行。
- 重启后显示的状态必须和磁盘上的任务记录一致。
- 不允许出现模型历史里有工具请求,却完全没有对应工具结果的情况。
- 父任务结束后,不能还有子 Agent 在后台继续修改文件。
- 任务事件必须带顺序号,客户端断线后可以从上次位置继续接收。
建议分阶段实现
第一阶段:统一任务记录
- 新建持久化 RunStore;
- 把内存中的 run 状态和消息去重迁入 RunStore;
- 让
run.get和 session 状态从 RunStore 读取; - 给事件增加 run 内顺序号。
第二阶段:保存关键步骤并实现重启恢复
- Agent Loop 在模型和工具边界保存进度;
- daemon 启动时扫描未完成任务;
- 文件工具、只读工具和外部命令采用不同恢复策略;
- 加入真实杀进程测试。
第三阶段:运行中控制
- 增加排队、转向、中断和恢复命令;
- 统一管理主 Agent、子 Agent、压缩和工具进程;
- TUI 与桌面端补齐对应状态。
验收标准
- 在模型回复中、文件写入后、权限等待中和子 Agent 运行中强制结束 daemon,重启两次后状态仍一致。
- 文件已经修改但结果未保存时,系统先检查文件,不会直接重复执行。
- 同一个请求跨重启重复发送 100 次,始终返回同一个 run ID。
- daemon 重启后,已存在的 run 不会错误显示为
unknown。 - 任务最终事件只有一个,消息历史、运行状态和事件回放互相一致。
- 父任务取消后,所有子 Agent 和工具进程在限定时间内停止。
- Linux 和 Windows 都有重启恢复集成测试。
和现有 Issue 的关系
- [Agent Core] 任务设置了 10 秒却可能跑几分钟:为 Agent 增加真正的截止时间 #69 负责真正的运行截止时间;
- [Agent Core] 父任务停止时也要停止并清理后台子 Agent #73 负责后台子 Agent 的清理和取消;
- [Agent Core] Agent 结束前等待压缩完成,避免事件和状态丢失 #77 负责压缩任务的正确收尾。
这些是本 Epic 的底层组成部分,但无法单独解决任务状态分散的问题。
维护者备注
底层可以考虑 SQLite WAL,或者带校验和的追加日志。关键不是具体数据库,而是用户消息、任务状态、工具执行记录和最终事件必须有统一提交顺序,不能继续依靠多个 JSON 文件碰巧保持一致。
Metadata
Metadata
Assignees
Labels
area/agent-coreAgent Loop、Runner、预算、取消与运行时生命周期Agent Loop、Runner、预算、取消与运行时生命周期area/harness编码 Agent 的任务生命周期、工作区事务、验证反馈与评测闭环编码 Agent 的任务生命周期、工作区事务、验证反馈与评测闭环challenge/C4-collaboration人机及多智能体协作人机及多智能体协作priority/P0必须优先解决必须优先解决type/epic大型课题大型课题
Activity
[-][Harness Epic] 建立可恢复、可转向、可审计的任务控制面[/-][+][Harness Epic] 重构 Run Kernel:以持久化日志统一状态、恢复与运行中控制[/+][-][Harness Epic] 重构 Run Kernel:以持久化日志统一状态、恢复与运行中控制[/-][+][Harness Epic] 让任务在断线或重启后还能接着做,并且不会重复执行[/+]neutronstar238 commentedon Aug 15, 2026
已实现本 Issue 的第一阶段「统一任务记录」,见 PR #100。
核心变化:
RunStore,把 run 的run_id/session_id/status/goal/起止时间与最终结果持久化到~/.sztu/runs/<run_id>/run.json(可用SZTU_RUNS_DIR覆盖,便于测试隔离)。agent.run与session.send_message在返回run_id前先落盘running记录;run 终态(completed/cancelled)落盘且不可覆盖,保证一个 run 只有一个最终结果。run.get优先读内存活动状态,重启后回退到 RunStore,不再返回unknown。running记录标记为cancelled(reason=daemon_restarted),使显示状态与磁盘一致。验证:WSL(Ubuntu-24.04)下
tests/unit/test_run_store.py9 通过、tests/integration/test_run_restart.py(含真实杀进程重启)3 通过;ruff check src tests scripts、mypy src、scripts/gen_protocol_doc.py --check全部通过。后续阶段(保存关键步骤并恢复、运行中排队/转向/中断/恢复、事件顺序号、子 Agent 统一管理)按 Epic 拆分为后续子任务继续推进。
feat(harness): 持久化 run 生命周期记录,重启后 run.get 不再返回 unknown