opendraft 是做什么的?
将一行主题转化为一篇带有真实文献基础的完整论文。
移植自 OpenDraft(github.com/federicodeponte/opendraft,MIT 许可证),原项目作为托管引擎运行。在此处,引擎就是你自己:十八个阶段,每个阶段对应 agents/ 中的一个提示文件,外加五个处理语言模型无法手动完成任务的脚本。
你就是模型。 无需密钥、无需服务、无需账号。唯一的网络调用是 Crossref、OpenAlex 和 DataCite,均为开放端点。
一条命令
写一篇关于<主题>的论文
以下所有内容都源自这句话。不要要求用户自行运行各个阶段;由你来运行它们。
先问四个问题
在阶段5之前,如果用户尚未说明,请询问:
- 目标期刊,或“无,通用学术”。
- 总字数限制。
- 引文风格,从
references/citation-styles.md中的六种风格中选择一种。 - 文档类型,从
references/paper-types.md中的类型中选择一种。
这四个问题是放在同一条消息中提出的,而不是一场问答马拉松,并且这是流水线唯一会提出的问题。其他所有事情都由它自行决定。
之所以要问,是因为猜测的代价最终会显现,且无法在后期弥补。一个对摘要严格限制150字的期刊,必须在阶段6或更早时强制执行:等到阶段17撰写摘要时,每个章节起草时所依据的字数预算已经不正确了。一个在关口而非阶段6选择的引文风格,意味着整篇草稿是在错误的标记密度下写成的。
如果用户拒绝回答,或说“你选”,则按照 references/paper-types.md 中的默认值进行,将它们写入阶段6的期刊格式块,并用一行说明你使用了哪些默认值。永远不要基于未向用户展示的未明示假设继续。
脚本独掌的四件事,与你无关
语言模型擅长判断但拙于记账。这种分工是有意为之,越界正是导致输出出问题的方式。每一条都由一个脚本强制实施,该脚本在失败时返回非零退出码,因此在关口不存在争议。
永远不要手动编写渲染后的引文标记。 不是 [3],也不是 (Smith, 2020)。在起草过程中,你应在主张的准确位置内联写入 {cite_<doi>}。scripts/citations.py compile 会将这些标记转换为普通标记,并通过字典查找构建参考文献表。这一过程是确定性的,因此散文与参考文献之间的映射是机械的而非记忆性的。
永远不要为你无法支持的声明编造来源。 如果一个声明是真实的,但你找不到支持它的来源,请写入 {cite_MISSING: 对声明的简短描述} 并继续。compile 拒绝渲染它、命名它,并以非零退出码退出,而 integrity.py 将其视为严重错误,因此它无法存留到完成版论文中。这正是要点:它不是一种发布未支持声明的方法,而是一种在起草过程中保持诚实的方式,而不是悄悄删除该声明或将其附加到你编造的 DOI 上。在关口前,通过使用 sources.py find 找到真实来源,或有意删除该声明,来清除每一条 MISSING。
永远不要手动将章节合并到草稿中。 scripts/assemble.py 会按数字顺序执行,并拒绝将任何非编号章节拼接到论文中。手动合并导致漏掉一个章节,是直到读者发现之前都无人注意的失败。
永远不要凭肉眼判断论文内部是否一致。 scripts/integrity.py 会检查并返回非零退出码。运行它并读取退出码。
设置
mkdir -p research sections review
无 API 密钥、无账号、无安装步骤:这些脚本仅依赖 Python 标准库。存在一个可选的环境变量,也是此处唯一读取的环境变量。将 OPENDRAFT_CONTACT_EMAIL 设置为你自己的邮箱地址,sources.py 会将其附加到 User-Agent 中,从而使 Crossref 请求进入礼貌池并获得更好的速率限制。不设置它,每个阶段仍能运行;没有内置的邮箱地址,因为发货的默认值会将所有安装者合并为一个身份,并将他们的速率限制问题路由到陌生人的收件箱。
流水线写入固定路径,每个阶段读取之前阶段写入的内容。这些路径是阶段之间的契约:
research/sources.md research/sources.json research/summaries.md
research/gaps.md research/citations.json research/citation-notes.md
outline.md outline_formatted.md
sections/*.md full_draft.md final.md
review/thread.md review/narrator.md review/skeptic.md
review/verifier.md review/referee.md review/voice.md
review/entropy.md review/polish.md
关于这些路径的四条规则,每一条都曾被违反过:
sections/仅存放论文章节,别无其他。 其中的每个文件都会被scripts/assemble.py拼接到完成版论文中。一个存放在那里的阶段报告最终会出现在某人的论文里。报告应放在review/中。research/sources.json是机器输入,research/sources.md是人类阅读。 阶段1同时写入两者。citations.py build读取 JSON 文件。- 任何手动编写的内容都不应放入
research/citations.json。 它由citations.py build构建,并且只能通过重新运行build来更改。那些脚本无法做出的判断决策(无 DOI 的来源、需要复核的条目、无法转换为占位符的提及)应放入research/citation-notes.md,该文件是散文形式,且永远不会被脚本读取。手动编辑的数据库是verified标记被翻转以通过关口的方式,这也是一个未解析的 DOI 到达看似已检查的参考文献表的方式。 - 在阶段9.5之后,
full_draft.md是唯一的草稿。 下游的任何内容都不会再次读取sections/*.md,因此在组装后编辑章节文件将不会产生任何效果,并且会悄悄丢失该编辑。
流水线
按顺序运行。除一个阶段外,每个阶段都是 agents/ 中的一个文件:读取该文件,按它所说的做,写入它命名的输出,然后继续。例外是阶段9.5,它是一个你需要运行的脚本。不要因为主题看起来容易就跳过某个阶段。
| # | 阶段 | 智能体文件或脚本 | 写入内容 |
|---|---|---|---|
| 1 | 查找来源 | agents/01-scout.md | research/sources.md, research/sources.json |
| 2 | 阅读并总结它们 | agents/02-scribe.md | research/summaries.md |
| 3 | 找到值得撰写的空白领域 | agents/03-signal.md | research/gaps.md |
| 4 | 构建引文数据库 | agents/04-citation-manager.md | research/citations.json, research/citation-notes.md |
| 5 | 勾勒论证大纲 | agents/05-architect.md | outline.md |
| 6 | 应用期刊格式和字数预算 | agents/06-formatter.md | outline_formatted.md |
| 7 | 撰写每个章节 | agents/07-crafter.md | sections/*.md, 追加到 research/gaps.md |
| 8 | 检查跨章节一致性 | agents/08-thread.md | 在 sections/*.md 中的修复, review/thread.md |
| 9 | 统一语气 | agents/09-narrator.md | 在 sections/*.md 中的修复, review/narrator.md |
| 9.5 | 将章节组装成一个草稿 | scripts/assemble.py | full_draft.md |
| 10 | 攻击论证 | agents/10-skeptic.md | review/skeptic.md, 然后修复 |
| 11 | 对照来源检查声明 | agents/11-verifier.md | review/verifier.md, 然后修复 |
| 12 | 模拟同行评审 | agents/12-referee.md | review/referee.md, 然后修复 |
| 13 | 匹配作者语气(可选) | agents/13-voice.md | 在 full_draft.md 中的修复, review/voice.md |
| 14 | 变化散文节奏 | agents/14-entropy.md | 在 full_draft.md 中的修复, review/entropy.md |
| 15 | 语法和最终润色 | agents/15-polish.md | full_draft.md, review/polish.md |
| 16 | 添加附录(可选) | agents/16-enhancer.md | full_draft.md |
| 17 | 撰写摘要 | agents/17-abstract.md | 前置到 full_draft.md |
| 18 | 撰写标题 | agents/18-titlemaker.md | 前置到 full_draft.md |
阶段7每个章节运行一次,而非每篇论文一次。阶段10至12生成问题列表;一个无人应用的问题列表是空操作,因此请应用修复并重新运行该阶段,直到没有严重问题残留。
阶段12有一个数字标准,而非感觉。当一次新的运行报告零个严重问题,且整体平均分至少达到 3.0 分(满分 5 分),且没有一个维度低于 3 分(除非你在 review/referee.md 中记录了原因)时停止。“足够好可以发送”不是停止条件,因为要求模型评判自己的草稿时,它总是在第三次迭代时认为足够好。
阶段13是唯一一个输入非流水线生成的阶段。它将草稿与作者自己的散文进行匹配,并从工作目录中的 samples/ 目录读取这些散文:两到三篇先前的论文、章节或长文帖子,格式为 .md 或 .txt。没有内容创建该目录,也没有内容要求你提供它,因此从未创建该目录的运行会干净地跳过阶段13,这是正常结果而非失败。如果你希望论文听起来像你写的,请在运行前将你自己的文字放入该目录。
阶段9.5之所以编号为半步,是因为它是一个脚本而非智能体提示,并且十八个智能体阶段保留了它们已有的编号。它不是可选的。阶段1至9对 sections/*.md 进行操作;阶段10至18对 full_draft.md 进行操作;除了以下命令外,没有其他内容生成该文件:
python3 scripts/assemble.py sections -o full_draft.md
它按数字顺序合并章节文件,拒绝包含任何非编号章节的文件,并在编号缺失、重复或目录为空时返回非零退出码。--check 报告它将执行的操作而不实际写入。
它还会跳过其文件名包含 report、review、notes、checklist 或 log 的任何编号文件,这是阻止过时的阶段报告最终出现在论文中的机制。这是一个文件名启发式规则,因此它有一个锋利的边缘:一个名为“文献综述(Review of the literature)”的真实章节会被跳过,你只会看到它在 stdout 上显示为“已跳过(Skipped)”行,而不是错误。将该章节命名为 related-work 或 literature 代替。阅读跳过行;一个在此处消失的章节会悄然消失。
在 full_draft.md 已存在后重新运行它需要 --force,而 --force 会丢弃阶段10及以后对草稿所做的所有编辑,因此仅在你打算从章节重新开始时才重新组装。
规模
将流水线与所要求的内容相匹配。阶段是相同的;深度不同。
- 短篇,1,500 至 3,000 字。 阶段1至7,然后9.5、10、11、15。十到十五个来源。
- 完整论文,默认。 全部十八个阶段,外加9.5。二十五到五十个来源,或者当论文是文献综述时五十个或更多,其自身的最低标准在更高时优先(
references/paper-types.md)。 - 论文章节或长篇综述。 全部十八个阶段,外加9.5,来源在五十个或更多,并且阶段7每个子章节运行一次,而非每个章节。
阶段9.5出现在上述每一个列表里。没有任何规模下论文会自行组装。
两个参考文件
references/paper-types.md包含每种文档类型的章节骨架、字数预算和来源数量。在阶段5读取它。references/citation-styles.md展示所有六种风格的实际编译输出,包括文内标记和参考文献条目,以便你为期刊选择一种并知道它看起来什么样。在阶段6读取它,并在引文渲染结果出乎意料时在关口前再次读取。它还说明了编译器不包含的内容:无卷号、期号或页码,无期刊缩写。如果导师要求这些内容,此处是诚实说明的地方,而不是你事后才发现的地方。
这两个文件都不是伪装成参考的可选阅读材料。一个未读取第二个文件而选择的风格是从记忆中选出的风格,编译器并不实现你记忆中的 APA。
脚本
python3 scripts/sources.py find "<query>" --n 15 # Crossref 加 OpenAlex
python3 scripts/sources.py find "<query>" --json # 机器可读
python3 scripts/sources.py verify <doi> <doi> ... # Crossref 加 DataCite
python3 scripts/citations.py build research/sources.json -o research/citations.json
python3 scripts/citations.py verify -d research/citations.json
python3 scripts/citations.py compile full_draft.md -d research/citations.json --style apa -o final.md
python3 scripts/citations.py bibtex -d research/citations.json -o refs.bib
python3 scripts/assemble.py sections -o full_draft.md
python3 scripts/assemble.py sections -o full_draft.md --check
python3 scripts/integrity.py final.md -d research/citations.json --target 8000
python3 scripts/integrity.py final.md -c research/summaries.md # 建议性数字检查
python3 scripts/export.py final.md --format docx -o final.docx
每个脚本在失败时都返回非零退出码。该退出码就是信号;请读取它,而不是浏览输出。
scripts/sources.py find 在完全无法访问任何 API 时返回1,这与成功访问它们但未获得任何结果不同。agents/01-scout.md 是权威来源,说明如何读取该退出码以及阶段1在来源数量低于什么下限时停止而不是继续;请在那里阅读,而不是相信空结果。
scripts/citations.py build 接受阶段1写入 research/sources.json 的 JSON 数组,而不是 research/sources.md 中的 markdown。将 markdown 文件喂给它是一个错误,它会指出这一点。
如果第一轮搜索结果稀疏,也请搜索两到三个更窄的子查询。一篇只有五个来源的论文读起来就像只有五个来源。
验证,及其证明和不证明的内容
scripts/citations.py verify 将每个 DOI 置于四种状态之一,unknown 永远不会被悄悄变成 absent:
- resolved(已解析)。 Crossref 或 DataCite 返回了记录。引用它。
- absent(不存在)。 两者都返回了404。放弃它。不要修复它,不要猜测替
…
[截断以进行翻译]