# Deep Research 系统

> 生物医药行业的 AI 驱动深度研究流水线。v0.20 起以项目自有 Python core + skills + model profiles 为核心，以麦肯锡/德勤式方法论产出专业级研究报告（PDF + DOCX）。

**当前状态**：v0.20 重构启动。核心编排从平台 prompt 迁移到项目自有 Python runtime：模型选择、skills、任务卡并发、中文原生成稿都由 `scripts/dr.py` 与 `configs/models.yaml` 驱动；OpenCode/Codex/Claude Code/Antigravity/Gemini CLI 只作为表层入口。
详见 `PLAN.md` 了解完整方案、版本记录与迭代路径。

---

## 快速开始

> 项目在 **macOS** 与 **Debian/Ubuntu** 上均通用。Python 包管理用 [**uv**](https://docs.astral.sh/uv/)（Rust 写的 Python 包管理器，比 pip 快 10-100 倍，Astral 出品）。

### 1. 一键初始化（推荐）

```bash
bash scripts/setup.sh
```

这个脚本会自动：
- 检查并安装 **uv**（没有会交互询问，走官方 `curl -LsSf https://astral.sh/uv/install.sh` 脚本）
- 调用 `uv sync` 按 `pyproject.toml` 创建 `.venv/` 并装所有依赖（ReportLab、matplotlib、biopython 等）
- uv 会自动管理 Python 版本（>=3.10），不依赖系统 Python
- 检查并提示缺失的系统二进制（pandoc、opencode）

**为什么选 uv 而不是 pip**：
- macOS Homebrew Python、Debian 12+ 默认 PEP 668 保护，`pip install` 到系统会被拒
- uv 把 venv + 版本管理 + 依赖解析一次搞定，不需要手动 `python -m venv` + `source activate` + `pip install`
- `uv sync` 10-30 秒装完一整个科学计算栈（pip 要 2-5 分钟）
- 有 `uv.lock` 锁定精确版本，mac 和 debian 装出来的环境完全一致

### 2. 安装系统二进制（setup.sh 不装这些）

```bash
# macOS
brew install pandoc
curl -fsSL https://opencode.ai/install | bash

# Debian/Ubuntu
sudo apt update && sudo apt install -y pandoc curl
curl -fsSL https://opencode.ai/install | bash
```

> uv 会帮你装 Python 本身，不需要再装 `python3-venv` / `python3-pip`。

### 3. 配置密钥

```bash
cp secrets.env.example secrets.env
# 编辑 secrets.env，填入：
#   - ZENMUX_API_KEY（必填，格式 sk-ai-v1-xxx）
#   - TAVILY_API_KEY / BRAVE_API_KEY / EXA_API_KEY
#   - NCBI_API_KEY（可选，PubMed 高频查询时用）
```

### 4. 下载中文字体

```bash
bash .opencode/templates/fonts/download-fonts.sh
```

下载思源宋体 + 思源黑体 + 霞鹜文楷（SIL OFL 许可，约 140MB）。

### 5. 验证 zenmux 端点与 cache（强烈推荐）

```bash
source scripts/activate.sh          # 一键激活 venv + 加载 secrets
bash scripts/verify-zenmux.sh       # 3 步自检
```

`verify-zenmux.sh` 会：
1. 测 OpenAI 兼容端点（Gemini 3.1 Pro Preview）
2. 测 Anthropic 兼容端点（Claude Haiku 4.5）
3. 测 Claude prompt cache 两次调用命中（Opus 4.7 cache 读取仅 0.5 USD/M tokens）

若第 3 步 `cache_read_input_tokens` 始终为 0，参见 `AGENTS.md` §6.5 诊断步骤。

### 6. 日常使用

```bash
cd ~/Documents/Projects/deep_research
source scripts/activate.sh          # 激活 venv + 载入 secrets
opencode                            # 启动 TUI
```

**退出 venv**：`deactivate`

**跑单个 Python 脚本**（不用先激活）：
```bash
uv run python scripts/build_report.py <slug>                    # 默认 ReportLab
uv run python scripts/build_report.py <slug> --engine quarto    # Quarto/xelatex
```

网络不稳或 API 限流时，把 `--workers` 降到 `3` 或 `1` 即可断点续跑。

### 多平台兼容

v0.20 后，OpenCode/Codex/Claude Code/Gemini CLI 都是 Python core 的 surface adapter。Antigravity 走 native 模式：用 `.agents/skills` 指导其自身模型按 phase 执行研究，Python core 只做辅助。详细调用方式见 `docs/platform-adapters.md`。

- OpenCode：使用 `.opencode/commands/*.md` 薄封装 Python CLI。
- Codex：使用 `AGENTS.md` + 部署到 `$CODEX_HOME` 的 adapter 文件，优先用 GPT 系列做代码/测试/审阅。
- Claude Code：使用 `.claude/skills/*/SKILL.md`，优先用 Opus/Sonnet 做访谈、结构讨论和中文风格审阅。
- Gemini CLI：使用 `GEMINI.md` 与 `.gemini/commands/dr/*.toml`，优先用 Gemini 做长上下文、多模态和替代框架审阅。
- Antigravity：作为 Agent Manager 打开仓库，使用 `.agents/rules` + `.agents/skills` 指导其用 Antigravity 模型配额原生执行 Deep Research；Python core 只做脚手架、校验、引用和出稿辅助。

部署 Codex adapter（不在仓库内创建 `.codex`）：

```bash
uv run python scripts/deploy_adapters.py codex --force
```

Codex adapter 会写到 `$CODEX_HOME` 或 `~/.codex`；已有文件会在 `--force` 覆盖前生成 `.bak` 备份。adapter 默认面向自动化研究：workspace 可写、命令不逐次审批、实时 web search 与脚本网络访问开启；Tavily / Brave / Exa MCP 会默认启用但不设为必需服务。

安全默认：部署脚本不会复制 `config.toml`，避免覆盖用户级 Codex 配置。只有明确需要安装本项目 bundled profile 时，才使用 `--include-config`。

部署 Antigravity workspace adapter（只写目标工作区 `.agents/`，默认不覆盖已有文件）：

```bash
uv run python scripts/deploy_adapters.py antigravity --target /path/to/workspace --dry-run
uv run python scripts/deploy_adapters.py antigravity --target /path/to/workspace
```

Antigravity 部署不会修改全局设置。若目标工作区已有同名 agents/rule/skill/workflow 文件，默认跳过；只有显式加 `--force` 时才会先生成 `.bak` 备份再覆盖。

部署到新环境后自检：

```bash
uv run python scripts/deploy_check.py
uv run python scripts/deploy_check.py --repair --force
```

运行平台无关 Python core：

```bash
uv run python scripts/dr.py init "研究主题" --slug <slug> --method mckinsey_market
uv run python scripts/dr.py frame <slug>
uv run python scripts/dr.py run <slug-or-topic> --method gmp_gap_assessment
uv run python scripts/dr.py research <slug> --workers 6
uv run python scripts/dr.py research <slug> --workers 6 --execute-packets
uv run python scripts/dr.py research <slug> --workers 6 --execute-packets --allow-search-fallback
uv run python scripts/dr.py research <slug> --workers 6 --build-briefs
uv run python scripts/dr.py research <slug> --workers 6 --assemble-chapters
uv run python scripts/dr.py review <slug>
uv run python scripts/dr.py finalize <slug>
```

OpenCode/Codex/Claude Code/Gemini CLI adapter 只包装这些 CLI，不再承担核心调度或模型选择。Antigravity 是例外：它优先按 `.agents/skills/antigravity-surface-adapter` 用自身模型配额执行研究，避免主流程重新落回 ZenMux。

模型与搜索 API 选择见：
- `docs/model-playbook.md`
- `docs/search-playbook.md`

模型预设配置文件：
- `configs/models.yaml`（统一预设，支持 `simple / medium / premium / cn_heavy / codex_native`）

推荐时机：在 `/dr-init` 访谈阶段就确定 `model_profile`。v0.20 后模型选择优先在 Python runtime 中解析，adapter agent 文件只是兼容层。

命令行查看解析后的模型映射：

```bash
uv run python scripts/dr.py models
uv run python scripts/dr.py models --list
uv run python scripts/dr.py models --profile premium
uv run python scripts/dr.py models --profile medium --model-override dr_verifier=zenmux/openai/gpt-5.4
uv run python scripts/dr.py skills validate
uv run python scripts/dr.py skills sync
uv run python scripts/dr.py methods list
uv run python scripts/dr.py methods show gmp_gap_assessment

# apply profile to agent files
uv run python scripts/dr.py apply-models --profile medium --target both --dry-run
uv run python scripts/dr.py apply-models --profile medium --target both
```

Sprint 5 回归检查（一键）：

```bash
uv run python scripts/sprint5_regression.py <slug>
```

统一搜索入口：

```bash
uv run python scripts/search.py "dual-target RNAi 2024" --route scholar --year-low 2023
uv run python scripts/search.py "FDA warning letter CAPA deviation change control pharmaceutical" --route evidence --json --trace
uv run python scripts/search.py "dual-target siRNA GalNAc" --route patents
```

v0.20 搜索分工：`evidence` 用 Exa highlights 做受控候选证据发现；`scholar/patents/news/fda` 保留专用路由；`general` 只做宽泛发现和兜底；Tavily Research 作为 Phase1 初扫、薄弱章节补证据和 Phase3 回炉工具，结果必须存盘、评分、去重后再进入 evidence packet。Antigravity 原生流程可优先使用 `paperfoot/search-cli` 的 `search` 命令作为统一检索前台，但所有结论仍必须回到本项目 Tier/source-quality 体系。

---

## 项目结构

```
deep_research/
├── PLAN.md                      # 完整方案（中断续接从此读起）
├── AGENTS.md                    # 跨平台研究方法论与运行规则
├── README.md                    # 本文件
├── secrets.env.example          # 密钥模板
├── secrets.env                  # 你的密钥（gitignore）
│
├── .opencode/
│   ├── opencode.json            # MCP 配置 + 权限
│   ├── agents/                  # Agent 定义
│   │   ├── dr-plan.md           # [MVP] 框架规划师 Opus 0.7
│   │   └── dr-pm.md             # [MVP] 项目经理 Sonnet 0.2
│   ├── skills/                  # 可复用技能
│   │   ├── search-strategy/     # [MVP] 检索策略总纲
│   │   ├── source-quality/      # [MVP] 信源评级
│   │   ├── length-budget/       # [MVP] 字数预算
│   │   └── pdf-reportlab/       # [MVP] PDF 模板使用
│   ├── commands/
│   │   ├── dr-init.md           # [MVP] /dr-init
│   │   └── dr-frame.md          # [MVP] /dr-frame
│   └── templates/
│       ├── report-template.py   # [MVP] ReportLab PDF 生成器
│       └── fonts/
│           ├── download-fonts.sh
│           └── README.md
│
├── projects/                    # 每个研究一个子目录
│   └── <slug>/
│       ├── manifest.json
│       ├── phase1/
│       ├── phase2/
│       ├── phase3/
│       └── phase4/
│
└── archive/                     # 完成项目归档
```

---

## 关键设计要点

### 1. Python core 防止上下文污染

OpenCode/Codex/Claude Code/Antigravity/Gemini CLI 的 subagent 或 agent thread 都可能把大上下文带入子会话。v0.20 起，默认并发由 Python runtime 的任务卡执行层控制：先生成 `phase1/research_brief.json` 固化研究意图和任务切分，再生成 `phase2/task_cards.json`，按任务卡产出 `phase2/packets/*.json`，经 `phase2/compressed_findings/*.json` 收束后再组装中文章节。平台 agent 只负责调用 CLI 和展示状态。

### 2. 信源分级（Tier 1-4 + 黑名单）

详见 `AGENTS.md` §4 与 `skills/source-quality/SKILL.md`。

- **Tier 1**：PubMed、顶刊、FDA/NMPA 监管、ClinicalTrials、专利
- **Tier 2**：权威咨询、系统综述、行业协会
- **Tier 3**：预印本、券商研报、会议摘要（需 Tier 1-2 支撑）
- **Tier 4**：通用搜索（仅做发现入口，不做证据）
- **黑名单**：自媒体、撤稿论文、软文

### 3. 字数硬性要求

| 类型 | 最小字数 |
|---|---|
| 综述 | 10,000 |
| 研究 | 30,000 |
| 投资 | 20,000 |
| 管理 | 15,000 |

Phase 1 分配章节配额，Phase 2 自检，不足返工。见 `skills/length-budget/SKILL.md`。

### 4. 中文 PDF 双引擎

`build_report.py` 现在支持两套 PDF 引擎，按需选择：

#### 引擎 A：ReportLab（默认，无额外依赖）

```bash
uv run python scripts/build_report.py <slug>
```

- 字体：思源宋 + 思源黑 + 霞鹜文楷（全 SIL OFL，可商用嵌入）
- 样式：集中在 `build_styles()`，所有字号行距单点维护
- 速度：30,000 字 3-5 秒出稿
- 局限：超宽表格（≥4 列且含长文本）需借助列宽 patch 或改为 bullet list 格式

#### 引擎 B：Quarto / xelatex（`--engine quarto`，推荐用于宽表报告）

```bash
uv run python scripts/build_report.py <slug> --engine quarto
```

- 排版引擎：xelatex（TeX Live / TinyTeX），LaTeX 级排版质量
- 字体：同样使用思源宋 + 思源黑，通过 fontspec 加载
- 宽表支持：超宽表通过 `longtable` + `tbl-colwidths` 精确指定列宽比例，不溢出
- 横向页面：通过 `{.landscape}` div 包裹超宽表，自动插入 `pdflscape` 代码（注意：101 行以上的 landscape longtable 可能触发 TeX `param_size` 上限，建议拆成 ≤20 行的子表块）
- 图表：暂不嵌入 matplotlib 图表（使用文字描述代替）

**安装 Quarto 引擎**（一次性，系统级）：

```bash
# 1. 安装 Quarto CLI
#    下载页：https://github.com/quarto-dev/quarto-cli/releases/latest
#    Linux 选 .deb 安装包，macOS 选 .pkg

# 2. 安装 TinyTeX（Quarto 内置命令）
quarto install tinytex

# 3. 安装中文 LaTeX 支持包
~/.TinyTeX/bin/x86_64-linux/tlmgr install ctex xecjk cjk xetex
# macOS 路径通常为：~/.TinyTeX/bin/universal-darwin/tlmgr

# 4. 注册思源字体到 fontconfig
# （先确认字体已下载：bash .opencode/templates/fonts/download-fonts.sh）
mkdir -p ~/.fonts
cp .opencode/templates/fonts/*.otf ~/.fonts/
cp .opencode/templates/fonts/*.ttf ~/.fonts/
cp .opencode/templates/fonts/ttf/*.ttf ~/.fonts/
fc-cache -fv ~/.fonts

# 5. 验证
quarto --version        # 应输出 1.x.x
fc-list | grep "Source Han"  # 应看到思源字体条目
```

**两引擎对比**：

| 指标 | ReportLab | Quarto/xelatex |
|------|-----------|----------------|
| 安装复杂度 | 无额外依赖 | 需安装 Quarto + TinyTeX |
| 渲染速度 | 3-5 秒 | 30-90 秒（LaTeX 编译） |
| 宽表格处理 | 需 workaround | longtable 原生支持 |
| 横向页面 | 不支持 | 支持（≤20 行/块） |
| 字体嵌入 | OTF 直接嵌入 | fontspec 系统字体 |
| 输出体积 | ~1.2 MB/100页 | ~0.9 MB/100页 |
| 目录生成 | 自定义实现 | LaTeX 自动 \tableofcontents |

### 5. zenmux 双 provider（Claude cache 关键）

Deep Research 大量使用**相同的长 system prompt + skill 内容**连续调用 Claude，如果 prompt cache 没生效，Opus/Sonnet 成本会翻 5-10 倍。所以本项目：

- **Claude 系列** → `zenmux-anthropic/claude-opus-4.7`（走 `https://zenmux.ai/api/anthropic`，`@ai-sdk/anthropic` 原生支持 `cache_control`）
- **非 Claude 系列** → `zenmux/google/gemini-3.1-pro-preview` 等（走 `https://zenmux.ai/api/v1`，隐式缓存自动生效）

Opus 4.7 cache 读取价格 0.5 USD/M tokens（对比输入 25 USD/M，节省 98%）。

**验证 cache 是否生效**：
1. 在 https://zenmux.ai/settings/logs 打开 API Call Logging
2. 运行一个真实 Claude/ZenMux 调用链路，例如 `uv run python scripts/dr.py research <slug> --execute-packets`
3. 第 2 次的 `cache_read_input_tokens` 字段应 > 0
4. 若始终为 0，检查 `configs/models.yaml` 中对应 role 是否走 `zenmux-anthropic/...`

---

## 已知待办

### 用户侧
1. [x] ~~在 zenmux 后台确认模型 slug，更新 `AGENTS.md` §6 映射表~~（已在 v0.2 完成，使用双 provider 架构）
2. [ ] 填写 `secrets.env`
3. [ ] 运行 `download-fonts.sh` 下载字体
4. [ ] 用一个小主题（如"5000 字 PD-1 综述"）跑通 MVP 流水线

### 系统侧（下一阶段）
- [x] Python core `init/frame/research/review/finalize/status` 骨架
- [x] OpenCode/Codex/Claude Code/Gemini CLI wrapper
- [x] Antigravity 专用 workspace rule + skill，按 Antigravity 模型配额原生执行研究
- [x] 用户资料导入基础能力：PDF 文本抽取 + FireRed OCR 扫描件识别 + phase0 落盘
- [ ] 用户资料导入增强：DOCX / PPTX / 表格抽取 / 版面结构化
- [ ] PDF reporting 包继续拆分：字体、宽表、引用、渲染验证

---

## 排错

### 字体下载失败
```bash
# 检查网络访问 GitHub
curl -I https://github.com

# 用镜像手动下载（见 .opencode/templates/fonts/README.md）
```

### MCP Server 启动失败
```bash
# 检查 npx 可用
which npx

# 验证 MCP server 可独立运行
npx -y tavily-mcp@latest
```

### 平台 agent 看起来没有真正并发
v0.20 不再用平台 subagent 作为默认并发机制。请检查 Python core 产物：

```bash
uv run python scripts/dr.py status <slug>
ls projects/<slug>/phase2/task_cards.json
ls projects/<slug>/phase2/packets
ls projects/<slug>/phase2/chapter_briefs
```

### ReportLab PDF 中文乱码
```bash
# 确认字体已注册（venv 里）
source scripts/activate.sh
ls .opencode/templates/fonts/*.otf | wc -l   # 应为 6+

# 手动测试
python .opencode/templates/report-template.py --help
```

### Quarto PDF 生成失败

**字体找不到（`Could not resolve font "Source Han Serif CN/I"`）**：

CJK 字体没有斜体变体，fontspec 默认会找 `/I` 导致报错。`build_report.py --engine quarto` 已通过 `mainfontoptions: [ItalicFont=...]` 自动绕开，无需手动处理。若自行编写 `.qmd`，需在 YAML 里加：

```yaml
format:
  pdf:
    pdf-engine: xelatex
    CJKmainfont: "Source Han Serif CN"
    mainfontoptions:
      - BoldFont=Source Han Serif CN
      - ItalicFont=Source Han Serif CN
      - BoldItalicFont=Source Han Serif CN
```

**`tlmgr` 找不到**：

TinyTeX 不在系统 PATH，用完整路径：
```bash
~/.TinyTeX/bin/x86_64-linux/tlmgr install <package>   # Linux
~/.TinyTeX/bin/universal-darwin/tlmgr install <package> # macOS
```

**`TeX capacity exceeded [parameter stack size]`**（landscape 大表）：

pdflscape 的 `\LS@makefcolumn` 在 101 行以上的 longtable 里递归过深，耗尽 TeX 的 `param_size`。解决方法：把超大表拆成每块 ≤20 行的子表，每块都包在 `{.landscape}` div 里：

```markdown
::: {.landscape}
| 列1 | 列2 | ... |
|---|---|---|
| 第1-20行 | ... |
:::

::: {.landscape}
| 列1 | 列2 | ... |
|---|---|---|
| 第21-40行 | ... |
:::
```

若使用 `build_report.py --engine quarto`，可通过传入预处理好的 `.md`（宽表已拆块）来避免此问题。

### uv 安装后找不到
uv 官方脚本把 uv 装到 `~/.local/bin/`。若终端里 `which uv` 找不到：
```bash
export PATH="$HOME/.local/bin:$PATH"
# 永久生效：
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc   # macOS zsh
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc  # debian bash
```

### Python 依赖安装报 externally-managed-environment
不要用系统 pip！改用 uv（本项目标准方案）：
```bash
bash scripts/setup.sh   # uv sync 会自建 .venv 并装依赖
```

### uv sync 很慢 / 下载超时
国内网络下，可用镜像加速：
```bash
export UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
# 或
export UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple
uv sync
```

### 每次开终端都要手动 source
用 `source scripts/activate.sh` 一次搞定。或装 [direnv](https://direnv.net/)：
```bash
# macOS
brew install direnv

# Debian
sudo apt install direnv

# 都装完后在项目根目录
echo 'source scripts/activate.sh' > .envrc
direnv allow
```
之后 `cd` 进项目目录就会自动激活，不用再想。

---

## 参考链接

- OpenCode 文档：https://opencode.ai/docs
- Agent 配置：https://opencode.ai/docs/agents
- 跨平台调用：`docs/platform-adapters.md`
- Skill 配置：https://opencode.ai/docs/skills
- MCP Servers：https://opencode.ai/docs/mcp-servers
- ReportLab 文档：https://docs.reportlab.com
- Quarto 文档：https://quarto.org/docs/output-formats/pdf-basics.html
- Quarto PDF 引擎：https://quarto.org/docs/output-formats/pdf-engine.html
- Quarto 表格文档：https://quarto.org/docs/authoring/tables.html
- 思源字体：https://github.com/adobe-fonts
- 霞鹜文楷：https://github.com/lxgw/LxgwWenKai

---

## 变更记录

- **v0.1** (2026-04-20) — MVP 路径 2 完成：dr-plan + dr-pm 两主 agent、4 个核心 skill、2 个命令、ReportLab 模板基础版、字体下载脚本
- **v0.2** (2026-04-20) — 双 provider 架构（zenmux-anthropic + zenmux），解决 Claude prompt cache 生效问题
- **v0.3** (2026-04-20) — 修正 v0.2 模型名（回到 Opus 4.7 / Sonnet 4.6 / Gemini 3.1 Pro / GPT-5.4 Pro 等真实 slug）；改 venv + requirements.txt 跨平台方案（macOS + Debian）；新增 `scripts/setup.sh`、`scripts/activate.sh`
- **v0.13** (2026-05-02) — `build_report.py` 新增 `--engine quarto` 选项：Quarto 1.9 + xelatex 引擎，解决 ReportLab 超宽表格渲染 bug（`negative availWidth`/`NoneType` 问题）；`report-template.py` 同步修复（`render_table_blocks` 分块 + 等宽列强制分配）；README 补充双引擎安装指南与排错
- **v0.20** (2026-05-05) — Skill-driven Python core 重构启动：新增 `scripts/runtime/*`、`scripts/dr.py init/frame/run/research/review/skills`、task-type 模型映射、中文原生 finalize 默认路径和 `scripts/reporting/*` 报告模块；OpenCode/Codex/Claude Code/Gemini CLI 命令降级为 Python CLI wrapper。
- **v0.20.1** (2026-05-07) — Antigravity-native adapter：新增 `.agents/rules/deep-research-antigravity.md`、`.agents/skills/antigravity-surface-adapter`，按 Gemini Flash 管理、Opus/Gemini 分 phase 执行研究；Python core 退为辅助工具。

见 `PLAN.md` §12 了解完整变更历史。
