fix(agent): 保留持久化Run完整内容
This commit is contained in:
@@ -118,7 +118,7 @@ cd frontend
|
||||
pnpm test
|
||||
```
|
||||
|
||||
当前回归基线为后端 80 项测试、前端 27 项测试,且生产构建通过。测试数量会随功能增长,以本地实际输出和 CI 为准。
|
||||
当前回归基线为后端 81 项测试、前端 27 项测试,且生产构建通过。测试数量会随功能增长,以本地实际输出和 CI 为准。
|
||||
|
||||
构建产物位于 `frontend/dist`,该目录不提交到 Git。
|
||||
|
||||
|
||||
+1
-1
@@ -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_<ID>` 注入;不要把真实密钥写入仓库。
|
||||
当前基线为 81 项测试通过。Provider API Key 可通过前端设置页写入,也可用 `OPENAI_API_KEY`、`DEEPSEEK_API_KEY` 或 `AINOTE_CREDENTIAL_<ID>` 注入;不要把真实密钥写入仓库。
|
||||
|
||||
团队接口清单见 `../docs/contracts/后端接口契约-开发版.md`,机器可读契约以运行时的 `/openapi.json` 为准。
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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 已持久化到 SQLite;SSE 帧携带 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-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。
|
||||
|
||||
@@ -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. 审阅结论
|
||||
|
||||
|
||||
Reference in New Issue
Block a user