Canvas 驱动的
Related Work 重写工作流

一套完整的 AI 辅助学术写作管道:从 Zotero 文献库出发,经过 Obsidian Canvas 结构化笔记, 到 Gemini CLI 分阶段提取证据,最终生成事实驱动、可核查的 Related Work 段落。 公开版核心是 batch.py(Zotero → Canvas);下游 RW 写作流水线以一篇真实 ToCHI 投稿 实践(2026.03)为案例呈现,参见 GitHub 仓库

Zotero + BetterBibTeX Obsidian Canvas Gemini CLI pdfplumber CodeX 事实核查 74 篇文献 ToCHI 投稿
动机

引用堆积 vs. 事实驱动

初稿 Related Work 最常见的问题是"引用堆积"——一句话后面跟 5–7 个引用,没有实质内容。 审稿人无法判断这些论文发现了什么、为什么被引用。

❌ 引用堆积型(常见初稿)

Gaze interaction has been studied extensively for selection tasks \cite{A, B, C, D, E, F, G}. Various approaches have been proposed to address the Midas Touch problem \cite{H, I, J, K}.

→ 两句话引了 11 篇论文,但读者不知道任何一篇的具体发现

✅ 事实驱动型(Sidenmark 风格)

Dwell-based selection remains a primary solution, with 600ms identified as the optimal duration to balance efficiency and workload \cite{niu2020}. For small targets, discrete magnification achieves 88% accuracy compared to only 28% for standard dwell \cite{skovsgaard2011}.

→ 每句话 = 一个具体事实 + 一个引用,论证清晰可追溯

💡 核心思路

不要让 AI 直接写 RW(会编造或泛化)。让 AI 做结构化提取, 输出 JSON,人工检查后再写作。所有中间产物保存,修改方向时不需要重跑。 Canvas 笔记库是结构化的知识来源——可以直接针对它提问,找出具体证据。

全景

端到端数据流

从 Zotero 文献库到最终 LaTeX 段落,整条管道分为 7 个阶段、4 种 AI Agent。

📚 Zotero SQLite batch.py 🤖 Gemini Flash 📋 74× .canvas 文件
📋 Canvas 文件 classify.py 📊 classify_result.json S1~S4 分类
📊 分类结果 screen → extract → write 📝 rw_draft_S1~S4.md
📝 草稿 integrate_rw.py 🤖 Gemini Pro 📄 rw_integrated.tex 🔍 CodeX ✅ 最终 LaTeX
74
Canvas 文献笔记
7
Pipeline 阶段
4
RW 小节
29
事实核查通过
核心流水线

七个阶段,逐步深入

点击任意阶段查看详细说明、代码示例和真实输出数据。

1
Canvas 生成
Gemini Flash
2
分类
Gemini Flash
3
筛选
Gemini Flash
4
证据提取
Gemini Flash ×5
5
草稿写作
Gemini Flash
6
LaTeX 整合
Gemini Pro
7
事实核查
CodeX

Stage 1:Canvas 批量生成 batch.py

🤖 Agent: Gemini Flash 📥 输入: Zotero SQLite + PDF 文件 📤 输出: 74 个 .canvas JSON 文件 ⏱ 并发 5 workers

从 Zotero 数据库读取指定 Collection 的所有论文,用 pdfplumber 提取 PDF 全文并编号每行, 然后调用 Gemini CLI 生成结构化的 Obsidian Canvas 知识鸟瞰图。 每个 Canvas 包含:文献元数据、研究背景、核心方案、实验设计、结论等节点,以及带页码的 Zotero 深链接。

