# Agent Field Notes · 可靠性实验 v3

Python 3.10+，仅标准库。先解压 ZIP，进入目录，再执行以下命令。默认使用合成技术材料和确定性摘要函数，不访问网络、不调用付费 API。

## 1. 正常执行

```sh
python3 cli.py memory-put
python3 cli.py submit
python3 cli.py run
python3 cli.py inspect
python3 evaluate.py
python3 -m unittest discover -s . -p test_lab.py -v
```

预期：任务 succeeded；4 个检查点；评测 passed 为 true。基础 18 项通过；完整第三版共 36 项，见第 8 节。`semantic_support` 和 `model_quality` 均为 `not_scored`，不是模型质量成绩。

## 2. 真正杀死进程后恢复

```sh
python3 cli.py submit --db crash.sqlite
python3 cli.py run --db crash.sqlite --lease-seconds 2 --fault after_collect
# 预期退出码 75；首次故障命令独立执行，不要用 && 串起整组
python3 cli.py inspect --db crash.sqlite
# 等待至少 2 秒，允许旧租约过期
python3 cli.py run --db crash.sqlite
python3 evaluate.py --db crash.sqlite
```

首次 inspect：running + collect 检查点。恢复：succeeded，generation=2，collect 只提交一次。不等待租约过期会返回 running，属于避免抢占的预期行为。默认任务 deadline 为提交后 600 秒；超时需使用新任务 ID。

## 3. 外部成功、本地未记录

```sh
python3 cli.py submit --db effect.sqlite
python3 cli.py run --db effect.sqlite --lease-seconds 2 --fault after_effect
# 等待至少 2 秒
python3 cli.py run --db effect.sqlite
python3 cli.py inspect --db effect.sqlite
```

`effect.sqlite.publisher.sqlite` 是模拟外部服务的独立数据库。恢复后 publish.deduplicated=true，receipts 只有 1 条。它演示具有持久幂等协议的服务，不证明任意远程服务恰好一次执行；未模拟网络分区或备份故障。

## 4. 删除记忆后阻止旧草稿继续发布

```sh
python3 cli.py memory-put --db memory.sqlite
python3 cli.py submit --db memory.sqlite
python3 cli.py run --db memory.sqlite --lease-seconds 2 --fault after_draft
python3 cli.py memory-forget --db memory.sqlite
# 等待至少 2 秒
python3 cli.py run --db memory.sqlite
python3 cli.py inspect --db memory.sqlite
```

预期 failed，错误 memory_changed_or_expired，没有 publish 检查点。修订/删除后请使用新 job ID 重新 submit。原检查点仍保留：这套代码实现失效阻断，不提供检查点、备份、日志的彻底删除。

## 5. 可选接入真实模型

本次交付只验证 HTTP 适配器的模拟响应和错误分类，没有消耗真实模型 API。该步骤由你本地明确执行，会向你选择的服务发送 DOCS 与有效记忆，可能计费。

先按提供方控制台设置 `LLM_API_KEY` 环境变量，再使用同一地域的模型名和完整 Chat Completions HTTPS 地址。不要把密钥写入源码、参数或截图。千问参考：https://help.aliyun.com/zh/model-studio/qwen-api-via-openai-chat-completions 。不同地域的 Key、模型与地址需一致。

```sh
python3 cli.py submit --db api.sqlite --job api-001 --provider api --model YOUR_MODEL_ID --endpoint https://YOUR_TRUSTED_HOST/compatible-mode/v1/chat/completions
python3 cli.py run --db api.sqlite
python3 cli.py inspect --db api.sqlite
python3 evaluate.py --db api.sqlite
```

将占位符换为控制台给出的值。适配器拒绝重定向；20 秒 socket 超时、800 输出 token 上限、1 MB 响应上限；不是整个请求的严格墙钟超时。模型必须返回裸 JSON，Markdown 围栏或未知引用 ID 会失败。429/5xx/连接错误进入 retry_wait，按 available_at 再运行；其他 HTTP 错误或格式错误停止。没有内置持续调度进程。每个任务最多 3 次认领，首次执行也计入。生产需另外实现总费用预算、心跳、全调用截止时间、模型质量审查。

## 文件与阅读位置

- runtime.py：任务、租约、检查点、重试、取消、模拟幂等发布。
- memory.py：作用域、版本替换、失效与过滤。
- provider.py：确定性替身与可选 HTTP 模型适配。
- evaluate.py：从实际事件和检查点评分。
- test_lab.py：18 个离线回归/故障测试。
- evidence.json：本次实际运行保存的故障恢复快照。
- validation.json：本次已执行测试的记录与边界。

## 接入公司 Java 项目

