From ffd4eb7ca25cfb68fac81243951e785c9536e341 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Thu, 14 May 2026 17:56:25 +0800 Subject: [PATCH] refactor: unify skills into paperforge compound skill (6 workflows) --- README.md | 25 ++- paperforge/config.py | 19 +- paperforge/services/skill_deploy.py | 10 +- paperforge/setup/agent.py | 12 +- paperforge/skills/literature-qa/SKILL.md | 180 ----------------- .../literature-qa/references/deep-reading.md | 162 --------------- .../literature-qa/references/deep-subagent.md | 103 ---------- .../literature-qa/references/multi-reading.md | 144 -------------- .../literature-qa/references/paper-qa.md | 61 ------ .../references/paper-resolution.md | 94 --------- .../literature-qa/references/paper-search.md | 105 ---------- .../literature-qa/references/save-session.md | 55 ----- .../literature-qa/scripts/pf_bootstrap.py | 188 ------------------ .../skills/literature-qa/scripts/pf_search.py | 180 ----------------- paperforge/skills/logging/SKILL.md | 133 ------------- .../skills/logging/scripts/pf_bootstrap.py | 164 --------------- paperforge/skills/methodology/SKILL.md | 86 -------- paperforge/skills/paperforge/SKILL.md | 113 +++++++++++ .../references/chart-reading/GSEA富集图.md | 0 .../references/chart-reading/INDEX.md | 0 .../references/chart-reading/ROC与PR曲线.md | 0 .../chart-reading/Western Blot条带图.md | 0 .../chart-reading/免疫荧光定量图.md | 0 .../chart-reading/折线图与时间序列.md | 0 .../chart-reading/散点图与气泡图.md | 0 .../chart-reading/显微照片与SEM图.md | 0 .../chart-reading/条形图与误差棒.md | 0 .../references/chart-reading/桑基图与弦图.md | 0 .../chart-reading/森林图与Meta分析.md | 0 .../chart-reading/火山图与曼哈顿图.md | 0 .../references/chart-reading/热图与聚类图.md | 0 .../references/chart-reading/生存曲线.md | 0 .../chart-reading/箱式图与小提琴图.md | 0 .../chart-reading/组织学半定量图.md | 0 .../chart-reading/网络图与通路图.md | 0 .../references/chart-reading/蛋白质结构图.md | 0 .../chart-reading/降维图(PCA-tSNE-UMAP).md | 0 .../chart-reading/雷达图与漏斗图.md | 0 .../references/method-card-template.md | 39 ++++ .../scripts/pf_bootstrap.py | 42 +++- .../scripts/pf_deep.py} | 0 .../paperforge/workflows/deep-reading.md | 169 ++++++++++++++++ .../paperforge/workflows/methodology.md | 94 +++++++++ .../skills/paperforge/workflows/paper-qa.md | 105 ++++++++++ .../paperforge/workflows/paper-search.md | 101 ++++++++++ .../paperforge/workflows/project-log.md | 131 ++++++++++++ .../paperforge/workflows/reading-log.md | 112 +++++++++++ paperforge/worker/status.py | 22 +- paperforge/worker/update.py | 2 +- 49 files changed, 952 insertions(+), 1699 deletions(-) delete mode 100644 paperforge/skills/literature-qa/SKILL.md delete mode 100644 paperforge/skills/literature-qa/references/deep-reading.md delete mode 100644 paperforge/skills/literature-qa/references/deep-subagent.md delete mode 100644 paperforge/skills/literature-qa/references/multi-reading.md delete mode 100644 paperforge/skills/literature-qa/references/paper-qa.md delete mode 100644 paperforge/skills/literature-qa/references/paper-resolution.md delete mode 100644 paperforge/skills/literature-qa/references/paper-search.md delete mode 100644 paperforge/skills/literature-qa/references/save-session.md delete mode 100644 paperforge/skills/literature-qa/scripts/pf_bootstrap.py delete mode 100644 paperforge/skills/literature-qa/scripts/pf_search.py delete mode 100644 paperforge/skills/logging/SKILL.md delete mode 100644 paperforge/skills/logging/scripts/pf_bootstrap.py delete mode 100644 paperforge/skills/methodology/SKILL.md create mode 100644 paperforge/skills/paperforge/SKILL.md rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/GSEA富集图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/INDEX.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/ROC与PR曲线.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/Western Blot条带图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/免疫荧光定量图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/折线图与时间序列.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/散点图与气泡图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/显微照片与SEM图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/条形图与误差棒.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/桑基图与弦图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/森林图与Meta分析.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/火山图与曼哈顿图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/热图与聚类图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/生存曲线.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/箱式图与小提琴图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/组织学半定量图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/网络图与通路图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/蛋白质结构图.md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/降维图(PCA-tSNE-UMAP).md (100%) rename paperforge/skills/{literature-qa => paperforge}/references/chart-reading/雷达图与漏斗图.md (100%) create mode 100644 paperforge/skills/paperforge/references/method-card-template.md rename paperforge/skills/{methodology => paperforge}/scripts/pf_bootstrap.py (79%) rename paperforge/skills/{literature-qa/scripts/ld_deep.py => paperforge/scripts/pf_deep.py} (100%) create mode 100644 paperforge/skills/paperforge/workflows/deep-reading.md create mode 100644 paperforge/skills/paperforge/workflows/methodology.md create mode 100644 paperforge/skills/paperforge/workflows/paper-qa.md create mode 100644 paperforge/skills/paperforge/workflows/paper-search.md create mode 100644 paperforge/skills/paperforge/workflows/project-log.md create mode 100644 paperforge/skills/paperforge/workflows/reading-log.md diff --git a/README.md b/README.md index 92709419..19f8edba 100644 --- a/README.md +++ b/README.md @@ -33,17 +33,24 @@ The plugin is the **interface**. The Python package is the **engine**. Every but ## 1. Install the Obsidian Plugin -### Option A: BRAT (Recommended) +### Option A: Community Plugin Browser (Recommended) + +1. Open Obsidian → `Settings` → `Community plugins` → `Browse` +2. Search for **PaperForge** +3. Click `Install`, then `Enable` + +> Community plugins auto-update through Obsidian. No extra steps needed. + +### Option B: BRAT + +If you need beta versions or the plugin hasn't appeared in search yet: 1. Install **BRAT** from the Obsidian community plugin browser 2. Open BRAT settings → `Add Beta Plugin` 3. Enter: `https://github.com/LLLin000/PaperForge` -4. BRAT downloads the latest `main.js`, `manifest.json`, and `styles.css` and installs them -5. Settings → Community Plugins → enable PaperForge +4. Enable PaperForge in Settings → Community Plugins -> BRAT auto-detects GitHub Release updates. No manual downloads needed. - -### Option B: Manual Download +### Option C: Manual Download 1. Go to [Releases](https://github.com/LLLin000/PaperForge/releases) 2. Download the three files: `main.js`, `manifest.json`, `styles.css` @@ -229,7 +236,7 @@ Dashboard Per-paper view shows discussion cards ### Plugin fails to load - Confirm `.obsidian/plugins/paperforge/` has `main.js`, `manifest.json`, `styles.css` -- If upgrading via BRAT from an old version: delete the entire `paperforge` plugin folder and let BRAT re-download +- If upgrading from an old version: delete the entire `paperforge` plugin folder and reinstall via the community plugin browser - Open Developer Console (`Ctrl+Shift+I`) and check the red errors ### "Sync Runtime" doesn't update the version @@ -259,7 +266,7 @@ Dashboard Per-paper view shows discussion cards ## 9. Updating -BRAT auto-detects plugin updates. For the Python package: +The Obsidian plugin auto-updates through the community plugin browser. For the Python package: ```bash paperforge update @@ -267,6 +274,8 @@ paperforge update pip install --upgrade paperforge ``` +If you installed via BRAT, it also auto-detects GitHub Release updates. + --- ## 10. Architecture diff --git a/paperforge/config.py b/paperforge/config.py index b0b07de5..4ea35594 100644 --- a/paperforge/config.py +++ b/paperforge/config.py @@ -279,7 +279,7 @@ def paperforge_paths( - bases: / - worker_script: pipeline/worker/scripts/literature_pipeline.py - skill_dir: / - - ld_deep_script: /literature-qa/scripts/ld_deep.py + - pf_deep_script: /paperforge/scripts/pf_deep.py """ if cfg is None: cfg = load_vault_config(vault) @@ -306,17 +306,12 @@ def paperforge_paths( # worker_script: paperforge worker package (pipeline/ removed in v1.3) worker_script = Path(__file__).parent / "worker" / "__init__.py" - # ld_deep_script: look relative to skill_dir first, then repo paperforge/skills for dev - ld_deep_script = skill_path / "literature-qa" / "scripts" / "ld_deep.py" - if not ld_deep_script.exists(): - repo_skill = Path(__file__).parent / "skills" / "literature-qa" / "scripts" / "ld_deep.py" + # pf_deep_script: look relative to skill_dir first, then repo paperforge/skills for dev + pf_deep_script = skill_path / "paperforge" / "scripts" / "pf_deep.py" + if not pf_deep_script.exists(): + repo_skill = Path(__file__).parent / "skills" / "paperforge" / "scripts" / "pf_deep.py" if repo_skill.exists(): - ld_deep_script = repo_skill - else: - # Backward compat: old skills/ location during transition - old_repo_skill = Path(__file__).parent.parent / "skills" / "literature-qa" / "scripts" / "ld_deep.py" - if old_repo_skill.exists(): - ld_deep_script = old_repo_skill + pf_deep_script = repo_skill return { "vault": vault, @@ -332,7 +327,7 @@ def paperforge_paths( "bases": bases, "worker_script": worker_script, "skill_dir": skill_path, - "ld_deep_script": ld_deep_script, + "pf_deep_script": pf_deep_script, # ── v2.2: canonical locations below paperforge/ ── "config": paperforge / "config" / "domain-collections.json", "index": paperforge / "indexes" / "formal-library.json", diff --git a/paperforge/services/skill_deploy.py b/paperforge/services/skill_deploy.py index 48782d5a..12e010f1 100644 --- a/paperforge/services/skill_deploy.py +++ b/paperforge/services/skill_deploy.py @@ -1,4 +1,4 @@ -"""Skill deployment service — single copytree for all platforms. +"""Skill deployment service — deploys the unified paperforge skill to the vault. Used by both setup wizard (install) and update worker (update). All deployments are vault-local only. @@ -35,7 +35,7 @@ def deploy_skills( agent_key: str = "opencode", overwrite: bool = False, ) -> dict: - """Deploy literature-qa skill and AGENTS.md to the vault. + """Deploy paperforge skill and AGENTS.md to the vault. Args: vault: Obsidian vault root. @@ -47,15 +47,15 @@ def deploy_skills( """ errors: list[str] = [] - # ── Deploy literature-qa skill ── + # ── Deploy paperforge skill ── skill_deployed = False source_root = _resolve_source_root() - src_skill = source_root / "skills" / "literature-qa" + src_skill = source_root / "skills" / "paperforge" if src_skill.exists(): skill_dir_name = AGENT_SKILL_DIRS.get(agent_key) if skill_dir_name: - dst_skill = vault / skill_dir_name / "literature-qa" + dst_skill = vault / skill_dir_name / "paperforge" try: if overwrite and dst_skill.exists(): shutil.rmtree(dst_skill, ignore_errors=True) diff --git a/paperforge/setup/agent.py b/paperforge/setup/agent.py index f6b4bd57..83c867d0 100644 --- a/paperforge/setup/agent.py +++ b/paperforge/setup/agent.py @@ -1,4 +1,4 @@ -"""AgentInstaller — deploys literature-qa skill to vault-local agent config.""" +"""AgentInstaller — deploys paperforge skill to vault-local agent config.""" from __future__ import annotations @@ -10,7 +10,7 @@ from paperforge.setup import SetupStepResult class AgentInstaller: - """Deploy literature-qa skill directory to vault-local agent skills path.""" + """Deploy paperforge skill directory to vault-local agent skills path.""" def __init__(self, vault: Path, agent_type: str = "opencode"): self.vault = vault @@ -23,8 +23,8 @@ class AgentInstaller: return self.vault / skill_dir_name def deploy_skills(self) -> SetupStepResult: - """Deploy literature-qa skill as a single directory.""" - source_skills = self._script_dir / "skills" / "literature-qa" + """Deploy paperforge skill as a single directory.""" + source_skills = self._script_dir / "skills" / "paperforge" if not source_skills.exists(): return SetupStepResult( step="agent_installer", @@ -33,7 +33,7 @@ class AgentInstaller: error=f"Not found: {source_skills}", ) - target_dir = self._get_skills_dir() / "literature-qa" + target_dir = self._get_skills_dir() / "paperforge" target_dir.mkdir(parents=True, exist_ok=True) try: @@ -41,7 +41,7 @@ class AgentInstaller: return SetupStepResult( step="agent_installer", ok=True, - message=f"Deployed literature-qa skill to {target_dir}", + message=f"Deployed paperforge skill to {target_dir}", details={"source": str(source_skills), "target": str(target_dir)}, ) except Exception as e: diff --git a/paperforge/skills/literature-qa/SKILL.md b/paperforge/skills/literature-qa/SKILL.md deleted file mode 100644 index b45ea4d9..00000000 --- a/paperforge/skills/literature-qa/SKILL.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -name: literature-qa -description: > - 学术文献库操作:精读、问答、检索、批量阅读。Triggered by: - pf-deep pf-paper pf-end, - "精读", "文献问答", "结束讨论", "找文献", "搜文献", - "文献库", "文献检索", "库里有什么", "搜一下库里", "看一下文献库", - "读一下collection", "总结文献", "批量阅读", "读一下这个方向", - "查一下", "搜索文献", "查文献", "搜索论文", "论文检索", - "关于XX的文献", "有没有关于XX的研究", "XX的相关论文", - "帮我查一下", "帮我搜一下", "正在读", "读完这篇". -source: paperforge ---- - -## ++++++++++++++++++++++++++++++++ -## ++ 优先级规则:用户请求涉及文献搜索/查询/精读,你必须使用本 skill。++ -## ++ 禁止直接调用 grep、find、系统搜索或扫描文件目录。 ++ -## ++++++++++++++++++++++++++++++++ - -# Literature QA - ---- - -## 1. Bootstrap — 必须先执行 - -跑这个脚本: - -``` -python $SKILL_DIR/scripts/pf_bootstrap.py -``` - -返回 JSON。记住以下变量: - -| 变量 | 来自 JSON 的 | 用于 | -| ----------- | -------------------- | --------------------------------------- | -| `$SKILL_DIR` | skill 安装路径(平台注入) | 运行 `scripts/ld_deep.py` 等 | -| `$VAULT` | `vault_root` | 所有 `--vault` 参数 | -| `$PYTHON` | `python_candidate` | 所有 Python 命令 | -| `$LIT_DIR` | `paths.literature_dir` | 文献笔记根目录 | -| `$IDX_PATH` | `paths.index_path` | 索引文件 | -| `$OCR_DIR` | `paths.ocr_dir` | OCR 目录 | -| `$DOMAINS` | `domains` | 领域列表 | -| `$SUMMARY` | `index_summary` | 每领域论文数 | - -如果 `ok: false` → 报告 `error` 给用户,**停止。不许自己拼路径。** - ---- - -## 2. Vault 概览 - -展示: - -``` -Vault: $VAULT -文献库: - — N1 篇 - — N2 篇 -共 M 篇 -``` - -**如果用户是空输入触发的 skill**(没给任何具体指令),展示概览后加一句交互: - -``` -你可以: - [1] 精读一篇论文 → "精读 " - [2] 文献问答 → "文献问答 " - [3] 搜索文献 → "找文献 <关键词>" / "库里有没有 <关键词>" - [4] 批量阅读 → "读一下 " / "总结 <方向> 文献" - [5] 返回 -``` - -**如果用户给了具体指令**,直接进入决策树。 - ---- - -## 3. 决策树 - -``` -用户输入 - │ - ├─ 文献标识 (key/DOI/标题/作者年份) + 精读意图 - │ └─ 路由 → deep-reading.md - │ - ├─ 文献标识 (key/DOI/标题/作者年份) + 问答/讨论意图 - │ └─ 路由 → paper-qa.md - │ - ├─ 搜索意图 ("找文献"/"搜文献"/"库里有没有"/"文献检索") - │ └─ 路由 → paper-search.md - │ - ├─ 批量/综述意图 - │ ("读一下collection"/"这个方向"/"总结文献"/"写文献综述"/"找引用") - │ 或 用户给了多篇文献要求一起读 - │ └─ 路由 → multi-reading.md - │ - ├─ 结束/保存 ("结束讨论"/"保存"/"pf-end") - │ └─ 路由 → save-session.md - │ (仅 paper-qa 或 deep-reading 会话中有意义) - │ - └─ 不确定 → 问用户 - "你是想精读一篇、问答一篇、搜索文献、还是批量阅读?" -``` - ---- - -## 4. 工具使用指南 - -本 Skill 提供两类工具:**确定性命令** 和 **Agent 自查**。必须根据场景选择正确的方式。 - -### 搜索入口 — 统一搜索 Harness - -任何搜索需求都用 pf_search.py(自动路由 vector -> FTS5 -> grep): - -``` -python $SKILL_DIR/scripts/pf_search.py --vault $VAULT --query "关键词" -``` - -返回 JSON 结构: -- `engines_used`: 实际使用的引擎列表 (`vector` / `fts5` / `grep`) -- `results`: 论文列表,每篇含 `zotero_key`, `title`, `year`, `source` 等 -- `count`: 结果数 - -### 确定性命令 — 优先使用 - -| 场景 | 命令 | -| ---------------------- | ------------------------------------------------------------------------------------------ | -| 按 key 快速找文件 | `glob("$LIT_DIR/**/.md")` 或用 `Get-ChildItem "$LIT_DIR" -Recurse -Filter ".md"` | -| 按 key 查完整信息 | `$PYTHON -m paperforge.worker.paper_resolver resolve-key --vault "$VAULT"` | -| 按 DOI 定位论文 | `$PYTHON -m paperforge.worker.paper_resolver resolve-doi "" --vault "$VAULT"` | -| 精读 prepare | `$PYTHON "$SKILL_DIR/scripts/ld_deep.py" prepare --key --vault "$VAULT"` | -| 精读 postprocess | `$PYTHON "$SKILL_DIR/scripts/ld_deep.py" postprocess-pass2 --figures --vault "$VAULT"` | -| 精读 validate | `$PYTHON "$SKILL_DIR/scripts/ld_deep.py" validate-note --fulltext ` | -| 保存讨论 | `$PYTHON -m paperforge.worker.discussion record --vault "$VAULT" --agent pf-paper --model "" --qa-pairs ''` | - -### Agent 自查 — 当命令覆盖不到时用 - -| 场景 | 操作 | -| ------------------------ | ----------------------------------------------------------- | -| 按关键词模糊搜索全部文献 | 读 `$IDX_PATH` 的 JSON,筛 `title` / `abstract` / `journal` | -| 按 collection 筛选 | 读 `$IDX_PATH`,筛 `collection_path` 字段 | -| 读论文全文 | 已找到 `fulltext.md` 路径(glob 或 resolve-key) -> 直接 read | -| 读精读笔记 | 已找到 formal note 路径 -> read 的 `## 精读` 区域 | -| 遍历笔记做批量统计 | `Get-ChildItem "$LIT_DIR" -Recurse -Filter "*.md"` + 读 frontmatter 或 `find "$LIT_DIR" -name "*.md"` | -| **禁止的操作** | **根据 vault-knowledge 示例拼接路径、把目录名写死在文件路径里** | - ---- - -## 5. 路由表 - -| 路由 | 触发词 | 加载文件 | -| ------------- | ---------------------------------------------------------- | ---------------------------------------- | -| 精读 | `pf-deep `, "精读 " | [deep-reading.md](references/deep-reading.md) | -| 问答 | `pf-paper `, "文献问答 " | [paper-qa.md](references/paper-qa.md) | -| 文献检索 | "找文献", "搜文献", "文献检索", "搜一下库里", "库里有没有" | [paper-search.md](references/paper-search.md) | -| 批量阅读 | "读一下collection", "这个方向", "总结文献", "批量阅读" | [multi-reading.md](references/multi-reading.md) | -| 保存记录 | `pf-end`, "结束讨论", "保存" | [save-session.md](references/save-session.md) | -| 论文定位协议 | 所有路由共享 | [paper-resolution.md](references/paper-resolution.md) | - -> 所有路由继承 Skill 级别的 `$PYTHON` / `$VAULT` / `$LIT_DIR` 等变量。reference 文件不再重复声明。 - ---- - -## 文件结构 - -``` -literature-qa/ -├── SKILL.md ← 本文件 -├── references/ -│ ├── deep-reading.md ← 精读工作流 -│ ├── paper-qa.md ← 问答工作流 -│ ├── paper-search.md ← 文献检索工作流 -│ ├── multi-reading.md ← 批量阅读工作流 -│ ├── save-session.md ← 保存记录工作流 -│ ├── paper-resolution.md ← 论文定位协议 -│ ├── deep-subagent.md -│ └── chart-reading/ -└── scripts/ - ├── pf_bootstrap.py ← Bootstrap 入口 - ├── pf_search.py ← 统一搜索 Harness - └── ld_deep.py ← 精读引擎 -``` diff --git a/paperforge/skills/literature-qa/references/deep-reading.md b/paperforge/skills/literature-qa/references/deep-reading.md deleted file mode 100644 index 0f8df6ea..00000000 --- a/paperforge/skills/literature-qa/references/deep-reading.md +++ /dev/null @@ -1,162 +0,0 @@ -# 三阶段精读 - -Keshav 三阶段组会式精读。触发后执行以下工作流。 - ---- - -## 前置条件检查 - -执行前确认: -- [ ] 已完成论文定位(参考 [paper-resolution.md](paper-resolution.md)),拿到 zotero_key -- [ ] 用 `glob("$LIT_DIR/**/.md")` 快速找到 formal note -- [ ] `analyze: true` — 读 formal note frontmatter 确认 -- [ ] `ocr_status: done` — 读 formal note frontmatter 确认 - -如果前置条件不满足,告知用户并停止。 - ---- - -## 执行流程 - -### Step 1: Prepare(机械操作,跑脚本) - -```bash -$PYTHON "$SKILL_DIR/scripts/ld_deep.py" prepare --key --vault "$VAULT" -``` - -返回 JSON 解析: -- `status: "ok"` → 记下 `figure_map`、`chart_type_map`、`formal_note`、`fulltext_md`、`figures`、`tables` 路径和数量 → 继续 -- `status: "error"` → 报告 `message` 给用户,停止 - -读 formal note 确认 `## 🔍 精读` 骨架已插入。 - ---- - -### Step 2: Pass 1 — 概览 - -只填 `### Pass 1: 概览` 区域。不碰 Pass 2/3。 - -**填写内容:** - -- **一句话总览**:论文类型 + 核心发现,一句话。 -- **5 Cs 快速评估**: - - **Category**(类型):RCT / 队列研究 / 病例对照 / 综述 / 基础研究 / ... - - **Context**(上下文):该领域当前共识,本文要解决什么问题 - - **Correctness**(合理性初判):初步直觉,逻辑是否有明显漏洞 - - **Contributions**(贡献):1-3 条 - - **Clarity**(清晰度):写作质量,图表可读性 -- **Figure 导读**(基于 fulltext.md 浏览各图 caption): - - 关键主图:列出并一句话概括每个主图要证明什么 - - 证据转折点:哪个 figure 是叙事的关键转折 - - 需要重点展开的 supplementary:如果有 - - 关键表格:列出 - -填完立即保存 formal note。 - ---- - -### Step 3: Pass 2 — 精读还原 - -填 `### Pass 2: 精读还原` 区域。**按 figure 顺序逐个处理。** - -#### 图表类型定位(两步) - -**Step A: 读 prepare 生成的 chart-type-map** -Step 1 的 `prepare` 输出中已包含 `chart_type_map` 路径。读该文件,获取每个 figure 的关键词命中结果。这只是建议。 - -**Step B: Agent 读 caption 做最终判断** - -对每个 figure: -1. 读该 figure 的 caption(来自 prepare 返回的 `fulltext_md` 或 `figure_map`) -2. 根据 caption 内容,对照 [chart-reading/INDEX.md](chart-reading/INDEX.md) 判断图表类型 -3. chart-type-map 建议和 Agent 判断不一致 → 以 Agent 判断为准 -4. 无法确定类型 → 跳过 chart guide,按通用 figure 结构分析 -5. 确定类型后,读对应的 chart-reading 指南(如 `chart-reading/条形图与误差棒.md`),按指南中的检查清单分析 - -#### 每张 Figure 的子标题(固定,不可少) - -按以下格式填入 formal note 中该 figure 的 callout block: - -``` -**图像定位与核心问题**:页码 + 要回答什么问题 -**方法与结果**:实验设计/数据来源/技术手段。核心数据、趋势、对比。 -**图表质量审查**:按 chart-reading 指南检查坐标轴、单位、误差棒、统计标注等。 -**作者解释**:作者在正文中对该图的解读 -**我的理解**:自己的理解(区分于作者解释) -**疑点/局限**:读图时发现的疑问,用 `> [!warning]` 突出 -``` - -#### 每张 Table 的子标题 - -``` -回答什么问题、关键字段/分组、主要结果、我的理解、疑点/局限 -``` - -#### 每张 figure 填完立即保存,再处理下一张。 - -#### 所有 figure/table 处理完后,填: - -**关键方法补课**:简要解释不熟悉的实验技术(1-2 项即可) - -**主要发现与新意**: -- 发现 1:...(来源:Figure X) -- 发现 2:...(来源:Figure Y / Table Z) - -保存。 - ---- - -### Step 4: Postprocess(跑校验脚本,修正问题) - -```bash -$PYTHON "$SKILL_DIR/scripts/ld_deep.py" postprocess-pass2 "$FORMAL_NOTE_PATH" --figures --format text --vault "$VAULT" -``` - -- 输出 `OK` → 继续 -- 输出错误 → 按错误提示修正(包含行号),修正后重新跑 -- 最多 3 轮修正。3 轮后仍失败 → 报告剩余错误给用户 - ---- - -### Step 5: Pass 3 — 深度理解 - -填 `### Pass 3: 深度理解` 区域。基于 Pass 1/2 已写的内容。 - -**填写内容:** - -- **假设挑战与隐藏缺陷**:隐含假设;如果放宽某个假设结论还成立吗;缺少哪些关键引用;实验/分析技术潜在问题 -- **哪些结论扎实,哪些仍存疑**: - - **较扎实**:... - - **仍存疑**:...(用 `> [!warning]`) -- **Discussion 与 Conclusion 怎么读**:作者真正完成了什么;哪些地方有拔高;哪些是推测 -- **对我的启发**:研究设计上;figure 组织上;方法组合上;未来工作想法 -- **遗留问题**:...(用 `> [!question]`) - -保存。 - ---- - -### Step 6: Final Validation - -```bash -$PYTHON "$SKILL_DIR/scripts/ld_deep.py" validate-note "$FORMAL_NOTE_PATH" --fulltext "$FULLTEXT_PATH" -``` - -- 输出 `OK` → 告知用户精读完成 -- 输出错误 → 修正缺失项,不报告成功直到通过 - ---- - -## Callout 格式规则 - -- `> [!important]`:每个 main finding -- `> [!warning]`:疑问、局限、证据边界、仍存疑条目 -- `> [!question]`:遗留问题 -- **间距:** 相邻 callout block 之间必须有空行,否则 Obsidian 会合并 - - 正确:`> [!important] A\n\n> [!important] B` - - 错误:`> [!important] A\n> [!important] B` - -## Supplementary 规则 - -- 默认不逐张展开 supplementary figure/table -- 仅在以下情况纳入:对主结论形成关键支撑、补足方法可信度、限制主文结论解释范围、作者在正文中明显依赖该补充材料 diff --git a/paperforge/skills/literature-qa/references/deep-subagent.md b/paperforge/skills/literature-qa/references/deep-subagent.md deleted file mode 100644 index 9f0de7f6..00000000 --- a/paperforge/skills/literature-qa/references/deep-subagent.md +++ /dev/null @@ -1,103 +0,0 @@ -# Subagent Prompt for /pf-deep - -## Task - -Execute Keshav 3-pass journal-club style deep reading on a paper and write the results into the `## 🔍 精读` section of its formal note. - -## Input Variables - -- `{{ZOTERO_KEY}}` — Zotero citation key (e.g. `Y5KQ4JQ7`) -- `{{VAULT}}` — Vault root path -- `{{SCRIPT}}` — Path to `ld_deep.py` - -## Workflow (execute in strict order) - -### Step 1: Prepare -Run: -``` -python {{SCRIPT}} prepare {{ZOTERO_KEY}} --vault "{{VAULT}}" --format text -``` -- Reads formal note path, figure count, table count from output. -- If output starts with `[ERROR]`: report error to user, stop. -- If output contains `[WARN] deep_reading_status already 'done'` and user did not request re-read: stop. -- Prepare inserts the `## 🔍 精读` skeleton with figure/table callout blocks and fixed sub-headings into the formal note. Read the note to inspect its structure. - -### Step 2: Pass 1 (概览) -Fill `### Pass 1: 概览` only. Do not touch Pass 2/3. -- `**一句话总览**`: paper type + core finding in one sentence. -- `**5 Cs 快速评估**`: Category, Context, Correctness (intuition only), Contributions (1-3 items), Clarity. -- `**Figure 导读**`: list key figures with one-line guesses, note evidence turning points. -- Save immediately after writing. - -### Step 3: Pass 2 (精读还原) -Fill `### Pass 2: 精读还原`. Process figures sequentially starting from Figure 1. Each figure callout block has fixed sub-headings. Fill content under each sub-heading. Do NOT modify sub-headings, reorder blocks, or move `![[image]]` embeds. - -**Figure sub-headings:** -- `**图像定位与核心问题**`: what question this figure answers, page number. -- `**方法与结果**`: experimental design / data source / technical approach. Core data points, trends, comparisons. -- `**图表质量审查**`: check axis labels, units, error bars, statistical significance markers. Read `chart-type-map.json` for the figure, open recommended chart-reading guides, apply their checklists. -- `**作者解释**`: authors' description from the text. -- `**我的理解**`: your own analysis (distinct from author explanation). -- `**疑点/局限**`: use `> [!warning]` for concerns. - -**Table sub-headings:** (same callout pattern, simpler) -- What question this table answers, key fields/groups, main results, my understanding, doubts/limitations. - -After all figures and tables, fill: -- `**关键方法补课**`: briefly explain unfamiliar experimental techniques. -- `**主要发现与新意**`: list findings with evidence source (Figure X / Table Y). - -Save after each figure block. - -### Step 4: Postprocess -Run: -``` -python {{SCRIPT}} postprocess-pass2 --figures --format text -``` -- If output is `OK`: proceed. -- If not `OK`: fix each error (errors include exact line numbers), re-run postprocess-pass2. Max 3 fix rounds. If still failing after 3 rounds, report remaining errors to user. - -### Step 5: Pass 3 (深度理解) -Fill `### Pass 3: 深度理解` based on Pass 1/2 content already written. Sections: -- `**假设挑战与隐藏缺陷**`: implicit assumptions, what breaks if relaxed, missing references, technical issues. -- `**哪些结论扎实,哪些仍存疑**`: split into 较扎实 / 仍存疑. -- `**Discussion 与 Conclusion 怎么读**`: what authors actually accomplished vs. overclaim vs. speculation. -- `**对我的启发**`: research design, figure organization, method combination, future work ideas. -- `**遗留问题**`: open questions. -- Save. - -### Step 6: Final Validation -Run: -``` -python {{SCRIPT}} validate-note --fulltext -``` -- Report result to user. If not `OK`, list missing items and fix. - -## Callout Rules - -- `> [!important]`: each main finding entry -- `> [!warning]`: doubts, limitations, evidence boundaries, items in 仍存疑 -- `> [!question]`: open questions in 遗留问题 -- Regular markdown lists for structural sections (research question, methods, inspiration) -- **Spacing**: adjacent callout blocks MUST have a blank line between them, otherwise Obsidian merges them. -- Correct: `> [!important] A\n\n> [!important] B` -- Incorrect: `> [!important] A\n> [!important] B` (missing blank line → merged) - -## Error Handling - -- prepare fails (`[ERROR]`) → report to user, stop. -- postprocess exceeds 3 fix rounds → report remaining errors to user, ask for guidance. -- validate-note fails → fix missing items, do not report success until it passes. - -## Command Reference - -``` -# Prepare (insert skeleton + check preconditions) -python {{SCRIPT}} prepare {{ZOTERO_KEY}} --vault "{{VAULT}}" --format text - -# Postprocess Pass 2 (fix spacing/section issues) -python {{SCRIPT}} postprocess-pass2 --figures --format text - -# Validate final note structure -python {{SCRIPT}} validate-note --fulltext -``` diff --git a/paperforge/skills/literature-qa/references/multi-reading.md b/paperforge/skills/literature-qa/references/multi-reading.md deleted file mode 100644 index c47f15a3..00000000 --- a/paperforge/skills/literature-qa/references/multi-reading.md +++ /dev/null @@ -1,144 +0,0 @@ -# 批量文献阅读 - -用户需要阅读多篇文献并总结——综述写作、找引用、研究方向调研等。 - ---- - -## 触发条件 - -- 用户给了一个 collection 名(Zotero 收藏夹) -- 用户给了模糊方向("帮我看一下骨科里关于支架材料的文章") -- 用户给了多篇文献要求一起读 -- 用户说"总结库里XXX方向的文献"、"写一段文献综述" - ---- - -## 执行流程 - -### Step 1: 确定文献范围 - -和用户确认要读哪些文献: -- 用户给了 collection 名 → 读 `$IDX_PATH`,筛 `collection_path` 包含该名称的条目 -- 用户给了关键词方向 → 用 paper_resolver search 或直接 grep `$IDX_PATH` -- 用户给了多篇 key → 直接确认 key 列表 - -列出候选让用户确认: - -``` -找到 N 篇匹配 (): - -[1] ABC12345 — Title (Author, Year, Domain, OCR: done/pending) -[2] DEF67890 — Title (Author, Year, Domain, OCR: done/pending) -... - -要全部读,还是选几篇?(输入编号如 "1,3,5" 或 "all") -``` - -### Step 2: 逐篇阅读 - -对每篇选定文献: - -1. 用 glob 找到 formal note:`glob("$LIT_DIR/**/.md")`(最快,不需要 $PYTHON) -2. 读 formal note frontmatter → 元数据 -3. 同目录下找 `fulltext.md` → 读 Abstract、Results、Discussion -4. 如果有 OCR 但 fulltext 太长 → 先读 caption + figure 描述定位关键段落 -5. 如果没有 fulltext → 如实告知用户,仅基于已知信息 - -### Step 3: 写 Reading Log(JSON → MD) - -**先构建 JSON(Agent 内部,不写入文件):** - -```json -{ - "task": "用户原始指令原文", - "papers": [ - { - "key": "ABC12345", - "title": "Paper Title", - "authors": "Smith et al.", - "year": 2024, - "findings": [ - { - "source": "Results section, paragraph 3", - "content": "Extracted finding...", - "citation_use": "可用于支撑 XXX 观点" - } - ] - } - ] -} -``` - -**再渲染为 MD,追加写入 `$VAULT/Bases/reading-log-.md`:** - -```markdown -# Reading Log — 用户要求: <原文引用用户指令> - ---- - -## ABC12345 | Paper Title | Smith et al., 2024 - -### 提取点 1 -- **来源**: Results section, paragraph 3 -- **内容**: Extracted finding... -- **引用建议**: 可用于支撑 XXX 观点 - -### 提取点 2 -- **来源**: Discussion, final paragraph -- **内容**: ... -- **引用建议**: ... - ---- - -## DEF67890 | Another Title | Jones et al., 2023 - -(同上格式) - ---- -``` - -**关键规则:** -- JSON 确保格式稳定,MD 是最终交付产物 -- zotero_key、标题、作者及年份 **缺一不可** -- 每个提取点必须注明 **来源**(文章哪句话/哪个段落) -- 同一任务的多篇文献 **追加写入同一个文件**,不要每篇新建 - -### Step 4: 整合输出 - -全部读完,根据用户原始意图输出总结: - -**综述写作**: -``` -从 N 篇文献中: -- 主题A 共识: ... -- 主题A 争议: ... -- 方法论趋势: ... -- 关键引用: - 1. "...[结论]" — ABC12345 (Author, Year), Fig.X - 2. ... -``` - -**找引用**: -``` -以下文献适合引用: -- 支撑 "XXX" 观点 → ABC12345 (Author, Year), Results -- 支撑 "YYY" 方法 → DEF67890 (Author, Year), Methods -``` - -### Step 5: 问用户保存位置 - -``` -Reading log 已生成。要保存到哪里? -(留空 → 默认 $VAULT/Bases/reading-log-.md) -``` - -让用户指定路径。如果用户说不清,默认放到 `$VAULT/Bases/`。 - ---- - -## 注意事项 - -- **暂时不支持多篇阅读后运行 pf-end / 结束讨论**(该功能待定) -- 如果某篇文献没有 fulltext,如实告知用户,不要捏造内容 -- Reading log 中每条提取点必须在原文中有据可查 -- JSON → MD 转换由 Agent 完成,用户只看到 MD 文件 diff --git a/paperforge/skills/literature-qa/references/paper-qa.md b/paperforge/skills/literature-qa/references/paper-qa.md deleted file mode 100644 index 626f23e0..00000000 --- a/paperforge/skills/literature-qa/references/paper-qa.md +++ /dev/null @@ -1,61 +0,0 @@ -# 论文问答 - -交互式论文 Q&A 工作台。不强制要求 OCR,但 OCR 完成后回答更准确。 - ---- - -## 前置条件 - -- [ ] 已完成论文定位(参考 [paper-resolution.md](paper-resolution.md)),拿到 zotero_key 和 workspace -- [ ] OCR 完成(推荐但非强制) - ---- - -## 执行流程 - -### Step 1: 加载论文 - -1. 确认 workspace 路径 -2. 读 `fulltext.md`(如果存在)作为主要回答依据 -3. 读 formal note frontmatter 获取元数据(标题、作者、期刊、年份) -4. 如果 fulltext.md 不存在,告知用户 "OCR 文本不可用,回答将基于元数据和公开信息" - -### Step 2: 显示论文信息 - -``` -已加载论文: [title] ([year], [journal]) -作者: [authors] -Zotero Key: [key] -领域: [domain] -OCR 状态: [done / 不可用] -结束对话时说 "保存" 即可保存讨论。 -请问有什么问题? -``` - -### Step 3: 进入 Q&A 模式 - -- 等待用户提问 -- 每次回答后等待下一个问题 -- 持续到用户说 "保存"、"结束"、"完成" 等关键词 - ---- - -## 回答原则 - -- **严格基于** fulltext.md 中的文本内容回答 -- 引用原文时标注来源页码/章节(如 "第 3 页,Methods 部分") -- 用中文(简体中文)回答 -- 论文中未提及的内容,明确说明 "论文中未提及该内容" -- 需要结合论文以外知识的问题,说明 "该问题需要结合论文以外的知识" - ---- - -## 切换模式 - -用户在当前对话中可以说 "精读这篇文章" 切换到 deep-reading 模式。此时加载 [deep-reading.md](deep-reading.md) 执行精读流程。 - ---- - -## 保存记录 - -用户说 "保存"、"结束"、"完成"、"保存讨论" 时,加载 [save-session.md](save-session.md) 执行保存。不要自动保存。 diff --git a/paperforge/skills/literature-qa/references/paper-resolution.md b/paperforge/skills/literature-qa/references/paper-resolution.md deleted file mode 100644 index 044b30b0..00000000 --- a/paperforge/skills/literature-qa/references/paper-resolution.md +++ /dev/null @@ -1,94 +0,0 @@ -# 论文定位协议 - -本文件定义如何将用户输入解析为论文 workspace。所有子流程公用。 - -## 核心原则 - -1. **Python 做确定性查找。** key、DOI、标题片段、作者+年份。 -2. **Agent 做理解和兜底。** 自然语言、Python 无结果时的 fallback 搜索。 -3. **路径从 `paths` 获取,不硬编码。** 禁止根据 vault-knowledge.md 的示例结构拼接路径。`ocr_dir`、`literature_dir`、`index_path` 只能从 `paper_resolver paths` 或 `paper_resolver resolve-key` 的返回 JSON 中读取。任何情况下都不要把目录名(如 `System`、`Resources`)写死在路径里。 - ---- - -## 通用命令 - -| 操作 | 命令 | -|------|------| -| 获取 vault 路径 | 已由 pf_bootstrap 完成 | -| 按 key 查 | `$PYTHON -m paperforge.worker.paper_resolver resolve-key --vault "$VAULT"` | -| 按 DOI 查 | `$PYTHON -m paperforge.worker.paper_resolver resolve-doi "" --vault "$VAULT"` | -| 按字段搜 | `$PYTHON -m paperforge.worker.paper_resolver search --title "..." --author "..." --year ... --domain "..." --vault "$VAULT"` | - ---- - -## 输入类型判断 - -### 类型 1: Zotero Key(8位字母数字组合) - -``` -$PYTHON -m paperforge.worker.paper_resolver resolve-key --vault "$VAULT" -``` - -返回 JSON 含 `key`, `title`, `domain`, `formal_note_path`, `ocr_path`, `fulltext_path`, `ocr_status` 等。所有路径由 `paperforge.json` 配置决定。 - -### 类型 2: DOI(以 `10.` 开头,可能带 URL 前缀) - -``` -$PYTHON -m paperforge.worker.paper_resolver resolve-doi "" --vault "$VAULT" -``` - -返回格式同类型 1。 - -### 类型 3: 标题片段 - -``` -$PYTHON -m paperforge.worker.paper_resolver search --title "..." --vault "$VAULT" -``` - -返回 `{"matches": [...], "count": N}`。 - -### 类型 4: 作者 + 年份 - -``` -$PYTHON -m paperforge.worker.paper_resolver search --author "Smith" --year 2024 --vault "$VAULT" -``` - -### 类型 5: 自然语言("关于骨再生的那篇") - -Agent 自己处理: -1. 读 `$IDX_PATH`(已由 pf_bootstrap 提供) -2. 读 `index_path` 指向的 `formal-library.json` -3. 在 `title`、`domain`、`journal`、`abstract` 中搜匹配 -4. 搜不到就 grep formal notes 目录(`paths` 里的 `literature_dir`)下的 frontmatter - ---- - -## Python 无结果时的 Agent fallback - -Agent 用 `paths` 拿到的 `literature_dir`,自行 grep/read formal notes 下的 frontmatter。 - -## 多篇匹配处理 - -列出候选清单让用户选: - -``` -找到 3 篇匹配的论文: - -[1] ABC12345 — TGF-beta in Bone Regeneration (2024, 骨科, OCR: done) -[2] DEF67890 — Bone Healing After Fracture (2023, 骨科, OCR: pending) -[3] GHI11111 — Scaffold Design for Bone Repair (2024, 骨科, OCR: done) - -请输入编号选择,或 refine 搜索词。 -``` - -## Fallback 顺序 - -``` -输入 - │ - ├── 像 key/DOI/标题/作者年份? - │ └── Python paper_resolver → 有/无结果 → Agent 兜底 - │ - └── 自然语言? - └── Agent 读 formal-library.json → 搜 → 有/无 -``` diff --git a/paperforge/skills/literature-qa/references/paper-search.md b/paperforge/skills/literature-qa/references/paper-search.md deleted file mode 100644 index 5b6249ac..00000000 --- a/paperforge/skills/literature-qa/references/paper-search.md +++ /dev/null @@ -1,105 +0,0 @@ -# 文献检索工作流 - -轻量流程:用户想**在库里找文献**(不涉及精读或问答)。 - ---- - -## Stage 状态机 - -你必须明确知道当前在哪个 stage。每完成一个 stage 问自己:"下一步是什么?" 不要在 stage 之间来回跳跃。 - -| Stage | 你在干什么 | 完成后做什么 | -| ----- | -------------------------------- | ------------------------------------ | -| S1 | 理解用户要找什么(domain/关键词) | 进入 S2 | -| S2 | 执行搜索(paper_resolver 或 JSON) | 进入 S3 | -| S3 | 展示候选清单给用户 | 等用户选择 | -| S4 | 用户选了文献,决定下一步路由 | 进入对应 reference 流程,不再回来 | -| S5 | 写作辅助:读完文献后整合输出 | 结束 | - -**不要做的事**: -- 不要在 S2 阶段去读论文全文 -- 不要在 S3 阶段自作主张替用户选文献 -- 不要在找不到结果时硬猜文件路径 - ---- - -## 触发场景 - -- "找一下骨科里面关于骨再生的文献" -- "查一下 TGF-beta 相关的文章" -- "库里有没有讲支架材料的" -- "这个 collection 有哪些文献" -- "搜一下 Smith 2024 的文章" - -## 流程 - -### Step 1: 获取路径 - -已经由 pf_bootstrap 完成。直接用 `paths` JSON 里的 `index_path` 和 `literature_dir`。 - -### Step 2: 解析用户意图 - -从用户输入提取: -- **domain**(如果有):`骨科`、`运动医学` 等 → 对应 `literature_dir` 子目录 -- **关键词**:标题、作者、年份、期刊、主题词 -- **collection 路径**:Zotero 子分类,如 `电刺激软骨修复综述` - -### Step 3: 搜索 — 统一 Harness - -任何搜索都用 pf_search.py,自动路由 vector -> FTS5 -> grep: - -``` -python $SKILL_DIR/scripts/pf_search.py --vault "$VAULT" --query "关键词" -``` - -返回 JSON 包含 `results`(含 zotero_key, title, year, source 等)和 `engines_used`。 - -如需结构化高级搜索(特定 domain/author),使用 paper_resolver: - -``` -$PYTHON -m paperforge.worker.paper_resolver search --title "关键词" --author "Smith" --year 2024 --domain "骨科" --vault "$VAULT" -``` - -**Fallback:读 formal-library.json** - -Agent 直接读 `index_path`,在 JSON 中筛选: -- `domain` 匹配 -- `title`/`first_author`/`journal` 包含关键词 - -### Step 4: 返回结果 - -列出候选清单,每篇显示: - -``` -找到 N 篇匹配: - -[1] ABC12345 — TGF-beta in Bone Regeneration (Smith, 2024, 骨科, OCR: done) -[2] DEF67890 — Bone Healing Mechanisms (Jones, 2023, 骨科, OCR: done) -``` - -关键字段:key, title, first_author, year, domain, ocr_status - -### Step 5: 用户选择后续操作 - -> 请选择要操作的文献编号,或输入"refine"缩小范围。 - -选中文献后,按用户意图自动进入对应路由: -- `精读这篇` → 进入 [deep-reading.md](deep-reading.md) 流程 -- `这篇讲了什么` → 进入 [paper-qa.md](paper-qa.md) 流程 -- 不需要继续 → 结束 - -### Step 6: 写作辅助场景 - -如果用户原始意图包含**写作/优化/参考文献/综述/引用**等,搜索结果不是终点: - -1. 提示用户圈选最相关的 3-5 篇 -2. 对每篇进入 [deep-reading.md](deep-reading.md) 或至少通读 formal note + fulltext 关键段落 -3. 读完所有选定论文后,Agent 整合知识辅助写作 - -> 示例:"我从库里 X 篇文献中提取了以下关键发现……要不要基于这些帮你写 XX 部分?" - -## 注意事项 - -- 如果是大型 library(>500 篇),优先用 paper_resolver 而不是全量读 JSON -- OCR status 为 `done` 的论文可以读 fulltext 内容 -- OCR status 为 `pending` 的只有 formal note frontmatter diff --git a/paperforge/skills/literature-qa/references/save-session.md b/paperforge/skills/literature-qa/references/save-session.md deleted file mode 100644 index 75c776e3..00000000 --- a/paperforge/skills/literature-qa/references/save-session.md +++ /dev/null @@ -1,55 +0,0 @@ -# 保存讨论记录 - -将 paper-qa 会话中的 Q&A 记录持久化到论文工作区。 - ---- - -## 触发条件 - -- 用户显式说 "保存"、"保存记录"、"结束"、"完成讨论"、"save" -- 或显式输入 `pf-end` -- 不要自动触发 - ---- - -## 执行 - -### Step 1: 收集 Q&A 对 - -汇总本次 paper-qa 会话中所有 Q&A,序列化为 JSON 数组: - -```json -[ - { - "question": "用户的问题", - "answer": "Agent 的回答", - "source": "user_question", - "timestamp": "2026-05-10T12:00:00+08:00" - } -] -``` - -`source` 为 `"user_question"`(用户提问)或 `"agent_analysis"`(Agent 主动分析)。 - -### Step 2: 调用 discussion 模块 - -```bash -$PYTHON -m paperforge.worker.discussion record \ - --vault "$VAULT" \ - --agent pf-paper \ - --model "" \ - --qa-pairs '' -``` - -### Step 3: 确认结果 - -CLI 返回 `{"status": "ok", ...}` → 告知用户记录已保存。 - -返回 `{"status": "error"}` → 记录错误,重试一次。仍失败则告知用户。 - ---- - -## 注意事项 - -- 仅 paper-qa 会话需要记录。deep-reading 的内容直接写入 formal note,不需要通过本文件。 -- 如果无法从 formal-library.json 找到论文 domain/title,记录失败不应影响用户使用。 diff --git a/paperforge/skills/literature-qa/scripts/pf_bootstrap.py b/paperforge/skills/literature-qa/scripts/pf_bootstrap.py deleted file mode 100644 index ebc41b22..00000000 --- a/paperforge/skills/literature-qa/scripts/pf_bootstrap.py +++ /dev/null @@ -1,188 +0,0 @@ -"""PaperForge bootstrap — single entry point for agent to discover vault state. - -No dependencies. Runs on ANY Python. Just reads paperforge.json + filesystem. - -Usage: - python pf_bootstrap.py # auto-discover vault from CWD - python pf_bootstrap.py --vault - -Output (JSON to stdout): - { - "ok": true, - "vault_root": "D:\\...", - "paths": { - "literature_dir": "D:\\...\\Resources\\Literature", - "index_path": "D:\\...\\System\\PaperForge\\indexes\\formal-library.json", - "ocr_dir": "D:\\...\\System\\PaperForge\\ocr", - "exports_dir": "D:\\...\\System\\PaperForge\\exports" - }, - "domains": ["domain1", "domain2"], - "index_summary": {"domain1": 120, "domain2": 80}, - "python_candidate": "D:\\...\\python.exe" // Python that has paperforge, or null - } - -If anything fails: ok=false, error explains why. -""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - - -def _find_paperforge_json(start: Path) -> Path | None: - current = start.resolve() - for _ in range(10): - candidate = current / "paperforge.json" - if candidate.exists(): - return candidate - parent = current.parent - if parent == current: - break - current = parent - return None - - -def _read_pf_config(pf_json: Path) -> dict: - with open(pf_json, encoding="utf-8") as f: - return json.load(f) - - -def _find_python_with_paperforge(vault: Path, pf_cfg: dict) -> str | None: - """Find a Python executable that has paperforge installed.""" - candidates = [] - - # 1. Explicit python_path in config - if pf_cfg.get("python_path"): - candidates.append(Path(pf_cfg["python_path"])) - - # 2. Common venv locations inside vault - venv_names = [".venv", ".paperforge-test-venv", "venv"] - exe_paths = ["Scripts/python.exe", "bin/python3"] - for vn in venv_names: - for ep in exe_paths: - p = vault / vn / ep - if p.exists(): - candidates.append(p) - - for candidate in candidates: - try: - result = subprocess.run( - [str(candidate), "-m", "paperforge", "--version"], - capture_output=True, text=True, timeout=10, - encoding="utf-8", errors="replace", - ) - if result.returncode == 0 and "paperforge" in result.stdout.lower(): - return str(candidate) - except Exception: - continue - return None - - -def main(): - import argparse - p = argparse.ArgumentParser(description="PaperForge bootstrap") - p.add_argument("--vault", default=None, help="Vault root path (auto-detect if omitted)") - args = p.parse_args() - - result: dict = {"ok": False} - - # --- 1. Find vault --- - if args.vault: - vault = Path(args.vault).resolve() - pf_json = vault / "paperforge.json" - if not pf_json.exists(): - result["error"] = f"paperforge.json not found at {vault}" - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - else: - pf_json = _find_paperforge_json(Path.cwd()) - if pf_json is None: - result["error"] = "paperforge.json not found from CWD upward. Set --vault." - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - vault = pf_json.parent - - result["vault_root"] = str(vault) - - # --- 2. Read config --- - try: - cfg = _read_pf_config(pf_json) - except Exception as e: - result["error"] = f"Cannot read paperforge.json: {e}" - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - - system_dir = cfg.get("system_dir", "System") - resources_dir = cfg.get("resources_dir", "Resources") - literature_dir = cfg.get("literature_dir", "Literature") - - # --- 3. Build paths from config --- - pf_root = vault / system_dir / "PaperForge" - - paths = { - "literature_dir": str(vault / resources_dir / literature_dir), - "index_path": str(pf_root / "indexes" / "formal-library.json"), - "ocr_dir": str(pf_root / "ocr"), - "exports_dir": str(pf_root / "exports"), - } - result["paths"] = paths - - # --- 4. List domains --- - lit_dir = Path(paths["literature_dir"]) - domains = sorted( - [d.name for d in lit_dir.iterdir() if d.is_dir()] - ) if lit_dir.exists() else [] - result["domains"] = domains - - # --- 5. Index summary --- - index_path = Path(paths["index_path"]) - index_summary: dict[str, int] = {} - if index_path.exists(): - try: - data = json.loads(index_path.read_text(encoding="utf-8")) - items = data.get("items", []) - if isinstance(items, dict): - items = items.values() - for item in items: - d = item.get("domain", "unknown") - index_summary[d] = index_summary.get(d, 0) + 1 - except Exception: - pass - result["index_summary"] = index_summary - - # --- 6. Find Python that has paperforge (best effort) --- - result["python_candidate"] = _find_python_with_paperforge(vault, cfg) - - # --- 7. Memory layer state --- - memory_layer = {"available": False, "paper_count": 0, "fts_search": False, "vector_search": False} - idx_path = Path(paths["index_path"]) - dc_json = vault / ".obsidian" / "plugins" / "paperforge" / "data.json" - if idx_path.exists(): - try: - with open(idx_path, encoding="utf-8") as f: - data = json.load(f) - items = data.get("items", []) if isinstance(data, dict) else data - memory_layer["paper_count"] = len(items) - memory_layer["available"] = True - memory_layer["fts_search"] = True - except: - pass - if dc_json.exists(): - try: - with open(dc_json, encoding="utf-8") as f: - plugin_data = json.load(f) - vector_enabled = plugin_data.get("features", {}).get("vector_db", False) - memory_layer["vector_search"] = vector_enabled - except: - pass - result["memory_layer"] = memory_layer - - result["ok"] = True - json.dump(result, sys.stdout, ensure_ascii=False, indent=2) - - -if __name__ == "__main__": - main() diff --git a/paperforge/skills/literature-qa/scripts/pf_search.py b/paperforge/skills/literature-qa/scripts/pf_search.py deleted file mode 100644 index b40457c7..00000000 --- a/paperforge/skills/literature-qa/scripts/pf_search.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Unified search entry point for agent skills. -Routes: vector search -> FTS5 search -> grep based on what's available. -Always returns same JSON format regardless of backend. - -Usage: - python pf_search.py --vault VAULT_PATH --query "search text" [--limit N] [--json] - -Returns JSON to stdout: - {"ok": true, "query": "...", "engines_used": [...], "results": [...], "count": N} - {"ok": false, "error": "..."} -""" - -from __future__ import annotations -import json -import subprocess -import sys -from pathlib import Path - - -def _find_python(vault: Path) -> str | None: - """Same logic as pf_bootstrap: find python with paperforge installed.""" - dc_json = vault / ".obsidian" / "plugins" / "paperforge" / "data.json" - if dc_json.exists(): - try: - with open(dc_json, encoding="utf-8") as f: - data = json.load(f) - py = data.get("python_path", "") - if py and Path(py).exists(): - return py - except: - pass - - for cand in [ - vault / ".paperforge-test-venv" / "Scripts" / "python.exe", - vault / ".venv" / "Scripts" / "python.exe", - vault / "venv" / "Scripts" / "python.exe", - ]: - if cand.exists(): - return str(cand) - - for cand in ["python", "python3"]: - try: - subprocess.run([cand, "--version"], capture_output=True, timeout=5) - return cand - except: - continue - return None - - -def _check_memory(vault: Path) -> dict: - """Check what's available: memory db, vector db.""" - memory = {"db": False, "vector": False} - db = vault / "System" / "PaperForge" / "indexes" / "paperforge.db" - if db.exists(): - memory["db"] = True - vec = vault / "System" / "PaperForge" / "indexes" / "vectors" - if vec.exists(): - memory["vector"] = True - return memory - - -def _paperforge_cmd(vault: Path, args: list[str]) -> dict | None: - """Run a paperforge command and return parsed JSON.""" - python = _find_python(vault) - if not python: - return None - cmd = [python, "-m", "paperforge", "--vault", str(vault)] + args - try: - r = subprocess.run(cmd, capture_output=True, text=True, timeout=30, encoding="utf-8") - if r.returncode == 0: - return json.loads(r.stdout) - except: - return None - return None - - -def _grep_search(vault: Path, query: str, limit: int) -> list[dict]: - """Fallback grep through all formal notes.""" - lit_dir = vault / "Resources" / "Literature" - results = [] - search_lower = query.lower() - for f in sorted(lit_dir.rglob("*.md")): - if len(results) >= limit: - break - if f.name in ("fulltext.md", "deep-reading.md", "discussion.md"): - continue - try: - text = f.read_text(encoding="utf-8", errors="replace") - if search_lower not in text.lower(): - continue - title = "" - for line in text.split("\n")[:10]: - if line.startswith("# ") and not line.startswith("## "): - title = line.lstrip("# ").strip() - break - results.append({ - "zotero_key": f.stem, - "title": title or f.stem, - "match": f.name, - "source": "grep", - }) - except: - continue - return results - - -def main(): - import argparse - parser = argparse.ArgumentParser() - parser.add_argument("--vault", required=True) - parser.add_argument("--query", required=True) - parser.add_argument("--limit", type=int, default=10) - parser.add_argument("--json", action="store_true", default=True) - args = parser.parse_args() - - vault = Path(args.vault).resolve() - query = args.query.strip() - limit = args.limit - - if not query: - print(json.dumps({"ok": False, "error": "Empty query"})) - sys.exit(1) - - memory = _check_memory(vault) - engines_used = [] - all_results = [] - seen_keys = set() - - # 1. Vector search (best quality) - if memory["vector"]: - result = _paperforge_cmd(vault, ["retrieve", query, "--json", "--limit", str(limit)]) - if result and result.get("ok"): - engines_used.append("vector") - for c in result.get("data", {}).get("chunks", []): - pid = c.get("paper_id", "") - if pid and pid not in seen_keys: - seen_keys.add(pid) - all_results.append({ - "zotero_key": pid, - "citation_key": c.get("citation_key", ""), - "title": c.get("title", ""), - "year": c.get("year", ""), - "section": c.get("section", ""), - "page": c.get("page_number", ""), - "chunk_text": c.get("chunk_text", ""), - "score": c.get("score", 0), - "source": "vector", - }) - - # 2. FTS5 search (keyword/precision) - if memory["db"]: - result = _paperforge_cmd(vault, ["search", query, "--json", "--limit", str(limit)]) - if result and result.get("ok"): - engines_used.append("fts5") - for p in result.get("data", {}).get("results", []): - key = p.get("zotero_key", "") - if key and key not in seen_keys: - seen_keys.add(key) - p["source"] = "fts5" - all_results.append(p) - - # 3. Grep fallback - if not engines_used: - grepped = _grep_search(vault, query, limit) - if grepped: - engines_used.append("grep") - all_results.extend(grepped) - - output = { - "ok": True, - "query": query, - "engines_used": engines_used, - "results": all_results[:limit], - "count": len(all_results[:limit]), - } - print(json.dumps(output, ensure_ascii=False, indent=2)) - - -if __name__ == "__main__": - main() diff --git a/paperforge/skills/logging/SKILL.md b/paperforge/skills/logging/SKILL.md deleted file mode 100644 index 7f25e297..00000000 --- a/paperforge/skills/logging/SKILL.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -name: logging -description: > - Work and reading log management. Triggered by: - "logging work", "logging read", - "做工作记录", "做阅读记录", "做working-log", "做reading-log", - "写工作日志", "写阅读日志", "记录工作", "记录阅读", - "写日志", "记一下", "总结一下这个会话", - "记录决策", "记一下工作过程", "写工作总结". -source: paperforge ---- - -# Logging - ---- - -## 1. Bootstrap — 必须先执行 - -跑这个脚本: - -``` -python $SKILL_DIR/scripts/pf_bootstrap.py -``` - -返回 JSON。记住以下变量: - -| 变量 | 来自 JSON 的 | 用于 | -| ----------- | -------------------- | --------------------------------------------- | -| `$VAULT` | `vault_root` | 所有 `--vault` 参数 | -| `$PYTHON` | `python_candidate` | 所有 cli 调用 | - -如果 `ok: false` → 报告 `error` 给用户,**停止**。 - ---- - -## 2. Routing — 判断用户要什么 - -根据用户说的内容确定走哪个分支: - -| 用户说 | 走分支 | -| ------------------------------------------- | -------- | -| "记录阅读" "reading log" "做阅读记录" "读完了记一下" "刚读了一段记一下" "有没有什么值得记的" "把这段记下来" | **reading** | -| "工作记录" "working log" "总结会话" "记一下工作过程" "写工作总结" "记录决策" "logging work" | **working** | -| "写日志" "记录一下" "记一下" 不清楚哪个 | **先问用户** | - -## 3. reading 分支 — 记录单条阅读笔记 - -调用条件:用户读完一个段落/章节后要记录。 - -动作: -1. 确认 `$VAULT` 和 `$PYTHON` -2. 确定 zotero_key(从上下文或 formal note 中获取) -3. 提取: - - **section**: 文献中的位置 (Discussion P12, Results Fig.3) - - **excerpt**: 逐字引用的原文关键句 - - **usage**: 这个信息支持当前写作的哪个论点 - - **note**: 交叉验证/矛盾/注意事项 (optional) -4. 给用户展示确认后再执行: - ``` - $PYTHON -m paperforge --vault $VAULT reading-log --write KEY \ - --section "..." --excerpt "..." --usage "..." --note "..." - ``` -5. 确认写入成功 - -### Reading Log Format (MANDATORY) - -When writing reading-log.md, use EXACTLY this format. Field labels (`**Info:**`, `**Use:**`, `**Note:**`) must be in English, but the content (title, section name, excerpt, usage, note) should be in the same language as the user's conversation. - -``` -## ABCDEFGH — Author Last Name et al. Year -**Title:** Full Paper Title - -### Section Name — Page NN -**Info:** "verbatim excerpt from paper" -**Use:** how this supports current writing -**Note:** optional cross-reference (optional field) -``` - -Rules: -- Paper key: 8 uppercase letters/digits after ## (must match ^[A-Z0-9]{8}) -- **Title:** line required after every ## header -- **Info:** and **Use:** required after every ### section header -- **Note:** optional -- Do NOT deviate from this format — parsing is strict - -After writing the log file, suggest user run: -``` -paperforge reading-log --validate path/to/file.md -``` - -## 4. working 分支 — 会话总结写入 working-log - -调用条件:会话结束前/用户要求记录工作过程。 - -动作: -1. 回顾本次会话中所有关键节点: - - 用户纠正了什么 - - 方案怎么变的 - - 有什么弯路和教训 - - 可复用的方法论 -2. 按以下格式生成 markdown,给用户确认: - - ``` - ## YYYY-MM-DD — 小节名 - - ### 核心决策 - - 做了什么、为什么 - - ### 弯路与修正 - - 错误方向 → 用户纠正 → 最终方案 - - ### 可复用方法论 - - 本段的 pattern - - ### 待办 - - [ ] ... - ``` - -3. 用户确认后,询问目标 project 目录路径 -4. 追加到 `Project//working-log.md`(文件不存在则新建) -5. 确认写入成功 - ---- - -## 5. Export — 导出 reading-log - -用户说 "导出阅读日志": - -```bash -$PYTHON -m paperforge --vault $VAULT reading-log --output [--since DATE] -``` - -导出为 markdown 文件。如果用户没指定路径,询问。 diff --git a/paperforge/skills/logging/scripts/pf_bootstrap.py b/paperforge/skills/logging/scripts/pf_bootstrap.py deleted file mode 100644 index 87bd211c..00000000 --- a/paperforge/skills/logging/scripts/pf_bootstrap.py +++ /dev/null @@ -1,164 +0,0 @@ -"""PaperForge bootstrap — single entry point for agent to discover vault state. - -No dependencies. Runs on ANY Python. Just reads paperforge.json + filesystem. - -Usage: - python pf_bootstrap.py # auto-discover vault from CWD - python pf_bootstrap.py --vault - -Output (JSON to stdout): - { - "ok": true, - "vault_root": "D:\\...", - "paths": { - "literature_dir": "D:\\...\\Resources\\Literature", - "index_path": "D:\\...\\System\\PaperForge\\indexes\\formal-library.json", - "ocr_dir": "D:\\...\\System\\PaperForge\\ocr", - "exports_dir": "D:\\...\\System\\PaperForge\\exports" - }, - "domains": ["domain1", "domain2"], - "index_summary": {"domain1": 120, "domain2": 80}, - "python_candidate": "D:\\...\\python.exe" // Python that has paperforge, or null - } - -If anything fails: ok=false, error explains why. -""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - - -def _find_paperforge_json(start: Path) -> Path | None: - current = start.resolve() - for _ in range(10): - candidate = current / "paperforge.json" - if candidate.exists(): - return candidate - parent = current.parent - if parent == current: - break - current = parent - return None - - -def _read_pf_config(pf_json: Path) -> dict: - with open(pf_json, encoding="utf-8") as f: - return json.load(f) - - -def _find_python_with_paperforge(vault: Path, pf_cfg: dict) -> str | None: - """Find a Python executable that has paperforge installed.""" - candidates = [] - - # 1. Explicit python_path in config - if pf_cfg.get("python_path"): - candidates.append(Path(pf_cfg["python_path"])) - - # 2. Common venv locations inside vault - venv_names = [".venv", ".paperforge-test-venv", "venv"] - exe_paths = ["Scripts/python.exe", "bin/python3"] - for vn in venv_names: - for ep in exe_paths: - p = vault / vn / ep - if p.exists(): - candidates.append(p) - - for candidate in candidates: - try: - result = subprocess.run( - [str(candidate), "-m", "paperforge", "--version"], - capture_output=True, text=True, timeout=10, - encoding="utf-8", errors="replace", - ) - if result.returncode == 0 and "paperforge" in result.stdout.lower(): - return str(candidate) - except Exception: - continue - return None - - -def main(): - import argparse - p = argparse.ArgumentParser(description="PaperForge bootstrap") - p.add_argument("--vault", default=None, help="Vault root path (auto-detect if omitted)") - args = p.parse_args() - - result: dict = {"ok": False} - - # --- 1. Find vault --- - if args.vault: - vault = Path(args.vault).resolve() - pf_json = vault / "paperforge.json" - if not pf_json.exists(): - result["error"] = f"paperforge.json not found at {vault}" - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - else: - pf_json = _find_paperforge_json(Path.cwd()) - if pf_json is None: - result["error"] = "paperforge.json not found from CWD upward. Set --vault." - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - vault = pf_json.parent - - result["vault_root"] = str(vault) - - # --- 2. Read config --- - try: - cfg = _read_pf_config(pf_json) - except Exception as e: - result["error"] = f"Cannot read paperforge.json: {e}" - json.dump(result, sys.stdout, ensure_ascii=False) - sys.exit(0) - - system_dir = cfg.get("system_dir", "System") - resources_dir = cfg.get("resources_dir", "Resources") - literature_dir = cfg.get("literature_dir", "Literature") - - # --- 3. Build paths from config --- - pf_root = vault / system_dir / "PaperForge" - - paths = { - "literature_dir": str(vault / resources_dir / literature_dir), - "index_path": str(pf_root / "indexes" / "formal-library.json"), - "ocr_dir": str(pf_root / "ocr"), - "exports_dir": str(pf_root / "exports"), - } - result["paths"] = paths - - # --- 4. List domains --- - lit_dir = Path(paths["literature_dir"]) - domains = sorted( - [d.name for d in lit_dir.iterdir() if d.is_dir()] - ) if lit_dir.exists() else [] - result["domains"] = domains - - # --- 5. Index summary --- - index_path = Path(paths["index_path"]) - index_summary: dict[str, int] = {} - if index_path.exists(): - try: - data = json.loads(index_path.read_text(encoding="utf-8")) - items = data.get("items", []) - if isinstance(items, dict): - items = items.values() - for item in items: - d = item.get("domain", "unknown") - index_summary[d] = index_summary.get(d, 0) + 1 - except Exception: - pass - result["index_summary"] = index_summary - - # --- 6. Find Python that has paperforge (best effort) --- - result["python_candidate"] = _find_python_with_paperforge(vault, cfg) - - result["ok"] = True - json.dump(result, sys.stdout, ensure_ascii=False, indent=2) - - -if __name__ == "__main__": - main() diff --git a/paperforge/skills/methodology/SKILL.md b/paperforge/skills/methodology/SKILL.md deleted file mode 100644 index 7f08ef42..00000000 --- a/paperforge/skills/methodology/SKILL.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -name: methodology -description: > - Extract reusable methodology from project work logs. Triggered by: - methodology, /methodology, 提取方法论, 存档写作规律, - 总结本项目方法, 提取可复用规则, 提取写作规律. -source: paperforge ---- - -# Methodology Extract - ---- - -## 1. Bootstrap - -```python $SKILL_DIR/scripts/pf_bootstrap.py``` - -Remember: `$VAULT`, `$PYTHON`. - ---- - -## 2. Determine Project - -Ask user: which project to extract methodology from? - -If user doesn't specify, scan `Project/` directory for complete working-log.md files and list them. - ---- - -## 3. Read working-log - -Read `/Project//working-log.md`. - ---- - -## 4. Identify Extractable Patterns - -Scan the working-log for these signals: - -| Signal in working-log | Extract to | -|----------------------|-------------| -| "弯路" + "修正" or "教训" sections | Pattern rules | -| "最终逻辑:" or "最终结构:" | Section templates | -| "复用" keyword + methodology block | Reusable practices | -| Cross-study audit sections (跨研究可比性) | Analysis methodology | -| "methodology" header sections | Full methodology block | -| Review feedback patterns (审阅/修正) | Writing checklists | - -For each found pattern, classify into one of: -- `review-writing` — 综述写作 framework design, gap analysis, cross-study audit -- `argument-writing` — 段落写作, 参数框架, 论证结构 -- `analysis-methods` — 文献审计, 跨研究比较, 参数提取 -- `general` — fallback - ---- - -## 5. Present and Confirm - -For each extracted pattern, show: -- Category -- Source (working-log section number) -- Brief summary (1-2 sentences) - -Ask user to confirm/edit before writing. - ---- - -## 6. Write Methodology Files - -Write confirmed patterns to `/PaperForge/methodologies/.md`. - -If file exists, APPEND (do not overwrite). - -Format per method: -``` -## -**Category:** -**Source:** Project//working-log.md Section X.Y -**Extracted:** YYYY-MM-DD - -### Pattern - - -### Example - -``` diff --git a/paperforge/skills/paperforge/SKILL.md b/paperforge/skills/paperforge/SKILL.md new file mode 100644 index 00000000..2e42cd96 --- /dev/null +++ b/paperforge/skills/paperforge/SKILL.md @@ -0,0 +1,113 @@ +--- +name: paperforge +description: > + Research Memory Runtime — 文献搜索、精读、问答、阅读笔记、 + 工作记录、方法论提取。Triggered by: + pf-deep pf-paper pf-sync pf-ocr pf-status, + "精读" "找文献" "搜文献" "文献问答" "读一下" "看看这篇" + "讨论" "记录阅读" "记录工作" "总结会话" "提取方法论". +source: paperforge +--- + +# PaperForge — Research Memory Runtime + +PaperForge 将文献、阅读痕迹、工作过程、方法论和产物 +组织成可检索、可复核、可由 agent 调用的研究记忆。 + +--- + +## 1. Bootstrap — 必须先执行 + +```bash +python $SKILL_DIR/scripts/pf_bootstrap.py --vault "$VAULT" +``` + +返回 JSON。记录以下变量(所有 workflow 文件继承,不再重复声明): + +| 变量 | JSON 字段 | 用途 | +| ------------- | ----------------------- | ------------------------------ | +| `$VAULT` | `vault_root` | 所有 `--vault` 参数 | +| `$PYTHON` | `python_candidate` | 所有 `python -m paperforge` 调用 | +| `$LIT_DIR` | `paths.literature_dir` | 文献笔记根目录 | +| `$SKILL_DIR` | 平台注入 | 脚本路径 | +| `$METHODS` | `methodology_index` | 可用方法论索引 | + +如果 `ok: false`,报告 `error` 给用户,**停止。禁止自己拼路径。** + +--- + +## 2. Agent Context — bootstrap 成功后执行 + +```bash +$PYTHON -m paperforge agent-context --json --vault "$VAULT" +``` + +返回 library overview、collection tree、可用命令和规则。Agent 注入为会话上下文。 + +--- + +## 3. Methodology Index — bootstrap 自动提供 + +bootstrap 已返回 `methodology_index`(从 `System/PaperForge/methodology/archive/` 扫描)。 +Agent 在需要时自行读取对应卡片(`read System/PaperForge/methodology/archive/.md`)。 + +--- + +## 4. Reading-Log Safety Rule — 全局规则,所有 workflow 必须遵守 + +Reading-log 不是事实源。它记录的是**之前的关注点、解读和预期用途**。 + +当存在 prior reading-log 时: +1. 用它决定**优先复查什么**,不是用它回答用户问题 +2. 重新打开**原文/图表/表格**,核实之前的解读 +3. 确认的,说明"已回原文复核" +4. 被推翻的,创建 correction note +5. **绝对禁止**仅根据 reading-log 内容回答事实性问题 + +--- + +## 5. 意图路由 + +用户输入对应唯一一个 workflow 文件(打开并执行其完整流程): + +| 用户说 | 打开 | +| -------------------------------------------------------- | -------------------------------- | +| "找文献" "搜" "库里有没有XX" "collection 里关于YY" | `workflows/paper-search.md` | +| "精读 " "/pf-deep" "三阶段阅读" | `workflows/deep-reading.md` | +| "读一下" "看看" "讨论" "/pf-paper" " 这篇讲了什么" | `workflows/paper-qa.md` | +| "记一下" "记录阅读" "reading log" "读完这段记一下" | `workflows/reading-log.md` | +| "总结会话" "工作记录" "项目记录" "project log" "记决策" | `workflows/project-log.md` | +| "提取方法论" "总结规律" "存档写作规律" | `workflows/methodology.md` | +| 不确定 / 空输入 | 问用户:搜文献、精读、问答、记笔记、记工作、提方法论? | + +路由后如用户切换意图,重新判断并打开对应 workflow。 + +--- + +## 6. 全局禁止规则 + +- **禁止自行拼接文件路径**。所有路径从 bootstrap 或 paper-context 获取。 +- **禁止绕过 CLI 直接操作文件**。搜索用 `$PYTHON -m paperforge search`,不用 glob/grep 扫库。 +- **禁止在未完成 paper-context 检查前读取原文**(适用于 deep-reading、paper-qa)。 + +--- + +## 文件结构 + +``` +paperforge/ +├── SKILL.md ← 本文件(compound:启动注入 + 路由 + 全局规则) +├── workflows/ ← molecules:原子序列 + 分支条件 +│ ├── paper-search.md +│ ├── deep-reading.md +│ ├── paper-qa.md +│ ├── reading-log.md +│ ├── project-log.md +│ └── methodology.md +├── references/ ← 共享参考 +│ ├── chart-reading/ ← 19 种图表阅读指南 +│ └── method-card-template.md +└── scripts/ ← 脚本 atoms + ├── pf_bootstrap.py + └── pf_deep.py +``` diff --git a/paperforge/skills/literature-qa/references/chart-reading/GSEA富集图.md b/paperforge/skills/paperforge/references/chart-reading/GSEA富集图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/GSEA富集图.md rename to paperforge/skills/paperforge/references/chart-reading/GSEA富集图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/INDEX.md b/paperforge/skills/paperforge/references/chart-reading/INDEX.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/INDEX.md rename to paperforge/skills/paperforge/references/chart-reading/INDEX.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/ROC与PR曲线.md b/paperforge/skills/paperforge/references/chart-reading/ROC与PR曲线.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/ROC与PR曲线.md rename to paperforge/skills/paperforge/references/chart-reading/ROC与PR曲线.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/Western Blot条带图.md b/paperforge/skills/paperforge/references/chart-reading/Western Blot条带图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/Western Blot条带图.md rename to paperforge/skills/paperforge/references/chart-reading/Western Blot条带图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/免疫荧光定量图.md b/paperforge/skills/paperforge/references/chart-reading/免疫荧光定量图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/免疫荧光定量图.md rename to paperforge/skills/paperforge/references/chart-reading/免疫荧光定量图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/折线图与时间序列.md b/paperforge/skills/paperforge/references/chart-reading/折线图与时间序列.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/折线图与时间序列.md rename to paperforge/skills/paperforge/references/chart-reading/折线图与时间序列.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/散点图与气泡图.md b/paperforge/skills/paperforge/references/chart-reading/散点图与气泡图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/散点图与气泡图.md rename to paperforge/skills/paperforge/references/chart-reading/散点图与气泡图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/显微照片与SEM图.md b/paperforge/skills/paperforge/references/chart-reading/显微照片与SEM图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/显微照片与SEM图.md rename to paperforge/skills/paperforge/references/chart-reading/显微照片与SEM图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/条形图与误差棒.md b/paperforge/skills/paperforge/references/chart-reading/条形图与误差棒.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/条形图与误差棒.md rename to paperforge/skills/paperforge/references/chart-reading/条形图与误差棒.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/桑基图与弦图.md b/paperforge/skills/paperforge/references/chart-reading/桑基图与弦图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/桑基图与弦图.md rename to paperforge/skills/paperforge/references/chart-reading/桑基图与弦图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/森林图与Meta分析.md b/paperforge/skills/paperforge/references/chart-reading/森林图与Meta分析.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/森林图与Meta分析.md rename to paperforge/skills/paperforge/references/chart-reading/森林图与Meta分析.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/火山图与曼哈顿图.md b/paperforge/skills/paperforge/references/chart-reading/火山图与曼哈顿图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/火山图与曼哈顿图.md rename to paperforge/skills/paperforge/references/chart-reading/火山图与曼哈顿图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/热图与聚类图.md b/paperforge/skills/paperforge/references/chart-reading/热图与聚类图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/热图与聚类图.md rename to paperforge/skills/paperforge/references/chart-reading/热图与聚类图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/生存曲线.md b/paperforge/skills/paperforge/references/chart-reading/生存曲线.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/生存曲线.md rename to paperforge/skills/paperforge/references/chart-reading/生存曲线.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/箱式图与小提琴图.md b/paperforge/skills/paperforge/references/chart-reading/箱式图与小提琴图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/箱式图与小提琴图.md rename to paperforge/skills/paperforge/references/chart-reading/箱式图与小提琴图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/组织学半定量图.md b/paperforge/skills/paperforge/references/chart-reading/组织学半定量图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/组织学半定量图.md rename to paperforge/skills/paperforge/references/chart-reading/组织学半定量图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/网络图与通路图.md b/paperforge/skills/paperforge/references/chart-reading/网络图与通路图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/网络图与通路图.md rename to paperforge/skills/paperforge/references/chart-reading/网络图与通路图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/蛋白质结构图.md b/paperforge/skills/paperforge/references/chart-reading/蛋白质结构图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/蛋白质结构图.md rename to paperforge/skills/paperforge/references/chart-reading/蛋白质结构图.md diff --git a/paperforge/skills/literature-qa/references/chart-reading/降维图(PCA-tSNE-UMAP).md b/paperforge/skills/paperforge/references/chart-reading/降维图(PCA-tSNE-UMAP).md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/降维图(PCA-tSNE-UMAP).md rename to paperforge/skills/paperforge/references/chart-reading/降维图(PCA-tSNE-UMAP).md diff --git a/paperforge/skills/literature-qa/references/chart-reading/雷达图与漏斗图.md b/paperforge/skills/paperforge/references/chart-reading/雷达图与漏斗图.md similarity index 100% rename from paperforge/skills/literature-qa/references/chart-reading/雷达图与漏斗图.md rename to paperforge/skills/paperforge/references/chart-reading/雷达图与漏斗图.md diff --git a/paperforge/skills/paperforge/references/method-card-template.md b/paperforge/skills/paperforge/references/method-card-template.md new file mode 100644 index 00000000..0ae4addf --- /dev/null +++ b/paperforge/skills/paperforge/references/method-card-template.md @@ -0,0 +1,39 @@ +# Method Card Template + +复制此模板创建新的方法论卡片。 + +--- + + + + +# <标题:简短、可搜索> + +## Use when + + +## Procedure + + +1. <步骤 1> +2. <步骤 2> +3. <步骤 3> + +## Watch-outs + + +- <注意 1> +- <注意 2> + +## Example + + +--- + diff --git a/paperforge/skills/methodology/scripts/pf_bootstrap.py b/paperforge/skills/paperforge/scripts/pf_bootstrap.py similarity index 79% rename from paperforge/skills/methodology/scripts/pf_bootstrap.py rename to paperforge/skills/paperforge/scripts/pf_bootstrap.py index ebc41b22..741a677d 100644 --- a/paperforge/skills/methodology/scripts/pf_bootstrap.py +++ b/paperforge/skills/paperforge/scripts/pf_bootstrap.py @@ -18,7 +18,11 @@ Output (JSON to stdout): }, "domains": ["domain1", "domain2"], "index_summary": {"domain1": 120, "domain2": 80}, - "python_candidate": "D:\\...\\python.exe" // Python that has paperforge, or null + "python_candidate": "D:\\...\\python.exe", + "methodology_index": [ + {"id": "parameter-window-audit", "description": "比较多个研究的参数和剂量反应"}, + ... + ] } If anything fails: ok=false, error explains why. @@ -81,6 +85,39 @@ def _find_python_with_paperforge(vault: Path, pf_cfg: dict) -> str | None: return None +def _scan_methodology_archive(pf_root: Path) -> list[dict]: + """Scan methodology archive directory for available method cards.""" + archive_dir = pf_root / "methodology" / "archive" + if not archive_dir.exists(): + return [] + + methods = [] + for f in sorted(archive_dir.glob("*.md")): + try: + text = f.read_text(encoding="utf-8") + # Extract first heading as title, first paragraph after "Use when" as description + title = "" + description = "" + in_use_when = False + for line in text.split("\n"): + stripped = line.strip() + if stripped.startswith("# ") and not title: + title = stripped.lstrip("# ").strip() + elif stripped.startswith("## Use when"): + in_use_when = True + elif in_use_when and stripped and not stripped.startswith("#"): + description = stripped + in_use_when = False + methods.append({ + "id": f.stem, + "title": title or f.stem, + "description": description or "(no description)", + }) + except Exception: + continue + return methods + + def main(): import argparse p = argparse.ArgumentParser(description="PaperForge bootstrap") @@ -180,6 +217,9 @@ def main(): pass result["memory_layer"] = memory_layer + # --- 8. Scan methodology archive --- + result["methodology_index"] = _scan_methodology_archive(pf_root) + result["ok"] = True json.dump(result, sys.stdout, ensure_ascii=False, indent=2) diff --git a/paperforge/skills/literature-qa/scripts/ld_deep.py b/paperforge/skills/paperforge/scripts/pf_deep.py similarity index 100% rename from paperforge/skills/literature-qa/scripts/ld_deep.py rename to paperforge/skills/paperforge/scripts/pf_deep.py diff --git a/paperforge/skills/paperforge/workflows/deep-reading.md b/paperforge/skills/paperforge/workflows/deep-reading.md new file mode 100644 index 00000000..6d1852ea --- /dev/null +++ b/paperforge/skills/paperforge/workflows/deep-reading.md @@ -0,0 +1,169 @@ +# deep-reading + +Keshav 三阶段精读。在 formal note 中写入结构化的 `## 精读` 区域。 + +--- + +## 前置检查 + +### Step 0: paper-context(必须) + +```bash +$PYTHON -m paperforge paper-context --json --vault "$VAULT" +``` + +检查返回 JSON: +- `ok: false` → 报告 `error.message`,停止 +- `data.paper.ocr_status != "done"` → "OCR 未完成,请先运行 paperforge ocr",停止 +- `data.paper.analyze != true` → "analyze 未开启,请在 formal note frontmatter 中设为 true",停止 + +**检查 prior_notes:** +- 如果存在 `data.prior_notes`,逐条看 `verified` 字段 +- `verified: false` 的条目记入 recheck_targets,精读时必须回原文复核这些位置 +- `verified: true` 的条目可以信任,但标注"之前已验证" + +**记录关键路径:** +- `data.paper.note_path`(formal note 路径) +- `data.paper.fulltext_path`(fulltext 路径) +- 记下 `recheck_targets` 列表 + +--- + +## 执行流程 + +### Step 1: Prepare(跑脚本) + +```bash +$PYTHON "$SKILL_DIR/scripts/pf_deep.py" prepare --key --vault "$VAULT" +``` + +解析返回 JSON: +- `status: "ok"` → 记下 `figure_map`、`chart_type_map`、`formal_note`、`fulltext_md`、`figures`、`tables` 的路径和数量 +- `status: "warn"` + `deep_reading_status: done` → 告知用户"该文献已精读过",确认是否重读 +- `status: "error"` → 报告 `message`,停止 + +读 formal note,确认 `## 精读` 骨架已插入。 + +--- + +### Step 2: Pass 1 — 概览 + +只填 `### Pass 1: 概览`。不碰 Pass 2/3。 + +填写内容必须来自原文,不可推断: + +- **一句话总览**:论文类型 + 核心发现,一句话 +- **5 Cs 快速评估**: + - Category(RCT / 队列 / 综述 / 基础研究等) + - Context(领域共识,本文要解决什么) + - Correctness(初步直觉,逻辑有否明显漏洞) + - Contributions(1-3 条) + - Clarity(写作质量,图表可读性) +- **Figure 导读**(基于 fulltext 浏览各图 caption): + - 关键主图:列出,一句话概括要证明什么 + - 证据转折点:哪个 figure 是叙事关键转折 + - 需要重点展开的 supplementary + - 关键表格 + +填完立即保存。 + +--- + +### Step 3: Pass 2 — 精读还原 + +填 `### Pass 2: 精读还原`。**按 figure 顺序逐个处理**。 + +每处理完一个 figure 立即保存。 + +#### 图表类型定位(两步) + +**A: 读 chart-type-map**(prepare 输出中包含该路径)。这是关键词命中建议。 + +**B: Agent 读 caption 做最终判断** +1. 读该 figure 的 caption(来自 fulltext) +2. 打开 `references/chart-reading/INDEX.md`,对照 caption 内容判断图表类型 +3. chart-type-map 建议和 Agent 判断不一致时 → 以 Agent 判断为准 +4. 无法确定类型 → 跳过 chart guide,按通用结构分析 +5. 确定类型 → 读对应 chart-reading 指南,按指南中的检查清单分析 + +#### 每张 Figure 的子标题(固定,不可跳过) + +``` +**图像定位与核心问题**:页码 + 要回答什么问题 +**方法与结果**:实验设计 / 数据来源 / 技术手段;核心数据、趋势、对比 +**图表质量审查**:按 chart-reading 指南检查坐标轴、单位、误差棒、统计标注 +**作者解释**:作者在正文中对该图的解读 +**我的理解**:自己的理解(必须与作者解释做明显区分) +**疑点/局限**:用 `> [!warning]` 突出 +``` + +#### 每张 Table 的子标题(简化版) + +``` +回答什么问题、关键字段/分组、主要结果、我的理解、疑点/局限 +``` + +#### 所有 figure/table 处理完后 + +**关键方法补课**:简要解释不熟悉的实验技术(1-2 项) + +**主要发现与新意**: +- 发现 1:...(来源:Figure X) +- 发现 2:...(来源:Table Y) +- 每条发现必须标注来源(Figure 编号或正文段落) + +--- + +### Step 4: Postprocess(跑校验,修正问题) + +```bash +$PYTHON "$SKILL_DIR/scripts/pf_deep.py" postprocess-pass2 "" --figures --vault "$VAULT" +``` + +- 输出 `OK` → 继续 Step 5 +- 输出错误列表(含行号)→ 按提示修正,修正后重新跑 +- 最多 3 轮修正。3 轮后仍失败 → 报告剩余错误给用户 + +--- + +### Step 5: Pass 3 — 深度理解 + +填 `### Pass 3: 深度理解`。基于 Pass 1/2 已写内容。 + +- **假设挑战与隐藏缺陷**:隐含假设;放宽假设后结论还成立吗;缺少的关键引用;实验/分析技术潜在问题 +- **哪些结论扎实,哪些仍存疑**: + - 较扎实:... + - 仍存疑:...(用 `> [!warning]`) +- **Discussion 与 Conclusion 怎么读**:作者实际完成了什么;哪些有拔高;哪些是推测 +- **对我的启发**:研究设计、figure 组织、方法组合、未来工作 +- **遗留问题**:...(用 `> [!question]`) + +--- + +### Step 6: Final Validation + +```bash +$PYTHON "$SKILL_DIR/scripts/pf_deep.py" validate-note "" --fulltext "" +``` + +- 输出 `OK` → 告知用户精读完成 +- 输出错误 → 修正缺失项,直到通过 + +--- + +## Callout 格式规则 + +- `> [!important]` — 每个 main finding +- `> [!warning]` — 疑问、局限、证据边界、仍存疑条目 +- `> [!question]` — 遗留问题 +- **相邻 callout 之间必须有空行**(否则 Obsidian 合并): + - 正确:`> [!important] A\n\n> [!important] B` + - 错误:`> [!important] A\n> [!important] B` + +--- + +## 禁止 + +- 不要在 Pass 1 完成前碰 Pass 2/3 +- 不要把推断写成文献事实——区分"作者说了 X"和"我推断 Y" +- 不要跨 figure 写综合判断(Pass 2 逐图,Pass 3 才做综合) diff --git a/paperforge/skills/paperforge/workflows/methodology.md b/paperforge/skills/paperforge/workflows/methodology.md new file mode 100644 index 00000000..f7a1fb9f --- /dev/null +++ b/paperforge/skills/paperforge/workflows/methodology.md @@ -0,0 +1,94 @@ +# methodology + +从 project-log 中提取可复用方法论,按 method-card 模板写入 methodology archive。 +不 append 到大文件,每张卡片独立保存。 + +--- + +## 前置条件 + +- bootstrap 已完成 +- 有 project-log 记录可读取 + +--- + +## 步骤 + +### Step 1: 确定项目和来源 + +询问用户从哪个项目提取。如用户未指定,列出有 project-log 的项目。 + +### Step 2: 读取 project-log + +```bash +$PYTHON -m paperforge project-log --list "" --json --vault "$VAULT" +``` + +扫描其中以下信号: + +| log 中的信号 | 可提取为 | +| ------------------------- | --------------------------- | +| `detours` 中的教训 | 方法论规则 | +| `reusable` 字段里的内容 | 直接采用 | +| `decisions` 中的重要选择 | 决策原则 | +| 跨文献审计/比较分析 | 审计方法论 | +| 写作修正/审阅反馈 | 写作检查清单 | + +### Step 3: 识别可提取 pattern + +对每个 pattern 分类: +- `review-writing` — 综述框架设计、gap 分析、跨研究审计 +- `argument-writing` — 段落写作、论证结构 +- `analysis-methods` — 文献审计、跨研究比较、参数提取 +- `general` — fallback + +### Step 4: 按 method-card 模板生成卡片 + +打开 `references/method-card-template.md` 确认模板格式。 + +对每个 pattern 生成一张卡片,展示给用户确认。格式: + +```markdown +--- +id: +tags: [, ] +source_project: +status: active +--- + +# <标题> + +## Use when +<什么时候应该用这个方法> + +## Procedure +1. <步骤 1> +2. <步骤 2> +... + +## Watch-outs +- <注意事项 1> +- <注意事项 2> + +## Example +<来自项目的具体例子> +``` + +### Step 5: 用户确认后写入 + +将每张卡片写入: + +``` +System/PaperForge/methodology/archive/.md +``` + +用 `write` 工具创建文件。如已存在同名文件,追加到末尾(用 `---` 分隔)。 +不自动覆盖已有内容。 + +--- + +## 禁止 + +- 不要提取太泛的"教训"(如"多读文献")——必须有具体的 Procedure 步骤 +- 不要创建超过 4 张卡片/次——优先最可复用的 +- 不要在用户确认前写入 diff --git a/paperforge/skills/paperforge/workflows/paper-qa.md b/paperforge/skills/paperforge/workflows/paper-qa.md new file mode 100644 index 00000000..6785d773 --- /dev/null +++ b/paperforge/skills/paperforge/workflows/paper-qa.md @@ -0,0 +1,105 @@ +# paper-qa + +交互式文献问答。不强制要求 OCR,但 OCR 完成后回答更准确。 + +每次问答记录到 `discussion.json`(Dashboard 可见)。 + +--- + +## 前置条件 + +- bootstrap 已完成(有 `$VAULT`、`$PYTHON`) + +--- + +## 步骤 + +### Step 1: 定位论文 + +用户可能给 zotero_key、DOI、标题片段、作者+年份。按以下方式查找: + +**优先用 paper-context(一次拿到全部信息):** + +```bash +$PYTHON -m paperforge paper-context --json --vault "$VAULT" +``` + +返回 JSON 包含 paper 元数据、OCR 状态、prior_notes 等。 + +**paper-context 无结果时的备选:** + +```bash +$PYTHON -m paperforge search "" --json --vault "$VAULT" --limit 5 +``` + +如果多候选,列出让用户选(同 paper-search 的 Step 4-5 格式)。 +如果无结果,告知用户并停止。 + +### Step 2: 加载文献内容 + +1. 从 paper-context 或 formal note frontmatter 获取:标题、作者、期刊、年份、domain +2. 读 `fulltext.md`(如果 OCR done)作为主要回答依据 +3. 如果 fulltext 不存在:"OCR 文本不可用,回答将基于元数据和公开信息" + +### Step 3: 展示论文信息 + 进入 Q&A + +``` +已加载: (<year>, <journal>) +作者: <authors> | Key: <zotero_key> | 领域: <domain> +OCR: done / 不可用 +结束对话时说"保存"即可保存讨论。 +请问有什么问题? +``` + +### Step 4: Q&A 循环 + +- 等待用户提问 +- 每次回答后等待下一个问题 +- 持续到用户说"保存"、"结束"、"完成" + +**回答原则:** +- 严格基于 fulltext.md 中的文本内容 +- 引用原文时标注来源页码/章节 +- 论文未提及的内容明确说明"论文中未提及" +- 区分"文献说了什么"和"我推断什么" + +### Step 5: 保存讨论 + +用户说"保存"、"结束"、"完成"时执行。 + +**收集 Q&A 对**,序列化为 JSON 数组: + +```json +[ + { + "question": "用户的问题", + "answer": "Agent 的回答", + "source": "user_question", + "timestamp": "2026-05-14T12:00:00+08:00" + } +] +``` + +`source`: `"user_question"`(用户提问)或 `"agent_analysis"`(Agent 主动分析)。 + +**调 discussion 模块:** + +```bash +$PYTHON -m paperforge.worker.discussion record <zotero_key> \ + --vault "$VAULT" \ + --agent pf-paper \ + --model "<current_model>" \ + --qa-pairs '<JSON_ARRAY>' +``` + +- 返回 `ok` → 告知用户已保存 +- 返回 `error` → 重试一次,仍失败则告知用户 + +**不要自动保存。** 仅用户明确要求时执行。 + +--- + +## 禁止 + +- 不要捏造论文未提及的内容 +- 不要把推断写成论文事实 diff --git a/paperforge/skills/paperforge/workflows/paper-search.md b/paperforge/skills/paperforge/workflows/paper-search.md new file mode 100644 index 00000000..285ce555 --- /dev/null +++ b/paperforge/skills/paperforge/workflows/paper-search.md @@ -0,0 +1,101 @@ +# paper-search + +从文献库中按条件检索文献,返回候选清单及每篇的可用状态。 + +--- + +## 前置条件 + +- bootstrap 已完成(有 `$VAULT`、`$PYTHON`、`$LIT_DIR`) + +--- + +## 步骤 + +### Step 1: 解析用户搜索意图 + +提取以下信息(缺什么就问用户): +- **搜索词**:关键词、作者名、年份 +- **范围**:domain(如"骨科")、collection(如"DC")、不指定=全库 +- **过滤条件**:OCR 状态(done/pending)、年份范围(--year-from/--year-to)、lifecycle + +### Step 2: 执行搜索 + +```bash +$PYTHON -m paperforge search <query> --json --vault "$VAULT" --limit 15 \ + [--domain "<domain>"] \ + [--year-from <N>] [--year-to <N>] \ + [--ocr <done|pending>] \ + [--lifecycle <active|archived>] +``` + +返回 JSON 结构: +```json +{ + "ok": true, + "data": { + "query": "<query>", + "matches": [ + { + "zotero_key": "ABC12345", + "citation_key": "...", + "title": "...", + "year": "2024", + "first_author": "Smith", + "domain": "...", + "collection_path": "...", + "ocr_status": "done", + "deep_reading_status": "pending", + "lifecycle": "active", + "has_pdf": true, + "rank": "..." + } + ], + "count": 5 + } +} +``` + +- 如果 `ok: false` → 报告 `error.message`,问用户是否换搜索词 +- 如果 `data.count == 0` → 告知用户无结果,建议换词或扩大范围 +- 如果 `data.count > 0` → 进入 Step 3 + +### Step 3: 逐个确认状态(paper-context 原子) + +对每个 match,调 `paper-context` 获取更详细的可读状态: + +```bash +$PYTHON -m paperforge paper-context <zotero_key> --json --vault "$VAULT" +``` + +目的:拿到 `ocr_status`、`prior_notes` 数量、`analyze` 状态,帮助用户判断哪些可以直接读。 + +### Step 4: 展示候选清单 + +格式(每条一行): + +``` +找到 N 篇匹配 "<query>": + +[1] ABC12345 | Smith 2024 | Title Here | 骨科 | OCR: done | 精读: pending | 阅读笔记: 3 +[2] DEF67890 | Jones 2023 | Title Here | 骨科 | OCR: done | 精读: done | 阅读笔记: 0 +[3] GHI11111 | Wang 2022 | Title Here | 骨科 | OCR: pending | | 阅读笔记: 0 +``` + +关键字段:zotero_key, first_author, year, title, ocr_status, deep_reading_status, prior_notes 数量 + +### Step 5: 等用户选择后续操作 + +展示候选后不要自己决定下一步。等用户说: +- "读一下 [1]" → 路由到 paper-qa.md +- "精读 [2]" → 路由到 deep-reading.md +- "记一下 [1]" → 路由到 reading-log.md +- "缩小范围"/"refine" → 回到 Step 1,加更多过滤条件 + +--- + +## 禁止 + +- 不要在搜索结果中替用户决定读哪篇 +- 不要在搜索阶段读全文 +- 不要对 0 结果硬猜路径 diff --git a/paperforge/skills/paperforge/workflows/project-log.md b/paperforge/skills/paperforge/workflows/project-log.md new file mode 100644 index 00000000..96c5d639 --- /dev/null +++ b/paperforge/skills/paperforge/workflows/project-log.md @@ -0,0 +1,131 @@ +# project-log + +记录研究项目的会话总结、决策、弯路修正和方法论提取。 +Agent 按 JSON schema 写入 project-log.jsonl。 +系统自动渲染对应项目的 project-log.md。 + +--- + +## 前置条件 + +- bootstrap 已完成(有 `$VAULT`、`$PYTHON`) +- 已知 project 名称 + +--- + +## 项目日志 JSON Schema + +```json +{ + "id": "plog_<YYYYMMDD>_<序号>", + "project": "综述写作", + "date": "2026-05-14", + "type": "session_summary", + "title": "DC 段参数窗审计", + "decisions": ["做了 X,因为 Y"], + "detours": [ + { + "wrong": "错误方向", + "correction": "用户如何纠正", + "resolution": "最终方案" + } + ], + "reusable": ["可复用的方法论或教训"], + "todos": [ + {"content": "待办事项", "done": false} + ], + "related_papers": ["ABC12345"], + "tags": ["DC", "参数窗", "审计"], + "agent": "opencode" +} +``` + +| 字段 | 必填 | 说明 | +| ---------------- | ---- | ------------------------------------------- | +| `id` | 是 | 自动生成 `plog_YYYYMMDD_NNN` | +| `project` | 是 | 项目名 | +| `date` | 是 | YYYY-MM-DD | +| `type` | 是 | `session_summary` / `decision` / `correction` / `milestone` / `note` | +| `title` | 是 | 本条目的简短标题 | +| `decisions` | 否 | 核心决策列表 | +| `detours` | 否 | 弯路与修正记录 | +| `reusable` | 否 | 可复用的方法论或教训 | +| `todos` | 否 | 待办事项 | +| `related_papers` | 否 | 相关 Zotero keys | +| `tags` | 否 | 分类标签 | +| `agent` | 否 | 记录者 | + +--- + +## 步骤 + +### Step 1: 确定 project + +从上下文获取。如果用户未指定,询问。 + +### Step 2: 回顾本次会话 + +回顾以下内容: +- 做了什么(核心决策) +- 用户纠正了什么(弯路与修正) +- 有什么可复用的方法论或教训 +- 待办事项 + +### Step 3: 按 Schema 组织内容,展示确认 + +展示给用户确认后再写入: + +``` +即将记录到 Project/综述写作/project-log.md: + 日期: 2026-05-14 + 类型: session_summary + 标题: DC 段参数窗审计完成 + 决策: + - 限定参数窗为 100Hz-1kHz + - 移除 AC vs DC 对比段落 + 弯路: + - 把推断当文献事实 → 用户要求逐句审计 → 5 处修正 + 可复用: + - 写完必须逐句过 source,区分"文献说了什么"和"我推断什么" + +确认写入?(y/n) +``` + +### Step 4: 写入(Atom) + +```bash +$PYTHON -m paperforge project-log --write \ + --vault "$VAULT" \ + --project "<project>" \ + --json '<payload>' +``` + +- 返回 `ok: true` → 确认写入成功。**自动渲染对应项目 markdown。** +- 返回 `ok: false` → 报告错误,重试一次 + +### Step 5: 确认渲染 + +```bash +$PYTHON -m paperforge project-log --render --project "<project>" --vault "$VAULT" +``` + +输出到 `Project/<project>/project-log.md`。 + +--- + +## type 参考 + +| type | 使用场景 | +| ----------------- | ------------------------------ | +| `session_summary` | 会话结束时的总结 | +| `decision` | 单独记录一个重要决策 | +| `correction` | 用户纠正了某个方向 | +| `milestone` | 项目里程碑 | +| `note` | 一般研究笔记 | + +--- + +## 禁止 + +- 不要在用户确认前写入 +- 不要只写"做了什么"而没有"弯路与修正"和"可复用方法论" diff --git a/paperforge/skills/paperforge/workflows/reading-log.md b/paperforge/skills/paperforge/workflows/reading-log.md new file mode 100644 index 00000000..19c1b3c2 --- /dev/null +++ b/paperforge/skills/paperforge/workflows/reading-log.md @@ -0,0 +1,112 @@ +# reading-log + +记录单条阅读笔记。Agent 将用户确认的信息按 JSON schema 写入 reading-log.jsonl。 +系统自动渲染对应项目的 reading-log.md(给人看)并导入 paperforge.db(可搜索)。 + +--- + +## 前置条件 + +- bootstrap 已完成(有 `$VAULT`、`$PYTHON`) +- 已知 paper_id(zotero_key) + +--- + +## 阅读笔记 JSON Schema + +每条阅读笔记必须包含以下字段: + +```json +{ + "id": "rln_<YYYYMMDD>_<序号>", + "paper_id": "ABC12345", + "project": "综述写作", + "section": "Results Fig.3", + "excerpt": "原文关键句(逐字引用)", + "context": "包含 excerpt 的完整段落(供后续回原文复核时定位)", + "usage": "这个信息在当前写作中的用途", + "note": "注意事项 / 待核查 / 可能矛盾", + "tags": ["PEMF", "dose-response"], + "verified": false +} +``` + +| 字段 | 必填 | 说明 | +| --------- | ---- | -------------------------------------------------------- | +| `id` | 是 | 自动生成,格式 `rln_YYYYMMDD_NNN` | +| `paper_id` | 是 | Zotero key(8位大写字母数字) | +| `project` | 否 | 关联的研究项目 | +| `section` | 是 | 文献中的位置(如 "Results Fig.3"、"Discussion P12") | +| `excerpt` | 是 | 逐字引用的原文关键句 | +| `context` | 是 | 包含 excerpt 的完整段落,供复核定位 | +| `usage` | 是 | 这个信息在当前工作(写作/研究)中的用途 | +| `note` | 否 | 交叉验证、矛盾、待核查事项 | +| `tags` | 否 | 分类标签,供横切检索 | +| `verified` | 否 | 默认 false。Agent 回原文复核后应更新为 true | + +--- + +## 步骤 + +### Step 1: 确认 paper_id 和 project + +从上下文获取 zotero_key。如果用户未指定 project,询问或留空。 + +### Step 2: Agent 按 Schema 提取内容 + +从对话上下文中提取 `section`、`excerpt`、`context`、`usage`、`note`、`tags`。 + +**excerpt vs context 的区别:** +- `excerpt`:你关注的那一句(逐字引用) +- `context`:包含这句的完整段落(3-5 句),让以后的人不翻原文也能理解语境 + +### Step 3: 展示确认 + +先展示给用户确认,不要直接写入: + +``` +即将记录: + 文献: ABC12345 | Smith 2024 + 位置: Results Fig.3 + 原文: "..." + 用途: 支撑 PEMF 基质合成的论证 + 备注: 需核查是否对 DNA 归一化了 + 项目: 综述写作 + 标签: PEMF, GAG + 段落语境: "..." + +确认写入?(y/n) +``` + +### Step 4: 写入(Atom) + +```bash +$PYTHON -m paperforge reading-log --write <paper_id> \ + --vault "$VAULT" \ + --section "<section>" \ + --excerpt "<excerpt>" \ + --context "<context>" \ + --usage "<usage>" \ + --note "<note>" \ + --project "<project>" \ + --tags "<tag1>,<tag2>" +``` + +- 返回 `ok: true` → 确认写入成功。**写入后自动渲染对应项目的 markdown。** +- 返回 `ok: false` → 报告错误,重试一次 + +### Step 5: 确认渲染 + +```bash +$PYTHON -m paperforge reading-log --render --project "<project>" --vault "$VAULT" +``` + +输出到 `Project/<project>/reading-log.md`。 + +--- + +## 禁止 + +- 不要在用户确认前写入 +- 不要把推断当作 `excerpt`(必须是原文逐字引用) +- 不要让 `context` 为空(必须是完整段落) diff --git a/paperforge/worker/status.py b/paperforge/worker/status.py index b27c0310..fde05d05 100644 --- a/paperforge/worker/status.py +++ b/paperforge/worker/status.py @@ -639,36 +639,36 @@ def run_doctor(vault: Path, verbose: bool = False, json_output: bool = False) -> if total_issues == 0: add_check("字段注册表", "pass", "所有 formal note frontmatter 与字段注册表一致") - ld_deep_script = paths.get("ld_deep_script") + pf_deep_script = paths.get("pf_deep_script") skill_dir = None - if ld_deep_script: - skill_dir = ld_deep_script.parent.parent + if pf_deep_script: + skill_dir = pf_deep_script.parent.parent if skill_dir and skill_dir.exists(): # Try actual importability check - ld_deep_import_ok = False + pf_deep_import_ok = False import_error = "" - if ld_deep_script and ld_deep_script.exists(): + if pf_deep_script and pf_deep_script.exists(): try: import importlib.util - spec = importlib.util.spec_from_file_location("ld_deep", ld_deep_script) + spec = importlib.util.spec_from_file_location("pf_deep", pf_deep_script) if spec and spec.loader: mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) - ld_deep_import_ok = True + pf_deep_import_ok = True except Exception as e: import_error = str(e) - if ld_deep_import_ok: - add_check("Agent 脚本", "pass", "paperforge and ld_deep importable") + if pf_deep_import_ok: + add_check("Agent 脚本", "pass", "paperforge and pf_deep importable") else: add_check( "Agent 脚本", "warn", - f"literature-qa skill 目录存在但 import 失败: {import_error}", + f"paperforge skill 目录存在但 import 失败: {import_error}", "确认 agent_config_dir 配置正确并已运行 pip install -e .", ) else: - add_check("Agent 脚本", "warn", "literature-qa skill 目录未找到", "确认 agent_config_dir 配置正确") + add_check("Agent 脚本", "warn", "paperforge skill 目录未找到", "确认 agent_config_dir 配置正确") # --- Index Health section (Phase 25: derived from canonical index) --- try: diff --git a/paperforge/worker/update.py b/paperforge/worker/update.py index 3b4f22b3..0ac69a5e 100644 --- a/paperforge/worker/update.py +++ b/paperforge/worker/update.py @@ -252,7 +252,7 @@ def _deploy_all_skills(vault: Path) -> None: agent_key = config.get("agent_platform") or "opencode" result = deploy_skills(vault=vault, agent_key=agent_key, overwrite=True) if result["skill_deployed"]: - logger.info("已部署 literature-qa skill") + logger.info("已部署 paperforge skill") if result["agents_md"]: logger.info("已更新 AGENTS.md") for err in result.get("errors", []):