paper-graph について
paper-graphは、テーマや論文に関する研究の展開をたどるスキルで、文献調査やアイデアの系譜整理に使えます。課題、解決策、論文間の発展経路をMermaid図で示すMarkdownレポートを作成し、LLM呼び出しはホストエージェントが担います。
主な機能
- 01テーマまたは起点論文による検索
- 02S2とDeepXivによる論文取得
- 03課題から解決策への分類
- 04解決策ごとの論文発展図
- 05Mermaid図を含む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詳細
Paper Graph
ユーザーが指定したトピック(または論文)に関する研究がどのように発展してきたかを、課題 → 解決策に分類し、解決策ごとの発展経路をたどる Mermaid 図を埋め込んだ Markdown レポートを作成します。
このスキルには、外部 LLM への依存関係はありません。ホストエージェントがすべての LLM 呼び出しを行い、このスキルは決定的なデータ取得機能(S2 / DeepXiv)、プロンプトテンプレート、Markdown パーサー、Mermaid レンダラーを提供します。以下のランブックを手順どおりに実行してください。
実行要件。 呼び出し元から parsed_query.json、seed.json、papers.json がすでに提供されている場合(たとえば input/ ディレクトリなど、保存場所は問いません)、これらを作業ディレクトリに同じ名前でコピーし、ステップ 1~5 は完了したものとしてステップ 6 から開始してください。それでも、classify から assemble までの決定的な CLI ステップはすべて実行してください。解析、エッジ監査、レンダリング、アセンブルを、手書きの Mermaid で置き換えてはいけません。終了前に、解析されたすべての解決策について、詳細レンダリングと監査判定ファイルが存在することを確認してください。そのファイルが空の JSON リストの場合も同様です。assemble_report のみを最終レポートの出力処理として扱ってください。呼び出し元から不確かな関係性を表示するよう求められた場合でも、その後でエッジを手書きで編集または追加してはいけません。不確実性は判定 JSON に保持してください。
このスキルを使用する場面
次のような依頼があった場合に実行します。
- 「<トピック>の歴史を見せて」/「<トピック>はどのように発展しましたか?」
- 「<論文>は何に由来していますか?」/「<論文>はどの研究を基盤にしていますか?」
- 「<論文>の重要な改善点や後続研究は何ですか?」
- 「<分野>におけるアイデアの系譜をたどって」/「<分野>の文献を分類して」
- 「<論文>の引用ツリー」/「<トピック>のアイデアの系譜」
次の場合は実行しません。
- ユーザーが1本の論文の要約、または単一の検索結果だけを求めている場合(関係性や発展に関する要望がない場合)。
- 学術以外の引用に関する依頼の場合。
- ユーザーがグラフではなく、通常の参考文献一覧を明示的に希望している場合。
入力と出力
入力:
- 研究クエリ:トピック、起点となる論文のタイトルや引用情報、またはその組み合わせ。自由形式のテキストです。
- 出力パス(必須):最終 Markdown レポートの保存先パスです。指定されていない場合は、実行前に確認してください。
- 取得する論文数(省略可):
fetch_papersの--nフラグで指定します。デフォルトは 10 です。 - Mermaid のテーマ
lightまたはdark(省略可):レンダリングステップの--theme、またはMERMAID_THEME環境変数で指定します。デフォルトは light です。
出力: ユーザーが指定したパスに、以下のセクションを含む単一の Markdown ファイルを出力します。
- 研究の目的(クエリから抽出)
- 概要分類 — ルート → 課題 → 解決策 → 論文参照を示す Mermaid グラフを1つ
- 解決策ごとの発展経路 — 解決策ごとに、論文間の「発展元」エッジ、発展の節目、未解決の課題を示す Mermaid グラフを1つ
- 論文の付録 — 論文タイトル/年/著者/アブストラクト/結論の抜粋を記載した番号付きリスト
Mermaid は ```mermaid フェンス内のテキストにすぎません。このファイルは GitHub、Obsidian、Mermaid 拡張機能を使った VS Code などで、そのまま表示できます。
セットアップ
必須の環境変数(不足している場合、スキルは詳細なエラーを出して失敗します):
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: ホストエージェントは独自のモデルと API キーを使用します。このスキルはプロンプトテンプレートを出力してレスポンスを解析しますが、LLM プロバイダーへの認証や呼び出しは行いません。
作業ディレクトリ: 実行ごとに 1 つのディレクトリを作成し、現在の作業ディレクトリからの相対パスを基準にします(例:./<basename>.work/)。/tmp/... などの絶対システムパスは避けてください。一部のハーネスでは、シェルとファイル読み取りツールが異なるファイルシステムルートにサンドボックス化されるため、一方では書き込み可能に見える絶対パスが、もう一方では見つからない場合があります。現在の作業ディレクトリからの相対パスなら、どこでも同じように機能します。不明な場合は、最初に一度 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
ファイル名はエージェントが選択します。以下の手順書で参照される名前に合わせるための推奨例です。<key> は解決策キー文字列 <s_major>.<s_minor>(例:1.1)です。各 format_papers 呼び出しでは、{allowed_numbers} プレースホルダーにそのまま入れられる (N), (N), ... 形式の <out>.allowed.txt が同じ場所に書き込まれます。
手順書
各ステップは、CLI 呼び出し(python scripts/cli.py <subcmd> …)または LLM 呼び出し(ホストエージェントが references/ からテンプレートを読み込み、プレースホルダーを置換してモデルを呼び出す)のいずれかです。すべてのスクリプトと参照ファイルのパスは、このスキルのディレクトリからの相対パスです。CLI の依存関係:pip install deepxiv-sdk httpx python-dotenv(スキルのルートにある requirements.txt にも記載)— ユーザーが作業している環境にインストールしてください。順番に実行してください。詳細ステップ(ステップ 10)と監査ステップ(ステップ 11)は、ホストが並行ツール呼び出しに対応している場合、解決策ごと/エッジごとに並行して実行できます。
references/*.md のプレースホルダー構文。 波括弧 1 つの {name} は、ホストが置換するテンプレートスロットです。波括弧 2 つの {{...}} はエスケープされたリテラル波括弧です。LLM に出力を求める JSON の例に波括弧が含まれる場合、プロンプト本文に現れます。波括弧 1 つのスロットだけを置換し、{{ と }} はそのままにしてください(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
クエリ内で検出された arxiv ID の論文レコードを、S2 形式の JSON 配列として出力します。該当する 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〜5 個のキーワードからなるフレーズを 2〜4 個含めます(フレーズ数とフレーズごとのキーワード数は別々のカウントです)。
モデルがコードフェンスを追加している場合は削除します。goal が空でないこと、また searches が空でない文字列のリストであることを検証します。レスポンスの形式が不正な場合は、LLM に一度だけ再度プロンプトを送ります。2 回目も失敗した場合は、明確なエラーメッセージを出して中止します。
検証済みの JSON を <workdir>/parsed_query.json に保存します。
ステップ 5 — fetch_papers(CLI、決定的)
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 クエリを 1 件)を使用し、DeepXiv で補完して --n 件まで論文を取得します。出力は S2 形式の論文辞書の JSON 配列です。S2 が何も返さず、DeepXiv も利用できない場合は、ゼロ以外の終了コードで終了します。その場合は、より具体的な検索フレーズを使ってステップ 4 を再度プロンプトしてください。
ステップ 6 — classify(LLM)
まず、完全な {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"] にある 1 文の文字列。ここでは定義ブロックを付けず、そのまま使用)に、{papers_input} を上記のテキストファイルの内容に置き換えます。LLM を呼び出します(温度は低く設定し、推論は不要です。離散的な分類判断のためです)。モデルがコードフェンスを追加した場合は、先頭と末尾から取り除きます。
生の応答をそのまま <workdir>/classify_raw.json に保存します。想定される形式は次のとおりです。
{"classifications": [
{"n": 1, "label": "CORE", "reason": "..."},
{"n": 2, "label": "ADJACENT", "reason": "..."},
{"n": 3, "label": "REJECT", "reason": "..."},
...
]}
その後、CLI を使って papers.json に統合します(形式を検証し、検証に失敗した場合はすべての論文を CORE にするフォールバックを適用します)。
python scripts/cli.py merge_classifications \
--classifications <workdir>/classify_raw.json \
--papers <workdir>/papers.json \
--out <workdir>/papers.json
merge_classifications は常に終了コード 0 で終了します。検証に失敗した場合は、CORE を指定する安全策を適用し、標準出力の成功行に … (FALLBACK: <reason>) というサフィックスを出力します。このサフィックスが表示された場合は、LLM に一度だけ再プロンプトします。2 回目もフォールバックが適用された場合は、すべての論文が CORE としてアウトラインに使われるため、すべて CORE の結果を受け入れて続行します。正常に処理された場合、成功行にはラベルごとの件数が表示され、FALLBACK のサフィックスは付きません。後続のサブコマンド(parse_outline、レンダラー、assemble_report)は、_label_of を介してこれらのラベルを読み取ります。
ステップ 7 — prefetch_sections(CLI、決定的、ベストエフォート)
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(LLM)
ここから先で繰り返し使う 2 つのプロンプト断片、goal_block({goal} の置換内容)と core_filter(CORE のみを対象にするフィルターと、その付随ファイル {allowed_numbers})をファイルとしてディスクに保存します。ステップ 10 と 11 ではこれらのファイルを読み戻すため、エージェントが会話の状態として保持する必要はありません。
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} を作成します(ステップ 10 との一貫性を保つため、ここでも同じ場所に .allowed.txt が出力されますが、ここでは重複します)。
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の内容。
LLM を呼び出します(温度は低く、約 0.2 に設定し、最大トークン数は約 8000 を許可します)。応答の先頭と末尾にコードフェンスがあれば取り除きます。プロンプトではモデルに生の 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
アウトラインの概要と、解決策ごとに 1 つの 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 のみを使用してください。
アウトラインで再掲された解決策の見出しは、同じ解決策を再び開きます。有効な論文が 1 つもない解決策(すべての番号が幻覚されたもの、または別の場所ですでに配置されているもの)は削除されます。コンテキストファイルも分類体系内のノードも作成されず、標準出力の成功メッセージにその解決策の名前が表示されます。そのため、すべてのコンテキストファイルの paper_nums は空ではありません。
アウトラインに解析可能な ## Challenge N: 見出しが含まれていない場合、または有効な論文が残る解決策がない場合は、終了コード 4 で終了し、標準エラー出力に診断メッセージを表示します。ステップ 8 を 1 回再プロンプトしてください。2 回目も失敗した場合は、ステップ 5 の --n の値を下げるか、クエリをより明確にしてください。
ステップ 10 — detail(LLM、ソリューションごと)
重要 — このステップをサブエージェントに委任する前に、以下の「ファンアウトタスクの指示」をお読みください。 SKILL.md を参照するよう指示したり、「ソリューション X について paper-graph を実行する」よう求めたりするサブエージェントへのプロンプトは、別の作業ディレクトリでステップ 1 からワークフロー全体を再開してしまい、その出力は親の実行では使用できません。
このステップの開始時に references/detail.md を一度読み込み、オーケストレーターでソリューションごとに置き換えます。ファンアウト先のサブエージェントがテンプレートを読むことはありません。
ステップ 9 で生成された各 <workdir>/solutions/<key>.json について、次のようにします。
(a) ソリューションごとの {papers_input} は CORE プールです。すべてのソリューションコンテキストの allowed は CORE セット全体なので、このブロックはすべてのソリューションで同じであり、ステップ 8 の <workdir>/papers_input_core.txt(およびその .allowed.txt サイドカーファイル)と一致します。この 2 つのファイルを再利用してください。ソリューションごとに 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の内容。
LLM を呼び出します(温度は約 0.2、スクラッチパッドとツリーを収めるため、最大トークン数は約 12000 を許可してください)。生の応答を <workdir>/details/<key>_raw.md に保存します。
(c) パースします(出力先は details/ ではなく、隣接する parsed/ ディレクトリです。こうすることで、ステップ 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 への入力になります。
ホストがツール呼び出しの同時実行に対応している場合は、ソリューションごとの LLM 呼び出しと、それに続く parse_detail の呼び出しをすべて並列でファンアウトします。
ファンアウトタスクの指示(ステップ 10b をソリューションごとにサブエージェントへ委任する場合)。 サブエージェントへのプロンプトは自己完結している必要があります。SKILL.md を参照するように指示したり、「ソリューション X について paper-graph を実行する」よう指示したり、CLI の実行を指定したりしないでください。オーケストレーターが置き換えを行い、サブエージェントに渡すのは次の内容だけです。
- すでに
{goal}、{challenge_name}、{solution_name}、{primary_numbers}、{papers_input}、{allowed_numbers}を置き換えた、完全なプロンプト文字列。 - 期待する応答形式:
references/detail.mdの出力仕様に従った、生の Markdown 形式の進化ツリー。 - 明示的な指示:「以下のプロンプトを使って LLM を呼び出し、生の Markdown 応答のみを返してください。他のファイルを読んだり、シェルコマンドを実行したり、他のスキルを呼び出したりしないでください。」
サブエージェントに SKILL.md を参照させたり、「ソリューション X について paper-graph を実行する」よう指示したり、CLI の実行を指定したりしないでください。サブエージェントが Markdown テキストを返したら、オーケストレーターがそれを <workdir>/details/<key>_raw.md に書き込み、parse_detail を実行します。サブエージェントが SKILL.md を読み直すと、ワークフロー全体がステップ 1 から再開されます。これを防ぐには、上記のようにサブエージェントへのプロンプトの範囲を限定してください。
ステップ 11 — audit_edge(LLM、エッジごと)
重要 — このステップをサブエージェントに委任する前に、以下の「ファンアウトタスクの概要」を読んでください。 ステップ 10 と同じオーケストレーション失敗が起きます。SKILL.md を参照するよう指示されたサブエージェントは、ワークフロー全体を最初からやり直します。
このステップの開始時に references/audit_edge.md を一度だけ読み込み、オーケストレーター側でエッジごとに置換します。ファンアウト先のサブエージェントがテンプレートを読むことはありません。
各 <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 — 300 文字までの引用を2つと理由を含めるのに十分な値)。応答を解析します:
{"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 は監査証跡に可能性のある関係として記録されますが、有向の系譜に関する主張としては表示されません。
ファンアウトタスクの概要(エッジごと、または解決結果ごとにサブエージェントへ委任する場合)。 ステップ 10 と同じ方針に従ってください。サブエージェントに SKILL.md を参照させたり、「paper-graph audit を実行する」よう指示したり、CLI の呼び出しを渡したりしないでください。オーケストレーターが自身で置換を行い、サブエージェントに渡すのは次のものだけです:
- 完全に置換済みのプロンプト文字列(
{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 の判定オブジェクトのみを返してください。他のファイルを読んだり、シェルコマンドを実行したり、他のスキルを呼び出したりしないでください。"
サブエージェントが判定 JSON を返したら、オーケストレーターは解決結果ごとのリストを集約し、<workdir>/verdicts/<key>.json に保存します。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": "..."}, ...]
監査したエッジごとに1件のレコードを書き、解決結果にエッジがない場合でもファイルを [] で書き込みます。ステップ 12 でこのファイルが必要です。各引用は監査結果のとおりに正確にコピーしてください。手作業で引用を修正したり短縮したりしないでください。
ステップ 12 のレンダラーは、papers.json に照らしてすべてのレコードを再チェックし、以下のすべての条件を満たす場合にのみエッジを描画します。それ以外は、有向の系譜として表示されません:
- 判定が
SUPPORTED_BY_ABSTRACTまたはSUPPORTED_BY_SECTIONであること。 - 各引用が20文字以上で、
NONEではなく、該当論文のタイトル、アブストラクト、または抜粋のいずれかに含まれていること。2つをまたいで含まれている場合は不可です(一致判定の前に改行、連続するスペース、印字上の引用符やダッシュは正規化されますが、文言と大文字・小文字は正規化されません)。 - ソースがターゲットより新しくないこと(年が不明の場合はチェックを省略し、その旨を記録します)。
- ソースとターゲットが異なる論文であり、どちらも
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 …)、無視された各判定レコード、さらにこの詳細出力内のどのエッジにも一致しない各判定(判定ファイルが古いか、別のソリューションのものだという兆候)について、1 行ずつ表示されます。同じ情報は、"audited": true の隣にある出力 JSON の (edges_rendered、edges_not_rendered、verdicts_unmatched、audit_downgrades、audit_notes) にも保存されます。レンダリングされないエッジはグラフに含まれません。無理に含めるために判定ファイルを編集しないでください。
ステップ 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 レポートを書き込みます。"audited": true のないレンダリング JSON(--verdicts なしでの実行時、または以前のバージョンで残されたもの)がある場合、書き込みを行う前に終了コード 2 でコマンドを停止します。該当ファイルについてステップ 12 を再実行してください。mermaid フィールドを含む *.json ファイルだけが使用されます。*_raw.md、*_input.txt、および同じ場所にあるレンダリング JSON 以外のファイルはスキップされます(該当するものがあれば、標準出力に件数が表示されます)。このため、workdir のレイアウトに従い、parse_detail の出力先は details/ ではなく、隣接する parsed/ ディレクトリになっています。
検証
ステップ 13 の完了後、レポートの最初の約 40 行を読み、次の点を確認してください。
- タクソノミーに少なくとも 2 つの課題があり、それぞれに少なくとも 1 つのソリューションがある。
- 各 Mermaid ブロックが
```mermaidで始まり、```で閉じている。 - ファイルの末尾に論文の付録がある。
いずれかを満たさない場合、最も可能性の高い原因は、アウトライン LLM(ステップ 8)が不正な Markdown を返したことです。ステップ 8 を再実行してください。2 回失敗した場合は、ステップ 5 の --n を小さくするか、クエリをより明確にしてください。
設計上の注意点(このスキルの編集者向け。実行時のエージェント向けではありません)
- 外部 LLM に依存しません:このスキルが提供するのは、データ取得機能、プロンプトテンプレート(
references/*.md)、パーサー、レンダラーです。ホストエージェントが LLM プロバイダーです。このため、OPENROUTER_API_KEYは不要で、llm.pyもありません。 - 監査ゲートは文章ではなくコードです:
scripts/audit.pyは、すべての判定レコード(ラベル、論文本文内で見つかった引用、時系列、自己エッジ、重複、循環)を再チェックします。render_detail_mermaidは--verdictsなしでは実行できず、出力に"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が使用します。実行時のエージェントがそのプレースホルダーを直接置換することはありません。ほかの 5 つのreferences/*.mdファイルは、ランブックが参照するエージェント向けテンプレートです。- テーマ付き Mermaid:
mermaid.pyではLIGHT_THEMEとDARK_THEMEを定義しています。レンダラーのサブコマンドは、テーマを名前(CLI 引数)→MERMAID_THEME環境変数 →"light"の順に解決します。各レンダリングでは、自己完結型の Mermaid グラフ(init ディレクティブ + classDefs + linkStyle)が出力されます。 - JSONL ログはデフォルトで有効です:各サブコマンドは、
--log noneが渡されない限り、出力の隣に<out>.log.jsonlを書き込みます。これらのログは、スキルを反復的に改善する人間の開発者向けです。実行時のエージェントはログを読み戻してはいけません。 papers.jsonはサブコマンドごとに一度チェックされます:このファイルを読み込む各ステップは、ファイルが論文オブジェクトの配列でない場合、後からフィールド不足で失敗するのではなく、該当するエントリ番号を示してコード 2 で終了します。- 障害時の優先方針:黙って処理するより、明確に知らせます。キーの不足、不正な LLM 出力、検索結果が 0 件の場合は、劣化した成果物を生成せず、理由を表示して処理を中止します。
- 英語のみ:上流の
paper-graphのプロンプトは二言語のラベルを出力していました。このスキルでは中国語を除去し、英語のみを残します。