diff --git a/README.md b/README.md index 547ec37..7bfa242 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ cd frontend pnpm test ``` -当前回归基线为后端 80 项测试、前端 27 项测试,且生产构建通过。测试数量会随功能增长,以本地实际输出和 CI 为准。 +当前回归基线为后端 81 项测试、前端 27 项测试,且生产构建通过。测试数量会随功能增长,以本地实际输出和 CI 为准。 构建产物位于 `frontend/dist`,该目录不提交到 Git。 diff --git a/backend/README.md b/backend/README.md index 2025871..c211fc1 100644 --- a/backend/README.md +++ b/backend/README.md @@ -23,7 +23,7 @@ uv run uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 uv run pytest ``` -当前基线为 80 项测试通过。Provider API Key 可通过前端设置页写入,也可用 `OPENAI_API_KEY`、`DEEPSEEK_API_KEY` 或 `AINOTE_CREDENTIAL_` 注入;不要把真实密钥写入仓库。 +当前基线为 81 项测试通过。Provider API Key 可通过前端设置页写入,也可用 `OPENAI_API_KEY`、`DEEPSEEK_API_KEY` 或 `AINOTE_CREDENTIAL_` 注入;不要把真实密钥写入仓库。 团队接口清单见 `../docs/contracts/后端接口契约-开发版.md`,机器可读契约以运行时的 `/openapi.json` 为准。 diff --git a/backend/app/agent/trace_repository.py b/backend/app/agent/trace_repository.py index b56b96a..e09759a 100644 --- a/backend/app/agent/trace_repository.py +++ b/backend/app/agent/trace_repository.py @@ -46,15 +46,17 @@ _BEARER_PATTERN = re.compile(r"(?i)\bBearer\s+[^\s,;]+") _API_KEY_PATTERN = re.compile(r"\bsk-[A-Za-z0-9_-]{8,}\b") -def sanitize_trace_value(value: Any, *, depth: int = 0) -> Any: - """递归净化 Trace 数据;键名疑似 Secret 时不保留原值。""" +def sanitize_trace_value( + value: Any, *, depth: int = 0, apply_limits: bool = True +) -> Any: + """递归净化持久化数据;可按审计用途限制体积,Secret 始终脱敏。""" - if depth >= MAX_TRACE_DEPTH: + if apply_limits and depth >= MAX_TRACE_DEPTH: return "[MAX_DEPTH]" if isinstance(value, dict): sanitized: dict[str, Any] = {} for index, (key, item) in enumerate(value.items()): - if index >= MAX_TRACE_COLLECTION: + if apply_limits and index >= MAX_TRACE_COLLECTION: sanitized["__truncated__"] = True break normalized = str(key).casefold().replace("-", "_") @@ -62,26 +64,33 @@ def sanitize_trace_value(value: Any, *, depth: int = 0) -> Any: "[REDACTED]" if normalized in _SECRET_KEYS or normalized.endswith(_SECRET_KEY_SUFFIXES) - else sanitize_trace_value(item, depth=depth + 1) + else sanitize_trace_value( + item, depth=depth + 1, apply_limits=apply_limits + ) ) return sanitized if isinstance(value, (list, tuple)): + source_items = value[:MAX_TRACE_COLLECTION] if apply_limits else value items = [ - sanitize_trace_value(item, depth=depth + 1) - for item in value[:MAX_TRACE_COLLECTION] + sanitize_trace_value( + item, depth=depth + 1, apply_limits=apply_limits + ) + for item in source_items ] - if len(value) > MAX_TRACE_COLLECTION: + if apply_limits and len(value) > MAX_TRACE_COLLECTION: items.append("[TRUNCATED]") return items if isinstance(value, str): value = _BEARER_PATTERN.sub("Bearer [REDACTED]", value) value = _API_KEY_PATTERN.sub("[REDACTED]", value) - if len(value) > MAX_TRACE_STRING: + if apply_limits and len(value) > MAX_TRACE_STRING: return f"{value[:MAX_TRACE_STRING]}...[TRUNCATED]" return value if value is None or isinstance(value, (str, int, float, bool)): return value - return sanitize_trace_value(str(value), depth=depth + 1) + return sanitize_trace_value( + str(value), depth=depth + 1, apply_limits=apply_limits + ) class AgentTraceRepository: @@ -354,6 +363,10 @@ class AgentTraceRepository: @staticmethod def _serialize_run(run: AgentRun) -> str: + # Run 是重启后 GET/list 的完整事实;只做 Secret 脱敏,不套用 Trace 摘要限长。 return json.dumps( - sanitize_trace_value(run.model_dump(mode="json")), ensure_ascii=False + sanitize_trace_value( + run.model_dump(mode="json"), apply_limits=False + ), + ensure_ascii=False, ) diff --git a/backend/tests/test_agent_core.py b/backend/tests/test_agent_core.py index 4901bb4..2cba7b7 100644 --- a/backend/tests/test_agent_core.py +++ b/backend/tests/test_agent_core.py @@ -242,6 +242,38 @@ def test_trace_redacts_secrets_and_truncates_large_values() -> None: run(scenario()) +def test_persisted_agent_run_preserves_long_input_and_output() -> None: + """审计事件可以限长,但重启后读取的 AgentRun 不能丢失正文。""" + + now = datetime.now(timezone.utc) + long_input = "输入" * 2_500 + long_output = "输出" * 2_500 + request = AgentRunCreateRequest( + input=long_input, + provider_id="mock", + model="mock-1", + ) + persisted = AgentRun( + run_id="run_long_content", + status=AgentRunStatus.completed, + input=long_input, + output=long_output, + provider_id=request.provider_id, + model=request.model, + max_steps=request.max_steps, + created_at=now, + updated_at=now, + ) + repository = AgentTraceRepository() + repository.create_run(persisted, request, {"model": request.model}) + + restored = repository.get_run(persisted.run_id) + + assert restored is not None + assert restored.input == long_input + assert restored.output == long_output + + async def _collect_events(iterator): return [event async for event in iterator] diff --git a/docs/architecture/AI笔记软件技术栈说明-团队版-v2.3.md b/docs/architecture/AI笔记软件技术栈说明-团队版-v2.3.md index 85922f9..a94e5a1 100644 --- a/docs/architecture/AI笔记软件技术栈说明-团队版-v2.3.md +++ b/docs/architecture/AI笔记软件技术栈说明-团队版-v2.3.md @@ -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 上接入: diff --git a/docs/contracts/后端接口契约-开发版.md b/docs/contracts/后端接口契约-开发版.md index 1ed8943..bc44e48 100644 --- a/docs/contracts/后端接口契约-开发版.md +++ b/docs/contracts/后端接口契约-开发版.md @@ -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 已持久化到 SQLite;SSE 帧携带 sequence `id`,断线后可以回放缺失事件。Trace API 与 Benchmark 共用同一事件事实,并在入库前执行 Secret 脱敏和结果限长。 diff --git a/docs/development/AI-Core与Agent-Core开发说明.md b/docs/development/AI-Core与Agent-Core开发说明.md index 077d066..a3b9731 100644 --- a/docs/development/AI-Core与Agent-Core开发说明.md +++ b/docs/development/AI-Core与Agent-Core开发说明.md @@ -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 diff --git a/docs/development/Knowledge与Retrieval-Core开发说明.md b/docs/development/Knowledge与Retrieval-Core开发说明.md index 13a6b42..c109a9e 100644 --- a/docs/development/Knowledge与Retrieval-Core开发说明.md +++ b/docs/development/Knowledge与Retrieval-Core开发说明.md @@ -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 项测试通过。 ## 当前实现 diff --git a/docs/development/前端壳子与接口层开发说明.md b/docs/development/前端壳子与接口层开发说明.md index 4ca1eb9..83a76c6 100644 --- a/docs/development/前端壳子与接口层开发说明.md +++ b/docs/development/前端壳子与接口层开发说明.md @@ -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 等编辑/渲染依赖带来的性能优化项,不影响构建成功或功能正确性;进入桌面打包前应通过手动分包或更细粒度动态加载继续优化。 diff --git a/docs/development/模型提供商与模型发现开发说明.md b/docs/development/模型提供商与模型发现开发说明.md index f9f78e1..5a6fa20 100644 --- a/docs/development/模型提供商与模型发现开发说明.md +++ b/docs/development/模型提供商与模型发现开发说明.md @@ -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 尚未实现,设置页正式预设不会使用这两种协议。 diff --git a/docs/guides/第一阶段测试验证操作手册.md b/docs/guides/第一阶段测试验证操作手册.md index a18ec82..1194ae9 100644 --- a/docs/guides/第一阶段测试验证操作手册.md +++ b/docs/guides/第一阶段测试验证操作手册.md @@ -68,7 +68,7 @@ uv run pytest -q -p no:cacheprovider 当前基线: ```text -80 passed +81 passed ``` 通过标准:退出码为 0、失败数为 0。用例数可以随功能增加,但不得低于当前基线。 diff --git a/docs/retrospectives/Agent-Core第二阶段问题与修复复盘.md b/docs/retrospectives/Agent-Core第二阶段问题与修复复盘.md index 79842d8..8ea4ee2 100644 --- a/docs/retrospectives/Agent-Core第二阶段问题与修复复盘.md +++ b/docs/retrospectives/Agent-Core第二阶段问题与修复复盘.md @@ -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-01:Agent 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-09:AgentRun 与审计摘要共用限长规则 + +### 原因 + +初版使用 `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。 diff --git a/docs/retrospectives/后端全面审阅问题与修复复盘.md b/docs/retrospectives/后端全面审阅问题与修复复盘.md index 7322f02..fc08ea6 100644 --- a/docs/retrospectives/后端全面审阅问题与修复复盘.md +++ b/docs/retrospectives/后端全面审阅问题与修复复盘.md @@ -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. 审阅结论