fix(agent): 保留持久化Run完整内容

This commit is contained in:
2026-09-01 10:05:58 +08:00
parent a5b709a46f
commit 0e8d4b7b9f
13 changed files with 123 additions and 44 deletions
@@ -2321,7 +2321,7 @@ Markdown Workspace
第一阶段 Plugin Runtime 已完成安装、启用、停用、权限和声明式 Tool 注册,建立 Skill 调用 Plugin Tool 的基础链路。Command、Settings 和 MCP 执行不计入第一阶段完成项。
截至 2026-09-01,上述第一阶段后端链路和 Web 联调前端均已完成,第二阶段前置的 Workspace 去 Mock 联调及 Agent Trace 持久化/恢复接口也已完成。当前验证基线为后端 80 项测试、前端 27 项测试及生产构建通过。向量链路当前使用 `HashEmbeddingProvider` 验证工程正确性,真实 Embedding 召回质量不属于该测试结论。
截至 2026-09-01,上述第一阶段后端链路和 Web 联调前端均已完成,第二阶段前置的 Workspace 去 Mock 联调及 Agent Trace 持久化/恢复接口也已完成。当前验证基线为后端 81 项测试、前端 27 项测试及生产构建通过。向量链路当前使用 `HashEmbeddingProvider` 验证工程正确性,真实 Embedding 召回质量不属于该测试结论。
第二阶段在既有 Contract 上接入:
@@ -174,7 +174,7 @@ RunCancelled
## 当前实现状态
更新至 2026-09-01:后端 80 项回归测试通过。
更新至 2026-09-01:后端 81 项回归测试通过。
- Chat、Agent Run、Agent Events、Tool 列表、Provider 配置生命周期、模型列表和连接测试已经接入 AI Core。
- Agent Run/Event 已持久化到 SQLiteSSE 帧携带 sequence `id`,断线后可以回放缺失事件。Trace API 与 Benchmark 共用同一事件事实,并在入库前执行 Secret 脱敏和结果限长。
@@ -2,7 +2,7 @@
> 本文档用于团队开发和模块联调,记录当前已经落地的核心边界与使用方式。
> 更新日期:2026-09-01。第一阶段 AI Core、Agent Core、Extension Core 和 Model Core 主链路已经完成;第二阶段 Agent Trace 持久化和可恢复 SSE 已落地,后端当前回归基线为 80 项测试通过。
> 更新日期:2026-09-01。第一阶段 AI Core、Agent Core、Extension Core 和 Model Core 主链路已经完成;第二阶段 Agent Trace 持久化和可恢复 SSE 已落地,后端当前回归基线为 81 项测试通过。
## 当前实现
@@ -178,7 +178,7 @@ POST /api/agent/runs/{run_id}/cancel
Run 与 AgentEvent 已写入 SQLite`run_id + sequence` 是幂等键。SSE 每帧包含 `id: sequence`;客户端可以通过 `Last-Event-ID` 请求头或 `after_sequence` 查询参数恢复缺失事件。Trace API 返回平铺事件、下一游标、分页状态、模型/工具调用统计、耗时、Token Usage 和创建 Run 时的配置快照,不负责生成前端树形布局。
运行时内存仍只保留最近 2000 个事件用于实时订阅,完整 Trace 以 SQLite 为准。AI Core 重启后,已经终止的 Run 可以继续查询和回放;重启前未终止的 Run 会收束为 `AGENT_PROCESS_RESTARTED`,避免永久停在 `running`。API Key、Authorization、Password、Secret、常见 `sk-`/Bearer 值在入库前脱敏超长字符串集合会截断。
运行时内存仍只保留最近 2000 个事件用于实时订阅,完整 Trace 以 SQLite 为准。AI Core 重启后,已经终止的 Run 可以继续查询和回放;重启前未终止的 Run 会收束为 `AGENT_PROCESS_RESTARTED`,避免永久停在 `running`。API Key、Authorization、Password、Secret、常见 `sk-`/Bearer 值在入库前脱敏。Event、Request 和配置快照中的超长字符串集合会截断;作为查询事实来源的 `AgentRun` 只脱敏、不限长,保证重启前后 input/output 内容一致
## Tool Calling
@@ -3,7 +3,7 @@
> 本文档用于团队开发和模块联调,记录 Knowledge Core / Retrieval Core 已经落地的
> 模块边界、数据模型、接口与使用方式,对应分工表中的杨星萱。
> 更新日期:2026-09-01。第一阶段 Knowledge/Retrieval 主链路已经完成,并已接入 Agent Tool Registry;完整后端回归基线为 80 项测试通过。
> 更新日期:2026-09-01。第一阶段 Knowledge/Retrieval 主链路已经完成,并已接入 Agent Tool Registry;完整后端回归基线为 81 项测试通过。
## 当前实现
@@ -187,12 +187,12 @@ pnpm build
```text
pnpm build passed
pnpm test 27 passed
uv run pytest 80 passed
uv run pytest 81 passed
preview smoke HTTP 200
git diff --check passed
```
当前前端使用 Vitest 执行 Store、Workspace API Adapter、SSE 恢复游标、文件树、编辑器组件、智能体标签、轻量动效约束、Markdown 对比度 Token、scoped CSS 选择器约束和 Shiki GitHub 双主题测试;`pnpm build` 同时执行 `vue-tsc -b` 与 Vite 生产构建。后端测试出现过 `.pytest_cache` 无法写入的 Windows 权限警告,不影响 80 项测试结果,也不涉及产品代码。
当前前端使用 Vitest 执行 Store、Workspace API Adapter、SSE 恢复游标、文件树、编辑器组件、智能体标签、轻量动效约束、Markdown 对比度 Token、scoped CSS 选择器约束和 Shiki GitHub 双主题测试;`pnpm build` 同时执行 `vue-tsc -b` 与 Vite 生产构建。后端测试出现过 `.pytest_cache` 无法写入的 Windows 权限警告,不影响 81 项测试结果,也不涉及产品代码。
Vite 当前会提示 Chat 与 Workspace 的部分异步 Chunk 超过 500 kB,这是 Milkdown、CodeMirror、KaTeX 和 Shiki 等编辑/渲染依赖带来的性能优化项,不影响构建成功或功能正确性;进入桌面打包前应通过手动分包或更细粒度动态加载继续优化。
@@ -104,4 +104,4 @@ pnpm build
自动化验证覆盖 Provider 预设、OpenAI-Compatible `/models` 请求与鉴权头、模型映射、前端自动刷新、排序去重及按 Provider 隔离错误。生产构建同时执行 Vue 和 TypeScript 类型检查。
当前完整回归基线:后端 80 项测试、前端 27 项测试通过,前端类型检查和生产构建通过。Provider 配置目前仍保存在内存 Registry,AI Core 重启后需要重新创建;凭据密文会保留。OpenAI Responses 与 Anthropic Messages Adapter 尚未实现,设置页正式预设不会使用这两种协议。
当前完整回归基线:后端 81 项测试、前端 27 项测试通过,前端类型检查和生产构建通过。Provider 配置目前仍保存在内存 Registry,AI Core 重启后需要重新创建;凭据密文会保留。OpenAI Responses 与 Anthropic Messages Adapter 尚未实现,设置页正式预设不会使用这两种协议。
@@ -68,7 +68,7 @@ uv run pytest -q -p no:cacheprovider
当前基线:
```text
80 passed
81 passed
```
通过标准:退出码为 0、失败数为 0。用例数可以随功能增加,但不得低于当前基线。
@@ -9,7 +9,7 @@
第一阶段 Agent Runtime 已经能够完成模型调用、Tool Calling、权限确认、取消、Usage 和 Citation,但 Run 与 Event 仍以进程内字典和列表为事实来源。第一阶段审阅加入的 Run/Event 数量上限解决了内存无界增长,却没有解决重启丢失、断线续传、Benchmark 复用和敏感数据审计等第二阶段问题。
本轮处理了 8 类问题:
本轮处理了 9 类问题:
| 编号 | 问题 | 级别 | 处理结果 |
| --- | --- | --- | --- |
@@ -18,11 +18,12 @@
| A-03 | SSE 断线后无法从指定事件恢复 | P1 | 支持 `Last-Event-ID``after_sequence` 和 SSE `id` |
| A-04 | 缺少可分页 Trace 与 Benchmark 配置快照 | P1 | 增加 Trace API、统计摘要与配置快照 |
| A-05 | Trace 缺少模型调用、父子关系和权限结果 | P1 | 增加第二阶段事件及耗时/parent 字段 |
| A-06 | Trace 可能保存 Secret 和超大 Tool Result | P0 | Run、Request、Config、Event 统一脱敏与截断 |
| A-06 | Trace 可能保存 Secret 和超大 Tool Result | P0 | 全部持久化副本统一脱敏,审计摘要限长 |
| A-07 | 进程重启后未终止 Run 永久显示运行中 | P1 | 自动收束为 `AGENT_PROCESS_RESTARTED` |
| A-08 | 前端 DTO 与 SSE Client 无法消费恢复协议 | P1 | 同步 TypeScript Contract、Service、标签和 SSE id |
| A-09 | AgentRun 与审计摘要共用限长规则 | P1 | Run 只脱敏不限长,保证重启前后内容一致 |
修复后的验证基线为:后端 80 项测试、前端 27 项测试、TypeScript 类型检查和生产构建通过。
修复后的验证基线为:后端 81 项测试、前端 27 项测试、TypeScript 类型检查和生产构建通过。
## 2. A-01Agent Run 与 Event 只存在于进程内存
@@ -267,7 +268,7 @@ Tool 参数和输出来自模型、插件或外部服务,属于不可信数据
### 解决思路
所有进入持久化边界的数据统一经过同一个净化函数,不能分别在 Router、Runtime 和 Repository 中维护不同规则。净化必须同时覆盖键名、常见密钥值模式、递归深度、字符串长度和集合大小
所有进入持久化边界的数据统一经过同一个净化函数,不能分别在 Router、Runtime 和 Repository 中维护不同脱敏规则。Secret 脱敏适用于全部副本;体积限制只适用于 Trace Event、Request 和 Config 等审计数据,不能改变对外查询所依赖的 AgentRun 事实
### 解决方案
@@ -280,17 +281,20 @@ Config Snapshot
AgentEvent Data
```
净化规则:
通用脱敏规则:
- `api_key`、Authorization、Access/Refresh Token、Password、Secret 等键替换为 `[REDACTED]`
- 常见 `sk-...``Bearer ...` 字符串模式直接替换;
- `credential_id` 等非明文引用保留,不误判为 Secret。
Event、Request 和 Config 审计副本额外执行限长:
- 单字符串最多保留 4096 个字符;
- 单集合最多保留 100 项;
- 递归深度最多 8 层;
- 超限位置使用明确的 `[TRUNCATED]``[MAX_DEPTH]` 标记
- `credential_id` 等非明文引用保留,不误判为 Secret。
- 超限位置使用明确的 `[TRUNCATED]``[MAX_DEPTH]` 标记
回归测试直接读取 `agent_runs` 原始 SQLite 字段,确认测试密钥没有落盘,避免只验证 API 响应造成假安全。
`AgentRun` Snapshot 仍经过同一套 Secret 脱敏,但不执行长度、集合和深度截断。回归测试直接读取 `agent_runs` 原始 SQLite 字段,确认测试密钥没有落盘,避免只验证 API 响应造成假安全。
## 8. A-07:重启后未终止 Run 永久显示运行中
@@ -352,7 +356,35 @@ Agent 的异步 Task 和 Permission Future 不能跨进程恢复。持久化 Run
Trace 时间线、树形布局、筛选、节点展开和 Citation 跳转仍由前端负责人实现。
## 10. 事务、顺序与恢复不变量
## 10. A-09AgentRun 与审计摘要共用限长规则
### 原因
初版使用 `sanitize_trace_value()` 同时处理 `run_json`、Request、Config 和 Event。该函数不仅脱敏,还会截断超过 4096 字符的字符串、超过 100 项的集合和超过 8 层的结构。`run_json` 随后又被 `GET /agent/runs/{run_id}` 和 Run 列表当作重启后的事实来源,因此审计数据的防膨胀规则意外改变了业务响应。
### 后果
- 常见的长模型回答在 AI Core 重启后只剩前 4096 个字符和截断标记;
- 长输入、Tool Result 和嵌套结果也可能丢失;
- 同一个 Run 在进程内与重启后的接口响应不一致;
- 前端刷新、Benchmark 复核和问题追踪无法取得原始运行结果;
- 原有测试只验证了 Trace 参数截断,没有比较重启前后的长正文。
审阅时使用 5000 字符 output 复现:写入前长度为 5000,重新读取后长度变为 4110,并以 `...[TRUNCATED]` 结尾。
### 解决思路
持久化边界包含两类数据:AgentRun 是业务事实,Trace Event、Request 和 Config 是可视化与审计摘要。两类数据必须共享 Secret 脱敏规则,但不能共享有损的体积限制。
### 解决方案
- `sanitize_trace_value()` 增加明确的 `apply_limits` 策略参数;
- 默认继续限长,保持 Event、Request 和 Config 的安全边界;
- `_serialize_run()` 使用 `apply_limits=False`,完整保留 input、output、Tool Result 和 Citation
- Secret 键名及 `sk-`、Bearer 模式在两种策略下始终脱敏;
- 增加超过 4096 字符的 input/output 持久化回归测试,直接从新 Repository 读取并逐字比较。
## 11. 事务、顺序与恢复不变量
本轮修复明确了以下不变量:
@@ -364,8 +396,9 @@ Trace 时间线、树形布局、筛选、节点展开和 Citation 跳转仍由
6. 重启后不能安全继续执行的 Run 必须明确失败,不能永久悬挂。
7. 所有持久化 Trace 数据先脱敏、再写入。
8. 前端按 `run_id + sequence` 去重,不能依赖一次网络读取对应一条 SSE Event。
9. AgentRun 的持久化副本不执行审计摘要限长,重启前后业务字段必须一致。
## 11. 验证方法
## 12. 验证方法
后端:
@@ -393,7 +426,7 @@ git diff --check
验证结果:
```text
backend pytest 80 passed
backend pytest 81 passed
backend compileall passed
frontend vitest 11 files / 27 tests passed
frontend type-check passed
@@ -409,22 +442,23 @@ git diff --check passed
- Model Call、Permission Resolved 和 Tool parent ID
- 进程中断 Run 自动生成唯一终止事件;
- API Key、Authorization 和超长 Tool 参数净化;
- 超长 AgentRun input/output 在持久化和重启读取后保持完整;
- 直接检查 SQLite,确认 Secret 未进入 Run/Request/Config Snapshot
- OpenAPI 发布 Trace 路径;
- 前端 SSE Client 发送和解析恢复游标。
测试仍会出现本机 `.pytest_cache` 无写入权限警告,不影响 80 项用例结果,也不涉及产品代码。
测试仍会出现本机 `.pytest_cache` 无写入权限警告,不影响 81 项用例结果,也不涉及产品代码。
## 12. 当前边界与后续工作
## 13. 当前边界与后续工作
### 12.1 本阶段明确不做
### 13.1 本阶段明确不做
- 不在后端生成前端 Trace 树形布局;
- 不在进程重启后自动重放未完成 Tool 副作用;
- 不把 Secret 明文放入 Trace、日志或 Benchmark
- 不为 Benchmark 建立绕过 Agent Runtime 的专用执行协议。
### 12.2 后续需要继续处理
### 13.2 后续需要继续处理
- 增加 Trace 保留、归档和被 Benchmark 引用时的保护策略;
- 引入保留窗口后实现 `TRACE_CURSOR_EXPIRED`
@@ -434,24 +468,24 @@ git diff --check passed
- 前端完成 Trace Timeline/Tree、筛选、节点详情和 Citation 跳转;
- 多进程或远程执行出现需求后,再设计带租约和幂等副作用的执行恢复。
## 13. 可复用经验
## 14. 可复用经验
### 13.1 资源上限不等于持久化
### 14.1 资源上限不等于持久化
限制内存 Run 和 Event 数量只能防止进程膨胀,不能解决重启、审计和报告复现。临时保护措施应在文档中明确标注,不能被误认为最终架构已经完成。
### 13.2 游标必须独立于缓存结构
### 14.2 游标必须独立于缓存结构
只要 sequence 来源于 `len(list)`、数组下标或当前页位置,裁剪和分页就可能破坏唯一性。可恢复事件流必须使用独立、单调且可持久化的逻辑序号。
### 13.3 恢复读取不等于恢复执行
### 14.3 恢复读取不等于恢复执行
恢复 Run/Trace 查询相对安全;恢复一个包含 Tool 副作用的执行任务需要额外的幂等、租约和补偿机制。在没有这些机制时,明确失败比重复执行更可靠。
### 13.4 脱敏要覆盖全部持久化副本
### 14.4 脱敏要覆盖全部持久化副本
同一敏感值可能同时出现在 Event、Run Snapshot、Request、Config、日志和报告中。只检查最终 API 响应无法证明数据没有落盘,安全测试应直接验证持久化介质。
### 13.5 生产者和消费者 Contract 必须同时更新
### 14.5 生产者和消费者 Contract 必须同时更新
后端新增事件类型、字段或 SSE 规则时,至少同步 Pydantic、OpenAPI、TypeScript DTO、Service 和协议测试。可视化页面可以由另一成员开发,但不能让对方从后端实现反推 Contract。
@@ -4,7 +4,7 @@
> 审阅范围:FastAPI、Knowledge / Retrieval Core、Agent Core、Extension Core、Provider Adapter、公共接口和后端开发文档。
> 文档用途:记录问题形成原因、实际影响、修复判断和落地方案,供后续开发文档、比赛材料与技术博客使用。
> 2026-09-01 状态补充:本文记录的缺陷均保持修复。此后又加入 Provider 预设、模型发现、DeepSeek/OpenAI 凭据解析、Fernet 加密存储和 Agent Trace 持久化,当前完整后端回归基线为 80 项测试通过。
> 2026-09-01 状态补充:本文记录的缺陷均保持修复。此后又加入 Provider 预设、模型发现、DeepSeek/OpenAI 凭据解析、Fernet 加密存储和 Agent Trace 持久化,当前完整后端回归基线为 81 项测试通过。
## 1. 审阅结论