# 2.1 同一个需求，聊天框和工作区各写一次“语音备忘录转文字”脚本

> 状态（2026-10-13）：A（聊天框）和 B（工作区 agent）各 3 次，都已跑完。B 一开始被权限检查拦下，后来鸭哥明确授权，由主线程运行。

## 怎么跑

都在 textbook 仓库根目录执行。本目录有自己的 `.venv`（openai、httpx、requests），A 和 B 用的是同一个 Python。

```bash
# 一次性：建 venv
cd experiments/2_1_workspace && uv venv .venv && uv pip install openai httpx requests && cd ../..

# 核对接口文档（用 W01 真调一次，结果在 api_check/api_check.json）
op run --env-file experiments/aib.env -- experiments/2_1_workspace/.venv/bin/python experiments/2_1_workspace/api_check/check_api.py

# A：聊天框，第 n 次
op run --env-file experiments/aib.env -- experiments/2_1_workspace/.venv/bin/python experiments/2_1_workspace/run_chat.py gemini-3.8-flash 1

# A 的反事实（不计入结果）：只把猜错的网址换成真的，再跑一次
op run --env-file experiments/aib.env -- experiments/2_1_workspace/.venv/bin/python experiments/2_1_workspace/counterfactual_url.py

# B：工作区 agent，第 n 次
op run --env-file experiments/aib.env -- experiments/2_1_workspace/.venv/bin/python experiments/2_1_workspace/run_agent.py 1

# 单独检查某个运行目录
experiments/2_1_workspace/.venv/bin/python experiments/2_1_workspace/check.py <run_dir>
```

## 实验设计

两种条件收到的是同一句话：

> 帮我写一个 Python 脚本，把 memos/ 文件夹里的语音备忘录用 AI Builder Space 的语音识别接口转成文字，每条存成一个同名的 .txt 文件放在 transcripts/ 里。API key 在环境变量 AI_BUILDER_TOKEN 里。

- **A：聊天框。** 一次 API 调用，消息里只有上面这句话，模型 `gemini-3.8-flash`。把回复里的代码原样存成 `script.py`，放进一个新文件夹（只有 `memos/`，没有 `API.md`），环境变量里有 key，不改一个字跑一次。
- **B：工作区 agent。** 一个新的临时文件夹，里面有 `memos/` 和 `API.md`。同一句话后面加一句“你在一个文件夹里工作，可以读文件、跑命令、看结果。”，再加一句安全规则（key 只从环境变量读，不写盘、不打印）。用 Antigravity CLI（`agy --print`，模型 `gemini-3.8-flash-high`）跑一个回合，agent 可以自己反复改、反复跑，人不插手。临时文件夹放在仓库外，免得 agent 读到 `common.py` 或 A 的结果；跑完复制回 `trials/B<n>/`。
- **成功标准（两边一样，`check.py`）：** 5 条备忘录（W01、W02、W05、L01、I01）每条都有 `transcripts/<同名>.txt`，非空，至少 10 个汉字，和原稿的字符相似度不低于 0.5。最后一条是为了排除“文件里写的是报错信息或整段 JSON”的情况。用 `corpus/transcripts_grok.json` 里的已知好结果核对过，5 条相似度都在 0.93 以上。
- `fixture/API.md` 只写了 `ai_builder_space/docs/prd.md`“Grok File Transcription”一节和 README 里的事实，返回格式和 curl 示例是用 W01、L01 真调出来的（`api_check/api_check.json`）。Base URL `https://space.ai-builders.com/backend/v1` 文档里没有写全，是实测确认的。

## 结果

### A：聊天框（gemini-3.8-flash）

| 次 | 一次成功？ | 写出的转写 | 失败原因 | 跑了几次 | 模型用时 | 脚本运行 |
|---|---|---|---|---|---|---|
| A1 | 否 | 0/5 | 网址是猜的：`https://api.ai-builder.space/v1/audio/transcriptions`，这个域名不存在 | 1 | 17.2 s | 0.4 s，退出码 0 |
| A2 | 否 | 0/5 | 同上，猜的网址一模一样 | 1 | 18.5 s | 0.1 s，退出码 0 |
| A3 | 否 | 0/5 | 同上，猜的基地址 `https://api.ai-builder.space/v1`，这个域名不存在 | 1 | 18.1 s | 7.0 s，退出码 0 |

三次的共同点：