关键技术

  • SQLite 安全读取:Zotero 持有写锁,先 copy DB 到临时文件再查询
  • 行编号:[0] text [1] text ... 供 Gemini 定位原文位置
  • 页码映射:[0-34]→p1 [35-67]→p2,生成 Zotero 深链接
  • Markdown fence 清理:Gemini 输出可能包裹 ```json,需要 strip

Checkpoint: 抽查 Canvas 质量

  • 打开 2-3 个 Canvas,确认节点结构完整
  • 检查 Zotero 深链接是否能正确跳转
  • 确认中文内容无截断或乱码
  • 检查引用索引 [N] 是否与原文对应
bash
# 批量生成 Canvas 文件
venv/bin/python batch.py -m gemini-3-flash-preview -w 5

# 可选:只处理特定 attachment keys
venv/bin/python batch.py --keys TEPVVD2P ABC12345

# Dry run:列出所有论文,不实际生成
venv/bin/python batch.py -n
python — 核心调用模式
# Gemini CLI 调用模式(所有脚本统一)
result = subprocess.run(
    ["gemini", "-m", model, "-p", prompt],
    capture_output=True, text=True, timeout=360
)
# prompt 里包含完整的 PDF 文本 + 元数据 + Canvas 格式要求
raw = result.stdout.strip()
# 清理 markdown fence
if raw.startswith("```"):
    raw = re.sub(r"^```\w*\n?", "", raw)
    raw = re.sub(r"\n?```$", "", raw)
canvas = json.loads(raw)

Stage 2:文献分类 classify.py

🤖 Agent: Gemini Flash 📥 输入: Canvas 文件 + RW 小节定义 📤 输出: classify_result.json + classify_table.md

读取每个 Canvas 文件的关键节点内容(元数据 + 结论 + 核心方案), 结合论文的 RW 小节定义,让 Gemini 判断每篇文献属于哪个小节。 输出结构化的分类结果和 Markdown 表格。

RW 小节定义(Gaze2Foot)

  • S1: Gaze-Based Selection & Midas Touch
  • S2: Multimodal Gaze(非足部多模态)
  • S3: Foot Input(足部交互通用研究)
  • S4: Gaze+Foot Systems
  • S0: Background/Context(仅铺垫)

输出 JSON 结构

[ { "citekey": "niu2020Improving", "rw_section": "S1", "modality": "dwell-time", "rw_use": "600ms optimal dwell", "contribution_for_gaze2foot": "dwell baseline parameter" } ]

Stage 3:定向筛选 screen.py

🤖 Agent: Gemini Flash 📥 输入: classify_result.json + Canvas 摘要 📤 输出: screen_result.json

区分哪些论文有"展开讲"的价值(有具体数字/对比/结论 → featured), 哪些只需要 \cite{} 提及(→ brief)。 每个小节有定制化的筛选标准。

筛选标准示例

S1 有没有量化的确认延迟/错误率/成功率?
S2 有没有比较第二通道 vs. 纯注视的 RT/SR?
S3 有没有量化不同足部动作的疲劳/精度差异?
S4 具体测了什么场景?关键结果数字是什么?

输出结构

{ "S1": [ { "citekey": "niu2020Improving", "priority": "featured", "reason": "600ms dwell 量化数据" }, { "citekey": "abe2013Automatic", "priority": "brief", "reason": "仅方法描述,无定量" } ] }

Stage 4:证据提取 extract.py

🤖 Agent: Gemini Flash ×5 并发 📥 输入: screen_result.json(featured 论文)+ Canvas 完整节点 📤 输出: evidence_S1.json ~ evidence_S4.json

对 featured 论文精确提取 1–2 句可直接引用的事实句。 每篇提取:具体技术 → 核心量化发现 → 局限性 → 可放入 RW 的英文句子。 这三要素决定 RW 段落的论证强度。

真实输出示例

{ "citekey": "skovsgaard2011Evaluating", "section": "S1", "technique": "discrete magnification for small-target gaze selection", "key_fact": "88% accuracy vs. 28% for standard dwell", "limitation": "still single-channel: localization and commitment share the ocular channel", "rw_sentence": "For small targets, discrete magnification techniques achieve 88% accuracy compared to only 28% for standard dwell \\cite{skovsgaard2011Evaluating}." }

