# zcbot 项目记忆（Codex）

更新时间：2026-07-27。内容从 Claude 项目 memory 迁移并按 Codex 使用方式压缩。`AGENTS.md` 存放每次任务都适用的强约束；本文件存放遇到相关问题时再读取的历史结论。若历史结论与当前代码、`DESIGN.md`、`PROGRESS.md` 或 `RUN.md` 冲突，以当前仓库事实为准并指出差异。

## 用户偏好与长期原则

- 一律使用中文与用户沟通。
- Git commit message 一律使用中文。
- Windows Python 脚本 stdout 可能是 GBK：使用 ASCII 状态标签，不用 emoji 或特殊装饰字符。
- `CHANGELOG.md` 提到海外模型时只用“国际旗舰模型”等泛称；具体型号只放在 `PROGRESS.md`、配置和 git log。
- 版本、CHANGELOG、PROGRESS 在 push 前统一更新；DESIGN 跟随产生架构或决策变化的 commit。
- 真实文件是事实源，不引入向量机制作为事实源；索引只能是可从 Markdown 重建的派生缓存。
- skill 禁令不写违规配方；脚本错误不向 agent 推荐安装命令或替代管线；真正硬约束检查产物。

## 生产与环境

- 本机 `.env` 的 `ZCBOT_DB_URL=127.0.0.1:6012...` 是生产数据库隧道。数据库测试必须显式使用 `ZCBOT_TEST_DB_URL`，绝不能默认读取 `.env` 后向库中插入会被调度器执行的任务。
- 生产机 `/data` 是独立的 `/dev/vdb1`、ext4、约 1 TB 数据盘。若执行 Stage C project quota，既定方案是短停服：停服务、卸载 `/data`、为 ext4 开启 project/quota、fstab 加 `prjquota`、重新挂载；每用户限额取 `config/agent.yaml`。

## 已落地机制

- 知识库于 2026-07-22 以 0.59.0 落地，设计见 `DESIGN.md` §3.8。它是机制而非 skill：个人小库使用 `<user_root>/.kb/<库名>/` 下的纯文件、INDEX、docs 和 sources；共享大库走院检索服务。agent 通过文件工具检索，不新增向量事实源。
- 方舟文档理解已落地到 `tools/read_document.py`：`doubao-seed-2-0-lite-260428` 可通过 `file_data` 的 `data:application/pdf;base64,...` 读取扫描 PDF；单页约 3600 万像素上限，约 100 页上下文上限。相关六个 skill 已有扫描件兜底。探针在 `scripts/probe_ark_doc.py`。
- paper_server 的历史 handoff 已完成：当前已有 `skills/research/SKILL.md`、`skills/research/paper.py`，不要把 `.claude/HANDOFF_paper_skill.md` 当作待办。

## 智能体稳定性历史结论

### 高轮数与重复调用

2026-06 的真实任务诊断确认三类根因：

1. 畸形 tool arguments 退化为合法空 `{}`，旧 malformed 检查未拦截；
2. 工具报错后原样重复调用；
3. 检索 query 不断微调但没有停止条件。

已通过批量工具与 `core/loop.py::_RepeatGuard` 缓解：同一工具与参数在无产出时累计，软阈值提示、硬阈值拦截。相关回归测试是 `tests/test_loop_repeat_guard.py`，诊断脚本位于 `scripts/diag_tool_repeat.py`、`diag_search_args.py`、`diag_error_retry.py`。

### 流式畸形 tool_call

DeepSeek 及部分网关模型曾把 arguments 流切片乱序，属于 provider wire 问题而非本地 builder 拼接。0.58.24 已在 `core/salvage.py::salvage_tool_arguments` 与 loop 中落地全有或全无的 salvage：从后缀寻找可完整解析的 JSON，并以工具 schema 顶层 key 白名单保护；无法恢复时继续走非流式重试。线上复盘显示 salvage 命中率约 85%，残差可由重试自愈，不应再次改 builder。观测事件为 `tool_salvaged` / `tool_malformed`，测试见 `tests/test_salvage.py`。

### GLM 空响应烧满输出

GLM 5.2 的空响应根因是网关默认开启 thinking，推理耗尽模型 65536 输出上限，返回 `finish_reason=length` 且无 content/tool_call。0.58.49 已按 GLM family 透传 `extra_body.thinking.type`，当前配置关闭 thinking，并记录空响应 finish_reason。

不要重新引入全局 `max_tokens` 窗口约束：该方案已探针验证后撤销，未解决根因且可能截断合法大 write。若未来需要成本闸，应设计任务级 token budget。

### unifyllm Claude tool_use 漏为正文

曾出现复杂请求下 Anthropic `tool_use` 未转换为 OpenAI `tool_calls`、直接漏成 Markdown 正文，loop 因 `tool_calls=[]` 误判完成。诊断脚本 `scripts/diag_narrated_toolcall_2a1bc25d.py` 曾稳定复现，但 2026-07-15 当日复测 6/6 已恢复，判断为网关瞬态或已修。复测时必须显式传 profile，不能依赖 task 当前模型。它与 arguments 畸形是不同问题，salvage 无法处理没有结构化 tool_call 的正文泄漏。

## 沙箱与 Chromium

Mermaid/Chromium 的历史故障最终有三个根因：

1. `init.sh` 对 `127.0.0.0/8 DROP` 阻断容器内 Puppeteer 到 Chromium DevTools，表现为恒定约 2 分 15 秒超时且 CPU 接近零；已改为优先允许 loopback。
2. Chromium 150.0.7871.46 点版本启动即崩；已增加刷新旋钮和 build canary。
3. `--pids-limit=256` 过低；已调到 1024。

排查同类问题必须用默认 entrypoint 和生产一致的 network/limits 启容器后再 `docker exec`。使用 `--entrypoint bash` 会绕过 `init.sh` 与 iptables，不能代表线上。build canary 也没有运行时 iptables，只能覆盖浏览器和字体问题。探针位于 `deploy/sandbox/probe_mermaid.sh`、`probe_chromium_bisect.sh`、`probe_chromium_round3.sh`。

## 领域

用户单位是中国建筑材料科学研究总院。代码、库、模板和示例默认服务于水泥/混凝土、玻璃、陶瓷、耐火和新型建材的材料研发、表征分析、实验建模与科研写作；不是建筑施工、BIM 或结构设计语境。
