关于 paper-graph
paper-graph 是用于追踪某个主题或论文研究脉络的技能,适合文献梳理与思想溯源。它生成包含 Mermaid 图的 Markdown 报告,连接挑战、解决方案及论文演进路径;LLM 调用由宿主 Agent 提供。
主要功能
- 01以主题或种子论文作为研究查询
- 02通过 S2 和 DeepXiv 获取论文
- 03挑战到解决方案的分类图
- 04按解决方案展示论文演进图
- 05包含 Mermaid 图的 Markdown 报告
适用场景
- 01追踪某主题的研究发展
- 02从论文梳理后续工作与思想脉络
- 03按挑战和解决方案整理文献
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ evoscientist/ evoskills --skill paper-graph安装后在 Claude Code 中输入/paper-graph确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
requirements.txtreferences/audit_edge.mdclassify.mddetail.mdoutline.mdparse_query.mdseed_paper_block.mdscripts/audit.pycli.pyconfig.pydeepxiv_client.pylogger.pymermaid.pypaper_md.pypipeline.pyprompts.pyweb_api.py详细介绍
论文脉络图
构建一份 Markdown 报告,其中嵌入 Mermaid 图,展示围绕用户指定主题(或论文)的研究如何演进——将研究归类为“挑战 → 解决方案”,并追踪每种解决方案的演进路径。
此技能不依赖外部大模型。宿主 Agent 提供所有大模型调用;此技能提供确定性数据抓取器(S2 / DeepXiv)、提示词模板、Markdown 解析器和 Mermaid 渲染器。请按以下运行手册逐步执行。
执行要求。 如果调用方已提供 parsed_query.json、seed.json 和 papers.json(无论这些文件位于何处,例如 input/ 目录中),请将它们复制到工作目录,并保留这些文件名;将步骤 1–5 视为已完成,从步骤 6 开始执行。仍须运行从 classify 到 assemble 的每个确定性 CLI 步骤;不得用手工编写的 Mermaid 替代解析、边审计、渲染或组装。完成前,请确认每个解析出的解决方案都有详细渲染结果和审计判定文件,即使该文件是空 JSON 列表也不例外。将 assemble_report 视为唯一的最终报告写入器:之后不得手动编辑或追加边,即使调用方要求展示不确定的关系也不例外。请在判定 JSON 中保留不确定性。
何时使用此技能
在用户提出以下类型的请求时触发:
- “展示
<topic>的发展历史”/“<topic>是如何演进的?” - “
<paper>源自哪里?”/“<paper>借鉴了哪些研究?” - “
<paper>有哪些重要改进/后续研究?” - “追踪
<field>中的思想脉络”/“给我一份<field>的文献分类” - “
<paper>的引用树”/“追踪<topic>的思想脉络”
以下情况跳过:
- 用户只想了解一篇论文的摘要或单条搜索结果(没有关系/演进方面的需求)。
- 请求涉及非学术引用工作。
- 用户明确要求提供纯书目清单,而非图谱。
输入和输出
输入:
- 研究查询:主题、种子论文标题/引文,或两者结合。可使用自由格式文本。
- 输出路径(必需):最终 Markdown 报告的路径。如果未提供,请在运行前询问。
- (可选) 要抓取的论文数量(
fetch_papers的--n标志)。默认值为 10。 - (可选) Mermaid 主题
light或dark(渲染步骤中的--theme,或MERMAID_THEME环境变量)。默认值为 light。
输出: 在用户指定路径生成一个 Markdown 文件,包含以下部分:
- 研究目标(从查询中提取)
- 高层分类——一张 Mermaid 图:根节点 → 挑战 → 解决方案 → 论文引用
- 各解决方案的演进路径——每种解决方案一张 Mermaid 图,展示论文之间“演进自”的边、演进节点和未解决的挑战
- 论文附录——论文编号列表,包含标题/年份/作者/摘要/结论摘录
Mermaid 只是 ```mermaid 代码围栏中的文本——该文件可直接在 GitHub、Obsidian、VS Code(安装 Mermaid 扩展)等平台上渲染。
设置
必需的环境变量(缺失时技能会详细报错):
S2_API_KEY— Semantic Scholar API 密钥。
可选环境变量:
DEEPXIV_API_TOKEN(或DEEPXIV_TOKEN)— DeepXiv arXiv 搜索回退方案。使用pip install deepxiv-sdk安装;使用deepxiv token自动配置。若没有此令牌,S2 必须独自满足引文编号预算。MERMAID_THEME—light(默认)或dark。每次渲染调用时可通过--theme覆盖。
**LLM:**宿主 Agent 使用自己的模型和 API 密钥。此技能会生成提示词模板并解析响应;不会进行身份验证,也不会调用任何 LLM 提供商。
工作目录:每次运行创建一个目录,以当前工作目录为基准使用相对路径(例如 ./<basename>.work/)。避免使用 /tmp/... 这类绝对系统路径——有些运行环境会将 shell 和文件读取工具沙箱隔离到不同的文件系统根目录,因此某个绝对路径对一方可能可写,对另一方却不可见。相对当前工作目录的路径在任何环境中都适用。不确定时,请在开始时运行一次 pwd。
建议的目录布局(假设最终报告输出到 <output>):
<output>.work/
├── query.txt user query (verbatim)
├── seed.json resolve_seed_papers
├── seed_block.txt format_seed_block
├── parsed_query.json LLM: parse_query output
├── goal_block.txt build_goal_block (reused in steps 8, 10, 11)
├── papers.json fetch_papers → prefetch_sections → classify-merged
├── papers_input.txt format_papers (full set)
├── classify_raw.json LLM: classify output (consumed by merge_classifications)
├── core_filter.json compute_core_filter (+ core_filter.json.allowed.txt)
├── papers_input_core.txt format_papers (CORE-only; + papers_input_core.txt.allowed.txt)
├── outline_raw.md LLM: outline output
├── outline.json parse_outline summary
├── outline_mermaid.json render_outline_mermaid
├── solutions/<key>.json per-solution context (one file per solution)
├── parsed/<key>.json parse_detail output (used by step 11 audit)
├── details/
│ ├── <key>_input.txt format_papers (optional: same content as papers_input_core.txt; + .allowed.txt sibling)
│ ├── <key>_raw.md LLM: detail output
│ └── <key>.json render_detail_mermaid (consumed by assemble)
├── verdicts/<key>.json [{source_n, target_n, verdict, source_quote, target_quote, reason}] from audit ([] when no edges)
└── <run>.log.jsonl files one next to each subcommand output, default-on
文件名由 Agent 自行选择——这些建议旨在与下方运行手册中的引用保持一致。<key> 是解题键字符串 <s_major>.<s_minor>(例如 1.1)。每次调用 format_papers 都会写入同级文件 <out>.allowed.txt,其中包含可直接填入 {allowed_numbers} 占位符的 (N), (N), ... 格式。
运行手册
每个步骤都是一次 CLI 调用(python scripts/cli.py <subcmd> …)或一次 LLM 调用(宿主 Agent 从 references/ 读取模板,替换占位符并调用模型)。所有脚本和参考文件路径均相对于此技能目录。CLI 依赖项:pip install deepxiv-sdk httpx python-dotenv(也列在技能根目录的 requirements.txt 中)——请安装到用户当前使用的环境中。按顺序运行;如果宿主支持并发工具调用,详情步骤(步骤 10)和审核步骤(步骤 11)可以按解答/边分别并行执行。
**references/*.md 中的占位符语法。**单大括号 {name} 是宿主必须替换的模板插槽。双大括号 {{...}} 是转义后的字面大括号——当示例 JSON 出现在提示词正文中,且 LLM 被要求输出该 JSON 时,就会用到这种写法。只替换单大括号插槽;保留 {{ 和 }} 不变(它们会让 LLM 在输出中将其读作 { 和 })。
步骤 1 — 保存用户查询
将用户的原始查询写入 <workdir>/query.txt。不要改述。可以使用多行查询。
步骤 2 — resolve_seed_papers(CLI,确定性)
python scripts/cli.py resolve_seed_papers \
--query-file <workdir>/query.txt \
--out <workdir>/seed.json
输出一个 S2 格式的论文记录 JSON 数组,其中包含查询中检测到的 arxiv ID 对应记录。若没有检测到 ID,或所有查询均失败,则输出空数组。
步骤 3 — format_seed_block(CLI,确定性)
python scripts/cli.py format_seed_block \
--seed <workdir>/seed.json \
--out <workdir>/seed_block.txt
将种子论文渲染成步骤 4 使用的 {seed_block} 提示词片段。输入为空时,输出也为空(占位符会被干净地移除)。
步骤 4 — parse_query(LLM)
读取 references/parse_query.md。将 {seed_block} 替换为 <workdir>/seed_block.txt 的内容,将 {query} 替换为 <workdir>/query.txt 的内容。调用 LLM(低温度,约为 0.1)。
按以下确切结构将响应解析为 JSON:
{"goal": "<one sentence>",
"searches": ["<keyword phrase 1>", "<keyword phrase 2>", "..."],
"definitions": {"<term>": "<one-sentence definition>", "...": "..."}}
根据提示词本身的说明,searches 应包含 2–4 个短语,每个短语含 2–5 个关键词(这是两个不同的计数:短语数量与每个短语的关键词数量)。
如果模型添加了代码围栏,请将其移除。验证 goal 非空,且 searches 是非空字符串列表。如果响应格式错误,再次提示 LLM 一次;若第二次仍失败,则中止并给出清晰的错误消息。
将验证通过的 JSON 保存到 <workdir>/parsed_query.json。
步骤 5 — fetch_papers(命令行,确定性)
python scripts/cli.py fetch_papers \
--parsed-query <workdir>/parsed_query.json \
--seed <workdir>/seed.json \
--n 10 \
--out <workdir>/papers.json
使用 S2 多重搜索(每个 searches[] 条目对应一个 S2 查询),并在必要时通过 DeepXiv 补足至多 --n 篇论文。输出为 S2 格式的论文字典 JSON 数组。如果 S2 未返回任何结果且 DeepXiv 不可用,则以非零状态码退出——返回步骤 4,使用更精准的搜索短语重新提示。
步骤 6 — classify(大模型)
首先构建完整的 {papers_input} 块:
python scripts/cli.py format_papers \
--papers <workdir>/papers.json \
--out <workdir>/papers_input.txt
阅读 references/classify.md。将 {goal} 替换为 goal_sentence(parsed_query.json["goal"] 中的单句字符串——此处不包含定义块),将 {papers_input} 替换为上方的文本文件。调用大模型(较低温度,无需推理——这是一个离散的编目决策)。如果模型添加了任何首尾代码围栏,将其去除。
将原始响应逐字保存到 <workdir>/classify_raw.json。预期格式:
{"classifications": [
{"n": 1, "label": "CORE", "reason": "..."},
{"n": 2, "label": "ADJACENT", "reason": "..."},
{"n": 3, "label": "REJECT", "reason": "..."},
...
]}
然后通过命令行工具合并到 papers.json(验证格式,并在验证失败时对所有论文应用失败软回退):
python scripts/cli.py merge_classifications \
--classifications <workdir>/classify_raw.json \
--papers <workdir>/papers.json \
--out <workdir>/papers.json
merge_classifications 始终以 0 退出;验证失败时会应用全 CORE 安全兜底,并在 stdout 成功行末尾打印 … (FALLBACK: <reason>) 后缀。看到该后缀时,再提示大模型一次;如果第二次尝试也触发回退,则接受全 CORE 结果并继续——每篇论文都会作为 CORE 进入大纲。正常运行时,成功行会显示各标签的数量,且没有 FALLBACK 后缀。下游子命令(parse_outline、渲染器、assemble_report)通过 _label_of 读取这些标签。
步骤 7 — prefetch_sections(命令行,确定性,尽力而为)
python scripts/cli.py prefetch_sections \
--in <workdir>/papers.json \
--out <workdir>/papers.json
通过原地修改每个论文字典来设置 _conclusion_section。为节省配额,会跳过标记为 REJECT 的论文。失败时不输出提示;没有 arxiv ID 或无法获取相应章节的论文,其 _conclusion_section 会设为 null。
执行此步骤后,后续任何 format_papers 调用都会自动将每篇论文的 _conclusion_section 嵌入格式化内容块中(标题为 Discussion/Conclusion excerpt:)——这就是大纲和细节提示词获取提示词模板所引用的 OC 来源信号的方式。无需额外步骤。
步骤 8 — outline(大模型)
将从此处开始反复使用的两个提示词片段——goal_block({goal} 替换内容)和 core_filter(仅 CORE 筛选条件及其 {allowed_numbers} 辅助文件)——写入磁盘。步骤 10 和 11 会读取这些文件,因此 Agent 无需将它们保留在对话状态中。
python scripts/cli.py build_goal_block \
--parsed-query <workdir>/parsed_query.json \
--out <workdir>/goal_block.txt
python scripts/cli.py compute_core_filter \
--papers <workdir>/papers.json \
--out <workdir>/core_filter.json
compute_core_filter 会将索引 JSON 写入 --out,并将对应的 (N), (N), ... {allowed_numbers} 格式写入同目录下的 <out>.allowed.txt。与此技能中的其他地方一样,没有分类结果的论文按 CORE 处理。如果分类器结果中没有 CORE,则回退到所有论文并打印 (FALLBACK: …)——流程会继续。
构建仅包含 CORE 的 {papers_input}(也会生成同目录下的 .allowed.txt,此处虽然多余,但与步骤 10 保持一致):
python scripts/cli.py format_papers \
--papers <workdir>/papers.json \
--filter <workdir>/core_filter.json \
--out <workdir>/papers_input_core.txt
阅读 references/outline.md。替换:
{goal}—<workdir>/goal_block.txt的内容。{papers_input}—<workdir>/papers_input_core.txt的内容。{allowed_numbers}—<workdir>/core_filter.json.allowed.txt的内容。
调用大模型(较低温度,约 0.2;允许最多约 8000 个 token)。去除响应首尾的代码围栏——提示词要求模型输出原始 Markdown,但有时会意外带上围栏。将清理后的 Markdown 保存到 <workdir>/outline_raw.md。
第 9 步 — parse_outline(CLI,确定性)
python scripts/cli.py parse_outline \
--raw <workdir>/outline_raw.md \
--papers <workdir>/papers.json \
--out <workdir>/outline.json \
--solutions-dir <workdir>/solutions
写入大纲摘要,并为每个解决方案写入一个 solutions/<key>.json 上下文文件。每个上下文文件的架构(在第 10 步和第 12 步中使用):
{
"challenge_idx": 1,
"challenge_name": "Quality of random-projection targets",
"solution_key": [1, 1],
"solution_key_str": "1.1",
"solution_name": "Optimization of the BEST-RQ pre-training objective",
"paper_nums": [1, 3],
"allowed": [1, 2, 3, 4]
}
paper_nums 是有效且去重后的主要分类归属;如果大纲重复列出某篇论文,则以首次归类为准。allowed 包含所有 CORE 论文,以便详细内容生成时可以找到规范的前置或后续论文,而无需重复其主要分类归属。format_papers --filter <solutions/key.json> 和 parse_detail --context <solutions/key.json> 都会自动读取 allowed。仅在 {primary_numbers} 提示词字段中使用 paper_nums。
如果大纲再次列出某个解决方案标题,就会重新打开同一个解决方案。如果某个解决方案没有任何有效论文(其编号均为虚构或已在其他位置归类),则会将其移除:不会为它生成上下文文件,也不会在分类体系中创建节点,stdout 上的成功信息会列出它。因此,每个上下文文件的 paper_nums 都非空。
如果大纲中没有可解析的 ## Challenge N: 标题,或者没有任何解决方案留下有效论文,则以代码 4 退出并在 stderr 输出诊断信息。重新提示第 8 步一次;如果第二次仍失败,则在第 5 步降低 --n,或优化查询。
步骤 10 — detail(大模型,每个解决方案)
关键 — 在将此步骤委派给子 Agent 之前,请先阅读下方的“扇出任务说明”。 如果子 Agent 提示词指向 SKILL.md,或要求“针对解决方案 X 运行 paper-graph”,它就会在单独的工作目录中从步骤 1 重新开始整个工作流,其输出对父级运行将毫无用处。
在此步骤开始时读取一次 references/detail.md;由编排器按解决方案分别替换内容。扇出的子 Agent 绝不能自行读取模板。
对于步骤 9 生成的每个 <workdir>/solutions/<key>.json:
(a) 每个解决方案的 {papers_input} 都是 CORE 池:每个解决方案上下文的 allowed 都是完整的 CORE 集合,因此每个解决方案的区块都相同,且与步骤 8 的 <workdir>/papers_input_core.txt 完全一致(及其 .allowed.txt 伴随文件)。复用这两个文件即可。按每个解决方案运行 format_papers --filter <workdir>/solutions/<key>.json --out <workdir>/details/<key>_input.txt 仍然有效,并会写入相同内容;只有在步骤 8 生成的文件已丢失时才需要这样做。
(b) 将以下内容代入 references/detail.md:
{goal}—<workdir>/goal_block.txt的内容(步骤 8 中构建的文件)。{challenge_name}— 来自解决方案上下文(challenge_name)。{solution_name}— 来自解决方案上下文(solution_name)。{primary_numbers}— 解决方案上下文中paper_nums的(N)值,以逗号分隔。{papers_input}—<workdir>/papers_input_core.txt的内容(或按解决方案生成的副本)。{allowed_numbers}—<workdir>/papers_input_core.txt.allowed.txt的内容。
调用大模型(温度约为 0.2;最多允许约 12000 个 token,以容纳思维草稿和树)。将原始响应保存到 <workdir>/details/<key>_raw.md。
(c) 解析它(输出放入同级的 parsed/ 目录,而不是 details/,以免步骤 13 的 assemble_report 将解析输出误当作渲染输出):
python scripts/cli.py parse_detail \
--raw <workdir>/details/<key>_raw.md \
--context <workdir>/solutions/<key>.json \
--out <workdir>/parsed/<key>.json
解析后的 JSON 中的 edges 列表是步骤 11 的输入。
如果宿主支持并发工具调用,则并行扇出所有按解决方案发起的大模型调用及其后续 parse_detail 调用。
扇出任务说明(将步骤 10b 按解决方案委派给子 Agent 时)。 子 Agent 的提示词必须自包含:不要让子 Agent 查看 SKILL.md,不要指示它“针对解决方案 X 运行 paper-graph”,也不要提供任何 CLI 调用。编排器自行完成替换,并仅向子 Agent 提供:
- 完全替换后的提示词字符串(已填入
{goal}、{challenge_name}、{solution_name}、{primary_numbers}、{papers_input}、{allowed_numbers})。 - 预期的响应格式:符合
references/detail.md输出规范的原始 Markdown 演化树。 - 一条明确指令:“使用下方提示词调用大模型,并且只返回原始 Markdown 响应。不要读取其他文件,不要运行任何 shell 命令,也不要调用任何其他技能。”
不要让子 Agent 查看 SKILL.md,不要指示它“针对解决方案 X 运行 paper-graph”,也不要提供任何 CLI 调用。子 Agent 返回 Markdown 文本;编排器将其写入 <workdir>/details/<key>_raw.md,并自行运行 parse_detail。如果子 Agent 重新读取 SKILL.md,就会从步骤 1 重新开始整个工作流——解决方法是按上述方式限定子 Agent 的提示词范围。
步骤 11 — audit_edge(LLM,每条边)
关键 — 在将此步骤委派给子 Agent 之前,请先阅读下方的“扇出任务说明”。 这与步骤 10 的编排失败模式相同:如果指向 SKILL.md,子 Agent 就会重新启动整个工作流。
在此步骤开始时只读取一次 references/audit_edge.md;由编排器逐条边替换占位符。扇出调用的子 Agent 绝不自行读取模板。
对于每个 <workdir>/parsed/<key>.json 的 edges 列表,根据源论文和目标论文的摘要及结论摘录审查每条边。
对于解决方案中的每条 {source_n, target_n, gap} 边:
- 查找源论文 =
<workdir>/papers.json中的papers[source_n - 1],目标论文 =papers[target_n - 1]。 - 替换
references/audit_edge.md中的占位符:{m_n}、{m_title}、{m_year}、{m_abstract}(截断至 1500 个字符)、{m_excerpt}(_conclusion_section或(no excerpt),截断至 1500 个字符)、{n_n}、{n_title}、{n_year}、{n_abstract}、{n_excerpt}、{gap_text}。 - 调用 LLM(低温度,约 0.1;关闭推理;最大约 1000 个 token——足以容纳两条最长 300 个字符的引文及理由)。解析响应:
{"verdict": "SUPPORTED_BY_ABSTRACT" | "SUPPORTED_BY_SECTION" | "INFERRED" | "REJECT",
"source_quote": "<verbatim source evidence or NONE>",
"target_quote": "<verbatim target evidence or NONE>",
"reason": "<one sentence>"}
解析失败时,默认判定为 REJECT。只有 SUPPORTED_BY_ABSTRACT 和 SUPPORTED_BY_SECTION 才会成为有向演化边。INFERRED 会记录可能存在的关系以供审计追踪,但不会呈现为有向谱系关系。
扇出任务说明(将逐条边或逐个解决方案委派给子 Agent 时)。 遵循与步骤 10 相同的规范:不要让子 Agent 查看 SKILL.md,不要指示其“运行 paper-graph audit”,也不要提供任何 CLI 调用。编排器自行替换占位符,并且只向子 Agent 提供:
- 完整替换后的提示词字符串(其中
{m_n}、{m_title}、{m_year}、{m_abstract}、{m_excerpt}、{n_n}、{n_title}、{n_year}、{n_abstract}、{n_excerpt}、{gap_text}均已填入)。 - 预期的响应格式:包含
verdict、source_quote、target_quote和reason的 JSON 对象。 - 明确指令:“使用下方提示词调用你的 LLM,并且只返回 JSON 判定对象。不要读取任何其他文件,不要运行任何 shell 命令,也不要调用任何其他技能。”
子 Agent 返回判定 JSON;编排器将每个解决方案的列表汇总到 <workdir>/verdicts/<key>.json。如果提供给子 Agent 的提示词引用了 SKILL.md,它就会从步骤 1 重新启动整个工作流——请确保任务说明范围明确。
将每个解决方案的所有判定汇总到 <workdir>/verdicts/<key>.json,保存为一个扁平列表:
[{"source_n": 1, "target_n": 3, "verdict": "SUPPORTED_BY_ABSTRACT",
"source_quote": "<verbatim source evidence>",
"target_quote": "<verbatim target evidence>", "reason": "..."}, ...]
每条已审查的边写入一条记录;即使解决方案没有边,也要写入文件([]):步骤 12 需要该文件。逐字复制审查返回的每条引文;不要手动修正或缩短引文。
步骤 12 的渲染器会针对 papers.json 重新检查每条记录,并且仅当以下所有条件均满足时才绘制边;其他情况均不会呈现为有向谱系关系:
- 判定为
SUPPORTED_BY_ABSTRACT或SUPPORTED_BY_SECTION; - 每条引文至少 20 个字符,不是
NONE,且出现在对应论文的标题、摘要或摘录其中之一内部——必须完整出现在其中一个文本块内,不能跨越两个文本块(匹配前会规范化换行符、重复空格及排版引号或破折号;措辞和大小写不会规范化); - 源论文不晚于目标论文(缺少年份时跳过检查并注明);
- 源论文和目标论文是
papers.json中不同的论文; - 如果多条记录指向同一条边,则每条记录都必须通过检查;
- 该边不属于受支持边构成的环(年份相同或缺失时可能出现)。
格式错误的记录(不是对象,或缺少整数类型的 source_n / target_n)会被忽略并报告;不会因此停止渲染。
步骤 12 — render_outline_mermaid + render_detail_mermaid(CLI,确定性)
python scripts/cli.py render_outline_mermaid \
--raw <workdir>/outline_raw.md \
--papers <workdir>/papers.json \
--out <workdir>/outline_mermaid.json \
[--theme dark]
对于每个解决方案:
python scripts/cli.py render_detail_mermaid \
--raw <workdir>/details/<key>_raw.md \
--context <workdir>/solutions/<key>.json \
--papers <workdir>/papers.json \
--verdicts <workdir>/verdicts/<key>.json \
--out <workdir>/details/<key>.json \
[--theme dark]
必须提供 --verdicts:如果不进行审计就渲染,所有声称的边都会被绘制出来。成功提示会报告已渲染的声称边数量,随后逐行列出未渲染的边(not rendered (1)->(3): source_quote not found …)、每条被忽略的 verdict 记录,以及每条未匹配此详情输出中任何边的 verdict(这表示 verdict 文件已过时或属于其他解决方案)。相同信息也会存储在输出 JSON 中(edges_rendered、edges_not_rendered、verdicts_unmatched、audit_downgrades、audit_notes),并与 "audited": true 并列。未渲染的边会保留在图外;不要编辑 verdict 文件来强行将其加入图中。
步骤 13 — assemble_report(CLI,确定性)
python scripts/cli.py assemble_report \
--parsed-query <workdir>/parsed_query.json \
--outline <workdir>/outline_mermaid.json \
--details-dir <workdir>/details \
--papers <workdir>/papers.json \
--out <user-supplied output path>
遍历 details/ 中的所有渲染 JSON,按 (challenge_idx, s_major, s_minor) 排序,并在用户指定的输出路径写入最终 Markdown 报告。若渲染 JSON 中没有 "audited": true(可能是未使用 --verdicts 的运行遗留文件,或旧版本生成的文件),命令会在写入任何内容前以退出代码 2 停止;请对其列出的文件重新运行步骤 12。只会处理包含 mermaid 字段的 *.json 文件;*_raw.md、*_input.txt 以及同目录下任何非渲染 JSON 都会跳过(若有跳过的文件,标准输出中会显示其数量)。因此,根据工作目录布局,parse_detail 的输出会放到同级的 parsed/ 目录,而不是 details/ 中。
验证
步骤 13 完成后,阅读报告开头约 40 行并确认:
- 分类至少包含 2 个挑战,且每个挑战至少有 1 个解决方案。
- 每个 Mermaid 代码块都以
```mermaid开始,并以```结束。 - 文件底部存在论文附录。
如果其中任何一项不符合要求,最可能的原因是大纲 LLM(步骤 8)返回了格式错误的 Markdown。请重新运行步骤 8;如果连续两次失败,请降低步骤 5 中的 --n,或优化查询。
设计说明(供编辑此技能的人阅读,不供运行时 Agent 阅读)
- 不依赖外部 LLM:此技能提供数据获取器、提示词模板(
references/*.md)、解析器和渲染器。宿主 Agent 是 LLM 提供方。因此无需OPENROUTER_API_KEY,也没有llm.py。 - 审核关卡由代码实现,而非文字说明:
scripts/audit.py会重新检查每条判定记录(标签、在论文原文中找到的引文、时间顺序、自环、重复项、循环)。没有--verdicts时,render_detail_mermaid无法运行;它会在输出中标记"audited": true,而assemble_report会拒绝未带此标记的渲染结果。mermaid.py从audit.SUPPORTED_VERDICTS获取用于绘制边的标签集合。 - 详情解析器的单一事实来源:
mermaid._parse_detail_markdown同时由detail_to_mermaid(渲染)和parse_detailCLI 子命令调用。对草稿内容剥离、论文/EP/OC 提取或幻觉内容丢弃逻辑的任何更改,都会同时影响两者。 references/seed_paper_block.md是内部模板片段,由format_seed_block使用;运行时 Agent 不会直接替换其中的占位符。其余五个references/*.md文件是运行手册所引用、面向 Agent 的模板。- 主题化 Mermaid:
mermaid.py定义了LIGHT_THEME和DARK_THEME。渲染器子命令按名称解析主题,顺序为 CLI 参数 →MERMAID_THEME环境变量 →"light"。每次渲染都会生成自包含的 Mermaid 图(初始化指令 + classDefs + linkStyle)。 - 默认启用 JSONL 日志:除非传入
--log none,否则每个子命令都会在输出文件旁写入<out>.log.jsonl。这些日志供开发者在迭代此技能时查看——运行时 Agent 不应回读这些日志。 - 每个子命令只检查一次
papers.json:每个读取该文件的步骤都会检查其是否为论文对象数组;如果不是,会以代码 2 退出并指出有问题的条目编号,而不是等到后续因缺少字段而失败。 - 优先采用明确失败模式:宁可报错,也不静默处理。缺少键、LLM 输出格式错误、搜索未找到论文——这些情况都会打印原因并中止,而不是生成质量下降的产物。
- 仅限英语:上游
paper-graph提示词会输出双语标签;此技能会去除中文,只保留英语。