From aae447b3698ca242428d3edd5d907c5c0f72597f Mon Sep 17 00:00:00 2001 From: Yoshiaki Sakae <89111154+yoshiaki-sakae@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:52:08 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E6=97=A5=E6=9C=AC=E8=AA=9E?= =?UTF-8?q?=E5=87=BA=E5=8A=9B=E3=81=AB=E5=AF=BE=E5=BF=9C=EF=BC=88=E6=A4=9C?= =?UTF-8?q?=E8=A8=BC=E6=B8=88=E3=81=BF=E3=81=AE=E5=AE=9F=E5=83=8D=E7=89=88?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 出力言語を中国語から日本語へローカライズ。必須セクション名・図表決定・ lint検証・SKILL.md・references・ドメインラベルを日本語化。実論文(arXiv:2608.23565)で end-to-end生成とlint全ゲート通過を確認済み。中国語ソース論文の解析用エイリアスは保持。 --- skills/deeppapernote/SKILL.md | 58 ++-- .../deeppapernote/references/deep-analysis.md | 52 ++-- .../references/domain_rules.yaml | 20 +- .../references/evidence-first.md | 22 +- .../references/figure-placement.md | 16 +- .../deeppapernote/references/final-writing.md | 98 +++---- .../deeppapernote/references/note-quality.md | 42 +-- .../references/obsidian-format.md | 84 +++--- .../deeppapernote/references/paper-types.md | 122 ++++----- .../scripts/build_synthesis_bundle.py | 2 +- skills/deeppapernote/scripts/common.py | 24 +- skills/deeppapernote/scripts/contracts.py | 248 ++++++++--------- skills/deeppapernote/scripts/lint_note.py | 250 +++++++++--------- skills/deeppapernote/scripts/plan_figures.py | 16 +- 14 files changed, 528 insertions(+), 526 deletions(-) diff --git a/skills/deeppapernote/SKILL.md b/skills/deeppapernote/SKILL.md index ad0f18f..9efdd99 100644 --- a/skills/deeppapernote/SKILL.md +++ b/skills/deeppapernote/SKILL.md @@ -1,6 +1,6 @@ --- name: deeppapernote -description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. +description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. Generates the note in Japanese by default (デフォルトで日本語の精読ノートを生成する). Japanese triggers: 「この論文の精読ノートを作って」「この論文をObsidianノートにまとめて」「論文を読んで日本語のMarkdownノートにして」. --- # DeepPaperNote @@ -10,11 +10,11 @@ Use this skill when the user wants one outcome: - generate a high-quality Markdown note - save the note into an Obsidian-style vault when configured, or into the current workspace when no vault is configured -Chinese trigger examples: -- `给这篇论文生成深度笔记` -- `写一篇高质量论文精读笔记` -- `把这篇文章整理成 obsidian 笔记` -- `读这篇论文并生成 md 笔记` +日本語のトリガー例: +- `この論文の精読ノートを作って` +- `この論文をObsidianノートにまとめて` +- `この論文を読んで高品質なMarkdownノートを生成して` +- `この論文を読んで md ノートにして` This skill is intentionally narrow: - it handles one paper at a time @@ -73,15 +73,15 @@ Global no-short-circuit rule: - stop and report which stage is blocked and which downstream required stages remain incomplete - do not describe the whole task as complete while required downstream stages are still pending -Completion-language rule: -- say `笔记已完成` only when the required workflow is actually complete -- say `已生成草稿` when drafting is done but lint, final readability review, or save is still pending -- say `已通过校验` only when lint has actually been run and passed -- say `已保存到 Obsidian` only when the write step has actually succeeded -- do not treat `lint 已通过` as equivalent to `整篇笔记已经润色完成` -- if final readability review is still pending, explicitly say the draft passed script lint but has not finished final language review -- if the workflow stopped early, name the current stage and the still-missing required stages instead of using completion language -- lint is a floor, not the writing objective +完了表現のルール: +- 必須ワークフローが実際に完了したときのみ `ノート完成` と言う +- 草稿はできたが lint・最終可読性レビュー・保存がまだ残っているときは `草稿を生成済み` と言う +- lint を実際に実行して通過したときのみ `検証を通過` と言う +- 書き込みステップが実際に成功したときのみ `Obsidian に保存済み` と言う +- `lint 通過` を `ノート全体の推敲が完了` と同義に扱わない +- 最終可読性レビューがまだ残っている場合は、草稿はスクリプトの lint を通過したが最終的な言語レビューは未完了である、と明示する +- ワークフローが途中で止まった場合は、完了表現を使わず、現在の段階と未達成の必須段階を明示する +- lint は下限であり、執筆の目的ではない ## Core Execution Contract @@ -92,10 +92,10 @@ Non-negotiable rules: - evidence-first: draft from the synthesis bundle, `source_manifest`, raw sections, coverage metadata, explicit `note_plan`, and inspected paper evidence; never finish from title/abstract/headings alone - raw-source authority: for ordinary PDFs, `*_raw_sections.jsonl` and `*_source_manifest.json` are the canonical reading material; old top-N evidence buckets, truncated `section_texts`, and `candidate_chunks` are not model-facing writing inputs - fail-closed: if a usable PDF or sufficient evidence cannot be obtained after supported acquisition paths, stop and ask for better source material rather than producing a finished degraded note -- model-first: scripts structure evidence, but the model must decide emphasis, contribution, mechanism, limitations, and final Chinese prose -- required structure: include the canonical required sections, with `原文摘要翻译` before `一句话总结` and a dedicated `创新点` section immediately after `原文摘要翻译` -- abstract translation: when abstract metadata exists, `原文摘要翻译` is a faithful Chinese translation of the original abstract, not a bilingual block and not the model's own summary -- mechanism depth: method, framework, and system papers should include `### 机制流程` under `方法主线`, normally as a 3 to 4 step numbered flow with input, operation, and output destination +- model-first: scripts structure evidence, but the model must decide emphasis, contribution, mechanism, limitations, and the final Japanese prose (最終的な日本語の文章) +- required structure: include the canonical required sections, with `要旨の翻訳` before `一言まとめ` and a dedicated `新規性` section immediately after `要旨の翻訳` +- abstract translation: when abstract metadata exists, `要旨の翻訳` is a faithful Japanese translation of the original abstract, not a bilingual block and not the model's own summary +- mechanism depth: method, framework, and system papers should include `### 機構フロー` under `手法の骨子`, normally as a 3 to 4 step numbered flow with input, operation, and output destination - placeholder-first figures: plan major figure/table placeholders first; replace one only when identity match and visual usability are both strong; otherwise keep the placeholder - final quality gates: lint is a floor; after lint passes, first run `final_quality_review` for analytical depth, then run `final_readability_review` for language polish, and rerun lint if either review edits the note @@ -141,20 +141,20 @@ Formal Save states: - Do not stop after a text-only draft just to ask whether the user wants figures inserted. Finish the figure replacement decision inside the same task unless the user explicitly asked for text only. - The note must use real heading levels: `#`, `##`, and `###`. - Every final note must start with an Obsidian YAML properties block above the `#` title heading. Include at least a `tags` field with a `papers/` value and useful `aliases`; include `date`, `doi`, or `arxiv_id` when known, and omit unavailable fields rather than inventing placeholders. -- `## 核心信息` must be a fixed metadata block only. Use only these fields, in this order, as `- 字段名: 值` bullets: `标题`, `标题翻译`, `作者`, `机构`, `发表时间`, `发表渠道`, `DOI`, `arXiv`, `论文链接`, `代码 / 项目`, `数据 / 资源`, `论文类型`. Omit unavailable fields; put any guide sentence, takeaway, or analysis in `一句话总结` or a later section instead. -- The note should include `原文摘要翻译` near the beginning when abstract metadata is available, before `一句话总结`. -- When abstract metadata is available, `原文摘要翻译` should directly translate the original paper abstract into Chinese rather than restating it as your own summary. -- The `原文摘要翻译` section itself should be Chinese-only; do not place English abstract sentences or English paragraph excerpts in that section. -- Do not mix later judgments, innovation summaries, or hindsight explanations into `原文摘要翻译`; keep it as the original abstract translated into Chinese. -- The note should include a dedicated `创新点` section immediately after `原文摘要翻译` and before `一句话总结`. -- The `创新点` section should not be empty praise. It should enumerate the paper's actual innovations and briefly explain why each one matters. +- `## 基本情報` must be a fixed metadata block only. Use only these fields, in this order, as `- フィールド名: 値` bullets: `タイトル`, `タイトル訳`, `著者`, `所属`, `発表時期`, `発表媒体`, `DOI`, `arXiv`, `論文リンク`, `コード / プロジェクト`, `データ / リソース`, `論文タイプ`. Omit unavailable fields; put any guide sentence, takeaway, or analysis in `一言まとめ` or a later section instead. +- The note should include `要旨の翻訳` near the beginning when abstract metadata is available, before `一言まとめ`. +- When abstract metadata is available, `要旨の翻訳` should directly translate the original paper abstract into Japanese rather than restating it as your own summary. +- The `要旨の翻訳` section itself should be Japanese-only; do not place English abstract sentences or English paragraph excerpts in that section. +- Do not mix later judgments, innovation summaries, or hindsight explanations into `要旨の翻訳`; keep it as the original abstract translated into Japanese. +- The note should include a dedicated `新規性` section immediately after `要旨の翻訳` and before `一言まとめ`. +- The `新規性` section should not be empty praise. It should enumerate the paper's actual innovations and briefly explain why each one matters. - High-quality notes should usually contain multiple meaningful `###` subheadings in the technical sections when the paper is non-trivial. - Generate the complete figure/table decision table and satisfy the generated `writing_contract.figure_table_contract` before drafting or saving. - After the synthesis bundle is built, complete the model-led Visual Review Gate and Figure/Table Decision Freeze before creating `note_plan`; no `review_pending` item may cross that boundary. - Pass the grounding and final-note figure gates before advancing; revise any failed decision coverage, insertion, structure, or status check. - An `insert` decision is complete only after Formal Save materializes the selected image into the paper-local `images/` directory and the write succeeds. -- The note must pass a style gate: no mixed Chinese-English prose lines except stable proper nouns or citation metadata. -- The style gate also rejects mechanical term-replacement artifacts such as `KV缓存 of`, `批量ing`, `In相关 Researcher`, or `Single 序列 generation`; rewrite the sentence naturally instead of preserving a partially translated phrase. +- The note must pass a style gate: no lines that mix Japanese prose with untranslated English sentences, except stable technical terms, proper nouns, or citation metadata. +- The style gate also rejects mechanical term-replacement artifacts such as `KVキャッシュ of`, `バッチ化ing`, `関連 Researcher において`, or `単一 シーケンス generation`; rewrite the sentence naturally instead of preserving a partially translated phrase. - Style gate enforcement: when `lint_note.py` output contains `passes_style_gate: false`, fix the reported issues and re-run lint. Keep fixing and re-running until lint passes — multiple rounds are normal and expected. Do not decide that any failure is an acceptable exception — proper nouns, math formulas, and citation metadata are not automatic exemptions. Only escalate to the user if the same failures appear unchanged across multiple rounds with no reduction, indicating the model is unable to make further progress independently. - If PDF or evidence quality is insufficient for a real deep note, fail closed: stop, report the blocked stage, and ask for the better PDF, OCR/source material, or other input needed to continue. @@ -172,7 +172,7 @@ Model-first rule: - central quantitative comparisons with three or more systems, settings, tasks, datasets, metrics, or ablation rows should normally be written as compact Markdown tables, followed by interpretation; do not leave the main result table as a loose bullet list when a table would be clearer - short papers still need a complete deep note: use the saved space to explain protocol details, ablations, limitations, and deployment or replication implications rather than compressing the note into a terse summary - after `final_quality_review` passes, reread the full note once more for readability; do not stop at formal compliance only -- in `final_readability_review`, ordinary English phrase leftovers should usually be rewritten into natural Chinese, while stable proper nouns may remain in English +- in `final_readability_review`, ordinary English phrase leftovers should usually be rewritten into natural Japanese, while stable proper nouns may remain in English - do not use `final_readability_review` to invent new facts, empty filler text, or shallower but safer wording just to satisfy lint The topic references above can improve difficult runs, but the normal execution path should not depend on reading all of them. diff --git a/skills/deeppapernote/references/deep-analysis.md b/skills/deeppapernote/references/deep-analysis.md index 0909834..3bd6260 100644 --- a/skills/deeppapernote/references/deep-analysis.md +++ b/skills/deeppapernote/references/deep-analysis.md @@ -4,7 +4,7 @@ Use this guide when the user wants a note that feels like a real research note r ## Goal -Produce a Chinese paper note that helps future rereading answer: +Produce a Japanese paper note that helps future rereading answer: - this paper is really solving what problem - the core route or method chain is what - which evidence actually supports the claim @@ -54,15 +54,15 @@ Use the synthesis bundle's paper-type contract and decide: - Write for future rereading, not for one-time display. - Prefer interpretation over translation. -- Prefer “这篇论文真正有价值的点是...” over “本文提出了...” style filler. -- Avoid pasting long English sentences into Chinese sections. +- Prefer “この論文が本当に価値のある点は…” over “本稿は…を提案した” style filler. +- Avoid pasting long English sentences into Japanese sections. - Do not fabricate metrics, ablations, or claims not supported by evidence. - When an individual claim has weak evidence within an otherwise sufficient Source Corpus, narrow the wording and state the claim boundary explicitly. - For method papers, write like a replication-minded researcher rather than a summary assistant. ## Section Guide -### 核心信息 +### 基本情報 Must include: - title @@ -75,7 +75,7 @@ Must include: - code repo or project page when available - domain -### 一句话总结 +### 一言まとめ Do not paraphrase the abstract. @@ -83,14 +83,14 @@ Answer: - what the paper's real contribution is - what the title may overstate -### 研究问题 +### 研究課題 Answer: - the concrete pain point - why existing methods are not enough - whether this is a new problem, a new angle on an old problem, or a more realistic reformulation -### 数据与任务定义 +### データとタスク定義 Must separate: - where the data comes from @@ -104,7 +104,7 @@ For clinical or social-science papers, spell out: - annotation or rating assumptions - whether the task is realistic or simplified -### 方法主线 +### 手法の骨子 This is usually where a shallow note fails. @@ -120,19 +120,19 @@ Explain: - make the reader feel the Input -> key transformation -> Output flow, not just the paper's terminology For method, framework, or system papers: -- default to an explicit `### 机制流程` subsection inside `方法主线` +- default to an explicit `### 機構フロー` subsection inside `手法の骨子` - write it as a 3 to 4 step numbered list rather than a long paragraph - each step should say what goes in, what operation happens, and where the output goes next -- if the paper has both training and inference details, use `### 机制流程` for the dominant execution chain and leave training recipe details to neighboring subsections +- if the paper has both training and inference details, use `### 機構フロー` for the dominant execution chain and leave training recipe details to neighboring subsections For complex papers, use `###` subheadings such as: -- `### 机制流程` -- `### 数据构建` -- `### 中间表征抽取` -- `### 模型结构` -- `### 训练与推理` +- `### 機構フロー` +- `### データ構築` +- `### 中間表現の抽出` +- `### モデル構造` +- `### 学習と推論` -### 关键结果 +### 主要な結果 Do not dump all metrics. @@ -148,7 +148,7 @@ For method papers, also ask: - if the paper reports ablations or removed-module comparisons, include at least one setting that hurt performance, made training unstable, or revealed a trade-off - if the evidence bundle contains no such negative ablation signal, say explicitly that the paper did not clearly report failed or unstable settings -### 深度分析 +### 深掘り分析 This is the most important part. @@ -169,14 +169,14 @@ Use the plan's `central_claims` as the spine of this section: - when the paper has Discussion or Limitations, explain the mechanism behind those caveats rather than copying them as a list Good subsections often include: -- `### 真正贡献是什么` -- `### 为什么结果成立` -- `### 哪些地方容易被误读` -- `### 训练目标` -- `### 推理与采样链路` -- `### 复杂度与扩展性` +- `### 真の貢献は何か` +- `### なぜ結果が成立するのか` +- `### どこが誤読されやすいか` +- `### 学習目標` +- `### 推論とサンプリングの流れ` +- `### 計算量と拡張性` -### 局限 +### 限界 Write real limitations, not polite filler. @@ -188,7 +188,7 @@ Prefer: - missing baselines - unrealistic task framing -### 我的笔记 +### 私のメモ Seed future follow-up with prompts such as: - one reusable idea @@ -201,7 +201,7 @@ Seed future follow-up with prompts such as: When the paper has useful visuals: - preserve placeholders for the important ones - prioritize one method figure, one data/task figure, and one result figure or table -- if a high-confidence pipeline or architecture figure clearly matches the core execution chain, place it in `### 机制流程` first +- if a high-confidence pipeline or architecture figure clearly matches the core execution chain, place it in `### 機構フロー` first - explain why each figure matters - keep original paper numbering such as `Fig. 1` or `Table 2` diff --git a/skills/deeppapernote/references/domain_rules.yaml b/skills/deeppapernote/references/domain_rules.yaml index 7e7a584..9153946 100644 --- a/skills/deeppapernote/references/domain_rules.yaml +++ b/skills/deeppapernote/references/domain_rules.yaml @@ -1,13 +1,15 @@ # User-editable domain routing taxonomy. # Application domains are tried before fallback method domains. +# label / route_to / specialized_folders は出力先フォルダ名(日本語)。 +# aliases / keywords は英語ソース本文との照合に使うため英語のまま残す。 domains: - - label: 医疗健康 + - label: 医療・健康 aliases: - healthcare - medical - clinical medicine specialized_folders: - - 心理健康 + - メンタルヘルス keywords: - clinical - patient @@ -75,7 +77,7 @@ domains: - investment methods: [] - - label: 机器人 + - label: ロボティクス aliases: - robotics - robotic @@ -92,7 +94,7 @@ domains: methods: - diffusion policy - - label: 软件工程 + - label: ソフトウェア工学 aliases: - software engineering keywords: @@ -122,8 +124,8 @@ domains: - bioinformatics methods: [] - - label: 心理健康 - route_to: 医疗健康 + - label: メンタルヘルス + route_to: 医療・健康 aliases: - mental health - psychology @@ -139,7 +141,7 @@ domains: - symptom methods: [] - - label: 推荐系统 + - label: 推薦システム aliases: - recommender systems - recommendation @@ -152,7 +154,7 @@ domains: methods: [] fallback_domains: - - label: 大模型 + - label: 大規模言語モデル aliases: - llm - large language model @@ -185,7 +187,7 @@ fallback_domains: - rlhf methods: [] - - label: 机器学习 + - label: 機械学習 aliases: - machine learning - ml diff --git a/skills/deeppapernote/references/evidence-first.md b/skills/deeppapernote/references/evidence-first.md index c805874..89acd08 100644 --- a/skills/deeppapernote/references/evidence-first.md +++ b/skills/deeppapernote/references/evidence-first.md @@ -48,11 +48,11 @@ The plan should state: - whether key formulas or complexity expressions need to appear in the final note Good note plans often add paper-specific sections such as: -- `### 数据构建` -- `### 量表代理特征抽取` -- `### 训练细节` -- `### 关键洞察` -- `### 为什么结果不等于临床可用` +- `### データ構築` +- `### 尺度の代理特徴抽出` +- `### 訓練の詳細` +- `### 重要な洞察` +- `### なぜ結果が臨床応用可能性を意味しないか` Recommended shape: @@ -61,8 +61,8 @@ Recommended shape: "paper_type": "AI_method", "paper_type_rationale": "The paper proposes a model mechanism and evaluates it against baselines; the script suggestion was treated only as a hint.", "dominant_domain": "machine learning", - "must_cover": ["数据构建", "方法主线", "关键消融"], - "key_numbers": ["主结果提升 3.2 points", "训练成本降低 40%"], + "must_cover": ["データ構築", "手法の骨子", "主要なアブレーション"], + "key_numbers": ["主要結果が3.2ポイント向上", "訓練コストが40%削減"], "real_comparisons": ["against the strongest reported baseline"], "central_claims": [ { @@ -80,9 +80,9 @@ Recommended shape: "followup_questions": ["Does the same state logging still help when external tools are slow, missing, or adversarially noisy?"], "section_plan": [ { - "section": "方法主线", + "section": "手法の骨子", "weight": "high", - "subsections": ["机制流程", "训练目标"], + "subsections": ["機構フロー", "訓練目標"], "evidence_sources": [{"section_id": "sec:method"}, {"pages": [4, 6]}] } ] @@ -102,8 +102,8 @@ Good final notes should: - add paper-specific subsections when the evidence supports them - avoid abstract-only rewriting - explain why a figure or table matters, not just attach it -- separate “作者声称了什么” from “论文真正证明了什么” -- carry the plan's claim boundaries into `深度分析` and `局限` +- separate “著者が何を主張しているか” from “論文が実際に何を証明しているか” +- carry the plan's claim boundaries into `深掘り分析` and `限界` - explain the mechanism deeply enough that an engineer could re-explain or re-implement the main flow ## Minimum Quality Bar diff --git a/skills/deeppapernote/references/figure-placement.md b/skills/deeppapernote/references/figure-placement.md index ce3cf01..b25d4a6 100644 --- a/skills/deeppapernote/references/figure-placement.md +++ b/skills/deeppapernote/references/figure-placement.md @@ -17,17 +17,17 @@ Priority order: ## Placement Logic -- Put method overview figures in `### 机制流程` when they directly explain the core execution chain -- If the match is weaker or the note does not need that micro-structure, keep them in `方法主线` -- Put data or task figures in `数据与任务定义` -- Put main result figures or tables in `关键结果` -- Put conceptual diagrams in `研究问题` or `深度分析` if they clarify the argument +- Put method overview figures in `### 機構フロー` when they directly explain the core execution chain +- If the match is weaker or the note does not need that micro-structure, keep them in `手法の骨子` +- Put data or task figures in `データとタスク定義` +- Put main result figures or tables in `主要な結果` +- Put conceptual diagrams in `研究課題` or `深掘り分析` if they clarify the argument ## What to Read Use: - figure captions -- nearby正文对 figure 的引用 +- 近傍の本文における figure への言及 - section context - candidate pages and candidate images from deterministic PDF asset extraction @@ -50,8 +50,8 @@ When the contract requires manual inspection, open the actual candidate image be ## Integrated Placement Rule -Every kept placeholder must be placed directly under the most relevant substantive section named by its `建议位置`. -Do not collect unresolved placeholders into a catch-all section such as `剩余图表占位`, `未放置图表`, `Remaining figures`, or `Leftover figures`. +Every kept placeholder must be placed directly under the most relevant substantive section named by its `推奨配置`. +Do not collect unresolved placeholders into a catch-all section such as `残りの図表プレースホルダ`, `未配置の図表`, `Remaining figures`, or `Leftover figures`. Rejecting an extraction candidate does not by itself require a final-note placeholder. The final placeholder set should come from semantic importance to the note, not from the number of failed extraction candidates. diff --git a/skills/deeppapernote/references/final-writing.md b/skills/deeppapernote/references/final-writing.md index 8e0f2e4..ac7a368 100644 --- a/skills/deeppapernote/references/final-writing.md +++ b/skills/deeppapernote/references/final-writing.md @@ -23,33 +23,33 @@ Include at least: Do not invent placeholder metadata values. Omit unavailable fields instead. Near the beginning of the note, include: -- `## 核心信息` -- `## 原文摘要翻译` -- `## 创新点` -- `## 一句话总结` +- `## 基本情報` +- `## 要旨の翻訳` +- `## 新規性` +- `## 一言まとめ` -`## 核心信息` is a fixed metadata block, not an analysis block. +`## 基本情報` is a fixed metadata block, not an analysis block. Rules for this section: - Core info field schema: use only the following fields, in this order, and no free prose: - `标题`, `标题翻译`, `作者`, `机构`, `发表时间`, `发表渠道`, `DOI`, `arXiv`, `论文链接`, `代码 / 项目`, `数据 / 资源`, `论文类型` -- keep each line in `- 字段名: 值` form -- omit fields that are unavailable or not applicable; do not add `未知`, `无`, or placeholder rows just to fill the schema + `タイトル`, `タイトル訳`, `著者`, `所属`, `発表時期`, `発表媒体`, `DOI`, `arXiv`, `論文リンク`, `コード / プロジェクト`, `データ / リソース`, `論文タイプ` +- keep each line in `- フィールド名: 値` form +- omit fields that are unavailable or not applicable; do not add `不明`, `なし`, or placeholder rows just to fill the schema - do not add ad hoc fields such as judgments, takeaways, or mini-summaries - do not move explanatory prose, evaluation, or "my view" sentences into this section -- move any paper-positioning or guide sentence to `一句话总结` or an analysis section, not under `核心信息` +- move any paper-positioning or guide sentence to `一言まとめ` or an analysis section, not under `基本情報` -The `原文摘要翻译` section should be a Chinese translation of the paper's original abstract: -- if the abstract is available, translate the original abstract into Chinese before the one-sentence summary +The `要旨の翻訳` section should be a Japanese translation of the paper's original abstract: +- if the abstract is available, translate the original abstract into Japanese before the one-sentence summary - do not let the summary replace the abstract -- do not treat `原文摘要翻译` as your own summary of the full paper; it is the original abstract translated into Chinese -- do not split this section into `### 英文原文` and `### 中文翻译` -- keep the section title exactly as `原文摘要翻译` -- the `原文摘要翻译` section itself must be written in Chinese; do not output English abstract sentences or English-original paragraphs here -- the Chinese abstract should be fluent and faithful, not a second `一句话总结` -- do not turn `原文摘要翻译` into a selective excerpt or a compressed highlight list -- do not add judgments, hindsight, or details learned from later sections of the paper into `原文摘要翻译`; only translate what the original abstract says - -The `创新点` section should be a dedicated top-level section after `原文摘要翻译` rather than a hidden bullet buried later. +- do not treat `要旨の翻訳` as your own summary of the full paper; it is the original abstract translated into Japanese +- do not split this section into `### 英語原文` and `### 日本語訳` +- keep the section title exactly as `要旨の翻訳` +- the `要旨の翻訳` section itself must be written in Japanese; do not output English abstract sentences or English-original paragraphs here +- the Japanese abstract should be fluent and faithful, not a second `一言まとめ` +- do not turn `要旨の翻訳` into a selective excerpt or a compressed highlight list +- do not add judgments, hindsight, or details learned from later sections of the paper into `要旨の翻訳`; only translate what the original abstract says + +The `新規性` section should be a dedicated top-level section after `要旨の翻訳` rather than a hidden bullet buried later. It should usually: - enumerate 3 to 5 paper-specific innovations - explain what problem each innovation addresses @@ -93,7 +93,7 @@ Scripts are not enough on their own for: - nuanced judgment - identifying what is easy to misread - deciding what the paper's real contribution is -- writing strong, natural Chinese analytical prose +- writing strong, natural Japanese analytical prose The language model should do all of the following: - use the grounded plan's selected paper type and section emphasis @@ -103,7 +103,7 @@ The language model should do all of the following: - select the truly central results - reconstruct the method or analysis flow - decide whether the paper needs explicit LaTeX formulas for the core objective, factorization, or complexity -- write the final note in clean Chinese +- write the final note in clean Japanese ## Final-Draft Standard @@ -118,15 +118,15 @@ When the source comparison is naturally tabular, especially with three or more c Keep only the rows and metrics that matter for understanding the paper, and follow the table with interpretation of what the numbers mean. If a paper is short, do not make the final note shallow; use the saved space to explain protocol details, ablations, limitations, and deployment or replication implications. -The final Chinese note must also pass a language-cleanliness check: -- no half-English half-Chinese prose lines +The final Japanese note must also pass a language-cleanliness check: +- no half-English half-Japanese prose lines - English is allowed only for stable proper nouns or citation metadata - if the style gate fails, do not write the note into Obsidian yet - do not write for the linter; lint is only a minimum floor, not the writing objective - after script lint passes, `final_quality_review` and then `final_readability_review` are still required before the note should be treated as polished and ready to save -正文术语策略: -- default to natural Chinese prose in正文分析 +本文の用語方針: +- default to natural Japanese prose in the main-body analysis - keep English only when it is a stable proper noun or source-faithful technical label - stable English that may remain: - model names @@ -136,13 +136,13 @@ The final Chinese note must also pass a language-cleanliness check: - math symbols - code tokens - original paper figure/table ids -- when any of the above retained English terms or standalone key numbers appear inline within Chinese prose, wrap them in backticks for visual separation -- English that should usually be rewritten into natural Chinese: +- when any of the above retained English terms or standalone key numbers appear inline within Japanese prose, wrap them in backticks for visual separation +- English that should usually be rewritten into natural Japanese: - ordinary English phrases - abstract descriptive phrases in analytical prose - leftover English wording that has no clear reason to remain -- when a first mention benefits from both forms, prefer Chinese-first wording with an English gloss in parentheses -- do not leave phrases such as `reasoning dataset`, `distillation risk`, or `reward model quality` directly inside Chinese prose when a natural Chinese rendering is available +- when a first mention benefits from both forms, prefer Japanese-first wording with an English gloss in parentheses +- do not leave phrases such as `reasoning dataset`, `distillation risk`, or `reward model quality` directly inside Japanese prose when a natural Japanese rendering is available For non-trivial papers, the note should usually not stop at only broad `##` sections. It should use meaningful `###` subheadings where they improve technical clarity. @@ -150,22 +150,22 @@ It should use meaningful `###` subheadings where they improve technical clarity. Draft only from a note plan that has already passed grounding. Use its paper type, evidence-backed claims, boundaries, limiting results, mechanism-result links, comparisons, reusable takeaways, and follow-up questions as writing commitments rather than reopening the planning contract here. Examples: -- `### 数据来源` -- `### 任务定义` -- `### 中间特征抽取` -- `### 训练细节` -- `### 哪些结果最重要` -- `### 哪些地方容易被误读` +- `### データソース` +- `### タスク定義` +- `### 中間特徴の抽出` +- `### 訓練の詳細` +- `### どの結果が最も重要か` +- `### 誤読されやすい箇所` For technical papers, also strongly consider subsections such as: -- `### 机制流程` -- `### 训练目标` -- `### 推理与采样链路` -- `### 关键实现细节` -- `### 复杂度与扩展性` -- `### 消融到底说明了什么` - -For method, framework, and system papers, prefer an explicit `### 机制流程` subsection instead of hiding the execution chain inside generic prose. +- `### 機構フロー` +- `### 訓練目標` +- `### 推論とサンプリングの流れ` +- `### 主要な実装の詳細` +- `### 計算量とスケーラビリティ` +- `### アブレーションが結局何を示すのか` + +For method, framework, and system papers, prefer an explicit `### 機構フロー` subsection instead of hiding the execution chain inside generic prose. That subsection should usually be a 3 to 4 step numbered list covering: - what the Input is - what the main intermediate transformations are @@ -173,7 +173,7 @@ That subsection should usually be a 3 to 4 step numbered list covering: - what the training or inference loop is actually doing - do not rely on a damaged Algorithm block to carry this explanation for you - do not let the steps collapse into module-name listing; each step should describe an operation -- if a high-confidence pipeline or architecture figure matches this execution chain, place it in `### 机制流程` +- if a high-confidence pipeline or architecture figure matches this execution chain, place it in `### 機構フロー` ## Formula Rule @@ -203,7 +203,7 @@ Use formulas sparingly and purposefully: ## Prose Cleanliness -Chinese paragraphs should read like natural prose, not like PDF fragments. +Japanese paragraphs should read like natural prose, not like PDF fragments. Do not leave: - mid-sentence line breaks after commas or semicolons @@ -235,7 +235,7 @@ Before outputting the final Markdown, first run `final_quality_review` and expli - does the note contain concrete numbers, dimensions, complexity terms, or formulas when the paper clearly depends on them? - can a reader familiar with Python and deep learning frameworks follow the core method from this note alone? - does the method section explain the mechanism rather than only summarize the claim? -- if this is a method/system/framework paper, does `方法主线` explicitly contain `### 机制流程` with a 3 to 4 step numbered list? +- if this is a method/system/framework paper, does `手法の骨子` explicitly contain `### 機構フロー` with a 3 to 4 step numbered list? - if the raw source reports negative or unstable ablation settings, did the note include at least one of them? - if the raw source does not clearly report such settings, did the note avoid inventing failed or unstable cases? - does the note contain at least one honest limitation and one paper-specific insight? @@ -248,8 +248,8 @@ After `final_quality_review`, run `final_readability_review`. This review is a language-and-expression pass, not a second evidence-judgment pass: - improve fluency and readability - remove stiff translations -- convert ordinary English phrase leftovers into natural Chinese -- remove mechanical term-replacement artifacts such as `KV缓存 of`, `批量ing`, `In相关 Researcher`, or `Single 序列 generation`; figure/table callout titles and captions count too +- convert ordinary English phrase leftovers into natural Japanese +- remove mechanical term-replacement artifacts such as `KVキャッシュ of`, `バッチing`, `In関連 Researcher`, or `Single シーケンス generation`; figure/table callout titles and captions count too - keep stable proper nouns when forcing a translation would sound worse - do not invent new facts, numbers, comparisons, or failure cases during this pass - do not use polish as an excuse to flatten the note into a safer but shallower summary diff --git a/skills/deeppapernote/references/note-quality.md b/skills/deeppapernote/references/note-quality.md index d420712..27aa717 100644 --- a/skills/deeppapernote/references/note-quality.md +++ b/skills/deeppapernote/references/note-quality.md @@ -16,24 +16,24 @@ The note is high quality only if it satisfies most of the checks below. ## Structural Checks The note should usually include: -- `核心信息` -- `原文摘要翻译` -- `创新点` -- `一句话总结` -- `研究问题` -- `数据与任务定义` -- `方法主线` -- `关键结果` -- `深度分析` -- `局限` -- `我的笔记` -- `引用` +- `基本情報` +- `要旨の翻訳` +- `新規性` +- `一言まとめ` +- `研究課題` +- `データとタスク定義` +- `手法の骨子` +- `主要な結果` +- `深掘り分析` +- `限界` +- `私のメモ` +- `参考文献` For non-trivial papers, it should usually also include multiple `###` subheadings inside: -- `数据与任务定义` -- `方法主线` -- `关键结果` -- `深度分析` +- `データとタスク定義` +- `手法の骨子` +- `主要な結果` +- `深掘り分析` Before the final note is written, the run should already have an inspectable, grounded plan whose analytical commitments are paper-specific. @@ -57,7 +57,7 @@ Bad sign: - It lists model names without explaining the pipeline. - It copies metrics without noting the evaluation setting. - It says the paper is innovative without locating the innovation. -- It has no dedicated `创新点` section and leaves the paper's novelty scattered across the note. +- It has no dedicated `新規性` section and leaves the paper's novelty scattered across the note. - It uses generic limitations such as "future work can use more data" and nothing more specific. - It flattens a technically rich paper into only `##` headings with no internal structure. @@ -70,7 +70,7 @@ Fail closed if any of these are missing: - enough metadata to label the note responsibly Also fail closed if: -- the final Chinese note still contains mixed-language prose lines +- the final Japanese note still contains mixed-language prose lines - English remains in full clauses rather than only stable proper nouns, model names, venues, URLs, or DOIs - figure placeholders include untranslated caption sentences that read like raw extraction rather than note prose @@ -89,8 +89,8 @@ For technical papers, strong notes should usually also contain: - at least one key formula or formal expression when the paper's contribution depends on it - formulas rendered as math rather than code formatting -When abstract metadata exists, strong notes should also make `原文摘要翻译` a faithful Chinese translation of the abstract: -- translate the original abstract into Chinese rather than rewriting it as your own summary +When abstract metadata exists, strong notes should also make `要旨の翻訳` a faithful Japanese translation of the abstract: +- translate the original abstract into Japanese rather than rewriting it as your own summary - avoid reducing it to a shorter interpretation-only summary -- keep this section as `原文摘要翻译`, not a bilingual original-plus-translation block +- keep this section as `要旨の翻訳`, not a bilingual original-plus-translation block - do not mix innovation takeaways, evaluation, or post-hoc interpretation into this section diff --git a/skills/deeppapernote/references/obsidian-format.md b/skills/deeppapernote/references/obsidian-format.md index a5fbc03..3cb09d3 100644 --- a/skills/deeppapernote/references/obsidian-format.md +++ b/skills/deeppapernote/references/obsidian-format.md @@ -7,16 +7,16 @@ - Use `###` only when a section genuinely needs internal structure. - Do not flatten everything into bullet points. - For method, system, benchmark, or clinical empirical papers, prefer meaningful `###` subheadings in technical sections instead of one long undifferentiated block. -- For method, framework, or system papers, default to `### 机制流程` inside `方法主线` and write it as a numbered 3 to 4 step flow. +- For method, framework, or system papers, default to `### 機構フロー` inside `手法の骨子` and write it as a numbered 3 to 4 step flow. ## File Naming Default file name: - sanitized English title with underscores - default note layout is folder-per-paper: - - `<领域>//.md` - - `<领域>//images/...` -- when deciding `<领域>`, prefer matching an existing first-level domain folder under the user's papers directory + - `<領域>//.md` + - `<領域>//images/...` +- when deciding `<領域>`, prefer matching an existing first-level domain folder under the user's papers directory - domain routing uses the editable taxonomy in `references/domain_rules.yaml`: application domains are checked before fallback method domains - reuse existing first-level folders conservatively; method-only evidence should not force reuse of an unrelated application folder - only create a new domain folder when no existing domain is a reasonable fit @@ -31,7 +31,7 @@ If the user already has a vault convention, preserve it. - Prefer short paragraphs over long bullet lists. - Use bullets for metadata and sharply list-shaped content. - Keep code or metric identifiers in backticks. -- When English proper nouns (model names, dataset names, method names, metric names, venue abbreviations) or standalone key numeric values appear inline within Chinese prose, wrap them in backticks for visual separation — e.g. `GPT-4`、`SQuAD`、`BLEU`、`87.3%`. +- When English proper nouns (model names, dataset names, method names, metric names, venue abbreviations) or standalone key numeric values appear inline within Japanese prose, wrap them in backticks for visual separation — e.g. `GPT-4`、`SQuAD`、`BLEU`、`87.3%`. - Preserve stable internal links where useful. - Use normal LaTeX delimiters for math: - inline math: `$...$` @@ -43,16 +43,16 @@ If the user already has a vault convention, preserve it. ## Core Info Block -`## 核心信息` is a fixed metadata zone. +`## 基本情報` is a fixed metadata zone. Formatting and scope rules: - Core info field schema: use only the following fields, in this order, and no free prose: - `标题`, `标题翻译`, `作者`, `机构`, `发表时间`, `发表渠道`, `DOI`, `arXiv`, `论文链接`, `代码 / 项目`, `数据 / 资源`, `论文类型` -- keep each entry in `- 字段名: 值` form + `タイトル`, `タイトル訳`, `著者`, `所属`, `発表時期`, `発表媒体`, `DOI`, `arXiv`, `論文リンク`, `コード / プロジェクト`, `データ / リソース`, `論文タイプ` +- keep each entry in `- フィールド名: 値` form - omit fields that are unavailable or not applicable; do not add placeholder rows just to fill the schema -- do not add interpretation, commentary, judgment, or takeaway lines inside `核心信息` +- do not add interpretation, commentary, judgment, or takeaway lines inside `基本情報` - do not use the last metadata bullet as a place to append extra analysis -- move explanatory content to `一句话总结`、`深度分析`、`我的笔记` or another true analysis section +- move explanatory content to `一言まとめ`、`深掘り分析`、`私のメモ` or another true analysis section ## YAML Frontmatter @@ -87,22 +87,22 @@ Rules: Use this callout format only for placeholders that remain unresolved in the final note: ```md -> [!figure] Fig. 3 数据分布与质量评估 -> 建议位置:数据与任务定义 -> 放置原因:这张图同时展示样本构成、对话长度统计和专家质检结果,是理解 `PsyInterview` 数据边界最重要的图之一。 -> 当前状态:保留占位;当前提取结果只拿到局部子图,无法稳定恢复成可独立解释的完整原图。 +> [!figure] Fig. 3 データ分布と品質評価 +> 推奨位置:データとタスク定義 +> 配置理由:この図はサンプル構成・対話長の統計・専門家による品質チェック結果を同時に示しており、`PsyInterview` データの境界を理解する上で最も重要な図の一つである。 +> 現在の状態:プレースホルダを保持。現在の抽出結果では局所的なサブ図しか取得できず、独立して解釈可能な完全な原図として安定的に復元できない。 ``` Formatting rules: - keep the original paper numbering, for example `Fig. 3` or `Table 2` - keep a short human-readable label on the first line -- always include `建议位置` -- always include `放置原因` -- always include `当前状态` +- always include `推奨位置` +- always include `配置理由` +- always include `現在の状態` -`当前状态` should be explicit, for example: -- `保留占位;未找到高置信度整图。` -- `保留占位;当前只匹配到疑似局部子图,不足以稳定替换。` +`現在の状態` should be explicit, for example: +- `プレースホルダを保持。高信頼度の全体図が見つからない。` +- `プレースホルダを保持。現在は疑わしい局所サブ図しかマッチせず、安定的な置き換えには不十分。` The structured `[FIGURE_PLACEHOLDER] ... [/FIGURE_PLACEHOLDER]` block is legacy/internal only. Do not use it in the final user-facing note unless you are debugging the pipeline. @@ -113,37 +113,37 @@ The embed must be followed immediately by exactly one italic caption line: ```md ![[Research/Papers/DeepPaperNote/paper_slug/images/page_003_img_01.png]] -*论文原图编号:Fig. 2。数据生成流程图。这里插入是因为它最能帮助理解方法主线。* +*論文原図番号:Fig. 2。データ生成のフロー図。ここに挿入するのは、手法の骨子の理解に最も役立つためである。* ``` ## Default Section Order -1. `核心信息` -2. `原文摘要翻译` -3. `创新点` -4. `一句话总结` -5. `研究问题` -6. `数据与任务定义` -7. `方法主线` -8. `关键结果` -9. `深度分析` -10. `局限` -11. `我的笔记` -12. `引用` - -When abstract metadata exists, `原文摘要翻译` should be a single Chinese translation block for the original abstract rather than a bilingual subsection pair. +1. `基本情報` +2. `要旨の翻訳` +3. `新規性` +4. `一言まとめ` +5. `研究課題` +6. `データとタスク定義` +7. `手法の骨子` +8. `主要な結果` +9. `深掘り分析` +10. `限界` +11. `私のメモ` +12. `参考文献` + +When abstract metadata exists, `要旨の翻訳` should be a single Japanese translation block for the original abstract rather than a bilingual subsection pair. This order is the stable backbone, not a full outline. When the paper is complex, add `###` subsections such as: -- `### 数据来源` -- `### 任务定义` -- `### 机制流程` -- `### 为什么结果成立` -- `### 哪些地方容易被误读` +- `### データソース` +- `### タスク定義` +- `### 機構フロー` +- `### なぜ結果が成立するのか` +- `### 誤読されやすい箇所` -## 引用 Section Format +## 参考文献 Section Format -Entries in `## 引用` should link to existing notes in the vault where possible. +Entries in `## 参考文献` should link to existing notes in the vault where possible. If the synthesis bundle includes `references.candidates`, use confirmed candidate `wikilink` values when present. When `wikilink` is empty, treat `display_text` as the plain-text fallback. Follow this priority order for each reference: diff --git a/skills/deeppapernote/references/paper-types.md b/skills/deeppapernote/references/paper-types.md index 758cff0..80e5f7a 100644 --- a/skills/deeppapernote/references/paper-types.md +++ b/skills/deeppapernote/references/paper-types.md @@ -11,103 +11,103 @@ Use `contracts_by_paper_type[note_plan.paper_type]` as the canonical structured ## `AI_method` section_semantics: -- 研究问题: 方法要解决的具体技术问题和现有方法短板。 -- 数据与任务定义: 数据集、输入输出、评测任务和实验设置。 -- 方法主线: 模型、算法、训练或推理机制。 -- 关键结果: 主结果、强基线、消融和关键数字。 -- 深度分析: 方法为什么有效、何处脆弱、复现和扩展代价。 +- 研究課題: 手法が解決しようとする具体的な技術課題と、既存手法の弱点。 +- データとタスク定義: データセット、入出力、評価タスク、実験設定。 +- 手法の骨子: モデル、アルゴリズム、学習または推論の機構。 +- 主要な結果: 主結果、強力なbaseline、ablation、重要な数値。 +- 深掘り分析: 手法がなぜ有効か、どこが脆弱か、再現と拡張のコスト。 recommended_subsections: -- 方法主线: `机制流程`, `模型结构`, `训练目标`, `推理与采样链路`, `关键实现细节` -- 关键结果: `主结果与强基线`, `消融到底说明了什么`, `失败或不稳定设置` -- 深度分析: `为什么有效`, `复杂度与扩展性`, `复现注意点` +- 手法の骨子: `機構フロー`, `モデル構造`, `学習目標`, `推論とサンプリングの経路`, `重要な実装の詳細` +- 主要な結果: `主結果と強力なbaseline`, `ablationが結局何を示しているか`, `失敗または不安定な設定` +- 深掘り分析: `なぜ有効か`, `計算量と拡張性`, `再現時の注意点` boundary_questions: -- 核心机制的收益由哪个实验或消融支撑,而不是只由主结果暗示? -- 哪些比较只能证明在当前数据、基线、算力或协议下有效,不能外推到通用场景? -- 论文是否给出失败、退化、不稳定或成本上升的证据;如果没有,结论边界是什么? +- 中核となる機構の効果は、主結果によって示唆されるだけでなく、どの実験やablationによって裏付けられているか? +- どの比較は、現在のデータ・baseline・計算資源・プロトコルの下でのみ有効であることを示すにとどまり、汎用的な場面へ外挿できないか? +- 論文は失敗・劣化・不安定・コスト増大の証拠を示しているか。示していない場合、結論の境界はどこにあるか? ## `benchmark_or_dataset` section_semantics: -- 研究问题: 这个 benchmark/dataset 想补足的评测或数据缺口。 -- 数据与任务定义: 数据来源、任务拆分、标签/题目定义、样本范围。 -- 方法主线: 数据构建、筛选、标注和评测协议,不写成模型 pipeline。 -- 关键结果: 基线表现、难度分布、覆盖范围和偏差。 -- 深度分析: 它真正测到了什么,以及不能代表什么。 +- 研究課題: この benchmark/dataset が補おうとしている評価またはデータのギャップ。 +- データとタスク定義: データの出所、タスクの分割、ラベル/設問の定義、サンプルの範囲。 +- 手法の骨子: データ構築、選別、アノテーション、評価プロトコル。モデルの pipeline としては書かない。 +- 主要な結果: baselineの性能、難易度の分布、カバー範囲とバイアス。 +- 深掘り分析: それが実際に測定できているものと、代表できないもの。 recommended_subsections: -- 数据与任务定义: `数据来源`, `任务拆分`, `标注/筛选协议` -- 方法主线: `构建流程`, `评测协议`, `Baseline 设置` -- 关键结果: `基线表现`, `难度分布`, `覆盖与偏差` -- 深度分析: `benchmark 真正测到了什么`, `适用边界` +- データとタスク定義: `データの出所`, `タスクの分割`, `アノテーション/選別プロトコル` +- 手法の骨子: `構築フロー`, `評価プロトコル`, `Baseline 設定` +- 主要な結果: `baselineの性能`, `難易度の分布`, `カバー範囲とバイアス` +- 深掘り分析: `benchmark が実際に測定できているもの`, `適用の境界` boundary_questions: -- 这个 benchmark/dataset 实际测量的构念是什么,哪些能力只是间接近似? -- 任务、标签、采样、过滤或评测协议会引入哪些覆盖缺口或偏差? -- 基线结果证明了评测集有区分度,还是只证明某类模型适应该协议? -- 样本时长、语料长度、人口统计、类别分布、数据可访问性或隐私限制如何影响复现和外推? +- この benchmark/dataset が実際に測定している構成概念は何か。どの能力は間接的な近似にすぎないか? +- タスク・ラベル・サンプリング・フィルタリング・評価プロトコルは、どのようなカバレッジの欠落やバイアスを持ち込むか? +- baselineの結果は、評価セットに識別力があることを示しているのか、それとも特定の種類のモデルがそのプロトコルに適応しているだけなのか? +- サンプルの長さ、コーパスの長さ、人口統計、クラス分布、データのアクセス可能性、プライバシー上の制約は、再現と外挿にどのように影響するか? ## `clinical_or_psychology_empirical` section_semantics: -- 研究问题: 临床、心理学或行为科学中的研究问题、假设或变量关系。 -- 数据与任务定义: 样本来源、纳排标准、变量/量表、测量方式。 -- 方法主线: 研究设计、分组、测量流程和统计分析路径。 -- 关键结果: 主要效应、相关性、组间差异、不确定性或显著性。 -- 深度分析: 结果解释、因果边界、临床/心理学意义和外推限制。 +- 研究課題: 臨床・心理学・行動科学における研究課題、仮説、または変数間の関係。 +- データとタスク定義: サンプルの出所、選択・除外基準、変数/尺度、測定方法。 +- 手法の骨子: 研究デザイン、群分け、測定手順、統計分析の道筋。 +- 主要な結果: 主要な効果、相関、群間差、不確実性または有意性。 +- 深掘り分析: 結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。 recommended_subsections: -- 数据与任务定义: `样本与纳排标准`, `变量与量表`, `测量流程` -- 方法主线: `研究设计`, `分析模型`, `主要比较` -- 关键结果: `主要效应`, `不确定性与显著性`, `临床或心理学解释` -- 深度分析: `因果解释边界`, `外推限制` +- データとタスク定義: `サンプルと選択・除外基準`, `変数と尺度`, `測定手順` +- 手法の骨子: `研究デザイン`, `分析モデル`, `主要な比較` +- 主要な結果: `主要な効果`, `不確実性と有意性`, `臨床または心理学的解釈` +- 深掘り分析: `因果的解釈の境界`, `外挿の限界` boundary_questions: -- 样本来源、纳排标准、测量工具和标注流程如何限制外推? -- 结果支持相关、预测、组间差异还是因果解释;不要越过论文设计能证明的范围。 -- 临床或心理学意义是否依赖未观测混杂、量表阈值、文本/语音缺失或场景约束? -- 样本构成、数据缺失、隐私限制或材料不可公开会怎样限制复现与再分析? +- サンプルの出所、選択・除外基準、測定ツール、アノテーション手順は、外挿をどのように制限するか? +- 結果が支持するのは相関か、予測か、群間差か、それとも因果的解釈か。論文のデザインが証明できる範囲を越えないこと。 +- 臨床または心理学的意義は、未観測の交絡、尺度の閾値、テキスト/音声の欠損、場面的な制約に依存していないか? +- サンプルの構成、データの欠損、プライバシー上の制約、資料が非公開であることは、再現と再分析をどのように制限するか? ## `humanities_or_social_science` section_semantics: -- 研究问题: 作者要解释的社会、文化、历史、制度或理论问题。 -- 数据与任务定义: 材料、案例、文本、访谈、档案或语料范围,不写成 ML task。 -- 方法主线: 理论框架、概念区分和论证路径。 -- 关键结果: 核心解释性发现、概念贡献或对既有观点的修正。 -- 深度分析: 论证强度、材料边界、解释替代性和可迁移性。 +- 研究課題: 著者が説明しようとする社会・文化・歴史・制度または理論上の問い。 +- データとタスク定義: 資料、事例、テキスト、インタビュー、アーカイブまたはコーパスの範囲。ML task としては書かない。 +- 手法の骨子: 理論的枠組み、概念の区別、論証の道筋。 +- 主要な結果: 中核となる解釈的知見、概念的貢献、または既存の見解への修正。 +- 深掘り分析: 論証の強さ、資料の境界、解釈の代替可能性、転用可能性。 recommended_subsections: -- 数据与任务定义: `材料范围`, `选择标准`, `案例或语料边界` -- 方法主线: `理论框架`, `概念区分`, `论证路径` -- 关键结果: `核心解释性发现`, `概念贡献` -- 深度分析: `论证强度`, `替代解释`, `材料边界` +- データとタスク定義: `資料の範囲`, `選択基準`, `事例またはコーパスの境界` +- 手法の骨子: `理論的枠組み`, `概念の区別`, `論証の道筋` +- 主要な結果: `中核となる解釈的知見`, `概念的貢献` +- 深掘り分析: `論証の強さ`, `代替的な解釈`, `資料の境界` boundary_questions: -- 作者的解释依赖哪些材料、案例或理论前提? -- 是否存在同样能解释材料的替代解释,论文如何排除或没有排除? -- 哪些结论是概念贡献或规范性判断,而不是可直接当作经验事实? +- 著者の解釈は、どの資料・事例・理論的前提に依存しているか? +- 同じ資料を同様に説明できる代替的な解釈は存在するか。論文はそれをどのように排除しているか、あるいは排除できていないか? +- どの結論が概念的貢献または規範的判断であって、そのまま経験的事実として扱えるものではないか? ## `survey_or_review` section_semantics: -- 研究问题: 综述试图整理的领域问题、争议或知识缺口。 -- 数据与任务定义: 纳入文献范围、检索/筛选标准和综述对象。 -- 方法主线: 分类体系、综述组织方式和证据综合逻辑,不写成单篇方法架构。 -- 关键结果: 领域共识、分歧、趋势、代表性方向和开放问题。 -- 深度分析: 综述覆盖的盲区、分类体系的解释力和未来研究机会。 +- 研究課題: サーベイが整理しようとしている分野の問い、論争、または知識のギャップ。 +- データとタスク定義: 対象とする文献の範囲、検索/選別の基準、サーベイの対象。 +- 手法の骨子: 分類体系、サーベイの構成方法、エビデンス統合の論理。個別論文の手法アーキテクチャとしては書かない。 +- 主要な結果: 分野の合意、相違、動向、代表的な方向性、未解決の問い。 +- 深掘り分析: サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。 recommended_subsections: -- 数据与任务定义: `综述范围`, `纳入/排除标准`, `文献覆盖` -- 方法主线: `分类体系`, `方法谱系`, `证据组织方式` -- 关键结果: `代表性方向`, `共识与分歧`, `开放问题` -- 深度分析: `分类体系的局限`, `未覆盖区域`, `后续研究机会` +- データとタスク定義: `サーベイの範囲`, `包含/除外基準`, `文献のカバレッジ` +- 手法の骨子: `分類体系`, `手法の系譜`, `エビデンスの構成方法` +- 主要な結果: `代表的な方向性`, `合意と相違`, `未解決の問い` +- 深掘り分析: `分類体系の限界`, `カバーされていない領域`, `今後の研究機会` boundary_questions: -- 检索范围、纳入排除标准或分类轴会遗漏哪些研究路线? -- 综述给出的是领域共识、作者分类,还是尚未解决的分歧? -- 哪些趋势结论来自覆盖范围内的文献分布,不能直接当作技术成熟度判断? +- 検索範囲、包含・除外基準、分類軸は、どの研究の流れを取りこぼすか? +- サーベイが示しているのは分野の合意か、著者による分類か、それともまだ解決されていない相違か? +- どの動向に関する結論が、カバー範囲内の文献分布に由来するにとどまり、そのまま技術成熟度の判断として扱えないか? ## Selection Rule diff --git a/skills/deeppapernote/scripts/build_synthesis_bundle.py b/skills/deeppapernote/scripts/build_synthesis_bundle.py index 539171b..2eeb786 100644 --- a/skills/deeppapernote/scripts/build_synthesis_bundle.py +++ b/skills/deeppapernote/scripts/build_synthesis_bundle.py @@ -345,7 +345,7 @@ def compact_writing_contract() -> dict: analysis_coverage["final_quality_review_checks"] ) return { - "language": "zh-CN", + "language": "ja", "contract_role": "manifest_quality_contract", "canonical_source": ( "SKILL.md defines the workflow; scripts/contracts.py defines " diff --git a/skills/deeppapernote/scripts/common.py b/skills/deeppapernote/scripts/common.py index b71ccb1..d68857a 100644 --- a/skills/deeppapernote/scripts/common.py +++ b/skills/deeppapernote/scripts/common.py @@ -2501,9 +2501,9 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: DEFAULT_DOMAIN_RULES: dict[str, list[dict[str, Any]]] = { "domains": [ { - "label": "医疗健康", + "label": "医療・健康", "aliases": ["healthcare", "medical", "clinical medicine"], - "specialized_folders": ["心理健康"], + "specialized_folders": ["メンタルヘルス"], "keywords": [ "clinical", "patient", @@ -2573,7 +2573,7 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: "methods": [], }, { - "label": "机器人", + "label": "ロボティクス", "aliases": ["robotics", "robotic"], "keywords": [ "robot", @@ -2589,7 +2589,7 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: "methods": ["diffusion policy"], }, { - "label": "软件工程", + "label": "ソフトウェア工学", "aliases": ["software engineering"], "keywords": [ "software engineering", @@ -2620,8 +2620,8 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: "methods": [], }, { - "label": "心理健康", - "route_to": "医疗健康", + "label": "メンタルヘルス", + "route_to": "医療・健康", "aliases": ["mental health", "psychology", "psychiatry"], "keywords": [ "depression", @@ -2636,7 +2636,7 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: "methods": [], }, { - "label": "推荐系统", + "label": "推薦システム", "aliases": ["recommender systems", "recommendation"], "keywords": [ "recommendation", @@ -2650,7 +2650,7 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: ], "fallback_domains": [ { - "label": "大模型", + "label": "大規模言語モデル", "aliases": ["llm", "large language model", "language model", "foundation model"], "keywords": [ "large language model", @@ -2681,7 +2681,7 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: "methods": [], }, { - "label": "机器学习", + "label": "機械学習", "aliases": ["machine learning", "ml"], "keywords": [ "machine learning", @@ -2974,10 +2974,10 @@ def infer_domain_label(title: str, abstract: str = "") -> str: paper_type, _ = infer_paper_type(title, abstract) if paper_type == "clinical_or_psychology_empirical": - return "医疗健康" + return "医療・健康" if paper_type == "AI_method": - return "机器学习" - return "未分类" + return "機械学習" + return "未分類" def is_probable_paper_folder(path: Path) -> bool: diff --git a/skills/deeppapernote/scripts/contracts.py b/skills/deeppapernote/scripts/contracts.py index e4a3dbe..20d9e1a 100644 --- a/skills/deeppapernote/scripts/contracts.py +++ b/skills/deeppapernote/scripts/contracts.py @@ -6,18 +6,18 @@ from typing import Any, TypedDict NOTE_REQUIRED_SECTIONS: tuple[str, ...] = ( - "核心信息", - "原文摘要翻译", - "创新点", - "一句话总结", - "研究问题", - "数据与任务定义", - "方法主线", - "关键结果", - "深度分析", - "局限", - "我的笔记", - "引用", + "基本情報", + "要旨の翻訳", + "新規性", + "一言まとめ", + "研究課題", + "データとタスク定義", + "手法の骨子", + "主要な結果", + "深掘り分析", + "限界", + "私のメモ", + "参考文献", ) PAPER_TYPE_VALUES: tuple[str, ...] = ( @@ -83,76 +83,76 @@ def required_field_value_error( PAPER_TYPE_SECTION_PROFILES: dict[str, dict[str, dict[str, Any]]] = { "AI_method": { "section_semantics": { - "研究问题": "方法要解决的具体技术问题和现有方法短板。", - "数据与任务定义": "数据集、输入输出、评测任务和实验设置。", - "方法主线": "模型、算法、训练或推理机制。", - "关键结果": "主结果、强基线、消融和关键数字。", - "深度分析": "方法为什么有效、何处脆弱、复现和扩展代价。", + "研究課題": "手法が解決しようとする具体的な技術課題と、既存手法の弱点。", + "データとタスク定義": "データセット、入出力、評価タスク、実験設定。", + "手法の骨子": "モデル・アルゴリズム・学習または推論の機構。", + "主要な結果": "主結果、強力なベースライン、アブレーション、重要な数値。", + "深掘り分析": "手法がなぜ有効か、どこが脆いか、再現・拡張のコスト。", }, "recommended_subsections": { - "方法主线": ["机制流程", "模型结构", "训练目标", "推理与采样链路", "关键实现细节"], - "关键结果": ["主结果与强基线", "消融到底说明了什么", "失败或不稳定设置"], - "深度分析": ["为什么有效", "复杂度与扩展性", "复现注意点"], + "手法の骨子": ["機構フロー", "モデル構造", "学習目標", "推論・サンプリング経路", "主要な実装詳細"], + "主要な結果": ["主要結果と強力なベースライン", "アブレーションが示すもの", "失敗・不安定な設定"], + "深掘り分析": ["なぜ有効か", "計算量とスケーラビリティ", "再現時の注意点"], }, }, "benchmark_or_dataset": { "section_semantics": { - "研究问题": "这个 benchmark/dataset 想补足的评测或数据缺口。", - "数据与任务定义": "数据来源、任务拆分、标签/题目定义、样本范围。", - "方法主线": "数据构建、筛选、标注和评测协议,不写成模型 pipeline。", - "关键结果": "基线表现、难度分布、覆盖范围和偏差。", - "深度分析": "它真正测到了什么,以及不能代表什么。", + "研究課題": "この benchmark/dataset が埋めようとする評価またはデータのギャップ。", + "データとタスク定義": "データ出所、タスク分割、ラベル/課題の定義、サンプル範囲。", + "手法の骨子": "データ構築・選別・アノテーション・評価プロトコル。モデルの pipeline としては書かない。", + "主要な結果": "ベースライン性能、難易度分布、カバレッジ範囲、バイアス。", + "深掘り分析": "実際に何を測れているか、そして何を代表できないか。", }, "recommended_subsections": { - "数据与任务定义": ["数据来源", "任务拆分", "标注/筛选协议"], - "方法主线": ["构建流程", "评测协议", "Baseline 设置"], - "关键结果": ["基线表现", "难度分布", "覆盖与偏差"], - "深度分析": ["benchmark 真正测到了什么", "适用边界"], + "データとタスク定義": ["データ出所", "タスク分割", "アノテーション/選別プロトコル"], + "手法の骨子": ["構築フロー", "評価プロトコル", "ベースライン設定"], + "主要な結果": ["ベースライン性能", "難易度分布", "カバレッジとバイアス"], + "深掘り分析": ["ベンチマークが実際に測るもの", "適用範囲の境界"], }, }, "clinical_or_psychology_empirical": { "section_semantics": { - "研究问题": "临床、心理学或行为科学中的研究问题、假设或变量关系。", - "数据与任务定义": "样本来源、纳排标准、变量/量表、测量方式。", - "方法主线": "研究设计、分组、测量流程和统计分析路径。", - "关键结果": "主要效应、相关性、组间差异、不确定性或显著性。", - "深度分析": "结果解释、因果边界、临床/心理学意义和外推限制。", + "研究課題": "臨床・心理学・行動科学における研究課題、仮説、変数間の関係。", + "データとタスク定義": "サンプル出所、選択・除外基準、変数/尺度、測定方法。", + "手法の骨子": "研究デザイン、群分け、測定手順、統計解析の道筋。", + "主要な結果": "主効果、相関、群間差、不確実性や有意性。", + "深掘り分析": "結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。", }, "recommended_subsections": { - "数据与任务定义": ["样本与纳排标准", "变量与量表", "测量流程"], - "方法主线": ["研究设计", "分析模型", "主要比较"], - "关键结果": ["主要效应", "不确定性与显著性", "临床或心理学解释"], - "深度分析": ["因果解释边界", "外推限制"], + "データとタスク定義": ["サンプルと選択・除外基準", "変数と尺度", "測定手順"], + "手法の骨子": ["研究デザイン", "分析モデル", "主要な比較"], + "主要な結果": ["主要な効果", "不確実性と有意性", "臨床・心理学的解釈"], + "深掘り分析": ["因果解釈の境界", "外挿の限界"], }, }, "humanities_or_social_science": { "section_semantics": { - "研究问题": "作者要解释的社会、文化、历史、制度或理论问题。", - "数据与任务定义": "材料、案例、文本、访谈、档案或语料范围,不写成 ML task。", - "方法主线": "理论框架、概念区分和论证路径。", - "关键结果": "核心解释性发现、概念贡献或对既有观点的修正。", - "深度分析": "论证强度、材料边界、解释替代性和可迁移性。", + "研究課題": "著者が説明しようとする社会・文化・歴史・制度・理論上の問い。", + "データとタスク定義": "資料、事例、テキスト、インタビュー、アーカイブ、コーパスの範囲。ML task としては書かない。", + "手法の骨子": "理論的枠組み、概念の区別、論証の道筋。", + "主要な結果": "中心的な解釈的発見、概念的貢献、既存の見解への修正。", + "深掘り分析": "論証の強さ、資料の境界、解釈の代替可能性、転用可能性。", }, "recommended_subsections": { - "数据与任务定义": ["材料范围", "选择标准", "案例或语料边界"], - "方法主线": ["理论框架", "概念区分", "论证路径"], - "关键结果": ["核心解释性发现", "概念贡献"], - "深度分析": ["论证强度", "替代解释", "材料边界"], + "データとタスク定義": ["資料の範囲", "選択基準", "事例・コーパスの境界"], + "手法の骨子": ["理論的枠組み", "概念の区別", "論証の道筋"], + "主要な結果": ["中心的な解釈的発見", "概念的貢献"], + "深掘り分析": ["論証の強さ", "代替的解釈", "資料の境界"], }, }, "survey_or_review": { "section_semantics": { - "研究问题": "综述试图整理的领域问题、争议或知识缺口。", - "数据与任务定义": "纳入文献范围、检索/筛选标准和综述对象。", - "方法主线": "分类体系、综述组织方式和证据综合逻辑,不写成单篇方法架构。", - "关键结果": "领域共识、分歧、趋势、代表性方向和开放问题。", - "深度分析": "综述覆盖的盲区、分类体系的解释力和未来研究机会。", + "研究課題": "サーベイが整理しようとする分野の問い、論争、知識のギャップ。", + "データとタスク定義": "対象とする文献範囲、検索/選別基準、レビュー対象。", + "手法の骨子": "分類体系、レビューの構成方法、証拠統合のロジック。単一論文の手法構成としては書かない。", + "主要な結果": "分野の合意、対立、トレンド、代表的な方向性、未解決問題。", + "深掘り分析": "サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。", }, "recommended_subsections": { - "数据与任务定义": ["综述范围", "纳入/排除标准", "文献覆盖"], - "方法主线": ["分类体系", "方法谱系", "证据组织方式"], - "关键结果": ["代表性方向", "共识与分歧", "开放问题"], - "深度分析": ["分类体系的局限", "未覆盖区域", "后续研究机会"], + "データとタスク定義": ["サーベイの範囲", "選定・除外基準", "文献カバレッジ"], + "手法の骨子": ["分類体系", "手法の系譜", "証拠の整理方法"], + "主要な結果": ["代表的な方向性", "合意と対立", "未解決問題"], + "深掘り分析": ["分類体系の限界", "未カバー領域", "今後の研究機会"], }, }, } @@ -160,22 +160,22 @@ def required_field_value_error( PAPER_TYPE_CONTRACTS: dict[str, dict[str, Any]] = { "AI_method": { "paper_type": "AI_method", - "reader_lens": "面向能复现方法机制的技术读者", + "reader_lens": "手法の機構を再現できる技術読者向け", "section_focus": [ - "问题设置", - "方法机制", - "训练/推理流程", - "关键公式", - "比较基线", - "消融与失败边界", + "問題設定", + "手法の機構", + "学習/推論フロー", + "主要な数式", + "比較ベースライン", + "アブレーションと失敗の境界", ], - "required_checks": ["需要说明机制流程、关键公式、实验设计、消融含义和失败边界。"], - "formula_rules": ["仅保留理解方法必需的 1 到 3 个关键公式,并解释其工程含义。"], - "avoid_rules": ["不要把非 AI_method 论文强行改写成模型架构。"], + "required_checks": ["機構フロー、主要な数式、実験デザイン、アブレーションの含意、失敗の境界を説明する必要がある。"], + "formula_rules": ["手法の理解に必須の 1〜3 個の主要な数式のみを残し、その工学的含意を説明する。"], + "avoid_rules": ["AI_method でない論文を無理にモデルアーキテクチャとして書き換えない。"], "boundary_questions": [ - "核心机制的收益由哪个实验或消融支撑,而不是只由主结果暗示?", - "哪些比较只能证明在当前数据、基线、算力或协议下有效,不能外推到通用场景?", - "论文是否给出失败、退化、不稳定或成本上升的证据;如果没有,结论边界是什么?", + "中心的な機構の利得は、主結果が示唆するだけでなく、どの実験やアブレーションで裏付けられているか?", + "どの比較が現在のデータ・ベースライン・計算資源・プロトコルの下でのみ有効で、汎用的な状況へ外挿できないか?", + "論文は失敗・劣化・不安定・コスト増の証拠を示しているか。示していない場合、結論の境界は何か?", ], **PAPER_TYPE_SECTION_PROFILES["AI_method"], "mechanism_flow_contract": { @@ -186,84 +186,84 @@ def required_field_value_error( }, "benchmark_or_dataset": { "paper_type": "benchmark_or_dataset", - "reader_lens": "面向要判断 benchmark/dataset 可用性和偏差边界的研究者", + "reader_lens": "benchmark/dataset の有用性とバイアスの境界を判断したい研究者向け", "section_focus": [ - "任务拆分", - "数据来源与构建流程", - "标注协议", - "评测指标", - "覆盖范围与偏差", - "样本统计与数据开放限制", + "タスク分割", + "データ出所と構築フロー", + "アノテーションプロトコル", + "評価指標", + "カバレッジ範囲とバイアス", + "サンプル統計とデータ公開制限", ], "required_checks": [ - "需要说明数据来源、构建/标注流程、评测指标、基线表现、样本统计、数据开放或隐私限制和适用边界。" + "データ出所、構築/アノテーションのフロー、評価指標、ベースライン性能、サンプル統計、データ公開やプライバシー制限、適用範囲の境界を説明する必要がある。" ], - "formula_rules": ["仅保留核心评测指标、采样规则或划分定义。"], - "avoid_rules": ["不要把数据构建流程写成模型 pipeline。"], + "formula_rules": ["中心的な評価指標、サンプリング規則、分割定義のみを残す。"], + "avoid_rules": ["データ構築フローをモデルの pipeline として書かない。"], "boundary_questions": [ - "这个 benchmark/dataset 实际测量的构念是什么,哪些能力只是间接近似?", - "任务、标签、采样、过滤或评测协议会引入哪些覆盖缺口或偏差?", - "基线结果证明了评测集有区分度,还是只证明某类模型适应该协议?", - "样本时长、语料长度、人口统计、类别分布、数据可访问性或隐私限制如何影响复现和外推?", + "この benchmark/dataset が実際に測定している構成概念は何で、どの能力は間接的な近似に過ぎないか?", + "タスク・ラベル・サンプリング・フィルタ・評価プロトコルは、どのようなカバレッジの欠落やバイアスを持ち込むか?", + "ベースライン結果は評価セットに識別力があることを示すのか、それとも特定タイプのモデルがこのプロトコルに適応しただけか?", + "サンプルの長さ、コーパス長、人口統計、クラス分布、データのアクセス可能性やプライバシー制限は、再現と外挿にどう影響するか?", ], **PAPER_TYPE_SECTION_PROFILES["benchmark_or_dataset"], }, "clinical_or_psychology_empirical": { "paper_type": "clinical_or_psychology_empirical", - "reader_lens": "面向关注临床/心理学样本、变量关系和外推边界的研究读者", + "reader_lens": "臨床/心理学のサンプル、変数関係、外挿の境界に注目する研究読者向け", "section_focus": [ - "样本来源", - "纳排标准", - "变量或量表", - "分析管线", - "效应量与不确定性", - "样本统计、伦理和数据可访问性", + "サンプル出所", + "選択・除外基準", + "変数または尺度", + "分析パイプライン", + "効果量と不確実性", + "サンプル統計・倫理・データのアクセス可能性", ], "required_checks": [ - "需要区分相关、预测、组间差异和因果解释,说明样本统计、伦理/隐私约束与外推边界。" + "相関・予測・群間差・因果的解釈を区別し、サンプル統計、倫理/プライバシー上の制約、外挿の境界を説明する必要がある。" ], - "formula_rules": ["仅保留核心统计模型、效应量、置信区间或量表定义。"], - "avoid_rules": ["不要把相关性、预测性能或组间差异写成未经证明的因果结论。"], + "formula_rules": ["中心的な統計モデル、効果量、信頼区間、尺度の定義のみを残す。"], + "avoid_rules": ["相関、予測性能、群間差を、証明されていない因果結論として書かない。"], "boundary_questions": [ - "样本来源、纳排标准、测量工具和标注流程如何限制外推?", - "结果支持相关、预测、组间差异还是因果解释;不要越过论文设计能证明的范围。", - "临床或心理学意义是否依赖未观测混杂、量表阈值、文本/语音缺失或场景约束?", - "样本构成、数据缺失、隐私限制或材料不可公开会怎样限制复现与再分析?", + "サンプル出所、選択・除外基準、測定ツール、アノテーション手順は、外挿をどのように制限するか?", + "結果は相関・予測・群間差・因果的解釈のどれを支持するか。論文のデザインが証明できる範囲を越えないこと。", + "臨床または心理学的意義は、未観測の交絡、尺度の閾値、テキスト/音声の欠落、場面の制約に依存していないか?", + "サンプル構成、データ欠損、プライバシー制限、資料の非公開は、再現と再分析をどのように制限するか?", ], **PAPER_TYPE_SECTION_PROFILES["clinical_or_psychology_empirical"], }, "humanities_or_social_science": { "paper_type": "humanities_or_social_science", - "reader_lens": "面向关注理论框架、材料解释和论证结构的研究读者", - "section_focus": ["研究对象", "材料来源", "理论框架", "论证路径", "概念贡献", "解释边界"], - "required_checks": ["需要区分作者论证、材料证据、规范性判断和实验事实。"], - "formula_rules": ["通常不强行保留公式;仅保留核心形式化定义或编码规则。"], - "avoid_rules": ["不要把规范性判断、文本解释或案例分析写成实验事实。"], + "reader_lens": "理論的枠組み、資料の解釈、論証の構造に注目する研究読者向け", + "section_focus": ["研究対象", "資料の出所", "理論的枠組み", "論証の道筋", "概念的貢献", "解釈の境界"], + "required_checks": ["著者の論証、資料的証拠、規範的判断、実験的事実を区別する必要がある。"], + "formula_rules": ["通常は数式を無理に残さない。中心的な形式的定義やコーディング規則のみを残す。"], + "avoid_rules": ["規範的判断、テキスト解釈、事例分析を実験的事実として書かない。"], "boundary_questions": [ - "作者的解释依赖哪些材料、案例或理论前提?", - "是否存在同样能解释材料的替代解释,论文如何排除或没有排除?", - "哪些结论是概念贡献或规范性判断,而不是可直接当作经验事实?", + "著者の解釈は、どの資料・事例・理論的前提に依存しているか?", + "同じく資料を説明できる代替的解釈は存在するか。論文はそれをどのように排除したか、あるいは排除していないか?", + "どの結論が概念的貢献や規範的判断であり、経験的事実として直接扱えないものか?", ], **PAPER_TYPE_SECTION_PROFILES["humanities_or_social_science"], }, "survey_or_review": { "paper_type": "survey_or_review", - "reader_lens": "面向需要梳理综述脉络、分类体系和证据边界的研究读者", + "reader_lens": "サーベイの脈絡、分類体系、証拠の境界を整理する必要のある研究読者向け", "section_focus": [ - "综述范围", - "纳入排除标准", - "主题分类", - "方法谱系", - "共识与分歧", - "开放问题", + "サーベイの範囲", + "選定・除外基準", + "テーマ分類", + "手法の系譜", + "合意と対立", + "未解決問題", ], - "required_checks": ["需要说明综述范围、文献选择、分类体系、共识分歧和开放问题。"], - "formula_rules": ["仅保留分类轴、纳入排除准则、证据汇总规则或 meta-analysis 统计量。"], - "avoid_rules": ["不要把综述中的代表性结论写成作者自己完成的单项实验结果。"], + "required_checks": ["サーベイの範囲、文献の選定、分類体系、合意と対立、未解決問題を説明する必要がある。"], + "formula_rules": ["分類軸、選定・除外の基準、証拠の集約規則、meta-analysis の統計量のみを残す。"], + "avoid_rules": ["サーベイ中の代表的な結論を、著者自身が行った単一の実験結果として書かない。"], "boundary_questions": [ - "检索范围、纳入排除标准或分类轴会遗漏哪些研究路线?", - "综述给出的是领域共识、作者分类,还是尚未解决的分歧?", - "哪些趋势结论来自覆盖范围内的文献分布,不能直接当作技术成熟度判断?", + "検索範囲、選定・除外基準、分類軸は、どの研究の流れを見落とすか?", + "サーベイが示すのは分野の合意か、著者の分類か、それとも未解決の対立か?", + "どのトレンド結論がカバレッジ内の文献分布に由来し、技術成熟度の判断として直接扱えないか?", ], **PAPER_TYPE_SECTION_PROFILES["survey_or_review"], }, @@ -276,12 +276,12 @@ def required_field_value_error( "note_plan_field_types": NOTE_PLAN_FIELD_TYPES, "note_plan_required_field_checks": REQUIRED_FIELD_CHECKS, "grounding_required_sections": ( - "研究问题", - "数据与任务定义", - "方法主线", - "关键结果", - "深度分析", - "局限", + "研究課題", + "データとタスク定義", + "手法の骨子", + "主要な結果", + "深掘り分析", + "限界", ), "allowed_grounding_reference_forms": ("section_id", "pages"), "excluded_model_input_fields": ( diff --git a/skills/deeppapernote/scripts/lint_note.py b/skills/deeppapernote/scripts/lint_note.py index fcc71e3..38002e6 100644 --- a/skills/deeppapernote/scripts/lint_note.py +++ b/skills/deeppapernote/scripts/lint_note.py @@ -20,46 +20,46 @@ REQUIRED_SECTIONS = NOTE_REQUIRED_SECTIONS CORE_INFO_FIELDS = [ - "标题", - "标题翻译", - "作者", - "机构", - "发表时间", - "发表渠道", + "タイトル", + "タイトル訳", + "著者", + "所属", + "発表時期", + "発表媒体", "DOI", "arXiv", - "论文链接", - "代码 / 项目", - "数据 / 资源", - "论文类型", + "論文リンク", + "コード / プロジェクト", + "データ / リソース", + "論文タイプ", ] CORE_INFO_FIELD_INDEX = {field: idx for idx, field in enumerate(CORE_INFO_FIELDS)} FIGURE_TARGET_SECTIONS = { - "研究问题", - "数据与任务定义", - "方法主线", - "关键结果", - "深度分析", - "局限", - "我的笔记", + "研究課題", + "データとタスク定義", + "手法の骨子", + "主要な結果", + "深掘り分析", + "限界", + "私のメモ", } FIGURE_BUCKET_RESIDUE_TOKENS = { - "剩余", + "残り", "残余", - "未放置", - "未处理", - "待补", + "未配置", + "未処理", + "補充待ち", } FIGURE_BUCKET_VISUAL_TOKENS = { - "图", + "図", "表", - "图片", - "图表", - "占位", + "画像", + "図表", + "プレースホルダ", } ENGLISH_FIGURE_BUCKET_RESIDUE_TOKENS = { @@ -85,9 +85,9 @@ r"""(?ix) ^\s* (?: - \[\s*(?:图表|图片|图|表)\s*占位\s*\|[^\]]+\] + \[\s*(?:図表|画像|図|表)\s*プレースホルダ\s*\|[^\]]+\] | - (?:图表|图片|图|表)\s*占位\s*[::]\s*\S+ + (?:図表|画像|図|表)\s*プレースホルダ\s*[::]\s*\S+ | \[\s*(?:figure|fig|table)\s+placeholder\s*(?:\||:|\]|-|\s+(?:fig(?:ure)?|table)\.?\s*\d) | @@ -99,9 +99,9 @@ REAL_IMAGE_STATUS_RE = re.compile( r""" (?: - 已\s*(?:替换|插入|复制|拷贝|物化|写入) + (?:差し替え|挿入|コピー|複製|マテリアライズ|書き込み)\s*済み | - (?:替换|插入)\s*为\s*真实图片 + 実画像\s*(?:に|として)\s*(?:差し替え|挿入) | \b(?:inserted|replaced|copied|materialized)\b ) @@ -112,19 +112,19 @@ USABLE_CANDIDATE_STATUS_RE = re.compile( r""" (?: - 候选[^。;,\n>]{0,24}(?]{0,24}(?]{0,12}候选 + (?]{0,12}候補 | - (?:图像|图片|表格|图|表)?\s*裁剪[^。;,\n>]{0,12}(?]{0,12}(?]{0,12}(?]{0,12}(?]{0,12}候选 + 高\s*信頼(?:度)?[^。;,\n>]{0,12}候補 | usable\s+candidate | @@ -141,11 +141,11 @@ USABLE_CANDIDATE_VISUAL_DEFECT_RE = re.compile( r""" (?: - 混入|污染|相邻|裁切|截断|切断|缺失|缺少|表体不完整|表格主体缺失|正文污染 + 混入|汚染|隣接|切り取り|切り詰め|切断|欠落|欠如|表本体が不完全|表本体の欠落|本文汚染 | - 只拿到|局部(?:子图|面板|截图|区域)|部分(?:子图|裁剪) + しか取得でき|局所(?:サブ図|パネル|切り出し|領域)|部分(?:サブ図|トリミング) | - 无法稳定|不可独立解释|质量门|reject_visual_quality + 安定的に復元できない|独立して解釈できない|品質ゲート|reject_visual_quality | partial|subpanel|contaminat|truncat|incomplete|missing | @@ -158,13 +158,13 @@ USABLE_CANDIDATE_MATERIALIZATION_BLOCKED_RE = re.compile( r""" (?: - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|工具|copy) + (?:materialize_figure_asset\.py|マテリアライズ|コピー|複製|書き込み|権限|permission|ツール|copy) [^。;\n]{0,40} - (?:失败|不足|拒绝|denied|blocked|error|报错) + (?:失敗|不足|拒否|denied|blocked|error|エラー) | - (?:失败|不足|拒绝|denied|blocked|error|报错) + (?:失敗|不足|拒否|denied|blocked|error|エラー) [^。;\n]{0,40} - (?:materialize|物化|复制|拷贝|写入|权限|permission|copy) + (?:materialize|マテリアライズ|コピー|複製|書き込み|権限|permission|copy) ) """, flags=re.IGNORECASE | re.VERBOSE, @@ -173,13 +173,13 @@ MISSING_ASSET_MATERIALIZATION_RE = re.compile( r""" (?: - (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing) + (?:アセット欠落|見つからない|存在しない|欠如|asset_candidate_missing|candidate\s+missing) [^。;\n]{0,50} - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked) + (?:materialize_figure_asset\.py|マテリアライズ|コピー|複製|書き込み|権限|permission|copy|blocked) | - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked) + (?:materialize_figure_asset\.py|マテリアライズ|コピー|複製|書き込み|権限|permission|copy|blocked) [^。;\n]{0,50} - (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing) + (?:アセット欠落|見つからない|存在しない|欠如|asset_candidate_missing|candidate\s+missing) ) """, flags=re.IGNORECASE | re.VERBOSE, @@ -207,9 +207,9 @@ (?: [\u4e00-\u9fff]+(?:ing|ed|s)\b | - \b[A-Za-z]{2,}相关\b + \b[A-Za-z]{2,}関連\b | - [\u4e00-\u9fff](?:缓存|块)?\s+(?:of|with|for|on|in|from|and)\b + [\u4e00-\u9fff](?:キャッシュ|ブロック)?\s+(?:of|with|for|on|in|from|and)\b | \b(?:of|with|for|on|in|from|and)\s+[\u4e00-\u9fff] | @@ -322,7 +322,7 @@ def find_missing_sections(text: str) -> list[str]: def front_matter_order_warnings(text: str) -> list[str]: warnings: list[str] = [] - required_order = ["## 原文摘要翻译", "## 创新点", "## 一句话总结"] + required_order = ["## 要旨の翻訳", "## 新規性", "## 一言まとめ"] positions = [] for section in required_order: idx = text.find(section) @@ -364,13 +364,13 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: METHOD_PAPER_SIGNAL_KEYWORDS = [ - "模型", - "框架", - "系统", - "模块", - "编码器", - "解码器", - "预融合", + "モデル", + "フレームワーク", + "システム", + "モジュール", + "エンコーダ", + "デコーダ", + "事前融合", "attention", "encoder", "decoder", @@ -379,25 +379,25 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: ] MECHANISM_IO_TOKENS = [ - "输入", - "输出", - "送入", - "送到", + "入力", + "出力", + "投入", + "渡す", "生成", - "得到", + "得る", ] MECHANISM_ACTION_TOKENS = [ "融合", "投影", - "压缩", - "对齐", - "池化", - "提取", - "编码", - "解码", - "拼接", - "查询", + "圧縮", + "整列", + "プーリング", + "抽出", + "符号化", + "復号", + "連結", + "クエリ", "更新", ] @@ -437,40 +437,40 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: } PLACEHOLDER_ONLY_PATTERNS = [ - r"^待补充[。.!!]*$", + r"^追記予定[。.!!]*$", r"^todo[。.!!]*$", - r"^暂无[。.!!]*$", - r"^略[。.!!]*$", - r"^参见原论文[。.!!]*$", - r"^这里记录.*[。.!!]*$", - r"^本节记录.*[。.!!]*$", + r"^なし[。.!!]*$", + r"^省略[。.!!]*$", + r"^原論文参照[。.!!]*$", + r"^ここに記録.*[。.!!]*$", + r"^本節に記録.*[。.!!]*$", ] GENERIC_INNOVATION_PATTERNS = [ - r"本文提出(?:了)?一种新方法", - r"具有创新性", + r"新しい手法を提案(?:する|した)?", + r"新規性が(?:ある|高い)", r"novel approach", - r"首次实现", + r"初めて実現(?:した)?", ] GENERIC_KEY_RESULT_PATTERNS = [ - r"实验结果表明方法有效", - r"结果表明.*有效", - r"取得(?:了)?较好效果", - r"性能.*优越", + r"実験結果.*手法.*有効", + r"結果.*有効(?:である|だ)", + r"良好な(?:結果|効果|性能)", + r"性能.*優(?:れ|位|越)", ] GENERIC_LIMITATION_PATTERNS = [ - r"未来工作.*更多数据", - r"需要更多数据", + r"今後の(?:課題|研究).*データ", + r"(?:さらなる|より多くの)データが必要", r"future work can", r"more data", - r"后续.*扩展", + r"今後.*拡張", ] -HONEST_MISSING_TOKENS = ("本文未给出", "论文未给出", "未报告", "没有报告", "未提供") -HONEST_MISSING_BASIS_TOKENS = ("依据", "正文", "附录", "表格", "coverage", "作者") -HONEST_MISSING_IMPACT_TOKENS = ("影响", "限制", "受限", "不能", "无法", "结论强度") +HONEST_MISSING_TOKENS = ("未報告", "報告されていない", "未提供", "示されていない", "記載がない") +HONEST_MISSING_BASIS_TOKENS = ("根拠", "本文", "付録", "表", "coverage", "著者") +HONEST_MISSING_IMPACT_TOKENS = ("影響", "制限", "制約", "できない", "不可能", "結論強度") DOUBLE_ESCAPED_TEX_COMMANDS = { "alpha", @@ -512,9 +512,9 @@ def is_exempt_line(line: str) -> bool: return True if ( stripped.startswith("> [!figure]") - or stripped.startswith("> 建议位置:") - or stripped.startswith("> 放置原因:") - or stripped.startswith("> 当前状态:") + or stripped.startswith("> 推奨位置:") + or stripped.startswith("> 配置理由:") + or stripped.startswith("> 現在の状態:") ): return True if re.search(r"https?://", stripped): @@ -556,7 +556,7 @@ def mixed_language_issues(text: str) -> list[dict[str, object]]: stripped = line.strip() section_name = section_name_for_line(lines, idx - 1) subsection_name = subsection_name_for_line(lines, idx - 1) - if section_name in {"核心信息", "引用"}: + if section_name in {"基本情報", "参考文献"}: continue if not re.search(r"[\u4e00-\u9fff]", stripped): continue @@ -620,11 +620,11 @@ def inspect_figure_callouts(text: str) -> list[str]: nxt = lines[j].strip() if not nxt.startswith(">"): break - if nxt.startswith("> 建议位置:"): + if nxt.startswith("> 推奨位置:"): has_location = True - if nxt.startswith("> 放置原因:"): + if nxt.startswith("> 配置理由:"): has_reason = True - if nxt.startswith("> 当前状态:"): + if nxt.startswith("> 現在の状態:"): has_status = True j += 1 if not has_location: @@ -648,9 +648,9 @@ def figure_callout_title(line: str) -> str: def figure_status_text(line: str) -> str: stripped = line.strip() - if not stripped.startswith("> 当前状态:"): + if not stripped.startswith("> 現在の状態:"): return "" - return stripped.removeprefix("> 当前状态:").strip() + return stripped.removeprefix("> 現在の状態:").strip() def has_accepted_usable_placeholder_reason(status_text: str) -> bool: @@ -748,8 +748,8 @@ def figure_callout_placement_issues(text: str) -> list[dict[str, object]]: nxt = lines[j].strip() if not nxt.startswith(">"): break - if nxt.startswith("> 建议位置:"): - location = nxt.removeprefix("> 建议位置:").strip() + if nxt.startswith("> 推奨位置:"): + location = nxt.removeprefix("> 推奨位置:").strip() break j += 1 @@ -919,14 +919,14 @@ def figure_structure_passes(text: str) -> bool: def core_info_structure_issues(text: str) -> list[dict[str, object]]: - body = section_body(text, "核心信息") + body = section_body(text, "基本情報") if not body: return [] issues: list[dict[str, object]] = [] seen_fields: set[str] = set() last_known_index = -1 - base_line = _line_number_from_offset(text, text.find("## 核心信息")) + base_line = _line_number_from_offset(text, text.find("## 基本情報")) for offset, raw_line in enumerate(body.splitlines(), start=1): stripped = raw_line.strip() @@ -987,7 +987,7 @@ def is_prose_line(line: str) -> bool: stripped = line.strip() if not stripped: return False - if stripped.startswith(("#", "-", "*", "> ", "```", "![[", "*论文原图编号")): + if stripped.startswith(("#", "-", "*", "> ", "```", "![[", "*論文原図番号")): return False if stripped.startswith("`") and stripped.endswith("`"): return False @@ -1304,14 +1304,14 @@ def cleaned_section_lines(body: str) -> list[str]: continue if ( stripped.startswith("> [!figure]") - or stripped.startswith("> 建议位置:") - or stripped.startswith("> 放置原因:") - or stripped.startswith("> 当前状态:") + or stripped.startswith("> 推奨位置:") + or stripped.startswith("> 配置理由:") + or stripped.startswith("> 現在の状態:") ): continue if stripped.startswith("!["): continue - if stripped.startswith("*论文原图编号:") and stripped.endswith("*"): + if stripped.startswith("*論文原図番号:") and stripped.endswith("*"): continue if stripped.startswith("> "): stripped = stripped[2:].strip() @@ -1405,7 +1405,7 @@ def has_reference_entry(text: str) -> bool: return True if re.search(r"\b[A-Z][A-Za-z-]+ et al\.?\s*,?\s*(?:19|20)\d{2}\b", normalized): return True - if re.search(r"(?:19|20)\d{2}.*(?:DOI|doi|会议|期刊|arXiv)", normalized): + if re.search(r"(?:19|20)\d{2}.*(?:DOI|doi|会議|論文誌|arXiv)", normalized): return True return False @@ -1417,53 +1417,53 @@ def inspect_substantive_content(text: str) -> list[dict[str, object]]: content = normalized_section_content(body) if is_placeholder_like(content): issues.append(issue(section, "section_empty_shell", "error", content or section)) - if section not in {"关键结果", "引用"} and is_honest_missing_declaration(content): + if section not in {"主要な結果", "参考文献"} and is_honest_missing_declaration(content): issues.append(issue(section, "section_honest_missing_not_allowed", "error", content)) - innovation = section_body(text, "创新点") + innovation = section_body(text, "新規性") innovation_content = normalized_section_content(innovation) innovation_units = meaningful_units(innovation, GENERIC_INNOVATION_PATTERNS) if not innovation_units: - issues.append(issue("创新点", "innovation_empty_shell", "error", innovation_content)) + issues.append(issue("新規性", "innovation_empty_shell", "error", innovation_content)) elif len(innovation_units) < 2: - issues.append(issue("创新点", "innovation_too_few_specific_points", "warning", innovation_content)) + issues.append(issue("新規性", "innovation_too_few_specific_points", "warning", innovation_content)) - key_results = section_body(text, "关键结果") + key_results = section_body(text, "主要な結果") key_results_content = normalized_section_content(key_results) if is_honest_missing_declaration(key_results_content): issues.append( issue( - "关键结果", + "主要な結果", "key_results_honest_missing_not_allowed", "error", key_results_content, ) ) elif not meaningful_units(key_results, GENERIC_KEY_RESULT_PATTERNS): - issues.append(issue("关键结果", "key_results_empty_shell", "error", key_results_content)) + issues.append(issue("主要な結果", "key_results_empty_shell", "error", key_results_content)) elif not has_number_token(key_results_content): issues.append( issue( - "关键结果", + "主要な結果", "key_results_quantitative_result_missing", "warning", key_results_content, ) ) - references = section_body(text, "引用") + references = section_body(text, "参考文献") references_content = normalized_section_content(references) if is_honest_missing_declaration(references_content): - issues.append(issue("引用", "references_unavailable_declared", "warning", references_content)) + issues.append(issue("参考文献", "references_unavailable_declared", "warning", references_content)) elif is_placeholder_like(references_content) or not has_reference_entry(references_content): - issues.append(issue("引用", "references_placeholder", "error", references_content)) + issues.append(issue("参考文献", "references_placeholder", "error", references_content)) - limitations = section_body(text, "局限") + limitations = section_body(text, "限界") limitations_content = normalized_section_content(limitations) if not meaningful_units(limitations, GENERIC_LIMITATION_PATTERNS): - issues.append(issue("局限", "limitations_empty_shell", "error", limitations_content)) + issues.append(issue("限界", "limitations_empty_shell", "error", limitations_content)) - for section in ("方法主线", "深度分析"): + for section in ("手法の骨子", "深掘り分析"): body = section_body(text, section) content = normalized_section_content(body) if not meaningful_units(body): @@ -1481,7 +1481,7 @@ def inspect_substantive_content(text: str) -> list[dict[str, object]]: def method_section_requires_mechanism_flow(text: str) -> bool: - body = section_body(text, "方法主线") + body = section_body(text, "手法の骨子") if not body: return False lower = body.lower() @@ -1494,11 +1494,11 @@ def mechanism_flow_warnings(text: str) -> list[str]: warnings: list[str] = [] if not method_section_requires_mechanism_flow(text): return warnings - if "### 机制流程" not in text: + if "### 機構フロー" not in text: warnings.append("mechanism_flow_subsection_missing") return warnings - body = subsection_body(text, "方法主线", "机制流程") + body = subsection_body(text, "手法の骨子", "機構フロー") if not body: warnings.append("mechanism_flow_subsection_empty") return warnings diff --git a/skills/deeppapernote/scripts/plan_figures.py b/skills/deeppapernote/scripts/plan_figures.py index c508550..13015cf 100644 --- a/skills/deeppapernote/scripts/plan_figures.py +++ b/skills/deeppapernote/scripts/plan_figures.py @@ -65,7 +65,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: r"(?:produces|achieves|outperforms|improves|reduces|increases)\b", text, ): - return "main_result", "关键结果", "这张图或表直接承载主结果,适合放在关键结果部分。" + return "main_result", "主要な結果", "この図または表は主要な結果を直接担っており、主要な結果セクションに配置するのが適切である。" if any( token in text for token in [ @@ -79,7 +79,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "identification of studies", ] ): - return "data_or_task_overview", "数据与任务定义", "这张图解释文献筛选或纳入流程;如果候选图质量足够,适合放在数据与任务定义部分帮助读者理解综述证据来源。" + return "data_or_task_overview", "データとタスク定義", "この図は文献のスクリーニングや選定フローを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がレビューの証拠の出所を理解する助けにするのが適切である。" if any( token in text for token in [ @@ -94,7 +94,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "process", ] ): - return "method_overview", "机制流程", "这张图概括了整体方法或系统流程;如果匹配置信度足够高,最适合放在 `### 机制流程` 帮助快速建立执行链理解。" + return "method_overview", "機構フロー", "この図は手法全体またはシステムのフローを俯瞰している。マッチの信頼度が十分に高ければ、`### 機構フロー` に配置し、実行チェーンの理解を素早く構築する助けにするのが最適である。" if any( token in text for token in [ @@ -108,7 +108,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "issue", ] ): - return "data_or_task_overview", "数据与任务定义", "这张图解释任务或数据集如何被构造;如果候选图质量足够,适合放在数据与任务定义部分帮助读者理解数据来源。" + return "data_or_task_overview", "データとタスク定義", "この図はタスクやデータセットがどのように構築されるかを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がデータの出所を理解する助けにするのが適切である。" if any( token in text for token in [ @@ -125,7 +125,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "attribute", ] ): - return "data_or_task", "数据与任务定义", "这张图更像任务设定或数据说明,放在数据与任务定义最合适。" + return "data_or_task", "データとタスク定義", "この図はタスク設定やデータの説明に近く、データとタスク定義に配置するのが最も適切である。" if any( token in text for token in [ @@ -142,10 +142,10 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "block translation", ] ): - return "method_detail", "方法主线", "这张图解释方法内部机制或关键执行状态,适合放在方法主线部分作为机制细节占位。" + return "method_detail", "手法の骨子", "この図は手法の内部機構や重要な実行状態を説明しており、手法の骨子セクションに機構詳細のプレースホルダとして配置するのが適切である。" if item_id.lower().startswith("table"): - return "table_result", "关键结果", "这是关键结果表,适合放在关键结果部分辅助定位核心数值。" - return "supporting_figure", "深度分析", "这张图更适合作为补充图,放在深度分析部分帮助解释作者论点。" + return "table_result", "主要な結果", "これは主要な結果を示す表であり、主要な結果セクションに配置して中心的な数値の把握を補助するのが適切である。" + return "supporting_figure", "深掘り分析", "この図は補足図として適しており、深掘り分析セクションに配置して著者の主張の説明を助けるのが適切である。" def build_figure_items(evidence_pack: dict, *, limit: int = 12) -> list[dict]: From f3d2769c80b52f28e66f2fa44ebfb5e002ddca80 Mon Sep 17 00:00:00 2001 From: Yoshiaki Sakae <89111154+yoshiaki-sakae@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:53:56 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20SKILL.md=20description=E3=81=AE?= =?UTF-8?q?=E3=82=B3=E3=83=AD=E3=83=B3=E3=81=A7YAML=E7=A0=B4=E7=B6=BB?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit description内の「Japanese triggers: 」のコロン+空白がフロントマターの プレーンスカラー解析を壊し、skills CLIがdeeppapernoteをスキップしていた。 --- skills/deeppapernote/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/deeppapernote/SKILL.md b/skills/deeppapernote/SKILL.md index 9efdd99..0af3ec4 100644 --- a/skills/deeppapernote/SKILL.md +++ b/skills/deeppapernote/SKILL.md @@ -1,6 +1,6 @@ --- name: deeppapernote -description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. Generates the note in Japanese by default (デフォルトで日本語の精読ノートを生成する). Japanese triggers: 「この論文の精読ノートを作って」「この論文をObsidianノートにまとめて」「論文を読んで日本語のMarkdownノートにして」. +description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. Generates the note in Japanese by default (デフォルトで日本語の精読ノートを生成する). Japanese trigger phrases 「この論文の精読ノートを作って」「この論文をObsidianノートにまとめて」「論文を読んで日本語のMarkdownノートにして」. --- # DeepPaperNote From eb7dfd53fe03025b2d9708eeff982e198b2fa36c Mon Sep 17 00:00:00 2001 From: Yoshiaki Sakae <89111154+yoshiaki-sakae@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:25:55 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=E5=87=BA=E5=8A=9B=E8=A8=80?= =?UTF-8?q?=E8=AA=9E=E3=82=92=E3=83=AD=E3=82=B1=E3=83=BC=E3=83=AB=E5=88=86?= =?UTF-8?q?=E9=9B=A2=EF=BC=88DEEPPAPERNOTE=5FOUTPUT=5FLANGUAGE,=20?= =?UTF-8?q?=E6=97=A2=E5=AE=9Aja=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 言語依存文字列(セクション名/フィールド名/小見出し/品質ヒューリスティック/出力言語コード)を scripts/locales/{zh,ja}.py に集約。contracts.py/lint_note.py/build_synthesis_bundle.py/ plan_figures.py はロケールから取得する薄い構造に変更。zhロケールは本家原文字列を忠実に保持し、 zhモードでcontractsは本家とビットレベル一致(コードのマージがクリーン)。 SKILL.md/references/common.pyドメインラベル/画像ステータス正規表現は日本語内容として維持(zh完全対応は今後)。 --- .../scripts/build_synthesis_bundle.py | 3 +- skills/deeppapernote/scripts/contracts.py | 217 +---------- skills/deeppapernote/scripts/lint_note.py | 161 ++------- .../deeppapernote/scripts/locales/__init__.py | 85 +++++ skills/deeppapernote/scripts/locales/ja.py | 338 ++++++++++++++++++ skills/deeppapernote/scripts/locales/zh.py | 328 +++++++++++++++++ skills/deeppapernote/scripts/plan_figures.py | 20 +- 7 files changed, 811 insertions(+), 341 deletions(-) create mode 100644 skills/deeppapernote/scripts/locales/__init__.py create mode 100644 skills/deeppapernote/scripts/locales/ja.py create mode 100644 skills/deeppapernote/scripts/locales/zh.py diff --git a/skills/deeppapernote/scripts/build_synthesis_bundle.py b/skills/deeppapernote/scripts/build_synthesis_bundle.py index 2eeb786..dd78872 100644 --- a/skills/deeppapernote/scripts/build_synthesis_bundle.py +++ b/skills/deeppapernote/scripts/build_synthesis_bundle.py @@ -21,6 +21,7 @@ PAPER_TYPE_VALUES, WRITING_CONTRACT_RULES, ) +from locales import get_locale def parser() -> argparse.ArgumentParser: @@ -345,7 +346,7 @@ def compact_writing_contract() -> dict: analysis_coverage["final_quality_review_checks"] ) return { - "language": "ja", + "language": get_locale()["OUTPUT_LANGUAGE_CODE"], "contract_role": "manifest_quality_contract", "canonical_source": ( "SKILL.md defines the workflow; scripts/contracts.py defines " diff --git a/skills/deeppapernote/scripts/contracts.py b/skills/deeppapernote/scripts/contracts.py index 20d9e1a..3f5cfee 100644 --- a/skills/deeppapernote/scripts/contracts.py +++ b/skills/deeppapernote/scripts/contracts.py @@ -5,20 +5,11 @@ from typing import Any, TypedDict -NOTE_REQUIRED_SECTIONS: tuple[str, ...] = ( - "基本情報", - "要旨の翻訳", - "新規性", - "一言まとめ", - "研究課題", - "データとタスク定義", - "手法の骨子", - "主要な結果", - "深掘り分析", - "限界", - "私のメモ", - "参考文献", -) +from locales import get_locale + +_LOCALE = get_locale() + +NOTE_REQUIRED_SECTIONS: tuple[str, ...] = _LOCALE["NOTE_REQUIRED_SECTIONS"] PAPER_TYPE_VALUES: tuple[str, ...] = ( "AI_method", @@ -80,194 +71,9 @@ def required_field_value_error( return "empty" if checks["array"]["non_empty"] and not value else "" return "invalid" -PAPER_TYPE_SECTION_PROFILES: dict[str, dict[str, dict[str, Any]]] = { - "AI_method": { - "section_semantics": { - "研究課題": "手法が解決しようとする具体的な技術課題と、既存手法の弱点。", - "データとタスク定義": "データセット、入出力、評価タスク、実験設定。", - "手法の骨子": "モデル・アルゴリズム・学習または推論の機構。", - "主要な結果": "主結果、強力なベースライン、アブレーション、重要な数値。", - "深掘り分析": "手法がなぜ有効か、どこが脆いか、再現・拡張のコスト。", - }, - "recommended_subsections": { - "手法の骨子": ["機構フロー", "モデル構造", "学習目標", "推論・サンプリング経路", "主要な実装詳細"], - "主要な結果": ["主要結果と強力なベースライン", "アブレーションが示すもの", "失敗・不安定な設定"], - "深掘り分析": ["なぜ有効か", "計算量とスケーラビリティ", "再現時の注意点"], - }, - }, - "benchmark_or_dataset": { - "section_semantics": { - "研究課題": "この benchmark/dataset が埋めようとする評価またはデータのギャップ。", - "データとタスク定義": "データ出所、タスク分割、ラベル/課題の定義、サンプル範囲。", - "手法の骨子": "データ構築・選別・アノテーション・評価プロトコル。モデルの pipeline としては書かない。", - "主要な結果": "ベースライン性能、難易度分布、カバレッジ範囲、バイアス。", - "深掘り分析": "実際に何を測れているか、そして何を代表できないか。", - }, - "recommended_subsections": { - "データとタスク定義": ["データ出所", "タスク分割", "アノテーション/選別プロトコル"], - "手法の骨子": ["構築フロー", "評価プロトコル", "ベースライン設定"], - "主要な結果": ["ベースライン性能", "難易度分布", "カバレッジとバイアス"], - "深掘り分析": ["ベンチマークが実際に測るもの", "適用範囲の境界"], - }, - }, - "clinical_or_psychology_empirical": { - "section_semantics": { - "研究課題": "臨床・心理学・行動科学における研究課題、仮説、変数間の関係。", - "データとタスク定義": "サンプル出所、選択・除外基準、変数/尺度、測定方法。", - "手法の骨子": "研究デザイン、群分け、測定手順、統計解析の道筋。", - "主要な結果": "主効果、相関、群間差、不確実性や有意性。", - "深掘り分析": "結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。", - }, - "recommended_subsections": { - "データとタスク定義": ["サンプルと選択・除外基準", "変数と尺度", "測定手順"], - "手法の骨子": ["研究デザイン", "分析モデル", "主要な比較"], - "主要な結果": ["主要な効果", "不確実性と有意性", "臨床・心理学的解釈"], - "深掘り分析": ["因果解釈の境界", "外挿の限界"], - }, - }, - "humanities_or_social_science": { - "section_semantics": { - "研究課題": "著者が説明しようとする社会・文化・歴史・制度・理論上の問い。", - "データとタスク定義": "資料、事例、テキスト、インタビュー、アーカイブ、コーパスの範囲。ML task としては書かない。", - "手法の骨子": "理論的枠組み、概念の区別、論証の道筋。", - "主要な結果": "中心的な解釈的発見、概念的貢献、既存の見解への修正。", - "深掘り分析": "論証の強さ、資料の境界、解釈の代替可能性、転用可能性。", - }, - "recommended_subsections": { - "データとタスク定義": ["資料の範囲", "選択基準", "事例・コーパスの境界"], - "手法の骨子": ["理論的枠組み", "概念の区別", "論証の道筋"], - "主要な結果": ["中心的な解釈的発見", "概念的貢献"], - "深掘り分析": ["論証の強さ", "代替的解釈", "資料の境界"], - }, - }, - "survey_or_review": { - "section_semantics": { - "研究課題": "サーベイが整理しようとする分野の問い、論争、知識のギャップ。", - "データとタスク定義": "対象とする文献範囲、検索/選別基準、レビュー対象。", - "手法の骨子": "分類体系、レビューの構成方法、証拠統合のロジック。単一論文の手法構成としては書かない。", - "主要な結果": "分野の合意、対立、トレンド、代表的な方向性、未解決問題。", - "深掘り分析": "サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。", - }, - "recommended_subsections": { - "データとタスク定義": ["サーベイの範囲", "選定・除外基準", "文献カバレッジ"], - "手法の骨子": ["分類体系", "手法の系譜", "証拠の整理方法"], - "主要な結果": ["代表的な方向性", "合意と対立", "未解決問題"], - "深掘り分析": ["分類体系の限界", "未カバー領域", "今後の研究機会"], - }, - }, -} +PAPER_TYPE_SECTION_PROFILES: dict[str, dict[str, dict[str, Any]]] = _LOCALE["PAPER_TYPE_SECTION_PROFILES"] -PAPER_TYPE_CONTRACTS: dict[str, dict[str, Any]] = { - "AI_method": { - "paper_type": "AI_method", - "reader_lens": "手法の機構を再現できる技術読者向け", - "section_focus": [ - "問題設定", - "手法の機構", - "学習/推論フロー", - "主要な数式", - "比較ベースライン", - "アブレーションと失敗の境界", - ], - "required_checks": ["機構フロー、主要な数式、実験デザイン、アブレーションの含意、失敗の境界を説明する必要がある。"], - "formula_rules": ["手法の理解に必須の 1〜3 個の主要な数式のみを残し、その工学的含意を説明する。"], - "avoid_rules": ["AI_method でない論文を無理にモデルアーキテクチャとして書き換えない。"], - "boundary_questions": [ - "中心的な機構の利得は、主結果が示唆するだけでなく、どの実験やアブレーションで裏付けられているか?", - "どの比較が現在のデータ・ベースライン・計算資源・プロトコルの下でのみ有効で、汎用的な状況へ外挿できないか?", - "論文は失敗・劣化・不安定・コスト増の証拠を示しているか。示していない場合、結論の境界は何か?", - ], - **PAPER_TYPE_SECTION_PROFILES["AI_method"], - "mechanism_flow_contract": { - "apply_when_paper_type_in": ["AI_method"], - "required_step_count": "3_to_4", - "required_step_fields": ["input", "operation", "output_destination"], - }, - }, - "benchmark_or_dataset": { - "paper_type": "benchmark_or_dataset", - "reader_lens": "benchmark/dataset の有用性とバイアスの境界を判断したい研究者向け", - "section_focus": [ - "タスク分割", - "データ出所と構築フロー", - "アノテーションプロトコル", - "評価指標", - "カバレッジ範囲とバイアス", - "サンプル統計とデータ公開制限", - ], - "required_checks": [ - "データ出所、構築/アノテーションのフロー、評価指標、ベースライン性能、サンプル統計、データ公開やプライバシー制限、適用範囲の境界を説明する必要がある。" - ], - "formula_rules": ["中心的な評価指標、サンプリング規則、分割定義のみを残す。"], - "avoid_rules": ["データ構築フローをモデルの pipeline として書かない。"], - "boundary_questions": [ - "この benchmark/dataset が実際に測定している構成概念は何で、どの能力は間接的な近似に過ぎないか?", - "タスク・ラベル・サンプリング・フィルタ・評価プロトコルは、どのようなカバレッジの欠落やバイアスを持ち込むか?", - "ベースライン結果は評価セットに識別力があることを示すのか、それとも特定タイプのモデルがこのプロトコルに適応しただけか?", - "サンプルの長さ、コーパス長、人口統計、クラス分布、データのアクセス可能性やプライバシー制限は、再現と外挿にどう影響するか?", - ], - **PAPER_TYPE_SECTION_PROFILES["benchmark_or_dataset"], - }, - "clinical_or_psychology_empirical": { - "paper_type": "clinical_or_psychology_empirical", - "reader_lens": "臨床/心理学のサンプル、変数関係、外挿の境界に注目する研究読者向け", - "section_focus": [ - "サンプル出所", - "選択・除外基準", - "変数または尺度", - "分析パイプライン", - "効果量と不確実性", - "サンプル統計・倫理・データのアクセス可能性", - ], - "required_checks": [ - "相関・予測・群間差・因果的解釈を区別し、サンプル統計、倫理/プライバシー上の制約、外挿の境界を説明する必要がある。" - ], - "formula_rules": ["中心的な統計モデル、効果量、信頼区間、尺度の定義のみを残す。"], - "avoid_rules": ["相関、予測性能、群間差を、証明されていない因果結論として書かない。"], - "boundary_questions": [ - "サンプル出所、選択・除外基準、測定ツール、アノテーション手順は、外挿をどのように制限するか?", - "結果は相関・予測・群間差・因果的解釈のどれを支持するか。論文のデザインが証明できる範囲を越えないこと。", - "臨床または心理学的意義は、未観測の交絡、尺度の閾値、テキスト/音声の欠落、場面の制約に依存していないか?", - "サンプル構成、データ欠損、プライバシー制限、資料の非公開は、再現と再分析をどのように制限するか?", - ], - **PAPER_TYPE_SECTION_PROFILES["clinical_or_psychology_empirical"], - }, - "humanities_or_social_science": { - "paper_type": "humanities_or_social_science", - "reader_lens": "理論的枠組み、資料の解釈、論証の構造に注目する研究読者向け", - "section_focus": ["研究対象", "資料の出所", "理論的枠組み", "論証の道筋", "概念的貢献", "解釈の境界"], - "required_checks": ["著者の論証、資料的証拠、規範的判断、実験的事実を区別する必要がある。"], - "formula_rules": ["通常は数式を無理に残さない。中心的な形式的定義やコーディング規則のみを残す。"], - "avoid_rules": ["規範的判断、テキスト解釈、事例分析を実験的事実として書かない。"], - "boundary_questions": [ - "著者の解釈は、どの資料・事例・理論的前提に依存しているか?", - "同じく資料を説明できる代替的解釈は存在するか。論文はそれをどのように排除したか、あるいは排除していないか?", - "どの結論が概念的貢献や規範的判断であり、経験的事実として直接扱えないものか?", - ], - **PAPER_TYPE_SECTION_PROFILES["humanities_or_social_science"], - }, - "survey_or_review": { - "paper_type": "survey_or_review", - "reader_lens": "サーベイの脈絡、分類体系、証拠の境界を整理する必要のある研究読者向け", - "section_focus": [ - "サーベイの範囲", - "選定・除外基準", - "テーマ分類", - "手法の系譜", - "合意と対立", - "未解決問題", - ], - "required_checks": ["サーベイの範囲、文献の選定、分類体系、合意と対立、未解決問題を説明する必要がある。"], - "formula_rules": ["分類軸、選定・除外の基準、証拠の集約規則、meta-analysis の統計量のみを残す。"], - "avoid_rules": ["サーベイ中の代表的な結論を、著者自身が行った単一の実験結果として書かない。"], - "boundary_questions": [ - "検索範囲、選定・除外基準、分類軸は、どの研究の流れを見落とすか?", - "サーベイが示すのは分野の合意か、著者の分類か、それとも未解決の対立か?", - "どのトレンド結論がカバレッジ内の文献分布に由来し、技術成熟度の判断として直接扱えないか?", - ], - **PAPER_TYPE_SECTION_PROFILES["survey_or_review"], - }, -} +PAPER_TYPE_CONTRACTS: dict[str, dict[str, Any]] = _LOCALE["PAPER_TYPE_CONTRACTS"] WRITING_CONTRACT_RULES: dict[str, Any] = { "required_sections": NOTE_REQUIRED_SECTIONS, @@ -275,14 +81,7 @@ def required_field_value_error( "note_plan_required_fields": NOTE_PLAN_REQUIRED_FIELDS, "note_plan_field_types": NOTE_PLAN_FIELD_TYPES, "note_plan_required_field_checks": REQUIRED_FIELD_CHECKS, - "grounding_required_sections": ( - "研究課題", - "データとタスク定義", - "手法の骨子", - "主要な結果", - "深掘り分析", - "限界", - ), + "grounding_required_sections": _LOCALE["GROUNDING_REQUIRED_SECTIONS"], "allowed_grounding_reference_forms": ("section_id", "pages"), "excluded_model_input_fields": ( "evidence", diff --git a/skills/deeppapernote/scripts/lint_note.py b/skills/deeppapernote/scripts/lint_note.py index 38002e6..004ab16 100644 --- a/skills/deeppapernote/scripts/lint_note.py +++ b/skills/deeppapernote/scripts/lint_note.py @@ -17,50 +17,21 @@ required_field_value_error, ) +from locales import get_locale + +_LOCALE = get_locale() + REQUIRED_SECTIONS = NOTE_REQUIRED_SECTIONS -CORE_INFO_FIELDS = [ - "タイトル", - "タイトル訳", - "著者", - "所属", - "発表時期", - "発表媒体", - "DOI", - "arXiv", - "論文リンク", - "コード / プロジェクト", - "データ / リソース", - "論文タイプ", -] +CORE_INFO_FIELDS = _LOCALE["CORE_INFO_FIELDS"] CORE_INFO_FIELD_INDEX = {field: idx for idx, field in enumerate(CORE_INFO_FIELDS)} -FIGURE_TARGET_SECTIONS = { - "研究課題", - "データとタスク定義", - "手法の骨子", - "主要な結果", - "深掘り分析", - "限界", - "私のメモ", -} +FIGURE_TARGET_SECTIONS = _LOCALE["FIGURE_TARGET_SECTIONS"] -FIGURE_BUCKET_RESIDUE_TOKENS = { - "残り", - "残余", - "未配置", - "未処理", - "補充待ち", -} +FIGURE_BUCKET_RESIDUE_TOKENS = _LOCALE["FIGURE_BUCKET_RESIDUE_TOKENS"] -FIGURE_BUCKET_VISUAL_TOKENS = { - "図", - "表", - "画像", - "図表", - "プレースホルダ", -} +FIGURE_BUCKET_VISUAL_TOKENS = _LOCALE["FIGURE_BUCKET_VISUAL_TOKENS"] ENGLISH_FIGURE_BUCKET_RESIDUE_TOKENS = { "remaining", @@ -322,7 +293,7 @@ def find_missing_sections(text: str) -> list[str]: def front_matter_order_warnings(text: str) -> list[str]: warnings: list[str] = [] - required_order = ["## 要旨の翻訳", "## 新規性", "## 一言まとめ"] + required_order = _LOCALE["FRONT_MATTER_REQUIRED_ORDER"] positions = [] for section in required_order: idx = text.find(section) @@ -363,43 +334,11 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: return issues -METHOD_PAPER_SIGNAL_KEYWORDS = [ - "モデル", - "フレームワーク", - "システム", - "モジュール", - "エンコーダ", - "デコーダ", - "事前融合", - "attention", - "encoder", - "decoder", - "pipeline", - "framework", -] +METHOD_PAPER_SIGNAL_KEYWORDS = _LOCALE["METHOD_PAPER_SIGNAL_KEYWORDS"] -MECHANISM_IO_TOKENS = [ - "入力", - "出力", - "投入", - "渡す", - "生成", - "得る", -] +MECHANISM_IO_TOKENS = _LOCALE["MECHANISM_IO_TOKENS"] -MECHANISM_ACTION_TOKENS = [ - "融合", - "投影", - "圧縮", - "整列", - "プーリング", - "抽出", - "符号化", - "復号", - "連結", - "クエリ", - "更新", -] +MECHANISM_ACTION_TOKENS = _LOCALE["MECHANISM_ACTION_TOKENS"] ENGLISH_FUNCTION_WORDS = { @@ -436,41 +375,17 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: "with", } -PLACEHOLDER_ONLY_PATTERNS = [ - r"^追記予定[。.!!]*$", - r"^todo[。.!!]*$", - r"^なし[。.!!]*$", - r"^省略[。.!!]*$", - r"^原論文参照[。.!!]*$", - r"^ここに記録.*[。.!!]*$", - r"^本節に記録.*[。.!!]*$", -] +PLACEHOLDER_ONLY_PATTERNS = _LOCALE["PLACEHOLDER_ONLY_PATTERNS"] -GENERIC_INNOVATION_PATTERNS = [ - r"新しい手法を提案(?:する|した)?", - r"新規性が(?:ある|高い)", - r"novel approach", - r"初めて実現(?:した)?", -] +GENERIC_INNOVATION_PATTERNS = _LOCALE["GENERIC_INNOVATION_PATTERNS"] -GENERIC_KEY_RESULT_PATTERNS = [ - r"実験結果.*手法.*有効", - r"結果.*有効(?:である|だ)", - r"良好な(?:結果|効果|性能)", - r"性能.*優(?:れ|位|越)", -] +GENERIC_KEY_RESULT_PATTERNS = _LOCALE["GENERIC_KEY_RESULT_PATTERNS"] -GENERIC_LIMITATION_PATTERNS = [ - r"今後の(?:課題|研究).*データ", - r"(?:さらなる|より多くの)データが必要", - r"future work can", - r"more data", - r"今後.*拡張", -] +GENERIC_LIMITATION_PATTERNS = _LOCALE["GENERIC_LIMITATION_PATTERNS"] -HONEST_MISSING_TOKENS = ("未報告", "報告されていない", "未提供", "示されていない", "記載がない") -HONEST_MISSING_BASIS_TOKENS = ("根拠", "本文", "付録", "表", "coverage", "著者") -HONEST_MISSING_IMPACT_TOKENS = ("影響", "制限", "制約", "できない", "不可能", "結論強度") +HONEST_MISSING_TOKENS = _LOCALE["HONEST_MISSING_TOKENS"] +HONEST_MISSING_BASIS_TOKENS = _LOCALE["HONEST_MISSING_BASIS_TOKENS"] +HONEST_MISSING_IMPACT_TOKENS = _LOCALE["HONEST_MISSING_IMPACT_TOKENS"] DOUBLE_ESCAPED_TEX_COMMANDS = { "alpha", @@ -556,7 +471,7 @@ def mixed_language_issues(text: str) -> list[dict[str, object]]: stripped = line.strip() section_name = section_name_for_line(lines, idx - 1) subsection_name = subsection_name_for_line(lines, idx - 1) - if section_name in {"基本情報", "参考文献"}: + if section_name in {_LOCALE["SEC_CORE_INFO"], _LOCALE["SEC_REFERENCES"]}: continue if not re.search(r"[\u4e00-\u9fff]", stripped): continue @@ -919,7 +834,7 @@ def figure_structure_passes(text: str) -> bool: def core_info_structure_issues(text: str) -> list[dict[str, object]]: - body = section_body(text, "基本情報") + body = section_body(text, _LOCALE["SEC_CORE_INFO"]) if not body: return [] @@ -1417,53 +1332,53 @@ def inspect_substantive_content(text: str) -> list[dict[str, object]]: content = normalized_section_content(body) if is_placeholder_like(content): issues.append(issue(section, "section_empty_shell", "error", content or section)) - if section not in {"主要な結果", "参考文献"} and is_honest_missing_declaration(content): + if section not in {_LOCALE["SEC_KEY_RESULTS"], _LOCALE["SEC_REFERENCES"]} and is_honest_missing_declaration(content): issues.append(issue(section, "section_honest_missing_not_allowed", "error", content)) - innovation = section_body(text, "新規性") + innovation = section_body(text, _LOCALE["SEC_INNOVATION"]) innovation_content = normalized_section_content(innovation) innovation_units = meaningful_units(innovation, GENERIC_INNOVATION_PATTERNS) if not innovation_units: - issues.append(issue("新規性", "innovation_empty_shell", "error", innovation_content)) + issues.append(issue(_LOCALE["SEC_INNOVATION"], "innovation_empty_shell", "error", innovation_content)) elif len(innovation_units) < 2: - issues.append(issue("新規性", "innovation_too_few_specific_points", "warning", innovation_content)) + issues.append(issue(_LOCALE["SEC_INNOVATION"], "innovation_too_few_specific_points", "warning", innovation_content)) - key_results = section_body(text, "主要な結果") + key_results = section_body(text, _LOCALE["SEC_KEY_RESULTS"]) key_results_content = normalized_section_content(key_results) if is_honest_missing_declaration(key_results_content): issues.append( issue( - "主要な結果", + _LOCALE["SEC_KEY_RESULTS"], "key_results_honest_missing_not_allowed", "error", key_results_content, ) ) elif not meaningful_units(key_results, GENERIC_KEY_RESULT_PATTERNS): - issues.append(issue("主要な結果", "key_results_empty_shell", "error", key_results_content)) + issues.append(issue(_LOCALE["SEC_KEY_RESULTS"], "key_results_empty_shell", "error", key_results_content)) elif not has_number_token(key_results_content): issues.append( issue( - "主要な結果", + _LOCALE["SEC_KEY_RESULTS"], "key_results_quantitative_result_missing", "warning", key_results_content, ) ) - references = section_body(text, "参考文献") + references = section_body(text, _LOCALE["SEC_REFERENCES"]) references_content = normalized_section_content(references) if is_honest_missing_declaration(references_content): - issues.append(issue("参考文献", "references_unavailable_declared", "warning", references_content)) + issues.append(issue(_LOCALE["SEC_REFERENCES"], "references_unavailable_declared", "warning", references_content)) elif is_placeholder_like(references_content) or not has_reference_entry(references_content): - issues.append(issue("参考文献", "references_placeholder", "error", references_content)) + issues.append(issue(_LOCALE["SEC_REFERENCES"], "references_placeholder", "error", references_content)) - limitations = section_body(text, "限界") + limitations = section_body(text, _LOCALE["SEC_LIMITS"]) limitations_content = normalized_section_content(limitations) if not meaningful_units(limitations, GENERIC_LIMITATION_PATTERNS): - issues.append(issue("限界", "limitations_empty_shell", "error", limitations_content)) + issues.append(issue(_LOCALE["SEC_LIMITS"], "limitations_empty_shell", "error", limitations_content)) - for section in ("手法の骨子", "深掘り分析"): + for section in (_LOCALE["SEC_METHOD"], _LOCALE["SEC_DEEP_ANALYSIS"]): body = section_body(text, section) content = normalized_section_content(body) if not meaningful_units(body): @@ -1481,7 +1396,7 @@ def inspect_substantive_content(text: str) -> list[dict[str, object]]: def method_section_requires_mechanism_flow(text: str) -> bool: - body = section_body(text, "手法の骨子") + body = section_body(text, _LOCALE["SEC_METHOD"]) if not body: return False lower = body.lower() @@ -1494,11 +1409,11 @@ def mechanism_flow_warnings(text: str) -> list[str]: warnings: list[str] = [] if not method_section_requires_mechanism_flow(text): return warnings - if "### 機構フロー" not in text: + if f'### {_LOCALE["SUBSEC_MECHANISM"]}' not in text: warnings.append("mechanism_flow_subsection_missing") return warnings - body = subsection_body(text, "手法の骨子", "機構フロー") + body = subsection_body(text, _LOCALE["SEC_METHOD"], _LOCALE["SUBSEC_MECHANISM"]) if not body: warnings.append("mechanism_flow_subsection_empty") return warnings diff --git a/skills/deeppapernote/scripts/locales/__init__.py b/skills/deeppapernote/scripts/locales/__init__.py new file mode 100644 index 0000000..7d2eb3d --- /dev/null +++ b/skills/deeppapernote/scripts/locales/__init__.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +"""出力言語ロケールの選択。 + +有効なロケールは環境変数 ``DEEPPAPERNOTE_OUTPUT_LANGUAGE`` で切り替える。 +未設定なら、他の設定と同様にシェル設定ファイル(~/.bashrc 等)の +``export DEEPPAPERNOTE_OUTPUT_LANGUAGE=...`` をフォールバックとして参照する。 + +- このフォークの既定は日本語 ("ja")。 +- 本家相当の挙動は "zh"(中国語)。 + +言語依存の文字列(セクション名・フィールド名・小見出し・品質ヒューリスティック等)は +すべて locales/.py の ``LOCALE`` 辞書に集約されており、contracts.py や +lint_note.py はここから取得する。こうすることで、本家のロジック変更はコード側で +クリーンにマージでき、言語データの差分はロケールファイルに閉じ込められる。 +""" +from __future__ import annotations + +import os +import re +from functools import lru_cache +from importlib import import_module +from pathlib import Path + +DEFAULT_LANGUAGE = "ja" +_ENV_VAR = "DEEPPAPERNOTE_OUTPUT_LANGUAGE" +_SUPPORTED = ("ja", "zh") +# env に無い場合に走査するシェル設定ファイル(common.shell_config_value と同じ並び) +_SHELL_CONFIG_FILES = ( + Path.home() / ".zshenv", + Path.home() / ".zprofile", + Path.home() / ".zshrc", + Path.home() / ".bash_profile", + Path.home() / ".bashrc", +) + + +def _normalize(value: str) -> str: + v = (value or "").strip().strip('"').strip("'").lower() + if not v: + return "" + if v.startswith("zh"): # zh, zh-cn, zh_cn, zh-hans など + return "zh" + if v.startswith("ja"): # ja, ja-jp, japanese + return "ja" + return v + + +def _from_shell_config() -> str: + pattern = re.compile(rf"^\s*(?:export\s+)?{re.escape(_ENV_VAR)}=(.*)$") + for path in _SHELL_CONFIG_FILES: + try: + if not path.is_file(): + continue + for raw in reversed(path.read_text(encoding="utf-8-sig").splitlines()): + line = raw.strip() + if not line or line.startswith("#"): + continue + m = pattern.match(line) + if m: + return m.group(1).strip() + except Exception: + continue + return "" + + +@lru_cache(maxsize=1) +def get_language() -> str: + """有効な出力言語コード("ja" / "zh")を返す。""" + candidate = _normalize(os.environ.get(_ENV_VAR, "")) + if not candidate: + candidate = _normalize(_from_shell_config()) + if candidate not in _SUPPORTED: + candidate = DEFAULT_LANGUAGE + return candidate + + +@lru_cache(maxsize=None) +def _load(language: str) -> dict: + module = import_module(f"{__name__}.{language}") + return module.LOCALE + + +def get_locale() -> dict: + """有効な言語の LOCALE 辞書を返す。""" + return _load(get_language()) diff --git a/skills/deeppapernote/scripts/locales/ja.py b/skills/deeppapernote/scripts/locales/ja.py new file mode 100644 index 0000000..0dcdec8 --- /dev/null +++ b/skills/deeppapernote/scripts/locales/ja.py @@ -0,0 +1,338 @@ +# -*- coding: utf-8 -*- +"""日本語ロケール。""" +LOCALE = {'CORE_INFO_FIELDS': ['タイトル', + 'タイトル訳', + '著者', + '所属', + '発表時期', + '発表媒体', + 'DOI', + 'arXiv', + '論文リンク', + 'コード / プロジェクト', + 'データ / リソース', + '論文タイプ'], + 'FIGURE_BUCKET_RESIDUE_TOKENS': {'補充待ち', '残り', '残余', '未配置', '未処理'}, + 'FIGURE_BUCKET_VISUAL_TOKENS': {'図', '表', '画像', 'プレースホルダ', '図表'}, + 'FIGURE_TARGET_SECTIONS': {'手法の骨子', '研究課題', '私のメモ', 'データとタスク定義', '限界', '主要な結果', '深掘り分析'}, + 'FRONT_MATTER_REQUIRED_ORDER': ['## 要旨の翻訳', '## 新規性', '## 一言まとめ'], + 'GENERIC_INNOVATION_PATTERNS': ['新しい手法を提案(?:する|した)?', + '新規性が(?:ある|高い)', + 'novel approach', + '初めて実現(?:した)?'], + 'GENERIC_KEY_RESULT_PATTERNS': ['実験結果.*手法.*有効', + '結果.*有効(?:である|だ)', + '良好な(?:結果|効果|性能)', + '性能.*優(?:れ|位|越)'], + 'GENERIC_LIMITATION_PATTERNS': ['今後の(?:課題|研究).*データ', + '(?:さらなる|より多くの)データが必要', + 'future work can', + 'more data', + '今後.*拡張'], + 'GROUNDING_REQUIRED_SECTIONS': ('研究課題', 'データとタスク定義', '手法の骨子', '主要な結果', '深掘り分析', '限界'), + 'HONEST_MISSING_BASIS_TOKENS': ('根拠', '本文', '付録', '表', 'coverage', '著者'), + 'HONEST_MISSING_IMPACT_TOKENS': ('影響', '制限', '制約', 'できない', '不可能', '結論強度'), + 'HONEST_MISSING_TOKENS': ('未報告', '報告されていない', '未提供', '示されていない', '記載がない'), + 'MECHANISM_ACTION_TOKENS': ['融合', '投影', '圧縮', '整列', 'プーリング', '抽出', '符号化', '復号', '連結', 'クエリ', '更新'], + 'MECHANISM_IO_TOKENS': ['入力', '出力', '投入', '渡す', '生成', '得る'], + 'METHOD_PAPER_SIGNAL_KEYWORDS': ['モデル', + 'フレームワーク', + 'システム', + 'モジュール', + 'エンコーダ', + 'デコーダ', + '事前融合', + 'attention', + 'encoder', + 'decoder', + 'pipeline', + 'framework'], + 'NOTE_REQUIRED_SECTIONS': ('基本情報', + '要旨の翻訳', + '新規性', + '一言まとめ', + '研究課題', + 'データとタスク定義', + '手法の骨子', + '主要な結果', + '深掘り分析', + '限界', + '私のメモ', + '参考文献'), + 'OUTPUT_LANGUAGE_CODE': 'ja', + 'PAPER_TYPE_CONTRACTS': {'AI_method': {'avoid_rules': ['AI_method でない論文を無理にモデルアーキテクチャとして書き換えない。'], + 'boundary_questions': ['中心的な機構の利得は、主結果が示唆するだけでなく、どの実験やアブレーションで裏付けられているか?', + 'どの比較が現在のデータ・ベースライン・計算資源・プロトコルの下でのみ有効で、汎用的な状況へ外挿できないか?', + '論文は失敗・劣化・不安定・コスト増の証拠を示しているか。示していない場合、結論の境界は何か?'], + 'formula_rules': ['手法の理解に必須の 1〜3 ' + '個の主要な数式のみを残し、その工学的含意を説明する。'], + 'mechanism_flow_contract': {'apply_when_paper_type_in': ['AI_method'], + 'required_step_count': '3_to_4', + 'required_step_fields': ['input', + 'operation', + 'output_destination']}, + 'paper_type': 'AI_method', + 'reader_lens': '手法の機構を再現できる技術読者向け', + 'recommended_subsections': {'主要な結果': ['主要結果と強力なベースライン', + 'アブレーションが示すもの', + '失敗・不安定な設定'], + '手法の骨子': ['機構フロー', + 'モデル構造', + '学習目標', + '推論・サンプリング経路', + '主要な実装詳細'], + '深掘り分析': ['なぜ有効か', + '計算量とスケーラビリティ', + '再現時の注意点']}, + 'required_checks': ['機構フロー、主要な数式、実験デザイン、アブレーションの含意、失敗の境界を説明する必要がある。'], + 'section_focus': ['問題設定', + '手法の機構', + '学習/推論フロー', + '主要な数式', + '比較ベースライン', + 'アブレーションと失敗の境界'], + 'section_semantics': {'データとタスク定義': 'データセット、入出力、評価タスク、実験設定。', + '主要な結果': '主結果、強力なベースライン、アブレーション、重要な数値。', + '手法の骨子': 'モデル・アルゴリズム・学習または推論の機構。', + '深掘り分析': '手法がなぜ有効か、どこが脆いか、再現・拡張のコスト。', + '研究課題': '手法が解決しようとする具体的な技術課題と、既存手法の弱点。'}}, + 'benchmark_or_dataset': {'avoid_rules': ['データ構築フローをモデルの pipeline ' + 'として書かない。'], + 'boundary_questions': ['この benchmark/dataset ' + 'が実際に測定している構成概念は何で、どの能力は間接的な近似に過ぎないか?', + 'タスク・ラベル・サンプリング・フィルタ・評価プロトコルは、どのようなカバレッジの欠落やバイアスを持ち込むか?', + 'ベースライン結果は評価セットに識別力があることを示すのか、それとも特定タイプのモデルがこのプロトコルに適応しただけか?', + 'サンプルの長さ、コーパス長、人口統計、クラス分布、データのアクセス可能性やプライバシー制限は、再現と外挿にどう影響するか?'], + 'formula_rules': ['中心的な評価指標、サンプリング規則、分割定義のみを残す。'], + 'paper_type': 'benchmark_or_dataset', + 'reader_lens': 'benchmark/dataset ' + 'の有用性とバイアスの境界を判断したい研究者向け', + 'recommended_subsections': {'データとタスク定義': ['データ出所', + 'タスク分割', + 'アノテーション/選別プロトコル'], + '主要な結果': ['ベースライン性能', + '難易度分布', + 'カバレッジとバイアス'], + '手法の骨子': ['構築フロー', + '評価プロトコル', + 'ベースライン設定'], + '深掘り分析': ['ベンチマークが実際に測るもの', + '適用範囲の境界']}, + 'required_checks': ['データ出所、構築/アノテーションのフロー、評価指標、ベースライン性能、サンプル統計、データ公開やプライバシー制限、適用範囲の境界を説明する必要がある。'], + 'section_focus': ['タスク分割', + 'データ出所と構築フロー', + 'アノテーションプロトコル', + '評価指標', + 'カバレッジ範囲とバイアス', + 'サンプル統計とデータ公開制限'], + 'section_semantics': {'データとタスク定義': 'データ出所、タスク分割、ラベル/課題の定義、サンプル範囲。', + '主要な結果': 'ベースライン性能、難易度分布、カバレッジ範囲、バイアス。', + '手法の骨子': 'データ構築・選別・アノテーション・評価プロトコル。モデルの ' + 'pipeline ' + 'としては書かない。', + '深掘り分析': '実際に何を測れているか、そして何を代表できないか。', + '研究課題': 'この ' + 'benchmark/dataset ' + 'が埋めようとする評価またはデータのギャップ。'}}, + 'clinical_or_psychology_empirical': {'avoid_rules': ['相関、予測性能、群間差を、証明されていない因果結論として書かない。'], + 'boundary_questions': ['サンプル出所、選択・除外基準、測定ツール、アノテーション手順は、外挿をどのように制限するか?', + '結果は相関・予測・群間差・因果的解釈のどれを支持するか。論文のデザインが証明できる範囲を越えないこと。', + '臨床または心理学的意義は、未観測の交絡、尺度の閾値、テキスト/音声の欠落、場面の制約に依存していないか?', + 'サンプル構成、データ欠損、プライバシー制限、資料の非公開は、再現と再分析をどのように制限するか?'], + 'formula_rules': ['中心的な統計モデル、効果量、信頼区間、尺度の定義のみを残す。'], + 'paper_type': 'clinical_or_psychology_empirical', + 'reader_lens': '臨床/心理学のサンプル、変数関係、外挿の境界に注目する研究読者向け', + 'recommended_subsections': {'データとタスク定義': ['サンプルと選択・除外基準', + '変数と尺度', + '測定手順'], + '主要な結果': ['主要な効果', + '不確実性と有意性', + '臨床・心理学的解釈'], + '手法の骨子': ['研究デザイン', + '分析モデル', + '主要な比較'], + '深掘り分析': ['因果解釈の境界', + '外挿の限界']}, + 'required_checks': ['相関・予測・群間差・因果的解釈を区別し、サンプル統計、倫理/プライバシー上の制約、外挿の境界を説明する必要がある。'], + 'section_focus': ['サンプル出所', + '選択・除外基準', + '変数または尺度', + '分析パイプライン', + '効果量と不確実性', + 'サンプル統計・倫理・データのアクセス可能性'], + 'section_semantics': {'データとタスク定義': 'サンプル出所、選択・除外基準、変数/尺度、測定方法。', + '主要な結果': '主効果、相関、群間差、不確実性や有意性。', + '手法の骨子': '研究デザイン、群分け、測定手順、統計解析の道筋。', + '深掘り分析': '結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。', + '研究課題': '臨床・心理学・行動科学における研究課題、仮説、変数間の関係。'}}, + 'humanities_or_social_science': {'avoid_rules': ['規範的判断、テキスト解釈、事例分析を実験的事実として書かない。'], + 'boundary_questions': ['著者の解釈は、どの資料・事例・理論的前提に依存しているか?', + '同じく資料を説明できる代替的解釈は存在するか。論文はそれをどのように排除したか、あるいは排除していないか?', + 'どの結論が概念的貢献や規範的判断であり、経験的事実として直接扱えないものか?'], + 'formula_rules': ['通常は数式を無理に残さない。中心的な形式的定義やコーディング規則のみを残す。'], + 'paper_type': 'humanities_or_social_science', + 'reader_lens': '理論的枠組み、資料の解釈、論証の構造に注目する研究読者向け', + 'recommended_subsections': {'データとタスク定義': ['資料の範囲', + '選択基準', + '事例・コーパスの境界'], + '主要な結果': ['中心的な解釈的発見', + '概念的貢献'], + '手法の骨子': ['理論的枠組み', + '概念の区別', + '論証の道筋'], + '深掘り分析': ['論証の強さ', + '代替的解釈', + '資料の境界']}, + 'required_checks': ['著者の論証、資料的証拠、規範的判断、実験的事実を区別する必要がある。'], + 'section_focus': ['研究対象', + '資料の出所', + '理論的枠組み', + '論証の道筋', + '概念的貢献', + '解釈の境界'], + 'section_semantics': {'データとタスク定義': '資料、事例、テキスト、インタビュー、アーカイブ、コーパスの範囲。ML ' + 'task ' + 'としては書かない。', + '主要な結果': '中心的な解釈的発見、概念的貢献、既存の見解への修正。', + '手法の骨子': '理論的枠組み、概念の区別、論証の道筋。', + '深掘り分析': '論証の強さ、資料の境界、解釈の代替可能性、転用可能性。', + '研究課題': '著者が説明しようとする社会・文化・歴史・制度・理論上の問い。'}}, + 'survey_or_review': {'avoid_rules': ['サーベイ中の代表的な結論を、著者自身が行った単一の実験結果として書かない。'], + 'boundary_questions': ['検索範囲、選定・除外基準、分類軸は、どの研究の流れを見落とすか?', + 'サーベイが示すのは分野の合意か、著者の分類か、それとも未解決の対立か?', + 'どのトレンド結論がカバレッジ内の文献分布に由来し、技術成熟度の判断として直接扱えないか?'], + 'formula_rules': ['分類軸、選定・除外の基準、証拠の集約規則、meta-analysis ' + 'の統計量のみを残す。'], + 'paper_type': 'survey_or_review', + 'reader_lens': 'サーベイの脈絡、分類体系、証拠の境界を整理する必要のある研究読者向け', + 'recommended_subsections': {'データとタスク定義': ['サーベイの範囲', + '選定・除外基準', + '文献カバレッジ'], + '主要な結果': ['代表的な方向性', + '合意と対立', + '未解決問題'], + '手法の骨子': ['分類体系', + '手法の系譜', + '証拠の整理方法'], + '深掘り分析': ['分類体系の限界', + '未カバー領域', + '今後の研究機会']}, + 'required_checks': ['サーベイの範囲、文献の選定、分類体系、合意と対立、未解決問題を説明する必要がある。'], + 'section_focus': ['サーベイの範囲', + '選定・除外基準', + 'テーマ分類', + '手法の系譜', + '合意と対立', + '未解決問題'], + 'section_semantics': {'データとタスク定義': '対象とする文献範囲、検索/選別基準、レビュー対象。', + '主要な結果': '分野の合意、対立、トレンド、代表的な方向性、未解決問題。', + '手法の骨子': '分類体系、レビューの構成方法、証拠統合のロジック。単一論文の手法構成としては書かない。', + '深掘り分析': 'サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。', + '研究課題': 'サーベイが整理しようとする分野の問い、論争、知識のギャップ。'}}}, + 'PAPER_TYPE_SECTION_PROFILES': {'AI_method': {'recommended_subsections': {'主要な結果': ['主要結果と強力なベースライン', + 'アブレーションが示すもの', + '失敗・不安定な設定'], + '手法の骨子': ['機構フロー', + 'モデル構造', + '学習目標', + '推論・サンプリング経路', + '主要な実装詳細'], + '深掘り分析': ['なぜ有効か', + '計算量とスケーラビリティ', + '再現時の注意点']}, + 'section_semantics': {'データとタスク定義': 'データセット、入出力、評価タスク、実験設定。', + '主要な結果': '主結果、強力なベースライン、アブレーション、重要な数値。', + '手法の骨子': 'モデル・アルゴリズム・学習または推論の機構。', + '深掘り分析': '手法がなぜ有効か、どこが脆いか、再現・拡張のコスト。', + '研究課題': '手法が解決しようとする具体的な技術課題と、既存手法の弱点。'}}, + 'benchmark_or_dataset': {'recommended_subsections': {'データとタスク定義': ['データ出所', + 'タスク分割', + 'アノテーション/選別プロトコル'], + '主要な結果': ['ベースライン性能', + '難易度分布', + 'カバレッジとバイアス'], + '手法の骨子': ['構築フロー', + '評価プロトコル', + 'ベースライン設定'], + '深掘り分析': ['ベンチマークが実際に測るもの', + '適用範囲の境界']}, + 'section_semantics': {'データとタスク定義': 'データ出所、タスク分割、ラベル/課題の定義、サンプル範囲。', + '主要な結果': 'ベースライン性能、難易度分布、カバレッジ範囲、バイアス。', + '手法の骨子': 'データ構築・選別・アノテーション・評価プロトコル。モデルの ' + 'pipeline ' + 'としては書かない。', + '深掘り分析': '実際に何を測れているか、そして何を代表できないか。', + '研究課題': 'この ' + 'benchmark/dataset ' + 'が埋めようとする評価またはデータのギャップ。'}}, + 'clinical_or_psychology_empirical': {'recommended_subsections': {'データとタスク定義': ['サンプルと選択・除外基準', + '変数と尺度', + '測定手順'], + '主要な結果': ['主要な効果', + '不確実性と有意性', + '臨床・心理学的解釈'], + '手法の骨子': ['研究デザイン', + '分析モデル', + '主要な比較'], + '深掘り分析': ['因果解釈の境界', + '外挿の限界']}, + 'section_semantics': {'データとタスク定義': 'サンプル出所、選択・除外基準、変数/尺度、測定方法。', + '主要な結果': '主効果、相関、群間差、不確実性や有意性。', + '手法の骨子': '研究デザイン、群分け、測定手順、統計解析の道筋。', + '深掘り分析': '結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。', + '研究課題': '臨床・心理学・行動科学における研究課題、仮説、変数間の関係。'}}, + 'humanities_or_social_science': {'recommended_subsections': {'データとタスク定義': ['資料の範囲', + '選択基準', + '事例・コーパスの境界'], + '主要な結果': ['中心的な解釈的発見', + '概念的貢献'], + '手法の骨子': ['理論的枠組み', + '概念の区別', + '論証の道筋'], + '深掘り分析': ['論証の強さ', + '代替的解釈', + '資料の境界']}, + 'section_semantics': {'データとタスク定義': '資料、事例、テキスト、インタビュー、アーカイブ、コーパスの範囲。ML ' + 'task ' + 'としては書かない。', + '主要な結果': '中心的な解釈的発見、概念的貢献、既存の見解への修正。', + '手法の骨子': '理論的枠組み、概念の区別、論証の道筋。', + '深掘り分析': '論証の強さ、資料の境界、解釈の代替可能性、転用可能性。', + '研究課題': '著者が説明しようとする社会・文化・歴史・制度・理論上の問い。'}}, + 'survey_or_review': {'recommended_subsections': {'データとタスク定義': ['サーベイの範囲', + '選定・除外基準', + '文献カバレッジ'], + '主要な結果': ['代表的な方向性', + '合意と対立', + '未解決問題'], + '手法の骨子': ['分類体系', + '手法の系譜', + '証拠の整理方法'], + '深掘り分析': ['分類体系の限界', + '未カバー領域', + '今後の研究機会']}, + 'section_semantics': {'データとタスク定義': '対象とする文献範囲、検索/選別基準、レビュー対象。', + '主要な結果': '分野の合意、対立、トレンド、代表的な方向性、未解決問題。', + '手法の骨子': '分類体系、レビューの構成方法、証拠統合のロジック。単一論文の手法構成としては書かない。', + '深掘り分析': 'サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。', + '研究課題': 'サーベイが整理しようとする分野の問い、論争、知識のギャップ。'}}}, + 'PLACEHOLDER_ONLY_PATTERNS': ['^追記予定[。.!!]*$', + '^todo[。.!!]*$', + '^なし[。.!!]*$', + '^省略[。.!!]*$', + '^原論文参照[。.!!]*$', + '^ここに記録.*[。.!!]*$', + '^本節に記録.*[。.!!]*$'], + 'SEC_ABSTRACT': '要旨の翻訳', + 'SEC_CORE_INFO': '基本情報', + 'SEC_DATA_TASK': 'データとタスク定義', + 'SEC_DEEP_ANALYSIS': '深掘り分析', + 'SEC_INNOVATION': '新規性', + 'SEC_KEY_RESULTS': '主要な結果', + 'SEC_LIMITS': '限界', + 'SEC_METHOD': '手法の骨子', + 'SEC_MY_NOTES': '私のメモ', + 'SEC_ONELINE': '一言まとめ', + 'SEC_REFERENCES': '参考文献', + 'SEC_RESEARCH_Q': '研究課題', + 'SUBSEC_MECHANISM': '機構フロー'} diff --git a/skills/deeppapernote/scripts/locales/zh.py b/skills/deeppapernote/scripts/locales/zh.py new file mode 100644 index 0000000..797a526 --- /dev/null +++ b/skills/deeppapernote/scripts/locales/zh.py @@ -0,0 +1,328 @@ +# -*- coding: utf-8 -*- +"""中国語ロケール(本家原文字列を忠実に保持)。""" +LOCALE = {'CORE_INFO_FIELDS': ['标题', + '标题翻译', + '作者', + '机构', + '发表时间', + '发表渠道', + 'DOI', + 'arXiv', + '论文链接', + '代码 / 项目', + '数据 / 资源', + '论文类型'], + 'FIGURE_BUCKET_RESIDUE_TOKENS': {'待补', '剩余', '未放置', '残余', '未处理'}, + 'FIGURE_BUCKET_VISUAL_TOKENS': {'表', '占位', '图片', '图表', '图'}, + 'FIGURE_TARGET_SECTIONS': {'方法主线', '数据与任务定义', '局限', '关键结果', '研究问题', '深度分析', '我的笔记'}, + 'FRONT_MATTER_REQUIRED_ORDER': ['## 原文摘要翻译', '## 创新点', '## 一句话总结'], + 'GENERIC_INNOVATION_PATTERNS': ['本文提出(?:了)?一种新方法', '具有创新性', 'novel approach', '首次实现'], + 'GENERIC_KEY_RESULT_PATTERNS': ['实验结果表明方法有效', '结果表明.*有效', '取得(?:了)?较好效果', '性能.*优越'], + 'GENERIC_LIMITATION_PATTERNS': ['未来工作.*更多数据', '需要更多数据', 'future work can', 'more data', '后续.*扩展'], + 'GROUNDING_REQUIRED_SECTIONS': ('研究问题', '数据与任务定义', '方法主线', '关键结果', '深度分析', '局限'), + 'HONEST_MISSING_BASIS_TOKENS': ('依据', '正文', '附录', '表格', 'coverage', '作者'), + 'HONEST_MISSING_IMPACT_TOKENS': ('影响', '限制', '受限', '不能', '无法', '结论强度'), + 'HONEST_MISSING_TOKENS': ('本文未给出', '论文未给出', '未报告', '没有报告', '未提供'), + 'MECHANISM_ACTION_TOKENS': ['融合', '投影', '压缩', '对齐', '池化', '提取', '编码', '解码', '拼接', '查询', '更新'], + 'MECHANISM_IO_TOKENS': ['输入', '输出', '送入', '送到', '生成', '得到'], + 'METHOD_PAPER_SIGNAL_KEYWORDS': ['模型', + '框架', + '系统', + '模块', + '编码器', + '解码器', + '预融合', + 'attention', + 'encoder', + 'decoder', + 'pipeline', + 'framework'], + 'NOTE_REQUIRED_SECTIONS': ('核心信息', + '原文摘要翻译', + '创新点', + '一句话总结', + '研究问题', + '数据与任务定义', + '方法主线', + '关键结果', + '深度分析', + '局限', + '我的笔记', + '引用'), + 'OUTPUT_LANGUAGE_CODE': 'zh-CN', + 'PAPER_TYPE_CONTRACTS': {'AI_method': {'avoid_rules': ['不要把非 AI_method 论文强行改写成模型架构。'], + 'boundary_questions': ['核心机制的收益由哪个实验或消融支撑,而不是只由主结果暗示?', + '哪些比较只能证明在当前数据、基线、算力或协议下有效,不能外推到通用场景?', + '论文是否给出失败、退化、不稳定或成本上升的证据;如果没有,结论边界是什么?'], + 'formula_rules': ['仅保留理解方法必需的 1 到 3 个关键公式,并解释其工程含义。'], + 'mechanism_flow_contract': {'apply_when_paper_type_in': ['AI_method'], + 'required_step_count': '3_to_4', + 'required_step_fields': ['input', + 'operation', + 'output_destination']}, + 'paper_type': 'AI_method', + 'reader_lens': '面向能复现方法机制的技术读者', + 'recommended_subsections': {'关键结果': ['主结果与强基线', + '消融到底说明了什么', + '失败或不稳定设置'], + '方法主线': ['机制流程', + '模型结构', + '训练目标', + '推理与采样链路', + '关键实现细节'], + '深度分析': ['为什么有效', + '复杂度与扩展性', + '复现注意点']}, + 'required_checks': ['需要说明机制流程、关键公式、实验设计、消融含义和失败边界。'], + 'section_focus': ['问题设置', + '方法机制', + '训练/推理流程', + '关键公式', + '比较基线', + '消融与失败边界'], + 'section_semantics': {'关键结果': '主结果、强基线、消融和关键数字。', + '数据与任务定义': '数据集、输入输出、评测任务和实验设置。', + '方法主线': '模型、算法、训练或推理机制。', + '深度分析': '方法为什么有效、何处脆弱、复现和扩展代价。', + '研究问题': '方法要解决的具体技术问题和现有方法短板。'}}, + 'benchmark_or_dataset': {'avoid_rules': ['不要把数据构建流程写成模型 pipeline。'], + 'boundary_questions': ['这个 benchmark/dataset ' + '实际测量的构念是什么,哪些能力只是间接近似?', + '任务、标签、采样、过滤或评测协议会引入哪些覆盖缺口或偏差?', + '基线结果证明了评测集有区分度,还是只证明某类模型适应该协议?', + '样本时长、语料长度、人口统计、类别分布、数据可访问性或隐私限制如何影响复现和外推?'], + 'formula_rules': ['仅保留核心评测指标、采样规则或划分定义。'], + 'paper_type': 'benchmark_or_dataset', + 'reader_lens': '面向要判断 benchmark/dataset ' + '可用性和偏差边界的研究者', + 'recommended_subsections': {'关键结果': ['基线表现', + '难度分布', + '覆盖与偏差'], + '数据与任务定义': ['数据来源', + '任务拆分', + '标注/筛选协议'], + '方法主线': ['构建流程', + '评测协议', + 'Baseline ' + '设置'], + '深度分析': ['benchmark ' + '真正测到了什么', + '适用边界']}, + 'required_checks': ['需要说明数据来源、构建/标注流程、评测指标、基线表现、样本统计、数据开放或隐私限制和适用边界。'], + 'section_focus': ['任务拆分', + '数据来源与构建流程', + '标注协议', + '评测指标', + '覆盖范围与偏差', + '样本统计与数据开放限制'], + 'section_semantics': {'关键结果': '基线表现、难度分布、覆盖范围和偏差。', + '数据与任务定义': '数据来源、任务拆分、标签/题目定义、样本范围。', + '方法主线': '数据构建、筛选、标注和评测协议,不写成模型 ' + 'pipeline。', + '深度分析': '它真正测到了什么,以及不能代表什么。', + '研究问题': '这个 ' + 'benchmark/dataset ' + '想补足的评测或数据缺口。'}}, + 'clinical_or_psychology_empirical': {'avoid_rules': ['不要把相关性、预测性能或组间差异写成未经证明的因果结论。'], + 'boundary_questions': ['样本来源、纳排标准、测量工具和标注流程如何限制外推?', + '结果支持相关、预测、组间差异还是因果解释;不要越过论文设计能证明的范围。', + '临床或心理学意义是否依赖未观测混杂、量表阈值、文本/语音缺失或场景约束?', + '样本构成、数据缺失、隐私限制或材料不可公开会怎样限制复现与再分析?'], + 'formula_rules': ['仅保留核心统计模型、效应量、置信区间或量表定义。'], + 'paper_type': 'clinical_or_psychology_empirical', + 'reader_lens': '面向关注临床/心理学样本、变量关系和外推边界的研究读者', + 'recommended_subsections': {'关键结果': ['主要效应', + '不确定性与显著性', + '临床或心理学解释'], + '数据与任务定义': ['样本与纳排标准', + '变量与量表', + '测量流程'], + '方法主线': ['研究设计', + '分析模型', + '主要比较'], + '深度分析': ['因果解释边界', + '外推限制']}, + 'required_checks': ['需要区分相关、预测、组间差异和因果解释,说明样本统计、伦理/隐私约束与外推边界。'], + 'section_focus': ['样本来源', + '纳排标准', + '变量或量表', + '分析管线', + '效应量与不确定性', + '样本统计、伦理和数据可访问性'], + 'section_semantics': {'关键结果': '主要效应、相关性、组间差异、不确定性或显著性。', + '数据与任务定义': '样本来源、纳排标准、变量/量表、测量方式。', + '方法主线': '研究设计、分组、测量流程和统计分析路径。', + '深度分析': '结果解释、因果边界、临床/心理学意义和外推限制。', + '研究问题': '临床、心理学或行为科学中的研究问题、假设或变量关系。'}}, + 'humanities_or_social_science': {'avoid_rules': ['不要把规范性判断、文本解释或案例分析写成实验事实。'], + 'boundary_questions': ['作者的解释依赖哪些材料、案例或理论前提?', + '是否存在同样能解释材料的替代解释,论文如何排除或没有排除?', + '哪些结论是概念贡献或规范性判断,而不是可直接当作经验事实?'], + 'formula_rules': ['通常不强行保留公式;仅保留核心形式化定义或编码规则。'], + 'paper_type': 'humanities_or_social_science', + 'reader_lens': '面向关注理论框架、材料解释和论证结构的研究读者', + 'recommended_subsections': {'关键结果': ['核心解释性发现', + '概念贡献'], + '数据与任务定义': ['材料范围', + '选择标准', + '案例或语料边界'], + '方法主线': ['理论框架', + '概念区分', + '论证路径'], + '深度分析': ['论证强度', + '替代解释', + '材料边界']}, + 'required_checks': ['需要区分作者论证、材料证据、规范性判断和实验事实。'], + 'section_focus': ['研究对象', + '材料来源', + '理论框架', + '论证路径', + '概念贡献', + '解释边界'], + 'section_semantics': {'关键结果': '核心解释性发现、概念贡献或对既有观点的修正。', + '数据与任务定义': '材料、案例、文本、访谈、档案或语料范围,不写成 ' + 'ML ' + 'task。', + '方法主线': '理论框架、概念区分和论证路径。', + '深度分析': '论证强度、材料边界、解释替代性和可迁移性。', + '研究问题': '作者要解释的社会、文化、历史、制度或理论问题。'}}, + 'survey_or_review': {'avoid_rules': ['不要把综述中的代表性结论写成作者自己完成的单项实验结果。'], + 'boundary_questions': ['检索范围、纳入排除标准或分类轴会遗漏哪些研究路线?', + '综述给出的是领域共识、作者分类,还是尚未解决的分歧?', + '哪些趋势结论来自覆盖范围内的文献分布,不能直接当作技术成熟度判断?'], + 'formula_rules': ['仅保留分类轴、纳入排除准则、证据汇总规则或 ' + 'meta-analysis 统计量。'], + 'paper_type': 'survey_or_review', + 'reader_lens': '面向需要梳理综述脉络、分类体系和证据边界的研究读者', + 'recommended_subsections': {'关键结果': ['代表性方向', + '共识与分歧', + '开放问题'], + '数据与任务定义': ['综述范围', + '纳入/排除标准', + '文献覆盖'], + '方法主线': ['分类体系', + '方法谱系', + '证据组织方式'], + '深度分析': ['分类体系的局限', + '未覆盖区域', + '后续研究机会']}, + 'required_checks': ['需要说明综述范围、文献选择、分类体系、共识分歧和开放问题。'], + 'section_focus': ['综述范围', + '纳入排除标准', + '主题分类', + '方法谱系', + '共识与分歧', + '开放问题'], + 'section_semantics': {'关键结果': '领域共识、分歧、趋势、代表性方向和开放问题。', + '数据与任务定义': '纳入文献范围、检索/筛选标准和综述对象。', + '方法主线': '分类体系、综述组织方式和证据综合逻辑,不写成单篇方法架构。', + '深度分析': '综述覆盖的盲区、分类体系的解释力和未来研究机会。', + '研究问题': '综述试图整理的领域问题、争议或知识缺口。'}}}, + 'PAPER_TYPE_SECTION_PROFILES': {'AI_method': {'recommended_subsections': {'关键结果': ['主结果与强基线', + '消融到底说明了什么', + '失败或不稳定设置'], + '方法主线': ['机制流程', + '模型结构', + '训练目标', + '推理与采样链路', + '关键实现细节'], + '深度分析': ['为什么有效', + '复杂度与扩展性', + '复现注意点']}, + 'section_semantics': {'关键结果': '主结果、强基线、消融和关键数字。', + '数据与任务定义': '数据集、输入输出、评测任务和实验设置。', + '方法主线': '模型、算法、训练或推理机制。', + '深度分析': '方法为什么有效、何处脆弱、复现和扩展代价。', + '研究问题': '方法要解决的具体技术问题和现有方法短板。'}}, + 'benchmark_or_dataset': {'recommended_subsections': {'关键结果': ['基线表现', + '难度分布', + '覆盖与偏差'], + '数据与任务定义': ['数据来源', + '任务拆分', + '标注/筛选协议'], + '方法主线': ['构建流程', + '评测协议', + 'Baseline ' + '设置'], + '深度分析': ['benchmark ' + '真正测到了什么', + '适用边界']}, + 'section_semantics': {'关键结果': '基线表现、难度分布、覆盖范围和偏差。', + '数据与任务定义': '数据来源、任务拆分、标签/题目定义、样本范围。', + '方法主线': '数据构建、筛选、标注和评测协议,不写成模型 ' + 'pipeline。', + '深度分析': '它真正测到了什么,以及不能代表什么。', + '研究问题': '这个 ' + 'benchmark/dataset ' + '想补足的评测或数据缺口。'}}, + 'clinical_or_psychology_empirical': {'recommended_subsections': {'关键结果': ['主要效应', + '不确定性与显著性', + '临床或心理学解释'], + '数据与任务定义': ['样本与纳排标准', + '变量与量表', + '测量流程'], + '方法主线': ['研究设计', + '分析模型', + '主要比较'], + '深度分析': ['因果解释边界', + '外推限制']}, + 'section_semantics': {'关键结果': '主要效应、相关性、组间差异、不确定性或显著性。', + '数据与任务定义': '样本来源、纳排标准、变量/量表、测量方式。', + '方法主线': '研究设计、分组、测量流程和统计分析路径。', + '深度分析': '结果解释、因果边界、临床/心理学意义和外推限制。', + '研究问题': '临床、心理学或行为科学中的研究问题、假设或变量关系。'}}, + 'humanities_or_social_science': {'recommended_subsections': {'关键结果': ['核心解释性发现', + '概念贡献'], + '数据与任务定义': ['材料范围', + '选择标准', + '案例或语料边界'], + '方法主线': ['理论框架', + '概念区分', + '论证路径'], + '深度分析': ['论证强度', + '替代解释', + '材料边界']}, + 'section_semantics': {'关键结果': '核心解释性发现、概念贡献或对既有观点的修正。', + '数据与任务定义': '材料、案例、文本、访谈、档案或语料范围,不写成 ' + 'ML ' + 'task。', + '方法主线': '理论框架、概念区分和论证路径。', + '深度分析': '论证强度、材料边界、解释替代性和可迁移性。', + '研究问题': '作者要解释的社会、文化、历史、制度或理论问题。'}}, + 'survey_or_review': {'recommended_subsections': {'关键结果': ['代表性方向', + '共识与分歧', + '开放问题'], + '数据与任务定义': ['综述范围', + '纳入/排除标准', + '文献覆盖'], + '方法主线': ['分类体系', + '方法谱系', + '证据组织方式'], + '深度分析': ['分类体系的局限', + '未覆盖区域', + '后续研究机会']}, + 'section_semantics': {'关键结果': '领域共识、分歧、趋势、代表性方向和开放问题。', + '数据与任务定义': '纳入文献范围、检索/筛选标准和综述对象。', + '方法主线': '分类体系、综述组织方式和证据综合逻辑,不写成单篇方法架构。', + '深度分析': '综述覆盖的盲区、分类体系的解释力和未来研究机会。', + '研究问题': '综述试图整理的领域问题、争议或知识缺口。'}}}, + 'PLACEHOLDER_ONLY_PATTERNS': ['^待补充[。.!!]*$', + '^todo[。.!!]*$', + '^暂无[。.!!]*$', + '^略[。.!!]*$', + '^参见原论文[。.!!]*$', + '^这里记录.*[。.!!]*$', + '^本节记录.*[。.!!]*$'], + 'SEC_ABSTRACT': '原文摘要翻译', + 'SEC_CORE_INFO': '核心信息', + 'SEC_DATA_TASK': '数据与任务定义', + 'SEC_DEEP_ANALYSIS': '深度分析', + 'SEC_INNOVATION': '创新点', + 'SEC_KEY_RESULTS': '关键结果', + 'SEC_LIMITS': '局限', + 'SEC_METHOD': '方法主线', + 'SEC_MY_NOTES': '我的笔记', + 'SEC_ONELINE': '一句话总结', + 'SEC_REFERENCES': '引用', + 'SEC_RESEARCH_Q': '研究问题', + 'SUBSEC_MECHANISM': '机制流程'} diff --git a/skills/deeppapernote/scripts/plan_figures.py b/skills/deeppapernote/scripts/plan_figures.py index 13015cf..8812994 100644 --- a/skills/deeppapernote/scripts/plan_figures.py +++ b/skills/deeppapernote/scripts/plan_figures.py @@ -8,6 +8,10 @@ from common import caption_preference_score, maybe_load_json_record, normalize_whitespace +from locales import get_locale + +_LOCALE = get_locale() + def parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(description=__doc__ or "plan figures") @@ -65,7 +69,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: r"(?:produces|achieves|outperforms|improves|reduces|increases)\b", text, ): - return "main_result", "主要な結果", "この図または表は主要な結果を直接担っており、主要な結果セクションに配置するのが適切である。" + return "main_result", _LOCALE["SEC_KEY_RESULTS"], "この図または表は主要な結果を直接担っており、主要な結果セクションに配置するのが適切である。" if any( token in text for token in [ @@ -79,7 +83,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "identification of studies", ] ): - return "data_or_task_overview", "データとタスク定義", "この図は文献のスクリーニングや選定フローを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がレビューの証拠の出所を理解する助けにするのが適切である。" + return "data_or_task_overview", _LOCALE["SEC_DATA_TASK"], "この図は文献のスクリーニングや選定フローを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がレビューの証拠の出所を理解する助けにするのが適切である。" if any( token in text for token in [ @@ -94,7 +98,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "process", ] ): - return "method_overview", "機構フロー", "この図は手法全体またはシステムのフローを俯瞰している。マッチの信頼度が十分に高ければ、`### 機構フロー` に配置し、実行チェーンの理解を素早く構築する助けにするのが最適である。" + return "method_overview", _LOCALE["SUBSEC_MECHANISM"], "この図は手法全体またはシステムのフローを俯瞰している。マッチの信頼度が十分に高ければ、`### 機構フロー` に配置し、実行チェーンの理解を素早く構築する助けにするのが最適である。" if any( token in text for token in [ @@ -108,7 +112,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "issue", ] ): - return "data_or_task_overview", "データとタスク定義", "この図はタスクやデータセットがどのように構築されるかを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がデータの出所を理解する助けにするのが適切である。" + return "data_or_task_overview", _LOCALE["SEC_DATA_TASK"], "この図はタスクやデータセットがどのように構築されるかを説明している。候補画像の品質が十分であれば、データとタスク定義セクションに配置し、読者がデータの出所を理解する助けにするのが適切である。" if any( token in text for token in [ @@ -125,7 +129,7 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "attribute", ] ): - return "data_or_task", "データとタスク定義", "この図はタスク設定やデータの説明に近く、データとタスク定義に配置するのが最も適切である。" + return "data_or_task", _LOCALE["SEC_DATA_TASK"], "この図はタスク設定やデータの説明に近く、データとタスク定義に配置するのが最も適切である。" if any( token in text for token in [ @@ -142,10 +146,10 @@ def classify_caption_kind(item_id: str, caption: str) -> tuple[str, str, str]: "block translation", ] ): - return "method_detail", "手法の骨子", "この図は手法の内部機構や重要な実行状態を説明しており、手法の骨子セクションに機構詳細のプレースホルダとして配置するのが適切である。" + return "method_detail", _LOCALE["SEC_METHOD"], "この図は手法の内部機構や重要な実行状態を説明しており、手法の骨子セクションに機構詳細のプレースホルダとして配置するのが適切である。" if item_id.lower().startswith("table"): - return "table_result", "主要な結果", "これは主要な結果を示す表であり、主要な結果セクションに配置して中心的な数値の把握を補助するのが適切である。" - return "supporting_figure", "深掘り分析", "この図は補足図として適しており、深掘り分析セクションに配置して著者の主張の説明を助けるのが適切である。" + return "table_result", _LOCALE["SEC_KEY_RESULTS"], "これは主要な結果を示す表であり、主要な結果セクションに配置して中心的な数値の把握を補助するのが適切である。" + return "supporting_figure", _LOCALE["SEC_DEEP_ANALYSIS"], "この図は補足図として適しており、深掘り分析セクションに配置して著者の主張の説明を助けるのが適切である。" def build_figure_items(evidence_pack: dict, *, limit: int = 12) -> list[dict]: From ecb42d95ee6ce1fb9ac72ba5d5b8024167f33c6c Mon Sep 17 00:00:00 2001 From: Yoshiaki Sakae <89111154+yoshiaki-sakae@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:43:35 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20upstream=20v2.3.0=20=E3=81=AE?= =?UTF-8?q?=E8=A8=80=E8=AA=9E=E3=82=A2=E3=83=BC=E3=82=AD=E3=83=86=E3=82=AF?= =?UTF-8?q?=E3=83=81=E3=83=A3=E4=B8=8A=E3=81=AB=E6=97=A5=E6=9C=AC=E8=AA=9E?= =?UTF-8?q?=EF=BC=88ja=EF=BC=89=E5=87=BA=E5=8A=9B=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E5=86=8D=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - localization: ja スキーマ(セクション名・基本情報フィールド・図表ラベル・機構フロー)と別名(ja / ja-JP / Japanese / 日本語)を追加 - user_configuration / run_pipeline / 各スクリプトの --language に ja を追加 - contracts: PAPER_TYPE_CONTRACTS_JA(5 論文タイプ)を追加 - plan_figures: 日本語の図表配置マップを追加 - lint_note: ja 向けに見出しプロファイルの厳格検査、簡体字残存ゲート(passes_style_gate)、 仮名を含む混在言語・文中改行判定、日本語トークン集合・状態正規表現を追加。 中国語向け機械翻訳痕跡チェックは zh-CN のみに限定 - common: ドメイン規則を DEEPPAPERNOTE_DOMAIN_RULES / ~/.deeppapernote/domain_rules.yaml で 上書き可能にし、既定フォルダ名も読み込んだ規則の alias から解決。.ja.md も論文フォルダ判定に含める - docs: output-language.md に日本語スキーマとスタイルゲートを追記、SKILL.md に日本語トリガー例を追加 - tests: tests/test_output_language_ja.py(23 件) --- skills/deeppapernote/SKILL.md | 13 +- .../references/output-language.md | 45 +- .../references/user-configuration.md | 2 +- .../scripts/build_synthesis_bundle.py | 2 +- skills/deeppapernote/scripts/common.py | 42 +- skills/deeppapernote/scripts/contracts.py | 147 +++++- .../deeppapernote/scripts/lint_grounding.py | 2 +- skills/deeppapernote/scripts/lint_note.py | 163 +++++- skills/deeppapernote/scripts/localization.py | 24 +- .../scripts/plan_figure_table_decisions.py | 2 +- skills/deeppapernote/scripts/plan_figures.py | 22 +- skills/deeppapernote/scripts/run_pipeline.py | 2 +- .../scripts/user_configuration.py | 4 +- .../scripts/write_obsidian_note.py | 2 +- tests/test_output_language_ja.py | 477 ++++++++++++++++++ 15 files changed, 904 insertions(+), 45 deletions(-) create mode 100644 tests/test_output_language_ja.py diff --git a/skills/deeppapernote/SKILL.md b/skills/deeppapernote/SKILL.md index 3e1bb2a..1dd56e9 100644 --- a/skills/deeppapernote/SKILL.md +++ b/skills/deeppapernote/SKILL.md @@ -1,6 +1,6 @@ --- name: deeppapernote -description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. +description: Generate a high-quality deep-reading note for a single paper and write it into an Obsidian-style vault. Use when the user gives a paper title, DOI, URL, arXiv ID, Zotero item, or local PDF and wants a polished Markdown note with strong structure, evidence-based analysis, and figure placeholders. Output language follows configuration (zh-CN, en, or ja). Japanese trigger phrases 「この論文の精読ノートを作って」「この論文をObsidianノートにまとめて」「論文を読んで日本語のMarkdownノートにして」. --- # DeepPaperNote @@ -20,6 +20,11 @@ English trigger examples: - `Generate a deep-reading note for this paper` - `Turn this paper into an Obsidian research note` +Japanese trigger examples: +- `この論文の精読ノートを作って` +- `この論文をObsidianノートにまとめて` +- `この論文を読んで日本語のMarkdownノートにして` + ## User Configuration Before a normal paper run, read `references/user-configuration.md` for configuration admission, migration, repair, Run Overrides, and Preference Changes. @@ -28,7 +33,7 @@ Resolve Run Overrides from the explicit request, CLI, and current process enviro ## Language Integrity Contract -After Configuration Readiness, resolve one `output_language` (`zh-CN` or `en`) for the run. `source_manifest.language_hint` describes source text only and never selects the note profile. +After Configuration Readiness, resolve one `output_language` (`zh-CN`, `en`, or `ja`) for the run. `source_manifest.language_hint` describes source text only and never selects the note profile. Bind that exact value through Save Target Admission → Figure Plan → Figure/Table Decisions → Synthesis Bundle → Note Plan → Grounding Lint → Final Note Lint → Final Quality Review → Final Readability Review → Formal Save: @@ -136,7 +141,7 @@ Non-negotiable rules: - fail-closed: if a usable PDF or sufficient evidence cannot be obtained after supported acquisition paths, stop and ask for better source material rather than producing a finished degraded note - model-first: scripts structure evidence, but the model must decide emphasis, contribution, mechanism, limitations, and final prose in the configured language - required structure: include the localized canonical sections in the order declared by `writing_contract.must_include_sections` -- abstract fidelity: preserve the original abstract's meaning without adding later evidence or model judgments; translate it in `zh-CN` mode and render it faithfully in English in `en` mode +- abstract fidelity: preserve the original abstract's meaning without adding later evidence or model judgments; translate it in `zh-CN` and `ja` modes and render it faithfully in English in `en` mode - mechanism depth: method, framework, and system papers should include the localized mechanism-flow subsection under the localized method section, normally as a 3 to 4 step numbered flow with input, operation, and output destination - placeholder-first figures: plan major figure/table placeholders first; replace one only when identity match and visual usability are both strong; otherwise keep the placeholder @@ -193,7 +198,7 @@ Formal Save states: - After the synthesis bundle is built, complete the model-led Visual Review Gate and Figure/Table Decision Freeze before creating `note_plan`; no `review_pending` item may cross that boundary. - Pass the grounding and final-note figure gates before advancing; revise any failed decision coverage, insertion, structure, or status check. - An `insert` decision is complete only after Formal Save materializes the selected image into the paper-local `images/` directory and the write succeeds. -- The note must pass the style gate for its configured language: `zh-CN` rejects mixed Chinese-English prose artifacts, while `en` rejects Chinese prose outside citation metadata. +- The note must pass the style gate for its configured language: `zh-CN` rejects mixed Chinese-English prose artifacts, `en` rejects Chinese prose outside citation metadata, and `ja` rejects simplified-Chinese leftovers and mixed Japanese-English prose outside citation metadata. - The style gate also rejects mechanical term-replacement artifacts such as `KV缓存 of`, `批量ing`, `In相关 Researcher`, or `Single 序列 generation`; rewrite the sentence naturally instead of preserving a partially translated phrase. - Style gate enforcement: when `lint_note.py` output contains `passes_style_gate: false`, fix the reported issues and re-run lint. Keep fixing and re-running until lint passes — multiple rounds are normal and expected. Do not decide that any failure is an acceptable exception — proper nouns, math formulas, and citation metadata are not automatic exemptions. Only escalate to the user if the same failures appear unchanged across multiple rounds with no reduction, indicating the model is unable to make further progress independently. - If PDF or evidence quality is insufficient for a real deep note, fail closed: stop, report the blocked stage, and ask for the better PDF, OCR/source material, or other input needed to continue. diff --git a/skills/deeppapernote/references/output-language.md b/skills/deeppapernote/references/output-language.md index 597b025..7926c68 100644 --- a/skills/deeppapernote/references/output-language.md +++ b/skills/deeppapernote/references/output-language.md @@ -1,13 +1,14 @@ # Output Language -DeepPaperNote supports two output schemas: +DeepPaperNote supports three output schemas: | Setting | Language | |---|---| | `zh-CN` | Simplified Chinese | | `en` | English | +| `ja` | Japanese | -Resolve the profile through `user-configuration.md`. For a single command, use `--language en` with `run_pipeline.py`, `build_synthesis_bundle.py`, `lint_note.py`, or `write_obsidian_note.py` where applicable. A Run Override never changes User Configuration. +Resolve the profile through `user-configuration.md`. For a single command, use `--language en` (or `--language ja`) with `run_pipeline.py`, `build_synthesis_bundle.py`, `lint_note.py`, or `write_obsidian_note.py` where applicable. A Run Override never changes User Configuration. ## Simplified Chinese note schema @@ -66,6 +67,46 @@ For a materialized image, use the normal image embed followed immediately by one The English style gate checks headings, Core Information labels, figure callouts, inserted-image captions, and prose. Mark original non-English metadata with inline code inside `Core Information`, or with inline code or a Markdown link inside `References`; closed fenced code blocks and URLs keep their normal source text. In prose, a CJK identifier or stable proper noun must be an HTTP Markdown link or Obsidian wikilink. A source formula may retain only these CJK `\operatorname{...}` labels: `输入`, `输出`, `损失`, `状态`, `动作`, `奖励`, `标签`, `样本`, `预测`, and `目标`. The surrounding text remains subject to the English gate; inline code and free-form math text do not exempt Chinese prose. +## Japanese note schema + +Use these top-level sections in this order: + +1. `基本情報` +2. `要旨の翻訳` +3. `新規性` +4. `一言まとめ` +5. `研究課題` +6. `データとタスク定義` +7. `手法の骨子` +8. `主要な結果` +9. `深掘り分析` +10. `限界` +11. `私のメモ` +12. `参考文献` + +The allowed 基本情報 fields, in order, are: + +`タイトル`, `タイトル訳`, `著者`, `所属`, `発表時期`, `発表媒体`, `DOI`, `arXiv`, `論文リンク`, `コード / プロジェクト`, `データ / リソース`, `論文タイプ`. + +Use `### 機構フロー` for the mechanism-flow subsection. Each figure placeholder uses: + +```md +> [!figure] Figure 2 図の内容を表す短い見出し +> 推奨位置:手法の骨子 +> 配置理由:この図は実行経路を明確にする。 +> 現在の状態:プレースホルダを保持。切り出した画像は不完全。 +``` + +For a materialized image, use the normal image embed followed immediately by one italic caption beginning with `論文原図番号:`. + +`要旨の翻訳` is a faithful Japanese translation of the source abstract. Preserve its meaning and scope; contribution claims, result interpretation, and hindsight judgment belong later unless the source abstract itself contains them. + +Write natural Japanese prose (常体・敬体 consistently within a note). Keep stable proper nouns, model names, dataset names, and metric names in their original form; do not force-translate them. Mechanism-flow steps should name 入力 / 操作 / 出力 explicitly. + +The Japanese style gate checks headings, 基本情報 labels, figure callouts, inserted-image captions, and prose. Because kanji are shared with Chinese, the gate looks for simplified-Chinese-only characters (机, 图, 论, 议, 态, ...) that never appear in Japanese; any such character in a heading, label, or prose line is a leftover from the Chinese template and fails `passes_style_gate`. Original Chinese metadata is allowed inside `基本情報` and `参考文献` when marked with inline code or a Markdown link, and URLs and closed fenced code blocks keep their source text. Lines that mix Japanese with four or more English words including English function words are also rejected as mixed-language prose. + +Domain folders: the skill ships `references/domain_rules.yaml` with Chinese folder labels. A Japanese Vault should keep a translated taxonomy outside the skill, either at `~/.deeppapernote/domain_rules.yaml` (next to `config.json`) or at the path named by `DEEPPAPERNOTE_DOMAIN_RULES`; both take precedence over the shipped file, and fallback folder labels are resolved through the `healthcare`, `machine learning`, and `unclassified` aliases of that taxonomy. + ## Contract ownership `SKILL.md` owns the cross-stage Language Integrity Contract. This reference owns only the profile-specific schema and labels above; apply them under the resolved language carried by that contract. diff --git a/skills/deeppapernote/references/user-configuration.md b/skills/deeppapernote/references/user-configuration.md index 77541c8..b372014 100644 --- a/skills/deeppapernote/references/user-configuration.md +++ b/skills/deeppapernote/references/user-configuration.md @@ -4,7 +4,7 @@ DeepPaperNote can resolve a run entirely from Run Overrides. It also supports on For isolated validation only, `DEEPPAPERNOTE_CONFIG_PATH` may relocate this one file for the current process. It is not a preference, does not create a second configuration layer, and must not be persisted into the configuration itself. -- `output_language`: `zh-CN` or `en` +- `output_language`: `zh-CN`, `en`, or `ja` - `save_mode`: `workspace` or `obsidian` - `obsidian_vault`: existing absolute directory, required only in Obsidian mode - `papers_dir`: safe relative path inside the Vault, required only in Obsidian mode diff --git a/skills/deeppapernote/scripts/build_synthesis_bundle.py b/skills/deeppapernote/scripts/build_synthesis_bundle.py index e1af552..fe9fdfb 100644 --- a/skills/deeppapernote/scripts/build_synthesis_bundle.py +++ b/skills/deeppapernote/scripts/build_synthesis_bundle.py @@ -35,7 +35,7 @@ def parser() -> argparse.ArgumentParser: required=True, help="Figure/table decision JSON path or string.", ) - p.add_argument("--language", default="", help="Run Override for output language: en or zh-CN.") + p.add_argument("--language", default="", help="Run Override for output language: en, zh-CN, or ja.") p.add_argument("--output", default="", help="Output JSON path.") return p diff --git a/skills/deeppapernote/scripts/common.py b/skills/deeppapernote/scripts/common.py index 1cb6865..34c074f 100644 --- a/skills/deeppapernote/scripts/common.py +++ b/skills/deeppapernote/scripts/common.py @@ -18,9 +18,11 @@ from typing import Any from user_configuration import ( + OUTPUT_LANGUAGES, inspect_configuration, resolve_preferences, resolve_run_overrides, + user_config_path, ) ARXIV_NS = { @@ -2955,11 +2957,27 @@ def _normalize_domain_rules( return normalized +def resolve_domain_rules_path() -> Path: + """Pick the domain taxonomy file: env override, then the user config directory, then the skill default. + + The skill ships Chinese folder labels; a user whose Vault uses another language + keeps a translated copy outside the skill so upstream updates never overwrite it. + """ + override = os.environ.get("DEEPPAPERNOTE_DOMAIN_RULES", "").strip() + if override: + return Path(override).expanduser() + user_rules = user_config_path().parent / "domain_rules.yaml" + if user_rules.is_file(): + return user_rules + return DOMAIN_RULES_PATH + + def load_domain_rules() -> dict[str, list[dict[str, Any]]]: try: - if not DOMAIN_RULES_PATH.exists(): + rules_path = resolve_domain_rules_path() + if not rules_path.exists(): return _copy_default_domain_rules() - parsed = _parse_domain_rules_yaml(DOMAIN_RULES_PATH.read_text(encoding="utf-8-sig")) + parsed = _parse_domain_rules_yaml(rules_path.read_text(encoding="utf-8-sig")) normalized = _normalize_domain_rules(parsed) if normalized is None: return _copy_default_domain_rules() @@ -3055,10 +3073,21 @@ def infer_domain_label(title: str, abstract: str = "") -> str: paper_type, _ = infer_paper_type(title, abstract) if paper_type == "clinical_or_psychology_empirical": - return "医疗健康" + return _domain_label_by_alias(rules, "healthcare", "医疗健康") if paper_type == "AI_method": - return "机器学习" - return "未分类" + return _domain_label_by_alias(rules, "machine learning", "机器学习") + return _domain_label_by_alias(rules, "unclassified", "未分类") + + +def _domain_label_by_alias(rules: dict[str, list[dict[str, Any]]], alias: str, default: str) -> str: + """Resolve a fallback folder label through the loaded taxonomy so translated rules stay in charge.""" + wanted = _normalized_domain_label(alias) + for section in DOMAIN_SECTIONS: + for rule in rules[section]: + aliases = {_normalized_domain_label(item) for item in _as_string_list(rule.get("aliases"))} + if wanted in aliases: + return _domain_route_label(rule) + return default def is_probable_paper_folder(path: Path) -> bool: @@ -3068,8 +3097,7 @@ def is_probable_paper_folder(path: Path) -> bool: (path / marker).exists() for marker in ( f"{path.name}.md", - f"{path.name}.zh-CN.md", - f"{path.name}.en.md", + *(f"{path.name}.{language}.md" for language in sorted(OUTPUT_LANGUAGES)), ".deeppapernote.json", ) ) diff --git a/skills/deeppapernote/scripts/contracts.py b/skills/deeppapernote/scripts/contracts.py index 0d37641..fc693a4 100644 --- a/skills/deeppapernote/scripts/contracts.py +++ b/skills/deeppapernote/scripts/contracts.py @@ -411,8 +411,153 @@ def required_field_value_error( } +PAPER_TYPE_CONTRACTS_JA: dict[str, dict[str, Any]] = { + "AI_method": { + "paper_type": "AI_method", + "reader_lens": "手法の機構を再現できる技術読者向け", + "section_focus": ["問題設定", "手法の機構", "学習/推論フロー", "主要な数式", "比較ベースライン", "アブレーションと失敗の境界"], + "required_checks": ["機構フロー、主要な数式、実験デザイン、アブレーションの含意、失敗の境界を説明する必要がある。"], + "formula_rules": ["手法の理解に必須の 1〜3 個の主要な数式のみを残し、その工学的含意を説明する。"], + "avoid_rules": ["AI_method でない論文を無理にモデルアーキテクチャとして書き換えない。"], + "boundary_questions": [ + "中心的な機構の利得は、主結果が示唆するだけでなく、どの実験やアブレーションで裏付けられているか?", + "どの比較が現在のデータ・ベースライン・計算資源・プロトコルの下でのみ有効で、汎用的な状況へ外挿できないか?", + "論文は失敗・劣化・不安定・コスト増の証拠を示しているか。示していない場合、結論の境界は何か?", + ], + "section_semantics": { + "データとタスク定義": "データセット、入出力、評価タスク、実験設定。", + "主要な結果": "主結果、強力なベースライン、アブレーション、重要な数値。", + "手法の骨子": "モデル・アルゴリズム・学習または推論の機構。", + "深掘り分析": "手法がなぜ有効か、どこが脆いか、再現・拡張のコスト。", + "研究課題": "手法が解決しようとする具体的な技術課題と、既存手法の弱点。", + }, + "recommended_subsections": { + "手法の骨子": ["機構フロー", "モデル構造", "学習目標", "推論・サンプリング経路", "主要な実装詳細"], + "主要な結果": ["主要結果と強力なベースライン", "アブレーションが示すもの", "失敗・不安定な設定"], + "深掘り分析": ["なぜ有効か", "計算量とスケーラビリティ", "再現時の注意点"], + }, + "mechanism_flow_contract": { + "apply_when_paper_type_in": ["AI_method"], + "required_step_count": "3_to_4", + "required_step_fields": ["input", "operation", "output_destination"], + }, + }, + "benchmark_or_dataset": { + "paper_type": "benchmark_or_dataset", + "reader_lens": "benchmark/dataset の有用性とバイアスの境界を判断したい研究者向け", + "section_focus": ["タスク分割", "データ出所と構築フロー", "アノテーションプロトコル", "評価指標", "カバレッジ範囲とバイアス", "サンプル統計とデータ公開制限"], + "required_checks": ["データ出所、構築/アノテーションのフロー、評価指標、ベースライン性能、サンプル統計、データ公開やプライバシー制限、適用範囲の境界を説明する必要がある。"], + "formula_rules": ["中心的な評価指標、サンプリング規則、分割定義のみを残す。"], + "avoid_rules": ["データ構築フローをモデルの pipeline として書かない。"], + "boundary_questions": [ + "この benchmark/dataset が実際に測定している構成概念は何で、どの能力は間接的な近似に過ぎないか?", + "タスク・ラベル・サンプリング・フィルタ・評価プロトコルは、どのようなカバレッジの欠落やバイアスを持ち込むか?", + "ベースライン結果は評価セットに識別力があることを示すのか、それとも特定タイプのモデルがこのプロトコルに適応しただけか?", + "サンプルの長さ、コーパス長、人口統計、クラス分布、データのアクセス可能性やプライバシー制限は、再現と外挿にどう影響するか?", + ], + "section_semantics": { + "データとタスク定義": "データ出所、タスク分割、ラベル/課題の定義、サンプル範囲。", + "主要な結果": "ベースライン性能、難易度分布、カバレッジ範囲、バイアス。", + "手法の骨子": "データ構築・選別・アノテーション・評価プロトコル。モデルの pipeline としては書かない。", + "深掘り分析": "実際に何を測れているか、そして何を代表できないか。", + "研究課題": "この benchmark/dataset が埋めようとする評価またはデータのギャップ。", + }, + "recommended_subsections": { + "データとタスク定義": ["データ出所", "タスク分割", "アノテーション/選別プロトコル"], + "手法の骨子": ["構築フロー", "評価プロトコル", "ベースライン設定"], + "主要な結果": ["ベースライン性能", "難易度分布", "カバレッジとバイアス"], + "深掘り分析": ["ベンチマークが実際に測るもの", "適用範囲の境界"], + }, + }, + "clinical_or_psychology_empirical": { + "paper_type": "clinical_or_psychology_empirical", + "reader_lens": "臨床/心理学のサンプル、変数関係、外挿の境界に注目する研究読者向け", + "section_focus": ["サンプル出所", "選択・除外基準", "変数または尺度", "分析パイプライン", "効果量と不確実性", "サンプル統計・倫理・データのアクセス可能性"], + "required_checks": ["相関・予測・群間差・因果的解釈を区別し、サンプル統計、倫理/プライバシー上の制約、外挿の境界を説明する必要がある。"], + "formula_rules": ["中心的な統計モデル、効果量、信頼区間、尺度の定義のみを残す。"], + "avoid_rules": ["相関、予測性能、群間差を、証明されていない因果結論として書かない。"], + "boundary_questions": [ + "サンプル出所、選択・除外基準、測定ツール、アノテーション手順は、外挿をどのように制限するか?", + "結果は相関・予測・群間差・因果的解釈のどれを支持するか。論文のデザインが証明できる範囲を越えないこと。", + "臨床または心理学的意義は、未観測の交絡、尺度の閾値、テキスト/音声の欠落、場面の制約に依存していないか?", + "サンプル構成、データ欠損、プライバシー制限、資料の非公開は、再現と再分析をどのように制限するか?", + ], + "section_semantics": { + "データとタスク定義": "サンプル出所、選択・除外基準、変数/尺度、測定方法。", + "主要な結果": "主効果、相関、群間差、不確実性や有意性。", + "手法の骨子": "研究デザイン、群分け、測定手順、統計解析の道筋。", + "深掘り分析": "結果の解釈、因果の境界、臨床/心理学的意義、外挿の限界。", + "研究課題": "臨床・心理学・行動科学における研究課題、仮説、変数間の関係。", + }, + "recommended_subsections": { + "データとタスク定義": ["サンプルと選択・除外基準", "変数と尺度", "測定手順"], + "手法の骨子": ["研究デザイン", "分析モデル", "主要な比較"], + "主要な結果": ["主要な効果", "不確実性と有意性", "臨床・心理学的解釈"], + "深掘り分析": ["因果解釈の境界", "外挿の限界"], + }, + }, + "humanities_or_social_science": { + "paper_type": "humanities_or_social_science", + "reader_lens": "理論的枠組み、資料の解釈、論証の構造に注目する研究読者向け", + "section_focus": ["研究対象", "資料の出所", "理論的枠組み", "論証の道筋", "概念的貢献", "解釈の境界"], + "required_checks": ["著者の論証、資料的証拠、規範的判断、実験的事実を区別する必要がある。"], + "formula_rules": ["通常は数式を無理に残さない。中心的な形式的定義やコーディング規則のみを残す。"], + "avoid_rules": ["規範的判断、テキスト解釈、事例分析を実験的事実として書かない。"], + "boundary_questions": [ + "著者の解釈は、どの資料・事例・理論的前提に依存しているか?", + "同じく資料を説明できる代替的解釈は存在するか。論文はそれをどのように排除したか、あるいは排除していないか?", + "どの結論が概念的貢献や規範的判断であり、経験的事実として直接扱えないものか?", + ], + "section_semantics": { + "データとタスク定義": "資料、事例、テキスト、インタビュー、アーカイブ、コーパスの範囲。ML task としては書かない。", + "主要な結果": "中心的な解釈的発見、概念的貢献、既存の見解への修正。", + "手法の骨子": "理論的枠組み、概念の区別、論証の道筋。", + "深掘り分析": "論証の強さ、資料の境界、解釈の代替可能性、転用可能性。", + "研究課題": "著者が説明しようとする社会・文化・歴史・制度・理論上の問い。", + }, + "recommended_subsections": { + "データとタスク定義": ["資料の範囲", "選択基準", "事例・コーパスの境界"], + "手法の骨子": ["理論的枠組み", "概念の区別", "論証の道筋"], + "主要な結果": ["中心的な解釈的発見", "概念的貢献"], + "深掘り分析": ["論証の強さ", "代替的解釈", "資料の境界"], + }, + }, + "survey_or_review": { + "paper_type": "survey_or_review", + "reader_lens": "サーベイの脈絡、分類体系、証拠の境界を整理する必要のある研究読者向け", + "section_focus": ["サーベイの範囲", "選定・除外基準", "テーマ分類", "手法の系譜", "合意と対立", "未解決問題"], + "required_checks": ["サーベイの範囲、文献の選定、分類体系、合意と対立、未解決問題を説明する必要がある。"], + "formula_rules": ["分類軸、選定・除外の基準、証拠の集約規則、meta-analysis の統計量のみを残す。"], + "avoid_rules": ["サーベイ中の代表的な結論を、著者自身が行った単一の実験結果として書かない。"], + "boundary_questions": [ + "検索範囲、選定・除外基準、分類軸は、どの研究の流れを見落とすか?", + "サーベイが示すのは分野の合意か、著者の分類か、それとも未解決の対立か?", + "どのトレンド結論がカバレッジ内の文献分布に由来し、技術成熟度の判断として直接扱えないか?", + ], + "section_semantics": { + "データとタスク定義": "対象とする文献範囲、検索/選別基準、レビュー対象。", + "主要な結果": "分野の合意、対立、トレンド、代表的な方向性、未解決問題。", + "手法の骨子": "分類体系、レビューの構成方法、証拠統合のロジック。単一論文の手法構成としては書かない。", + "深掘り分析": "サーベイがカバーしていない盲点、分類体系の説明力、今後の研究機会。", + "研究課題": "サーベイが整理しようとする分野の問い、論争、知識のギャップ。", + }, + "recommended_subsections": { + "データとタスク定義": ["サーベイの範囲", "選定・除外基準", "文献カバレッジ"], + "手法の骨子": ["分類体系", "手法の系譜", "証拠の整理方法"], + "主要な結果": ["代表的な方向性", "合意と対立", "未解決問題"], + "深掘り分析": ["分類体系の限界", "未カバー領域", "今後の研究機会"], + }, + }, +} + + def paper_type_contracts(language: str | None = None) -> dict[str, dict[str, Any]]: - return deepcopy(PAPER_TYPE_CONTRACTS_EN if normalize_output_language(language) == "en" else PAPER_TYPE_CONTRACTS) + resolved = normalize_output_language(language) + if resolved == "en": + return deepcopy(PAPER_TYPE_CONTRACTS_EN) + if resolved == "ja": + return deepcopy(PAPER_TYPE_CONTRACTS_JA) + return deepcopy(PAPER_TYPE_CONTRACTS) WRITING_CONTRACT_RULES: dict[str, Any] = { "required_sections": NOTE_REQUIRED_SECTIONS, diff --git a/skills/deeppapernote/scripts/lint_grounding.py b/skills/deeppapernote/scripts/lint_grounding.py index 9c6b257..561ca2a 100644 --- a/skills/deeppapernote/scripts/lint_grounding.py +++ b/skills/deeppapernote/scripts/lint_grounding.py @@ -191,7 +191,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument( "--language", default="", - help="Run Override for output language: en or zh-CN.", + help="Run Override for output language: en, zh-CN, or ja.", ) return p diff --git a/skills/deeppapernote/scripts/lint_note.py b/skills/deeppapernote/scripts/lint_note.py index 619babd..10e88cd 100644 --- a/skills/deeppapernote/scripts/lint_note.py +++ b/skills/deeppapernote/scripts/lint_note.py @@ -63,6 +63,11 @@ def figure_prefix(key: str) -> str: "未放置", "未处理", "待补", + # ja + "残り", + "未配置", + "未処理", + "補充待ち", } FIGURE_BUCKET_VISUAL_TOKENS = { @@ -71,6 +76,11 @@ def figure_prefix(key: str) -> str: "图片", "图表", "占位", + # ja + "図", + "画像", + "図表", + "プレースホルダ", } ENGLISH_FIGURE_BUCKET_RESIDUE_TOKENS = { @@ -100,6 +110,10 @@ def figure_prefix(key: str) -> str: | (?:图表|图片|图|表)\s*占位\s*[::]\s*\S+ | + \[\s*(?:図表|画像|図|表)\s*プレースホルダ\s*\|[^\]]+\] + | + (?:図表|画像|図|表)\s*プレースホルダ\s*[::]\s*\S+ + | \[\s*(?:figure|fig|table)\s+placeholder\s*(?:\||:|\]|-|\s+(?:fig(?:ure)?|table)\.?\s*\d) | (?:figure|fig|table)\s+placeholder\s*(?:\||:|-|\s+(?:fig(?:ure)?|table)\.?\s*\d) @@ -114,6 +128,10 @@ def figure_prefix(key: str) -> str: | (?:替换|插入)\s*为\s*真实图片 | + (?:置換|挿入|コピー|複製|書き込み|物化)\s*(?:済み|した|完了) + | + 実(?:画像|図)(?:に|へ)\s*(?:置換|差し替え) + | \b(?:inserted|replaced|copied|materialized)\b ) """, @@ -137,6 +155,14 @@ def figure_prefix(key: str) -> str: | 高\s*置信(?:度)?[^。;,\n>]{0,12}候选 | + 候補[^。;,\n>]{0,24}(?]{0,12}(?]{0,12}候補 + | usable\s+candidate | readable\s+crop @@ -158,6 +184,8 @@ def figure_prefix(key: str) -> str: | 无法稳定|不可独立解释|质量门|reject_visual_quality | + 混入|汚染|隣接|切れ|途切れ|欠落|欠けて|不完全|単独では解釈できない|品質ゲート + | partial|subpanel|contaminat|truncat|incomplete|missing | caption\s*(?:missing|cut|truncated) @@ -169,13 +197,13 @@ def figure_prefix(key: str) -> str: USABLE_CANDIDATE_MATERIALIZATION_BLOCKED_RE = re.compile( r""" (?: - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|工具|copy) + (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|工具|copy|コピー|書き込み|権限|ツール) [^。;\n]{0,40} - (?:失败|不足|拒绝|denied|blocked|error|报错) + (?:失败|不足|拒绝|denied|blocked|error|报错|失敗|拒否|エラー) | - (?:失败|不足|拒绝|denied|blocked|error|报错) + (?:失败|不足|拒绝|denied|blocked|error|报错|失敗|拒否|エラー) [^。;\n]{0,40} - (?:materialize|物化|复制|拷贝|写入|权限|permission|copy) + (?:materialize|物化|复制|拷贝|写入|权限|permission|copy|コピー|書き込み|権限) ) """, flags=re.IGNORECASE | re.VERBOSE, @@ -184,13 +212,13 @@ def figure_prefix(key: str) -> str: MISSING_ASSET_MATERIALIZATION_RE = re.compile( r""" (?: - (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing) + (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing|候補が(?:ない|見つからない)|アセット(?:が)?(?:欠落|なし)) [^。;\n]{0,50} - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked) + (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked|コピー|書き込み|権限) | - (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked) + (?:materialize_figure_asset\.py|物化|复制|拷贝|写入|权限|permission|copy|blocked|コピー|書き込み|権限) [^。;\n]{0,50} - (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing) + (?:资产缺失|未找到|没有|缺少|asset_candidate_missing|candidate\s+missing|候補が(?:ない|見つからない)|アセット(?:が)?(?:欠落|なし)) ) """, flags=re.IGNORECASE | re.VERBOSE, @@ -211,6 +239,18 @@ def figure_prefix(key: str) -> str: r"\\operatorname\{(?:输入|输出|损失|状态|动作|奖励|标签|样本|预测|目标)\}" ) ENGLISH_MATH_SPAN_RE = re.compile(r"\$\$[^$\n]+\$\$|\$[^$\n]+\$") +# 漢字(zh-CN / ja 共通)と仮名(ja)。混在言語判定と文中改行判定で使う。 +TARGET_SCRIPT_RE = re.compile(r"[\u3040-\u30ff\u4e00-\u9fff]") +# \u65e5\u672c\u8a9e\u306e\u5e38\u7528\u6f22\u5b57\u30fb\u65b0\u5b57\u4f53\u306b\u306f\u73fe\u308c\u306a\u3044\u7c21\u4f53\u5b57\u3002ja \u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3067 +# \u4e2d\u56fd\u8a9e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u898b\u51fa\u3057\u30fb\u30e9\u30d9\u30eb\uff08\u673a\u5236\u6d41\u7a0b / \u5efa\u8bae\u4f4d\u7f6e \u306a\u3069\uff09\u306e\u6b8b\u5b58\u3092\u691c\u51fa\u3059\u308b\u3002 +# \u65e5\u672c\u8a9e\u3067\u3082\u4f7f\u3046\u5b57\uff08\u6570\u30fb\u636e\u30fb\u540e\u30fb\u6ca1\u30fb\u91cc\u30fb\u4e0e\u30fb\u4f1a\u30fb\u533a\u30fb\u53cc\u30fb\u968f\u30fb\u51c6 \u306a\u3069\uff09\u306f\u610f\u56f3\u7684\u306b\u542b\u3081\u306a\u3044\u3002 +SIMPLIFIED_CHINESE_ONLY_CHARS = ( + "\u4eec\u8fd9\u4e2a\u4e3a\u4ece\u8bf4\u65f6\u673a\u5173\u952e\u7ed3\u8bba\u56fe\u5e94\u8be5\u8fdb\u663e\u5b9e\u73b0\u8ba4\u8bc6\u8ba9\u8fd8\u8fc7\u6837\u79cd\u4e49\u672f\u573a\u8bad\u7ec3\u8bd5\u9a8c\u8bc4\u6d4b\u6784\u9636" + "\u8f93\u6001\u52a8\u52a1\u5904\u5907\u7ea7\u7c7b\u7ec4\u7ec7\u7edc\u7f51\u7ebf\u7f16\u8bd1\u5bfc\u603b\u8fbe\u53d1\u957f\u95e8\u95ee\u9898\u4f18\u52bf\u9009\u62e9\u9519\u8bef\u786e\u590d\u6742\u7b80\u5355\u5c42\u91ca\u8ba1\u8bbe" + "\u6743\u9884\u68c0\u62a5\u8865\u635f\u6807\u9891\u89c6\u89c9\u8bed\u8bcd\u6c47\u5e93\u522b\u7f13\u5757\u89c4\u73af\u7edf\u4f20\u53d8\u6362\u8f6c\u8f7d\u8f6e\u8f66\u9875\u7efc\u4e60\u5bf9\u8bf7\u8c22\u4e48\u5417\u5462\u5427\u5f88" + "\u8bae\u7b5b\u7eb3" +) +SIMPLIFIED_CHINESE_ONLY_RE = re.compile(f"[{''.join(sorted(set(SIMPLIFIED_CHINESE_ONLY_CHARS)))}]") RUNTIME_ARTIFACT_REFERENCE_PATTERNS = [ re.compile( @@ -253,7 +293,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument("--plan-file", default="", help="Optional note_plan JSON path. Defaults to sibling .plan.json.") p.add_argument("--output", default="", help="Output JSON path.") p.add_argument("--paper-id", default="", help="Canonical paper id.") - p.add_argument("--language", default="", help="Run Override for output language: en or zh-CN.") + p.add_argument("--language", default="", help="Run Override for output language: en, zh-CN, or ja.") return p @@ -369,8 +409,13 @@ def front_matter_order_warnings(text: str) -> list[str]: return warnings +STRICT_TOP_LEVEL_PROFILE_LANGUAGES = {"en", "ja"} +# 中国語プロファイル向けの機械翻訳痕跡パターンは日本語には適用しない +MECHANICAL_TRANSLATION_CHECK_LANGUAGES = {"zh-CN"} + + def english_top_level_section_warnings(text: str) -> list[str]: - if ACTIVE_LANGUAGE != "en": + if ACTIVE_LANGUAGE not in STRICT_TOP_LEVEL_PROFILE_LANGUAGES: return [] actual = re.findall(r"^##\s+(.+?)\s*$", text, flags=re.MULTILINE) return [] if actual == list(REQUIRED_SECTIONS) else ["top_level_section_profile_invalid"] @@ -421,6 +466,13 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: "model", "system", "module", + # ja + "モデル", + "フレームワーク", + "システム", + "モジュール", + "エンコーダ", + "デコーダ", ] MECHANISM_IO_TOKENS = [ @@ -434,6 +486,11 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: "output", "produces", "returns", + # ja + "入力", + "出力", + "渡す", + "得る", ] MECHANISM_ACTION_TOKENS = [ @@ -456,6 +513,16 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: "decode", "update", "aggregate", + # ja + "整列", + "圧縮", + "投影", + "抽出", + "符号化", + "復号", + "連結", + "更新", + "集約", ] @@ -501,6 +568,13 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: r"^参见原论文[。.!!]*$", r"^这里记录.*[。.!!]*$", r"^本节记录.*[。.!!]*$", + # ja + r"^未記入[。.!!]*$", + r"^後で追記[。.!!]*$", + r"^なし[。.!!]*$", + r"^省略[。.!!]*$", + r"^原論文を参照[。.!!]*$", + r"^ここに.*を記録.*[。.!!]*$", ] GENERIC_INNOVATION_PATTERNS = [ @@ -508,6 +582,10 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: r"具有创新性", r"novel approach", r"首次实现", + # ja + r"新しい手法を提案(?:する|した)?", + r"新規性が(?:ある|高い)", + r"初めて実現(?:した)?", ] GENERIC_KEY_RESULT_PATTERNS = [ @@ -515,6 +593,11 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: r"结果表明.*有效", r"取得(?:了)?较好效果", r"性能.*优越", + # ja + r"実験結果.*手法.*有効", + r"結果.*有効(?:である|だ)", + r"良好な(?:結果|効果|性能)", + r"性能.*優(?:れ|位|越)", ] GENERIC_LIMITATION_PATTERNS = [ @@ -523,11 +606,27 @@ def inspect_reference_hygiene(text: str) -> list[dict[str, object]]: r"future work can", r"more data", r"后续.*扩展", + # ja + r"今後の(?:課題|研究).*データ", + r"(?:さらなる|より多くの)データが必要", + r"今後.*拡張", ] -HONEST_MISSING_TOKENS = ("本文未给出", "论文未给出", "未报告", "没有报告", "未提供") -HONEST_MISSING_BASIS_TOKENS = ("依据", "正文", "附录", "表格", "coverage", "作者") -HONEST_MISSING_IMPACT_TOKENS = ("影响", "限制", "受限", "不能", "无法", "结论强度") +HONEST_MISSING_TOKENS = ( + "本文未给出", "论文未给出", "未报告", "没有报告", "未提供", + # ja + "報告されていない", "未報告", "未提供", "示されていない", "記載がない", +) +HONEST_MISSING_BASIS_TOKENS = ( + "依据", "正文", "附录", "表格", "coverage", "作者", + # ja + "根拠", "本文", "付録", "表", +) +HONEST_MISSING_IMPACT_TOKENS = ( + "影响", "限制", "受限", "不能", "无法", "结论强度", + # ja + "影響", "制限", "制約", "できない", "不可能", "結論の強さ", +) DOUBLE_ESCAPED_TEX_COMMANDS = { "alpha", @@ -604,6 +703,29 @@ def subsection_name_for_line(lines: list[str], line_index: int) -> str: return current_subsection +def simplified_chinese_leftover_issue( + lines: list[str], idx: int, stripped: str +) -> dict[str, object] | None: + """日本語ノートに簡体字中国語の見出し・ラベル・本文が残っていれば問題として返す。 + + 漢字は日中で共有されるため、日本語では使われない簡体字(機→机、図→图 など) + だけを手がかりにする。基本情報と参考文献の原語メタデータ、URL、インラインコード、 + リンクは対象外。 + """ + section_name = section_name_for_line(lines, idx - 1) + checked = stripped + if section_name in {section("core_information"), section("references")}: + for pattern in ENGLISH_METADATA_SOURCE_SPAN_PATTERNS: + checked = pattern.sub("", checked) + if stripped.startswith(f"*{FIGURE_LABELS['original_caption']}"): + checked = ENGLISH_METADATA_SOURCE_SPAN_PATTERNS[0].sub("", checked) + checked = ENGLISH_CJK_ENTITY_LINK_RE.sub("", checked) + checked = HTTP_URL_RE.sub("", checked) + if not SIMPLIFIED_CHINESE_ONLY_RE.search(checked): + return None + return {"line_number": idx, "line": stripped, "reason": "simplified_chinese_text_present"} + + def mixed_language_issues(text: str) -> list[dict[str, object]]: issues: list[dict[str, object]] = [] lines = text.splitlines() @@ -638,13 +760,18 @@ def mixed_language_issues(text: str) -> list[dict[str, object]]: if re.search(r"[\u4e00-\u9fff]", checked): issues.append({"line_number": idx, "line": stripped, "reason": "non_english_text_present"}) continue + if ACTIVE_LANGUAGE == "ja" and idx not in fenced_code_lines: + simplified_issue = simplified_chinese_leftover_issue(lines, idx, stripped) + if simplified_issue: + issues.append(simplified_issue) + continue if is_exempt_line(line): continue section_name = section_name_for_line(lines, idx - 1) subsection_name = subsection_name_for_line(lines, idx - 1) if section_name in {section("core_information"), section("references")}: continue - if not re.search(r"[\u4e00-\u9fff]", stripped): + if not TARGET_SCRIPT_RE.search(stripped): continue english_words = re.findall(r"\b[A-Za-z][A-Za-z0-9.-]*\b", stripped) if len(english_words) < 4: @@ -664,7 +791,7 @@ def mixed_language_issues(text: str) -> list[dict[str, object]]: def mechanical_translation_artifact_issues(text: str) -> list[dict[str, object]]: - if ACTIVE_LANGUAGE == "en": + if ACTIVE_LANGUAGE not in MECHANICAL_TRANSLATION_CHECK_LANGUAGES: return [] issues: list[dict[str, object]] = [] for idx, line in enumerate(text.splitlines(), start=1): @@ -1099,9 +1226,9 @@ def suspicious_mid_sentence_linebreaks(text: str) -> list[dict[str, object]]: if re.search(r"[。!?.!?::]$", current): continue if not re.search(r"[,,;;、))\]」』]$", current): - if not re.search(r"[A-Za-z0-9`\u4e00-\u9fff]$", current): + if not re.search(r"[A-Za-z0-9`\u3040-\u30ff\u4e00-\u9fff]$", current): continue - if not re.match(r"^[A-Za-z0-9`\u4e00-\u9fff((“‘\"]", nxt): + if not re.match(r"^[A-Za-z0-9`\u3040-\u30ff\u4e00-\u9fff((“‘\"]", nxt): continue issues.append( { @@ -1479,7 +1606,7 @@ def meaningful_units(body: str, generic_patterns: list[str] | None = None) -> li def has_number_token(text: str) -> bool: - return bool(re.search(r"\d+(?:\.\d+)?\s*(?:%|%|[A-Za-z\u4e00-\u9fff]{0,8})", text)) + return bool(re.search(r"\d+(?:\.\d+)?\s*(?:%|%|[A-Za-z\u3040-\u30ff\u4e00-\u9fff]{0,8})", text)) def is_honest_missing_declaration(text: str) -> bool: diff --git a/skills/deeppapernote/scripts/localization.py b/skills/deeppapernote/scripts/localization.py index a73b028..62208b6 100644 --- a/skills/deeppapernote/scripts/localization.py +++ b/skills/deeppapernote/scripts/localization.py @@ -8,8 +8,12 @@ from typing import Any DEFAULT_OUTPUT_LANGUAGE = "zh-CN" -SUPPORTED_OUTPUT_LANGUAGES = ("zh-CN", "en") -_ALIASES = {"zh": "zh-CN", "zh-cn": "zh-CN", "zh_cn": "zh-CN", "chinese": "zh-CN", "en": "en", "en-us": "en", "en_us": "en", "english": "en"} +SUPPORTED_OUTPUT_LANGUAGES = ("zh-CN", "en", "ja") +_ALIASES = { + "zh": "zh-CN", "zh-cn": "zh-CN", "zh_cn": "zh-CN", "chinese": "zh-CN", + "en": "en", "en-us": "en", "en_us": "en", "english": "en", + "ja": "ja", "ja-jp": "ja", "ja_jp": "ja", "japanese": "ja", "日本語": "ja", +} _SCHEMAS: dict[str, dict[str, Any]] = { "zh-CN": { "sections": {"core_information": "核心信息", "abstract": "原文摘要翻译", "contributions": "创新点", "one_sentence_summary": "一句话总结", "research_questions": "研究问题", "data_and_task": "数据与任务定义", "method": "方法主线", "key_results": "关键结果", "deep_analysis": "深度分析", "limitations": "局限", "my_notes": "我的笔记", "references": "引用"}, @@ -34,6 +38,22 @@ ], }, }, + "ja": { + "sections": {"core_information": "基本情報", "abstract": "要旨の翻訳", "contributions": "新規性", "one_sentence_summary": "一言まとめ", "research_questions": "研究課題", "data_and_task": "データとタスク定義", "method": "手法の骨子", "key_results": "主要な結果", "deep_analysis": "深掘り分析", "limitations": "限界", "my_notes": "私のメモ", "references": "参考文献"}, + "core_info_fields": ("タイトル", "タイトル訳", "著者", "所属", "発表時期", "発表媒体", "DOI", "arXiv", "論文リンク", "コード / プロジェクト", "データ / リソース", "論文タイプ"), + "core_info_aliases": {}, + "figure_labels": {"location": "推奨位置:", "reason": "配置理由:", "status": "現在の状態:", "original_caption": "論文原図番号:"}, + "mechanism_flow": "機構フロー", + "abstract_contract": { + "source": "source_abstract", + "requirement": "faithful_rendering_in_output_language", + "forbidden_additions": [ + "later_contribution_claims", + "later_result_interpretation", + "hindsight_judgment", + ], + }, + }, } def normalize_output_language(value: str | None = None) -> str: diff --git a/skills/deeppapernote/scripts/plan_figure_table_decisions.py b/skills/deeppapernote/scripts/plan_figure_table_decisions.py index 9c25338..82fe238 100644 --- a/skills/deeppapernote/scripts/plan_figure_table_decisions.py +++ b/skills/deeppapernote/scripts/plan_figure_table_decisions.py @@ -44,7 +44,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument( "--language", default="", - help="Run Override for output language: en or zh-CN.", + help="Run Override for output language: en, zh-CN, or ja.", ) return p diff --git a/skills/deeppapernote/scripts/plan_figures.py b/skills/deeppapernote/scripts/plan_figures.py index 039e3f9..2d768c3 100644 --- a/skills/deeppapernote/scripts/plan_figures.py +++ b/skills/deeppapernote/scripts/plan_figures.py @@ -17,7 +17,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument("--assets", default="", help="PDF assets JSON path or string.") p.add_argument("--output", default="", help="Output JSON path.") p.add_argument("--paper-id", default="", help="Canonical paper id.") - p.add_argument("--language", default="", help="Output language: en or zh-CN.") + p.add_argument("--language", default="", help="Output language: en, zh-CN, or ja.") p.add_argument("--max-items", type=int, default=12, help="Maximum number of figure/table items to keep. 0 means keep all.") return p @@ -160,13 +160,29 @@ def _classify_caption_kind_zh(item_id: str, caption: str) -> tuple[str, str, str "supporting_figure": ("Deep Analysis", "This supporting visual helps explain the authors' argument in Deep Analysis."), } +JAPANESE_FIGURE_PLACEMENT: dict[str, tuple[str, str]] = { + "main_result": ("主要な結果", "この図表は主結果を担っており、主要な結果に置くのが適切である。"), + "data_or_task_overview": ("データとタスク定義", "この図はデータとタスクの出所・構築・選別・範囲を説明する。"), + "method_overview": ("機構フロー", "この図は手法またはシステムの流れを要約しており、対応が確実なら機構フローに置く。"), + "data_or_task": ("データとタスク定義", "この図はタスク設定・サンプル・データセットを明確にする。"), + "method_detail": ("手法の骨子", "この図は内部機構や実行状態を説明しており、手法の骨子に置くのが適切である。"), + "table_result": ("主要な結果", "この結果表は読者が中心的な数値的証拠を見つける助けになる。"), + "supporting_figure": ("深掘り分析", "この補助図は深掘り分析で著者の論点を説明する助けになる。"), +} + +LOCALIZED_FIGURE_PLACEMENT: dict[str, dict[str, tuple[str, str]]] = { + "en": ENGLISH_FIGURE_PLACEMENT, + "ja": JAPANESE_FIGURE_PLACEMENT, +} + def classify_caption_kind(item_id: str, caption: str, language: str | None = None) -> tuple[str, str, str]: result = _classify_caption_kind_zh(item_id, caption) - if normalize_output_language(language) != "en": + placement = LOCALIZED_FIGURE_PLACEMENT.get(normalize_output_language(language)) + if placement is None: return result kind = result[0] - section, reason = ENGLISH_FIGURE_PLACEMENT[kind] + section, reason = placement[kind] return kind, section, reason diff --git a/skills/deeppapernote/scripts/run_pipeline.py b/skills/deeppapernote/scripts/run_pipeline.py index cadc46b..542e615 100644 --- a/skills/deeppapernote/scripts/run_pipeline.py +++ b/skills/deeppapernote/scripts/run_pipeline.py @@ -34,7 +34,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument( "--language", default="", - choices=("", "en", "zh-CN"), + choices=("", "en", "zh-CN", "ja"), help="Run Override for the output language contract.", ) p.add_argument("--save-mode", choices=("workspace", "obsidian"), default="") diff --git a/skills/deeppapernote/scripts/user_configuration.py b/skills/deeppapernote/scripts/user_configuration.py index 8e09dbf..7b02156 100644 --- a/skills/deeppapernote/scripts/user_configuration.py +++ b/skills/deeppapernote/scripts/user_configuration.py @@ -13,7 +13,7 @@ from pathlib import Path, PureWindowsPath from typing import Any, Mapping, Sequence -OUTPUT_LANGUAGES = {"zh-CN", "en"} +OUTPUT_LANGUAGES = {"zh-CN", "en", "ja"} SAVE_MODES = {"workspace", "obsidian"} KNOWN_FIELDS = ("output_language", "save_mode", "obsidian_vault", "papers_dir") ALWAYS_REQUIRED = ("output_language", "save_mode") @@ -136,7 +136,7 @@ def _validate(configuration: Mapping[str, Any]) -> tuple[list[dict[str, str]], l language = str(configuration.get("output_language", "")).strip() save_mode = str(configuration.get("save_mode", "")).strip() if language and language not in OUTPUT_LANGUAGES: - issues.append(_issue("output_language", "invalid_enum", "Expected zh-CN or en.")) + issues.append(_issue("output_language", "invalid_enum", "Expected zh-CN, en, or ja.")) if save_mode and save_mode not in SAVE_MODES: issues.append(_issue("save_mode", "invalid_enum", "Expected workspace or obsidian.")) if save_mode == "obsidian": diff --git a/skills/deeppapernote/scripts/write_obsidian_note.py b/skills/deeppapernote/scripts/write_obsidian_note.py index 0cb3790..369dab1 100644 --- a/skills/deeppapernote/scripts/write_obsidian_note.py +++ b/skills/deeppapernote/scripts/write_obsidian_note.py @@ -108,7 +108,7 @@ def parser() -> argparse.ArgumentParser: p.add_argument("--filename", default="", help="Explicit note filename.") p.add_argument("--asset-subdir", default="images", help="Asset folder name relative to the note directory.") p.add_argument("--paper-id", default="", help="Canonical paper id.") - p.add_argument("--language", default="", help="Run Override for output language: en or zh-CN.") + p.add_argument("--language", default="", help="Run Override for output language: en, zh-CN, or ja.") p.add_argument( "--preflight", action="store_true", diff --git a/tests/test_output_language_ja.py b/tests/test_output_language_ja.py new file mode 100644 index 0000000..02a4d0b --- /dev/null +++ b/tests/test_output_language_ja.py @@ -0,0 +1,477 @@ +"""日本語(ja)出力プロファイルの契約・lint・図表配置を検証する。""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +import pytest +from build_synthesis_bundle import compact_writing_contract +from contracts import paper_type_contracts +from lint_grounding import validate_note_plan +from localization import ( + normalize_output_language, + require_artifact_output_language, + required_sections, +) +from plan_figures import build_figure_items +from user_configuration import inspect_configuration + +LINT_SCRIPT = Path(__file__).resolve().parents[1] / "skills/deeppapernote/scripts/lint_note.py" + +JAPANESE_SECTIONS = ( + "基本情報", + "要旨の翻訳", + "新規性", + "一言まとめ", + "研究課題", + "データとタスク定義", + "手法の骨子", + "主要な結果", + "深掘り分析", + "限界", + "私のメモ", + "参考文献", +) + + +def japanese_note() -> str: + return """--- +tags: + - papers/methods +aliases: + - "Auditable Tool Use" +date: 2024 +doi: 10.1234/example +--- + +# Auditable Tool Use + +## 基本情報 + +- タイトル: Auditable Tool Use +- タイトル訳: 監査可能なツール利用 +- 著者: Smith et al. +- 発表時期: 2024 +- 発表媒体: Example Journal +- DOI: 10.1234/example +- 論文タイプ: AI_method + +## 要旨の翻訳 + +本論文は多段階の質問応答のために監査可能な状態機械を構築し、明示的な失敗記録が回答の信頼性を改善するかを評価する。 + +## 新規性 + +- 証拠選択とツール状態の追跡を一つの実行記録に統合し、失敗した証拠が黙って信頼できる入力に変わることを防ぐ。 +- 証拠の欠落と推論の誤りを区別する明示的なロールバック状態を追加し、最終回答の追跡を可能にする。 + +## 一言まとめ + +明示的なツール状態の記録は、多段階質問応答における誤りの伝播を減らす。 + +## 研究課題 + +検索が不完全で、外部ツールが失敗し、中間結果が誤用されるとき、多段階質問応答システムはどのように追跡可能性を保てるか。 + +## データとタスク定義 + +入力は質問、候補証拠、利用可能なツールであり、出力は回答、状態トレース、そして完了が支持されない場合の失敗ラベルである。 + +## 手法の骨子 + +### 機構フロー + +1. **入力:** 質問と候補証拠。**操作:** 関連する証拠を抽出する。**出力:** 根拠づけられた初期状態。 +2. **入力:** 現在の状態とツール登録簿。**操作:** 要求を利用可能なツールに整列させる。**出力:** 計画された呼び出し。 +3. **入力:** ツール出力と確信度。**操作:** 状態を更新するかロールバックする。**出力:** 監査可能な実行記録。 +4. **入力:** 最終状態。**操作:** 回答または拒否を復号する。**出力:** 出所を伴う応答。 + +> [!figure] Figure 1 システム概要 +> 推奨位置:手法の骨子 +> 配置理由:この図は証拠とツール状態が実行チェーンをどう流れるかを示す。 +> 現在の状態:プレースホルダを保持。抽出した切り出しは不完全で、単独では解釈できない。 + +## 主要な結果 + +三つのデータセットにわたり、回答精度は 71.2% から 78.5% に向上し、追跡不能な誤りは 18% から 9% に減少した。 + +## 深掘り分析 + +重要な貢献はスコアの向上だけではない。失敗した呼び出しは隠れた中間状態ではなく検査可能な証拠となり、監査と的を絞った復旧を支える。 + +## 限界 + +評価は英語の質問応答データと狭いツール集合に限られており、マルチモーダルなツールや高遅延サービスに対する頑健性は確立していない。 + +## 私のメモ + +状態記録の設計は、ソース資料の欠落とモデルの解釈失敗を分離するため、証拠優先の論文ワークフローでも再利用できる。 + +## 参考文献 + +- Smith et al. (2024). Auditable Tool Use for Multi-hop Question Answering. DOI: 10.1234/example +""" + + +def plan_payload() -> dict: + return { + "output_language": "ja", + "paper_type": "AI_method", + "paper_type_rationale": "モデルの機構を提案し評価する論文である。", + "dominant_domain": "reasoning", + "must_cover": ["手法の骨子"], + "key_numbers": ["78.5%"], + "real_comparisons": ["71.2% 対 78.5%"], + "central_claims": [{ + "claim": "手法は追跡可能性を改善する。", + "supporting_evidence": [{"section_id": "sec:results"}], + "what_it_actually_proves": "報告されたプロトコルはツール状態を記録する。", + "what_it_does_not_prove": "本番環境での頑健性は証明していない。", + }], + "claim_boundaries": ["証拠は報告されたワークフローに限られる。"], + "negative_or_limiting_results": ["マルチモーダルなツールは試験されていない。"], + "mechanism_result_map": ["ロールバック状態が追跡不能な誤りの減少を説明する。"], + "comparative_positioning": ["回答のみのベースラインと比較。"], + "reuse_takeaways": ["失敗状態を明示的に追跡する。"], + "followup_questions": ["欠落・遅延したツール出力を試験する。"], + "section_plan": [{"section": "手法の骨子", "evidence_sources": [{"section_id": "sec:method"}]}], + } + + +def run_lint(tmp_path: Path, note: str, *extra_args: str) -> dict: + note_path = tmp_path / "paper.md" + plan_path = tmp_path / "paper.plan.json" + output_path = tmp_path / "lint.json" + note_path.write_text(note, encoding="utf-8") + plan_path.write_text(json.dumps(plan_payload(), ensure_ascii=False), encoding="utf-8") + subprocess.run( + [ + sys.executable, + str(LINT_SCRIPT), + *extra_args, + "--input", + str(note_path), + "--plan-file", + str(plan_path), + "--output", + str(output_path), + ], + check=True, + ) + return json.loads(output_path.read_text(encoding="utf-8")) + + +def test_japanese_language_aliases() -> None: + assert normalize_output_language("ja") == "ja" + assert normalize_output_language("Japanese") == "ja" + assert normalize_output_language("ja-JP") == "ja" + assert normalize_output_language("日本語") == "ja" + assert require_artifact_output_language({"output_language": "ja"}, "Note Plan", "ja") == "ja" + with pytest.raises(ValueError): + require_artifact_output_language({"output_language": "ja"}, "Note Plan", "en") + + +def test_japanese_user_configuration_accepts_ja(tmp_path: Path) -> None: + path = tmp_path / "config.json" + path.write_text( + json.dumps({"output_language": "ja", "save_mode": "workspace"}), + encoding="utf-8", + ) + result = inspect_configuration(config_path=path, environ={}) + assert result["state"] == "ready" + assert result["affected_fields"] == [] + assert result["configuration"]["output_language"] == "ja" + + +def test_japanese_user_configuration_reports_invalid_enum_with_ja_choice(tmp_path: Path) -> None: + path = tmp_path / "config.json" + path.write_text( + json.dumps({"output_language": "fr", "save_mode": "workspace"}), + encoding="utf-8", + ) + result = inspect_configuration(config_path=path, environ={}) + assert result["state"] != "ready" + messages = [issue["message"] for issue in result["issues"]] + assert any("ja" in message for message in messages) + + +def test_japanese_contract_exposes_localized_schema() -> None: + contract = compact_writing_contract("ja") + assert contract["language"] == "ja" + assert tuple(contract["must_include_sections"]) == JAPANESE_SECTIONS + assert tuple(required_sections("ja")) == JAPANESE_SECTIONS + assert contract["mechanism_flow_heading"] == "機構フロー" + assert contract["core_info_fields"] == [ + "タイトル", + "タイトル訳", + "著者", + "所属", + "発表時期", + "発表媒体", + "DOI", + "arXiv", + "論文リンク", + "コード / プロジェクト", + "データ / リソース", + "論文タイプ", + ] + assert contract["figure_labels"] == { + "location": "推奨位置:", + "reason": "配置理由:", + "status": "現在の状態:", + "original_caption": "論文原図番号:", + } + assert contract["abstract_contract"]["requirement"] == "faithful_rendering_in_output_language" + # 日本語プロファイルの契約本文に簡体字中国語のセクション名が残っていないこと + dumped = json.dumps(contract, ensure_ascii=False) + for leftover in ("核心信息", "方法主线", "关键结果", "深度分析", "我的笔记"): + assert leftover not in dumped + + +def test_japanese_paper_type_contracts_use_japanese_section_names() -> None: + contracts = paper_type_contracts("ja") + assert set(contracts) == { + "AI_method", + "benchmark_or_dataset", + "clinical_or_psychology_empirical", + "humanities_or_social_science", + "survey_or_review", + } + ai_method = contracts["AI_method"] + assert "手法の骨子" in ai_method["section_semantics"] + assert "機構フロー" in ai_method["recommended_subsections"]["手法の骨子"] + assert ai_method["mechanism_flow_contract"]["required_step_count"] == "3_to_4" + for contract in contracts.values(): + assert set(contract["section_semantics"]) <= set(JAPANESE_SECTIONS) + assert set(contract["recommended_subsections"]) <= set(JAPANESE_SECTIONS) + + +def test_japanese_grounding_accepts_japanese_section_plan() -> None: + plan = plan_payload() + plan["central_claims"][0]["supporting_evidence"] = [{"section_id": "sec:method"}] + plan["section_plan"] = [ + { + "section": section, + "focus": f"{section} に固有の証拠と分析上の役割を説明する。", + "evidence_sources": [{"section_id": "sec:method"}], + } + for section in ("研究課題", "データとタスク定義", "手法の骨子", "主要な結果", "深掘り分析", "限界") + ] + manifest = { + "coverage": {"total_pages": 10, "text_truncated": False}, + "sections": [{"section_id": "sec:method", "title": "Method", "page_start": 1, "page_end": 10}], + "pages": [], + } + assert validate_note_plan(plan, manifest, "ja") == [] + + +def test_japanese_figure_plan_uses_japanese_targets_and_reasons() -> None: + items = build_figure_items( + { + "figure_captions": [{"id": "Figure 1", "caption": "Overview of the system architecture."}], + "table_captions": [{"id": "Table 1", "caption": "Main results on three benchmarks."}], + }, + language="ja", + ) + by_id = {item["id"]: item for item in items} + assert by_id["Figure 1"]["section"] == "機構フロー" + assert by_id["Table 1"]["section"] == "主要な結果" + dumped = json.dumps(items, ensure_ascii=False) + assert "この" in dumped + for leftover in ("方法主线", "关键结果", "深度分析", "机制流程"): + assert leftover not in dumped + + +def test_japanese_note_passes_every_lint_gate_from_user_configuration( + tmp_path: Path, configured_user_home: Path +) -> None: + configured_user_home.write_text( + json.dumps({"output_language": "ja", "save_mode": "workspace"}), + encoding="utf-8", + ) + payload = run_lint(tmp_path, japanese_note()) + assert payload["output_language"] == "ja" + assert payload["warnings"] == [] + assert all(value is True for key, value in payload.items() if key.startswith("passes_")) + + +@pytest.mark.parametrize( + ("original", "invalid"), + [ + ("> 推奨位置:", "> 建议位置:"), + ("> 現在の状態:", "> Current status:"), + ("### 機構フロー", "### 机制流程"), + ("- タイトル:", "- 标题:"), + ], +) +def test_japanese_lint_requires_exact_labels_and_mechanism_heading( + tmp_path: Path, original: str, invalid: str +) -> None: + payload = run_lint(tmp_path, japanese_note().replace(original, invalid), "--language", "ja") + all_gates_pass = all(value is True for key, value in payload.items() if key.startswith("passes_")) + assert payload["warnings"] or not all_gates_pass + + +@pytest.mark.parametrize( + "leftover_line", + [ + "### 机制流程", + "> 建议位置:手法の骨子", + "这张图解释方法内部机制。", + ], +) +def test_japanese_lint_rejects_simplified_chinese_leftovers(tmp_path: Path, leftover_line: str) -> None: + note = japanese_note().replace( + "重要な貢献はスコアの向上だけではない。", + f"重要な貢献はスコアの向上だけではない。\n\n{leftover_line}", + ) + payload = run_lint(tmp_path, note, "--language", "ja") + assert payload["passes_style_gate"] is False + assert "mixed_language_lines_present" in payload["warnings"] + assert any( + issue["reason"] == "simplified_chinese_text_present" for issue in payload["mixed_language_issues"] + ) + + +def test_japanese_lint_keeps_shared_kanji_and_source_metadata(tmp_path: Path) -> None: + # 日中で共有される漢字(数・与・会・後 など)と、参考文献の原語メタデータは許容する + note = japanese_note().replace( + "- Smith et al. (2024). Auditable Tool Use for Multi-hop Question Answering. DOI: 10.1234/example", + "- Smith et al. (2024). Auditable Tool Use for Multi-hop Question Answering. DOI: 10.1234/example\n" + "- 张伟 (2023). `可审计的工具使用`. DOI: 10.1234/example2", + ).replace( + "状態記録の設計は、", + "数値と証拠を与える設計は会議後にも再利用でき、状態記録の設計は、", + ) + payload = run_lint(tmp_path, note, "--language", "ja") + assert payload["passes_style_gate"] is True + + +def test_japanese_lint_requires_complete_top_level_heading_order(tmp_path: Path) -> None: + note = ( + japanese_note() + .replace("## 手法の骨子", "## TEMP") + .replace("## 主要な結果", "## 手法の骨子") + .replace("## TEMP", "## 主要な結果") + ) + payload = run_lint(tmp_path, note, "--language", "ja") + assert payload["passes_basic_structure"] is False + assert "top_level_section_profile_invalid" in payload["warnings"] + + +def test_japanese_lint_flags_english_prose_mixed_into_japanese(tmp_path: Path) -> None: + note = japanese_note().replace( + "重要な貢献はスコアの向上だけではない。", + "重要な貢献は the improvement of the score だけではない and it is also about the auditability。", + ) + payload = run_lint(tmp_path, note, "--language", "ja") + assert payload["passes_style_gate"] is False + assert "mixed_language_lines_present" in payload["warnings"] + + +def test_japanese_lint_rejects_generic_japanese_filler(tmp_path: Path) -> None: + note = japanese_note().replace( + "- 証拠選択とツール状態の追跡を一つの実行記録に統合し、失敗した証拠が黙って信頼できる入力に変わることを防ぐ。", + "- 本論文は新しい手法を提案する。", + ).replace( + "- 証拠の欠落と推論の誤りを区別する明示的なロールバック状態を追加し、最終回答の追跡を可能にする。", + "- 新規性が高い。", + ) + payload = run_lint(tmp_path, note, "--language", "ja") + assert not all(value is True for key, value in payload.items() if key.startswith("passes_")) + + +def test_run_pipeline_accepts_ja_language_override() -> None: + script = Path(__file__).resolve().parents[1] / "skills/deeppapernote/scripts/run_pipeline.py" + result = subprocess.run( + [sys.executable, str(script), "--help"], + capture_output=True, + text=True, + encoding="utf-8", + check=True, + ) + assert "ja" in result.stdout + + +JAPANESE_DOMAIN_RULES = """ +domains: + - label: 医療・健康 + aliases: + - healthcare + - medical + specialized_folders: + - メンタルヘルス + keywords: + - clinical + - patient + methods: [] + - label: メンタルヘルス + route_to: 医療・健康 + aliases: + - mental health + keywords: + - depression + methods: [] +fallback_domains: + - label: 機械学習 + aliases: + - machine learning + keywords: + - deep learning + - neural network + methods: [] + - label: 未分類 + aliases: + - unclassified + keywords: [] + methods: [] +""".strip() + "\n" + + +def test_domain_rules_can_be_overridden_by_environment_path(tmp_path: Path, monkeypatch) -> None: + import common + + rules_path = tmp_path / "ja_domain_rules.yaml" + rules_path.write_text(JAPANESE_DOMAIN_RULES, encoding="utf-8") + monkeypatch.setenv("DEEPPAPERNOTE_DOMAIN_RULES", str(rules_path)) + + assert common.infer_domain_label("Deep learning for depression screening", "patient cohort") == "医療・健康" + assert common.infer_domain_label("A neural network for image denoising") == "機械学習" + + +def test_domain_rules_next_to_user_configuration_are_preferred_over_skill_default( + tmp_path: Path, monkeypatch, configured_user_home: Path +) -> None: + import common + + monkeypatch.delenv("DEEPPAPERNOTE_DOMAIN_RULES", raising=False) + (configured_user_home.parent / "domain_rules.yaml").write_text(JAPANESE_DOMAIN_RULES, encoding="utf-8") + + assert common.infer_domain_label("A neural network for image denoising") == "機械学習" + + +def test_domain_fallback_labels_follow_loaded_rules(tmp_path: Path, monkeypatch) -> None: + """ルールにヒットしない場合の既定ラベルも、読み込んだ規則の alias から解決する。""" + import common + + rules_path = tmp_path / "ja_domain_rules.yaml" + rules_path.write_text(JAPANESE_DOMAIN_RULES, encoding="utf-8") + monkeypatch.setenv("DEEPPAPERNOTE_DOMAIN_RULES", str(rules_path)) + + # ルールのキーワードには一切ヒットしないが論文タイプが AI_method と推定されるタイトル + assert common.infer_domain_label( + "A new encoder-decoder model with attention for translation", + "We propose a model architecture and train it end to end; ablations show gains.", + ) == "機械学習" + assert common.infer_domain_label( + "Randomized controlled trial of cognitive therapy", + "We enrolled participants with anxiety and measured symptom scores at follow-up.", + ) == "医療・健康" + # 何にも該当しない場合(人文系の論文タイプ) + assert common.infer_domain_label( + "An ethnographic study of eighteenth-century poetry circles", + "An interpretive account of literary sociability.", + ) == "未分類" From 0a695c59e12906ff4861a77b97e0b6c6ec38f816 Mon Sep 17 00:00:00 2001 From: Yoshiaki Sakae <89111154+yoshiaki-sakae@users.noreply.github.com> Date: Wed, 9 Sep 2026 13:18:34 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20output=5Flanguage=3Dja=20=E3=81=AE?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=AE=E3=83=89?= =?UTF-8?q?=E3=83=A1=E3=82=A4=E3=83=B3=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80?= =?UTF-8?q?=E5=90=8D=E3=82=92=E8=87=AA=E5=8B=95=E9=81=B8=E6=8A=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Obsidian へ保存する際のドメインフォルダ名が、日本語出力でも中国語 (机器学习・医疗健康 など)になっていた。原因は同梱の references/domain_rules.yaml が中国語ラベルのみで、フォルダ名解決の 経路(resolve_domain_subdir → infer_domain_label → load_domain_rules)が output_language を一切参照していなかったこと。 - references/domain_rules.ja.yaml を追加(alias/keyword は共通、ラベルのみ日本語化、未分類を明示) - resolve_domain_rules_path / load_domain_rules / infer_domain_label / domain_name_score に output_language を引き回し、 resolve_domain_subdir は config["output_language"] から言語を取得 - ENV・~/.deeppapernote/domain_rules.yaml の優先順位は従来どおり維持 - 同梱 YAML が読めない場合の既定ルールも DOMAIN_LABEL_TRANSLATIONS で日本語化 - 既存の Vault フォルダ再利用の挙動は言語に関係なく維持 - references/output-language.md の記述を更新 Co-Authored-By: Claude Code --- .../references/domain_rules.ja.yaml | 209 ++++++++++++++++++ .../references/output-language.md | 2 +- skills/deeppapernote/scripts/common.py | 118 +++++++--- tests/test_output_language_ja.py | 143 ++++++++++++ 4 files changed, 444 insertions(+), 28 deletions(-) create mode 100644 skills/deeppapernote/references/domain_rules.ja.yaml diff --git a/skills/deeppapernote/references/domain_rules.ja.yaml b/skills/deeppapernote/references/domain_rules.ja.yaml new file mode 100644 index 0000000..86d93ab --- /dev/null +++ b/skills/deeppapernote/references/domain_rules.ja.yaml @@ -0,0 +1,209 @@ +# Japanese domain routing taxonomy, selected automatically when output_language is ja. +# Same aliases/keywords as domain_rules.yaml; only folder labels are translated. +# Copy to ~/.deeppapernote/domain_rules.yaml (or point DEEPPAPERNOTE_DOMAIN_RULES at a copy) to customize. +# Application domains are tried before fallback method domains. +domains: + - label: 医療・健康 + aliases: + - healthcare + - medical + - clinical medicine + specialized_folders: + - メンタルヘルス + keywords: + - clinical + - patient + - patients + - depression + - anxiety + - mental health + - psychiatric + - psychology + - therapy + - counseling + - symptom + - diagnosis + - screening + - hospital + - healthcare + - medical + methods: [] + + - label: 法律 + aliases: + - legal + - law + keywords: + - legal + - law + - court + - judge + - contract + - statute + - regulation + - litigation + - case law + methods: [] + + - label: 教育 + aliases: + - education + - educational + keywords: + - education + - student + - teacher + - classroom + - curriculum + - tutoring + - learning analytics + - pedagogy + methods: [] + + - label: 金融 + aliases: + - finance + - financial + keywords: + - finance + - financial + - stock + - market + - trading + - portfolio + - risk + - credit + - banking + - investment + methods: [] + + - label: ロボティクス + aliases: + - robotics + - robotic + keywords: + - robot + - robotics + - robotic + - manipulation + - navigation + - control policy + - locomotion + - autonomous driving + - embodied + methods: + - diffusion policy + + - label: ソフトウェア工学 + aliases: + - software engineering + keywords: + - software engineering + - code generation + - program repair + - bug + - repository + - developer + - code review + - test generation + - compiler + methods: [] + + - label: 生物医学 + aliases: + - biomedical + - bioinformatics + keywords: + - biomedical + - genomics + - protein + - drug discovery + - molecular + - cell + - gene + - bioinformatics + methods: [] + + - label: メンタルヘルス + route_to: 医療・健康 + aliases: + - mental health + - psychology + - psychiatry + keywords: + - depression + - anxiety + - mental health + - psychiatric + - psychology + - therapy + - counseling + - symptom + methods: [] + + - label: 推薦システム + aliases: + - recommender systems + - recommendation + keywords: + - recommendation + - recommender + - ctr prediction + - ranking system + - personalization + methods: [] + +fallback_domains: + - label: 大規模言語モデル + aliases: + - llm + - large language model + - language model + - foundation model + keywords: + - large language model + - llm + - foundation model + - gpt + - transformer + - instruction tuning + - pretrain + - pre-training + - language model + - agent + - multi-agent + - multi agent + - reasoning + - multimodal + - retrieval-augmented generation + - rag + - in-context learning + - long-context + - long context + - mixture-of-experts + - mixture of experts + - moe + - alignment + - rlhf + methods: [] + + - label: 機械学習 + aliases: + - machine learning + - ml + keywords: + - machine learning + - deep learning + - neural network + - representation learning + - reinforcement learning + - computer vision + - graph neural network + - speech recognition + methods: [] + + - label: 未分類 + aliases: + - unclassified + keywords: [] + methods: [] diff --git a/skills/deeppapernote/references/output-language.md b/skills/deeppapernote/references/output-language.md index 7926c68..ddb2655 100644 --- a/skills/deeppapernote/references/output-language.md +++ b/skills/deeppapernote/references/output-language.md @@ -105,7 +105,7 @@ Write natural Japanese prose (常体・敬体 consistently within a note). Keep The Japanese style gate checks headings, 基本情報 labels, figure callouts, inserted-image captions, and prose. Because kanji are shared with Chinese, the gate looks for simplified-Chinese-only characters (机, 图, 论, 议, 态, ...) that never appear in Japanese; any such character in a heading, label, or prose line is a leftover from the Chinese template and fails `passes_style_gate`. Original Chinese metadata is allowed inside `基本情報` and `参考文献` when marked with inline code or a Markdown link, and URLs and closed fenced code blocks keep their source text. Lines that mix Japanese with four or more English words including English function words are also rejected as mixed-language prose. -Domain folders: the skill ships `references/domain_rules.yaml` with Chinese folder labels. A Japanese Vault should keep a translated taxonomy outside the skill, either at `~/.deeppapernote/domain_rules.yaml` (next to `config.json`) or at the path named by `DEEPPAPERNOTE_DOMAIN_RULES`; both take precedence over the shipped file, and fallback folder labels are resolved through the `healthcare`, `machine learning`, and `unclassified` aliases of that taxonomy. +Domain folders: the skill ships `references/domain_rules.yaml` with Chinese folder labels and `references/domain_rules.ja.yaml` with Japanese ones (医療・健康, 法律, 教育, 金融, ロボティクス, ソフトウェア工学, 生物医学, メンタルヘルス, 推薦システム, 大規模言語モデル, 機械学習, 未分類). When the resolved `output_language` is `ja`, `resolve_domain_subdir` selects the Japanese file automatically, so a Japanese Vault gets Japanese folder names without extra setup. To customize labels, keep a copy outside the skill, either at `~/.deeppapernote/domain_rules.yaml` (next to `config.json`) or at the path named by `DEEPPAPERNOTE_DOMAIN_RULES`; both take precedence over any shipped file, and fallback folder labels are resolved through the `healthcare`, `machine learning`, and `unclassified` aliases of that taxonomy. Existing domain folders already present under the papers root are still reused regardless of language. ## Contract ownership diff --git a/skills/deeppapernote/scripts/common.py b/skills/deeppapernote/scripts/common.py index 34c074f..a485141 100644 --- a/skills/deeppapernote/scripts/common.py +++ b/skills/deeppapernote/scripts/common.py @@ -2571,6 +2571,21 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: DOMAIN_RULES_PATH = Path(__file__).resolve().parents[1] / "references" / "domain_rules.yaml" +# Translations of the hardcoded Chinese fallback labels, applied only when no taxonomy +# file could be loaded (e.g. the shipped YAML is unreadable). Labels absent here are +# identical in both languages (法律, 教育, 金融, 生物医学). +DOMAIN_LABEL_TRANSLATIONS: dict[str, dict[str, str]] = { + "ja": { + "医疗健康": "医療・健康", + "机器人": "ロボティクス", + "软件工程": "ソフトウェア工学", + "心理健康": "メンタルヘルス", + "推荐系统": "推薦システム", + "大模型": "大規模言語モデル", + "机器学习": "機械学習", + "未分类": "未分類", + }, +} DOMAIN_LIST_KEYS = ( "aliases", "keywords", @@ -2782,12 +2797,29 @@ def resolve_note_asset_dir(note_path: str | Path, asset_subdir: str) -> Path: } -def _copy_default_domain_rules() -> dict[str, list[dict[str, Any]]]: +def _translate_domain_label(label: str, output_language: str | None) -> str: + return DOMAIN_LABEL_TRANSLATIONS.get(output_language or "", {}).get(label, label) + + +def _copy_default_domain_rules( + output_language: str | None = None, +) -> dict[str, list[dict[str, Any]]]: + def translate(value: Any) -> str: + return _translate_domain_label(str(value), output_language) + + def copy_rule(rule: dict[str, Any]) -> dict[str, Any]: + copied: dict[str, Any] = {} + for key, value in rule.items(): + if key in ("label", "route_to"): + copied[key] = translate(value) + elif key == "specialized_folders": + copied[key] = [translate(item) for item in value] + else: + copied[key] = list(value) if isinstance(value, list) else value + return copied + return { - section: [ - {key: list(value) if isinstance(value, list) else value for key, value in rule.items()} - for rule in DEFAULT_DOMAIN_RULES[section] - ] + section: [copy_rule(rule) for rule in DEFAULT_DOMAIN_RULES[section]] for section in DOMAIN_SECTIONS } @@ -2957,11 +2989,23 @@ def _normalize_domain_rules( return normalized -def resolve_domain_rules_path() -> Path: - """Pick the domain taxonomy file: env override, then the user config directory, then the skill default. +def shipped_domain_rules_path(output_language: str | None = None) -> Path: + """Return the shipped taxonomy: `domain_rules..yaml` when present, else the default.""" + language = (output_language or "").strip() + if language: + localized = DOMAIN_RULES_PATH.with_name(f"domain_rules.{language}.yaml") + if localized.is_file(): + return localized + return DOMAIN_RULES_PATH + + +def resolve_domain_rules_path(output_language: str | None = None) -> Path: + """Pick the domain taxonomy file: env override, then the user config directory, then the + skill default. - The skill ships Chinese folder labels; a user whose Vault uses another language - keeps a translated copy outside the skill so upstream updates never overwrite it. + The default file ships Chinese folder labels and `domain_rules.ja.yaml` ships Japanese + ones, chosen by `output_language`; a user who wants other labels keeps a copy outside + the skill so upstream updates never overwrite it. """ override = os.environ.get("DEEPPAPERNOTE_DOMAIN_RULES", "").strip() if override: @@ -2969,21 +3013,21 @@ def resolve_domain_rules_path() -> Path: user_rules = user_config_path().parent / "domain_rules.yaml" if user_rules.is_file(): return user_rules - return DOMAIN_RULES_PATH + return shipped_domain_rules_path(output_language) -def load_domain_rules() -> dict[str, list[dict[str, Any]]]: +def load_domain_rules(output_language: str | None = None) -> dict[str, list[dict[str, Any]]]: try: - rules_path = resolve_domain_rules_path() + rules_path = resolve_domain_rules_path(output_language) if not rules_path.exists(): - return _copy_default_domain_rules() + return _copy_default_domain_rules(output_language) parsed = _parse_domain_rules_yaml(rules_path.read_text(encoding="utf-8-sig")) normalized = _normalize_domain_rules(parsed) if normalized is None: - return _copy_default_domain_rules() + return _copy_default_domain_rules(output_language) return normalized except Exception: - return _copy_default_domain_rules() + return _copy_default_domain_rules(output_language) def _normalized_domain_label(value: str) -> str: @@ -3051,9 +3095,11 @@ def _score_domain_for_inference(rule: dict[str, Any], text: str, *, fallback: bo ) -def infer_domain_label(title: str, abstract: str = "") -> str: +def infer_domain_label( + title: str, abstract: str = "", *, output_language: str | None = None +) -> str: lower = normalize_whitespace(f"{title} {abstract}").lower() - rules = load_domain_rules() + rules = load_domain_rules(output_language) scored: list[tuple[int, str]] = [] for rule in rules["domains"]: score = _score_domain_for_inference(rule, lower, fallback=False) @@ -3073,21 +3119,28 @@ def infer_domain_label(title: str, abstract: str = "") -> str: paper_type, _ = infer_paper_type(title, abstract) if paper_type == "clinical_or_psychology_empirical": - return _domain_label_by_alias(rules, "healthcare", "医疗健康") + return _domain_label_by_alias(rules, "healthcare", "医疗健康", output_language) if paper_type == "AI_method": - return _domain_label_by_alias(rules, "machine learning", "机器学习") - return _domain_label_by_alias(rules, "unclassified", "未分类") + return _domain_label_by_alias(rules, "machine learning", "机器学习", output_language) + return _domain_label_by_alias(rules, "unclassified", "未分类", output_language) -def _domain_label_by_alias(rules: dict[str, list[dict[str, Any]]], alias: str, default: str) -> str: +def _domain_label_by_alias( + rules: dict[str, list[dict[str, Any]]], + alias: str, + default: str, + output_language: str | None = None, +) -> str: """Resolve a fallback folder label through the loaded taxonomy so translated rules stay in charge.""" wanted = _normalized_domain_label(alias) for section in DOMAIN_SECTIONS: for rule in rules[section]: - aliases = {_normalized_domain_label(item) for item in _as_string_list(rule.get("aliases"))} + aliases = { + _normalized_domain_label(item) for item in _as_string_list(rule.get("aliases")) + } if wanted in aliases: return _domain_route_label(rule) - return default + return _translate_domain_label(default, output_language) def is_probable_paper_folder(path: Path) -> bool: @@ -3119,13 +3172,20 @@ def existing_domain_dirs(config: dict[str, Any]) -> list[str]: return names -def domain_name_score(domain_name: str, label: str, title: str, abstract: str) -> int: +def domain_name_score( + domain_name: str, + label: str, + title: str, + abstract: str, + *, + output_language: str | None = None, +) -> int: name = domain_name.strip().lower() score = 0 if name == label.lower(): score += 100 lower = normalize_whitespace(f"{title} {abstract}").lower() - rules = load_domain_rules() + rules = load_domain_rules(output_language) label_rules = _rules_for_label(rules, label) label_is_application = any(section == "domains" for section, _ in label_rules) known_fallback_name = any( @@ -3155,13 +3215,17 @@ def resolve_domain_subdir( ) -> str: if subdir.strip(): return subdir.strip() - label = infer_domain_label(title, abstract) + # Folder labels follow the note's output language (ja -> domain_rules.ja.yaml). + output_language = str(config.get("output_language", "")).strip() or None + label = infer_domain_label(title, abstract, output_language=output_language) existing = existing_domain_dirs(config) if existing: best_name = "" best_score = -1 for domain_name in existing: - score = domain_name_score(domain_name, label, title, abstract) + score = domain_name_score( + domain_name, label, title, abstract, output_language=output_language + ) if score > best_score: best_name = domain_name best_score = score diff --git a/tests/test_output_language_ja.py b/tests/test_output_language_ja.py index 02a4d0b..fba57d5 100644 --- a/tests/test_output_language_ja.py +++ b/tests/test_output_language_ja.py @@ -475,3 +475,146 @@ def test_domain_fallback_labels_follow_loaded_rules(tmp_path: Path, monkeypatch) "An ethnographic study of eighteenth-century poetry circles", "An interpretive account of literary sociability.", ) == "未分類" + + +# --- 同梱の日本語ドメイン規則(フォルダ名の日本語化) --- + +SHIPPED_JA_DOMAIN_RULES = ( + Path(__file__).resolve().parents[1] / "skills/deeppapernote/references/domain_rules.ja.yaml" +) + + +@pytest.fixture +def no_user_domain_rules(monkeypatch, configured_user_home: Path) -> Path: + """ENV も ~/.deeppapernote/domain_rules.yaml も存在しない、素の状態を作る。""" + monkeypatch.delenv("DEEPPAPERNOTE_DOMAIN_RULES", raising=False) + user_rules = configured_user_home.parent / "domain_rules.yaml" + assert not user_rules.exists() + return user_rules + + +def test_shipped_japanese_domain_rules_cover_every_default_label() -> None: + """同梱の ja 規則は zh-CN 規則と同じ alias 集合を持ち、ラベルは全て日本語である。""" + import common + + zh = common._parse_domain_rules_yaml(common.DOMAIN_RULES_PATH.read_text(encoding="utf-8-sig")) + ja = common._parse_domain_rules_yaml(SHIPPED_JA_DOMAIN_RULES.read_text(encoding="utf-8-sig")) + assert common._normalize_domain_rules(ja) is not None + + def alias_set(rules: dict) -> set[str]: + return { + alias.lower() + for section in common.DOMAIN_SECTIONS + for rule in rules.get(section, []) + for alias in rule.get("aliases", []) + } + + # ja は zh-CN の alias を全て持ち、さらに unclassified を明示する + assert alias_set(zh) <= alias_set(ja) + assert "unclassified" in alias_set(ja) + + zh_labels = {rule["label"] for section in common.DOMAIN_SECTIONS for rule in zh[section]} + ja_labels = {rule["label"] for section in common.DOMAIN_SECTIONS for rule in ja[section]} + # 日中で表記が同じラベル(法律・教育・金融・生物医学)以外は、中国語のまま残っていないこと + shared = {"法律", "教育", "金融", "生物医学"} + assert (zh_labels & ja_labels) <= shared + assert {"医療・健康", "機械学習", "大規模言語モデル", "未分類"} <= ja_labels + + +def test_infer_domain_label_uses_shipped_japanese_rules_for_ja(no_user_domain_rules: Path) -> None: + import common + + assert common.infer_domain_label( + "Deep learning for depression screening", "patient cohort", output_language="ja" + ) == "医療・健康" + assert common.infer_domain_label( + "A neural network for image denoising", output_language="ja" + ) == "機械学習" + assert common.infer_domain_label( + "Diffusion Policy for Robot Manipulation", "robotic control", output_language="ja" + ) == "ロボティクス" + # 言語未指定・zh-CN では従来どおり中国語ラベル + assert common.infer_domain_label("A neural network for image denoising") == "机器学习" + assert common.infer_domain_label( + "A neural network for image denoising", output_language="zh-CN" + ) == "机器学习" + + +def test_resolve_domain_subdir_follows_config_output_language( + tmp_path: Path, no_user_domain_rules: Path +) -> None: + """write_obsidian_note.py が渡す config の output_language でフォルダ名の言語が決まる。""" + import common + + vault = tmp_path / "vault" + (vault / "Research" / "Papers").mkdir(parents=True) + base = {"save_mode": "obsidian", "obsidian_vault": str(vault), "papers_dir": "Research/Papers"} + + assert common.resolve_domain_subdir( + {**base, "output_language": "ja"}, title="A neural network for image denoising" + ) == "機械学習" + assert common.resolve_domain_subdir( + {**base, "output_language": "zh-CN"}, title="A neural network for image denoising" + ) == "机器学习" + + +def test_resolve_domain_subdir_ja_still_reuses_existing_vault_folder( + tmp_path: Path, no_user_domain_rules: Path +) -> None: + """Vault に既存フォルダがあれば、言語に関係なくそれを再利用する(既存挙動の維持)。""" + import common + + vault = tmp_path / "vault" + (vault / "Research" / "Papers" / "Machine Learning").mkdir(parents=True) + config = { + "save_mode": "obsidian", + "obsidian_vault": str(vault), + "papers_dir": "Research/Papers", + "output_language": "ja", + } + assert common.resolve_domain_subdir( + config, title="A neural network for image denoising" + ) == "Machine Learning" + + +def test_user_overrides_still_beat_shipped_japanese_rules( + tmp_path: Path, monkeypatch, no_user_domain_rules: Path +) -> None: + import common + + custom = JAPANESE_DOMAIN_RULES.replace("label: 機械学習", "label: ML研究") + rules_path = tmp_path / "custom.yaml" + rules_path.write_text(custom, encoding="utf-8") + monkeypatch.setenv("DEEPPAPERNOTE_DOMAIN_RULES", str(rules_path)) + assert common.infer_domain_label( + "A neural network for image denoising", output_language="ja" + ) == "ML研究" + + monkeypatch.delenv("DEEPPAPERNOTE_DOMAIN_RULES") + no_user_domain_rules.write_text(custom, encoding="utf-8") + assert common.infer_domain_label( + "A neural network for image denoising", output_language="ja" + ) == "ML研究" + + +def test_ja_fallback_labels_stay_japanese_when_shipped_rules_are_unreadable( + tmp_path: Path, monkeypatch, no_user_domain_rules: Path +) -> None: + """同梱 YAML が壊れていても、ja の最終フォールバックラベルが中国語に戻らない。""" + import common + + broken = tmp_path / "domain_rules.ja.yaml" + broken.write_text("domains: [\n", encoding="utf-8") + monkeypatch.setattr(common, "DOMAIN_RULES_PATH", tmp_path / "domain_rules.yaml") + + label = common.infer_domain_label( + "An ethnographic study of eighteenth-century poetry circles", + "An interpretive account of literary sociability.", + output_language="ja", + ) + assert label == "未分類" + assert common.infer_domain_label( + "A new encoder-decoder model with attention for translation", + "We propose a model architecture and train it end to end; ablations show gains.", + output_language="ja", + ) == "機械学習"