⚠️ 经验:如果 Canvas 节点文本太长(>8000 tokens), 直接传给 Flash 会有截断风险,建议只传"实验"和"结论"节点。

Stage 5:RW 草稿写作 write_rw.py

🤖 Agent: Gemini Flash 📥 输入: evidence_S*.json + screen_result.json + 参考段落 📤 输出: rw_draft_S1~S4.md

用 evidence 文件驱动 Gemini 写出 Sidenmark 风格的段落草稿。 每句话 = 一个具体事实 + 一个引用,不堆积。每段有论点驱动的开头句。

Prompt 策略

  • 给出 featured 论文的 rw_sentence(可直接用或改写)
  • 给出 brief 论文列表(只需 \cite{} 分组提及)
  • 指定段落论点(这段话最终要说明什么 gap)
  • 附带参考文本样本(Sidenmark 2020 的段落)

Sidenmark 风格规律

  • 每句 = 一个事实 + 一个引用
  • 段首有论点驱动的开头句
  • 每小节末尾有显式的本文连接语
  • gap 是分布式的,不全堆在最后

草稿 S1 节选(真实输出):

LaTeX
Dwell-based selection remains a primary solution, with 600ms
identified as the optimal duration to balance efficiency and
workload \cite{niu2020Improving}. For small targets, discrete
magnification achieves 88% accuracy compared to only 28% for
standard dwell \cite{skovsgaard2011Evaluating}, while the
Bubble Gaze Cursor reduces selection errors by 54.0% using
area cursors and kinematic triggering \cite{choi2020Bubble}.

Stage 6:LaTeX 整合 integrate_rw.py

🤖 Agent: Gemini Pro(必须用 Pro) 📥 输入: rw_draft + 当前 LaTeX 骨架 📤 输出: rw_integrated_21/22/23.tex

以 draft 为素材、LaTeX 骨架为框架,用 Gemini Pro 做最终 rewrite。 Flash 在整合写作质量上明显偏弱——这一步必须用 Pro。

三条关键约束

  • 保持骨架:LaTeX 段落结构和 gap statement 不可改动
  • 替换堆积:识别 \cite{A, B, C, D} 形式的句子,用具体事实句替换
  • 显式连接:每小节末尾加一句明确导向本文的过渡语

Gap Statement 分布策略

§2.1 过渡连接 — "...the design principle on which [Paper] is founded."
§2.2 定位连接 — "[Paper] exploits this property by dedicating [channel] to..."
§2.3 完整 gap + 贡献 — "Two questions remain... Our work addresses both gaps."

Stage 7:事实核查 CodeX exec

🔍 Agent: CodeX CLI 📥 输入: rw_integrated.tex + Canvas 文件 📤 输出: verification_report.md

核查 RW 中所有带数字的陈述是否与 Canvas 原始记录一致。 CodeX 擅长"读文件 + 判断"类任务,逐文件读取 Canvas 并交叉比对。 每次运行约 5–10 分钟。

bash
# CodeX 调用示例
/Applications/Codex.app/Contents/Resources/codex exec "
Task: Verify factual claims in §2.1 of rw_integrated_21.tex.
For each claim with a number, find the source Canvas file,
read it, check if the number/fact matches.
Output markdown table: claim | status (✅/⚠️/❌) | source | notes.
Canvas dir: ./canvas-out/
Output: verification_report.md
"

真实核查结果(节选):

ClaimStatusNotes
600ms optimal dwell — niu2020 Canvas 明确推荐 600ms
88% accuracy vs 28% — skovsgaard2011 数字完全匹配
5.30 wpm text entry — scottmackenzie2011 ⚠️ Canvas 记录 4.37 wpm 平均、4.58 wpm 最优,需修正
54.0% error reduction — choi2020 ⚠️ 54.0% 归因于 lens condition,非 Bubble Gaze Cursor 整体
98.6% trigger success — ya2024magilock 两个数字完全匹配
⚠️ PARTIAL 通常意味着数字来源于不同实验条件,需人工判断是否需要修改。 情况罕见,更常见是措辞有偏差。
工具链