先只读复制业务样本。将任务状态迁移到现有关系数据库，保留 task_id、输入摘要、generation、租约、步骤唯一键。由调度器认领，事务 Service 负责短事务，模型调用放在事务外；业务工具由原权限服务执行校验。独立报告发布器替换为支持持久幂等键的报告服务。不要让模型直接决定租户身份或执行任意 SQL。

缺少身份认证、HTTP 服务、生产调度、数据库迁移、监控部署和真实外部服务集成；第三版增加本地审批模拟，尚无真实审批后台。它是可复现工程实验，不是完整生产 Agent 平台。

## 6. 第三版新增：审批后恢复

这一扩展继续使用同一套 collect → draft → verify → publish。`approval_cli.py` 默认 fixture，无网络；`--actor` 只是本地身份测试夹具，不是登录或生产认证。

```sh
python3 approval_cli.py submit
python3 approval_cli.py run
python3 approval_cli.py inspect
```

预期 `waiting_approval`；collect/draft/verify 已保存，尚无报告服务写入。审查 `checkpoints.draft` 与 `approval.action_json`（动作、目标、作用域、申请人、输入和内容摘要）。复制 `approval.binding_hash`，替换下面占位符：

```sh
python3 approval_cli.py approve --actor reviewer --hash REVIEWED_BINDING_HASH
python3 approval_cli.py run
python3 approval_cli.py inspect
```

预期 approved → succeeded；只保存一条报告。重复同一审批响应不会重复授予，也不会重新生成草稿。默认审批有效期 120 秒，受任务 600 秒 deadline 与记忆有效期进一步限制；没有后台过期扫描器，run/decide 触发时校验。

负例（每个终止场景用独立 --db 或新 --job）：

- `--actor requester`：自审批拒绝。
- `--actor outsider`：作用域不符拒绝。
- `--actor viewer`：没有审批角色，拒绝。
- `reject --hash REVIEWED_BINDING_HASH`：任务 failed，不能复用旧审批。
- 等待审批过期后 approve/run：expired/failed，不发送。
- 人工修改草稿或更正记忆后恢复：invalidated，不发送。不要直接编辑数据库来修复任务。

## 7. 审批版的关键故障：发出后过期，不代表没执行

```sh
python3 approval_cli.py submit --db approved-effect.sqlite
python3 approval_cli.py run --db approved-effect.sqlite
python3 approval_cli.py inspect --db approved-effect.sqlite
python3 approval_cli.py approve --db approved-effect.sqlite --hash REVIEWED_BINDING_HASH
python3 approval_cli.py run --db approved-effect.sqlite --lease-seconds 2 --fault after_effect
# 等待至少 2 秒；故障命令预期独立退出 75
python3 approval_cli.py reconcile --db approved-effect.sqlite
python3 approval_cli.py inspect --db approved-effect.sqlite
```

预期 `effect_confirmed`，publish.fresh_write_performed=false。它只读查询独立报告库并补记已有回执，不再次发布。即使审批后来过期、记忆后来撤回，也可以核对过去的业务效果。`effect_confirmed` 是“已确认过去发生的写入”，不等同于新的授权或任务正常成功。

将故障点换成 `before_effect`：运行时已持久化 dispatching，但故障发生在调用外部服务之前。恢复时找不到回执，将停在 reconciling，**不会自动重发**。测试知道没发出，恢复进程只凭数据库不知道；在真实网络中，找不到回执也可能是仍在执行或查询延迟。

`cancel` 在 dispatching 之前使任务 cancelled；之后只登记取消意图并进入 reconciling。若后来查到回执，就记录 effect_confirmed，保留 cancel_requested_after_dispatch。代码没有业务撤销接口，不声称取消已经撤销报告。

## 8. 完整回归与证据

```sh
python3 -m unittest discover -s . -p 'test_*.py' -v
```

第三版包含 18 项基础测试与 18 项审批测试，共 36 项。审批测试包含两个进程同时批准/拒绝、过期、内容变更、身份夹具拒绝、未知结果不重发。`approval-evidence.json` 是本次实际运行快照；`validation.json` 保留最新验证记录。

生产接入必须替换 Principal 夹具：从已认证会话获取操作者，由服务端权限系统生成角色和授权作用域；前端不可以提交 roles/allowed_scopes。审批页面必须显示具体草稿与动作，回传它看到的 hash。Hash 仅用于绑定内容，不是数字签名或身份凭证。需要另建服务端审批 API、身份认证、业务授权网关和生产调度。

重新生成本地测试报告与审批故障快照：`python3 record_validation.py`。它会运行完整回归并写 validation.json、validation.log、approval-evidence.json，仍不访问外部服务。