- 三次都猜了同一个不存在的域名 `api.ai-builder.space`（DNS 查不到）。真实地址是 `space.ai-builders.com/backend/v1`，模型没有任何渠道知道。
- 除了网址，其他部分基本是对的：鉴权都写成 `Authorization: Bearer <key>`，都按 OpenAI 兼容的接口上传，都正确地遍历 `memos/`、按同名写 `.txt`。三份代码在网址那一行都留了注释，也就是模型自己知道这里是猜的。
- 三个脚本都把每个文件的报错打印出来然后继续，最后打出完成信息，退出码 0，`transcripts/` 是空的。只看退出码会以为成功了。

**反事实（不计入成功率，`counterfactual_url.json`）：** 只把网址换成真的，其他一个字不改：

| 次 | 换网址后 | 说明 |
|---|---|---|
| A1 | 成功，5/5 | 相似度 0.91–1.00 |
| A2 | 成功，5/5 | 相似度 0.93–0.99 |
| A3 | 成功，5/5 | 相似度 0.91–1.00 |

所以 A 卡住的就是一个它无从得知的事实（网址），换上之后 3 次全部能用。

### B：工作区 agent

#### B 的结果（2026-10-13，鸭哥授权后由主线程运行）

| 试验 | 一次成功 | 转写成功 | 用时 | 写进文件的 key |
|---|---|---|---|---|
| B1 | 是 | 5/5 | 127.6 秒 | 无 |
| B2 | 是 | 5/5 | 106.6 秒 | 无 |
| B3 | 是 | 5/5 | 112.7 秒 | 无 |

- 3 次都用了正确的地址 `https://space.ai-builders.com/backend/v1/audio/grok-transcription`。这个地址只写在 `API.md` 里，说明 agent 读了文档。不过 agy 的事件日志不记录具体读了哪些文件，所以没法从日志直接确认。
- agent 自己建了虚拟环境、装了依赖、写了脚本，然后运行脚本，再看 `transcripts/` 里的结果，确认 5 条都转写成功后才收尾。对话只有一个回合，人没有插手。
- 3 次都没有把 key 写进任何文件。

## 注意事项

- n=3，每个条件一个模型。A 只试了 `gemini-3.8-flash`，没有换模型复现。
- A 的失败几乎全部来自“它不可能知道这个接口的地址”，而不是代码能力问题。这正是这一节想说明的：模型知道新东西的唯一渠道是输入。但它也意味着这个对比在设计上偏向 B，因为 B 的 `API.md` 直接写了答案。B 真正要证明的不是“它更聪明”，而是“它能读到这份文档，而且跑一次就能发现 A3 那种问题”。
- A 的代码是在本目录的 `.venv` 里跑的，里面预先装了 `requests`。如果读者的环境没装，A 会更早失败在 `ImportError`。
- A 的“退出码 0 但什么都没写出来”是一个值得在正文里点出的现象：聊天框写出来的代码没法替你确认它真的跑通了。
- 成功标准里的相似度阈值 0.5 是为了排除 JSON 原文和报错，不是在评转写质量。

## 文件

- `fixture/memos/`：5 条 wav，复制自 `corpus/audio/`。`fixture/API.md`：给 B 的接口文档。
- `api_check/`：接口核对脚本和真实返回（`words` 已截断）。
- `run_chat.py`、`run_agent.py`、`check.py`、`counterfactual_url.py`：运行与检查脚本。
- `trials/A<n>/`：`response.md`（模型原始回复）、`run/script.py`（原样代码）、`run_stdout.txt`、`run_stderr.txt`、`result.json`、`counterfactual_url_fixed/`（只换网址后的运行）。
- `results.json`：汇总。

## 教材正文移出的细节（2.1 第二轮重写，2026 年 10 月）

- 聊天框条件猜错的地址 `https://api.ai-builder.space/v1/audio/transcriptions` 和真实地址 `https://space.ai-builders.com/backend/v1` 不再出现在正文里，正文只说“3 次都猜了同一个不存在的接口地址”。
- 工作区条件每次用时 107 到 128 秒（见上表），正文不再给时间。
- 实验 prompt 原文里的“API key 在环境变量 AI_BUILDER_TOKEN 里”不进正文。正文的动手部分也不再让读者在终端里 `export AI_BUILDER_TOKEN=...`，改为让 AI 建一个存 key 的文件（如 `.env`），读者自己粘贴 key，再让 AI 确认读得到。
- 旧版动手 prompt 里写死的接口地址删掉了，改为让 AI 自己查 AI Builder Space 的文档；接口地址只放在正文末尾的“当前载体”里，作为 AI 卡住时的备选信息。
- intake-skill（仅 Mac）从动手段挪到了“当前载体”。