四个核心工具

整条管道依赖的外部工具和它们的角色分工。

📚

Zotero + BetterBibTeX

文献管理 + citekey 映射。SQLite 数据库提供论文元数据,BetterBibTeX 提供稳定的 citekey。

zotero://open-pdf/library/items/{key}?page=N

🗂️

Obsidian Canvas

结构化文献笔记。每篇论文一个 .canvas 文件,包含元数据、研究背景、核心方案、实验、结论节点。标准 JSON 格式(nodes + edges)。

🤖

Gemini CLI

AI 引擎,通过 subprocess 调用,非 API。Flash 用于 Stage 1–5(速度快),Pro 用于 Stage 6(整合写作质量更高)。

gemini -m model -p "prompt"

🔍

CodeX CLI

OpenAI 的 CodeX 执行器,擅长"读文件 + 判断"类任务。用于 Stage 7 事实核查——逐文件读取 Canvas 并比对 RW 中的数字。

模型选择速查

阶段 模型 原因
Canvas 生成 / 分类 / 筛选 / 提取 / 草稿 gemini-3-flash-preview 速度快、结构化 JSON 输出稳定
LaTeX 整合 gemini-2.5-pro 整合写作质量 Flash 明显偏弱
事实核查 CodeX 默认模型 逐文件阅读 + 判断,非生成型任务
质量检验

完成度自检清单

完成后的 Related Work 应满足以下标准(点击可打勾):

复用指南

移植到你的下一篇论文

整条管道设计为可复用。以下是移植到新论文时需要调整的 6 个点。

重新分类文献

修改 classify.py 中的 RW_SECTIONS 定义,按新论文的 RW 小节重新定义 S1–S4(或更多)。 重跑 classify.py 生成新的 classify_result.json

定制筛选标准

修改 screen.py 中的 SECTION_CRITERIA, 根据新论文的核心论点定制每个小节的筛选问题(什么信息值得"展开讲")。

定制提取问题

修改 extract.py 中每个 section 的提取问题。 围绕新论文"要说明什么 gap"来设计,每小节 3 个问题。

替换 LaTeX 骨架

integrate_rw.py 中替换为新论文当前的 LaTeX RW 文本。 保留段落结构和 gap statement 的位置。

更新连接语指令

修改 integrate_rw.py 中的连接语指令—— 改为新论文的名字和贡献方向,使每小节末尾正确导向本文。

更新硬编码路径

修改每个脚本顶部的配置块:ZOTERO_DBZOTERO_STORAGECOLLECTION_NAMEOUTPUT_DIRCITEKEY_MAP

文件结构一览

project structure
zotero-canvas-batch/
├── batch.py                       # 核心:PDF → Canvas(公开版主入口)
├── rename_patch.py                # Canvas 重命名 + 补丁 citekey
├── SKILL.md                       # Agent / Claude Code 调用接口契约
├── README.md                      # 项目说明
├── .env.example                   # 环境变量模板
└── examples/
    └── rw-pipeline/               # 参考实现(非通用,作模板)
        ├── classify.py            # Stage 2: 文献分类
        ├── screen.py              # Stage 3: featured / brief 筛选
        ├── extract.py             # Stage 4: 证据提取
        ├── write_rw.py            # Stage 5: RW 段落草稿
        └── README.md              # 如何把这套模板替换成你自己的 RW 大纲

说明:教程页中演示的 Stage 6 (integrate_rw.py,LaTeX 整合) 和 Stage 7(CodeX 事实核查)是 Gaze2Foot 投稿实践中的额外环节, 因为高度论文专属,公开仓不再随附;可按 §复用 部分自行重建。