feat(skills): add idea-forge academic research skill

This commit is contained in:
Ct201314 2026-06-12 20:03:09 +08:00
parent b45241dcda
commit 3baf75f2d3
5 changed files with 517 additions and 0 deletions

View File

@ -0,0 +1,68 @@
---
name: idea-forge
version: 1.0.0
description: "研究念头拆解器:把一句模糊的研究念头用 5W1H 拆成能下手的问题清单,给出文献/方法/应用/交叉/时间五维缺口分析框架,并收敛成一份含假设、证据需求、支持/证伪标准与最小可行下一步的研究契约卡。当用户提到「帮我把这个研究想法理清楚」「开题没头绪」「5W1H 拆题」「找研究缺口」「研究计划」「research ideation」时触发。"
metadata:
requires:
optional_bins: ["python"]
---
# idea-forge研究念头拆解器
把脑子里那团浆糊,理成一串能查、能做的问题。它不替你想 idea而是把模糊念头
拆成结构化问题与一份可追溯的研究契约卡。
## 何时使用本技能
- 刚开题、方向还模糊,不知道从哪下手
- 有个大方向,想拆成具体能查的问题
- 想把零散想法整理成一份初步研究计划
## 与同类工具的区别
本技能是**纯本地的结构化脚手架生成器**:不联网、不调用任何在线检索或文献管理
服务,仅用 Python 标准库。它的价值不在「替你检索」,而在「把念头拆成对的问题」——
通过从念头中识别概念槽位(方法/任务/指标/领域),生成贴合你具体输入的引导问题,
而不是套空模板。
## 三个核心能力
| 能力 | 说明 |
|------|------|
| 5W1H 拆题 | 从念头识别槽位,生成 What/Why/Who/When/Where/How 六维贴合式问题 |
| 缺口分析框架 | 文献/方法/应用/交叉/时间五维度的定位空白检查角度 |
| 研究契约卡 | 研究问题 + 假设 + 证据需求 + 支持/证伪标准 + 最小下一步 + SMART 自检 |
## 工作流:拆解一个研究念头
### 方式 A配套脚本推荐
```bash
python scripts/forge.py --idea "用对比学习改进医学影像分割的小样本精度"
python scripts/forge.py --idea-file idea.txt --format json --output card.md
```
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `--idea` | string | 是* | 研究念头(一句话,*或用 `--idea-file` |
| `--idea-file` | path | 否 | 从文件读取念头 |
| `--format` | string | 否 | `markdown`(默认)或 `json` |
| `--output` | path | 否 | 输出文件 |
### 方式 BAgent 直接按框架引导
若不便运行脚本,可直接参照 [references/frameworks.md](references/frameworks.md) 中的
5W1H 槽位映射与契约卡模板,对用户念头逐项追问、填写。
## 注意事项
- 输出中的 `____` 是需用户填写的占位,工具只搭脚手架不替用户拍板。
- 概念词库覆盖常见跨学科术语;未识别到槽位时会提示用户在念头中点明。
- 全程离线,不产生任何网络请求。
## References
- [frameworks.md](references/frameworks.md) — 5W1H 槽位映射、缺口五维、契约卡字段
- [usage.md](references/usage.md) — 输入建议与典型场景

View File

@ -0,0 +1,51 @@
# 拆题框架参考
## 概念槽位识别
工具从念头中匹配四类概念词,作为生成贴合式问题的锚点:
| 槽位 | 含义 | 示例词 |
|------|------|--------|
| methods | 拟用方法/技术 | 对比学习、transformer、扩散模型、单细胞测序、有限元 |
| tasks | 要解决的任务 | 分割、检测、预测、生成、诊断、重建 |
| properties | 关心的指标/属性 | 精度、鲁棒性、小样本、可解释性、效率 |
| domains | 应用领域 | 医学、材料、金融、遥感、自然语言 |
词库覆盖机器学习、生物、材料、物理等常见跨学科术语,可在 `forge.py` 中扩充。
## 5W1H 槽位映射
每一维都把识别到的槽位嵌入问题,使其贴合具体念头而非空模板:
| 维度 | 关注点 |
|------|--------|
| What | 任务定义、量化指标、研究边界 |
| Why | 现有不足的根因、价值、不做的代价 |
| Who | 受益者、同行团队、目标读者 |
| When | 新旧问题、近年进展、时效性 |
| Where | 验证数据/环境、泛化边界、复现条件 |
| How | 核心机制设想、关键对照实验、消融 |
## 缺口分析五维
| 维度 | 定位角度 |
|------|---------|
| 文献缺口 | 研究不足/结论矛盾的子问题 |
| 方法缺口 | 共同失败模式、未试过的方法组合 |
| 应用缺口 | 实验室到部署的「最后一公里」 |
| 交叉缺口 | 邻近领域可迁移的工具 |
| 时间缺口 | 新数据/新工具让旧问题值得重做 |
## 研究契约卡字段
| 字段 | 作用 |
|------|------|
| 研究问题 | 一句可证伪的问题 |
| 核心假设 | 机制 + 预期效果幅度 |
| 现有证据 | 支持假设的已知工作 |
| 缺失证据 | 还差哪些实验/数据 |
| 支持标准 | 满足什么条件算支持 |
| 证伪标准 | 什么结果推翻假设 |
| 最小可行下一步 | 一周内能给方向性证据的最小实验 |
契约卡的价值在于把「想法」变成「可被推翻的命题」——有了证伪标准,研究才可证伪、可推进。

View File

@ -0,0 +1,39 @@
# 输入建议与典型场景
## 怎么写念头能得到更好的拆解
工具靠识别念头中的概念词来生成贴合问题。念头里点明方法、任务、指标、领域,
拆解会更具体。
对比:
- 偏弱:「我想研究图神经网络」——只识别到方法,问题会偏泛。
- 较好:「用图神经网络提升药物分子性质预测的准确率」——方法(图神经网络)、
任务(预测)、指标(准确率)、领域(隐含化学/生物)都有,问题贴合度高。
不必写得很长,一句话点到「用什么方法、做什么任务、改善什么指标、在什么领域」即可。
## 典型场景
### 场景一:开题没头绪
丢一个大方向进去,拿到六维问题清单后,逐条回答,方向自然收敛。
```bash
python scripts/forge.py --idea "用大模型辅助材料发现"
```
### 场景二:想知道还剩什么没做
重点看「研究缺口分析框架」一节,对照五个维度逐一排查自己方向的空白。
### 场景三:整理成初步研究计划
把「研究契约卡」填完,就是一份可讨论的开题骨架——尤其是证伪标准,逼自己把
模糊的「我觉得会更好」变成「满足什么条件才算更好」。
## 局限
- 不替你检索文献,也不评价 idea 好坏——它只负责把念头拆成对的问题。
- 概念词库有限,冷门领域可能识别不全,可手动补充词库或直接按框架自行追问。
- 输出是脚手架,真正的研究判断仍需你来做。

View File

@ -0,0 +1,278 @@
"""idea-forge研究念头拆解器。
把一句模糊的研究念头我想用对比学习改进医学图像分割的小样本性能
拆成能下手的结构化问题清单与一份可追溯的研究契约卡
三个核心能力
1. 5W1H 拆题从念头中识别概念槽位对象/方法/属性/领域结合槽位生成
贴合输入的六维引导问题而非泛泛的空模板
2. 缺口分析框架从文献/方法/应用/交叉/时间五个维度给出定位研究空白的
检查角度
3. 研究契约卡把念头收敛为研究问题假设所需证据支持/证伪标准与最小
可行下一步并用 SMART 原则自检可行性
本工具是纯本地的结构化脚手架生成器不联网不调用任何在线检索或文献管理
服务仅用 Python 标准库可在离线或受限环境中运行它不替你想 idea而是
把脑中那团浆糊理成一串能查能做的问题
用法
python forge.py --idea "用对比学习改进医学图像分割的小样本性能"
python forge.py --idea-file idea.txt --format json --output card.md
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Any
if hasattr(sys.stdout, "reconfigure"):
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
# ---------------------------------------------------------------------------
# 概念词库——用于从念头中识别成分(跨学科常见词,可按需扩充)
# ---------------------------------------------------------------------------
METHOD_WORDS = [
"对比学习", "自监督", "监督学习", "无监督", "半监督", "迁移学习", "强化学习",
"神经网络", "卷积", "transformer", "注意力", "图神经网络", "扩散模型",
"生成对抗", "贝叶斯", "聚类", "回归", "分类", "决策树", "随机森林",
"知识蒸馏", "联邦学习", "元学习", "提示学习", "微调", "预训练",
"测序", "转录组", "单细胞", "crispr", "质谱", "蛋白质组", "基因编辑",
"有限元", "蒙特卡洛", "分子动力学", "密度泛函", "第一性原理",
]
PROPERTY_WORDS = [
"性能", "精度", "准确率", "召回", "鲁棒性", "稳健性", "可解释性", "效率",
"速度", "泛化", "小样本", "零样本", "长尾", "公平性", "隐私", "安全",
"可扩展性", "实时", "低功耗", "成本", "灵敏度", "特异性", "稳定性",
]
DOMAIN_WORDS = [
"医学", "医疗", "临床", "影像", "病理", "金融", "材料", "化学", "生物",
"遥感", "自然语言", "语音", "推荐", "自动驾驶", "机器人", "气候", "能源",
"教育", "农业", "工业", "网络安全", "社交网络", "知识图谱",
]
TASK_WORDS = [
"分割", "检测", "识别", "分类", "预测", "生成", "翻译", "问答", "摘要",
"推荐", "排序", "聚类", "降噪", "重建", "配准", "跟踪", "优化", "诊断",
]
STOPWORDS = set("的了和与及其在对为是用做想要把对于通过基于一个这个那个研究问题方法".split())
def _match(text: str, vocab: list[str]) -> list[str]:
"""在文本中匹配词库中出现的词(大小写不敏感)。"""
low = text.lower()
found = []
for w in vocab:
if w.lower() in low and w not in found:
found.append(w)
return found
def parse_idea(idea: str) -> dict[str, list[str]]:
"""从念头中识别概念槽位。"""
return {
"methods": _match(idea, METHOD_WORDS),
"properties": _match(idea, PROPERTY_WORDS),
"domains": _match(idea, DOMAIN_WORDS),
"tasks": _match(idea, TASK_WORDS),
}
def _first(slot: list[str], default: str) -> str:
return slot[0] if slot else default
def build_5w1h(idea: str, slots: dict[str, list[str]]) -> dict[str, list[str]]:
"""结合槽位生成贴合输入的六维引导问题。"""
method = _first(slots["methods"], "你打算用的方法")
prop = _first(slots["properties"], "你关心的指标")
domain = _first(slots["domains"], "你的应用领域")
task = _first(slots["tasks"], "你要解决的任务")
return {
"What研究什么": [
f"你要解决的核心任务「{task}」具体如何定义?输入输出分别是什么?",
f"衡量「{prop}」的量化指标是什么?怎么测?基线是多少?",
f"研究对象的边界在哪?哪些情况算成功,哪些不在范围内?",
],
"Why为什么重要": [
f"现有方法在「{task}」上「{prop}」不足的根因是什么?",
f"如果解决了,能带来什么理论或应用价值?谁会在意这个结果?",
"不做这个研究,最坏的后果或被忽视的代价是什么?",
],
"Who谁受益/谁在做)": [
f"{domain}」领域里谁最需要这个结果?是研究者、工程师还是终端用户?",
f"目前还有哪些团队在做「{method}」相关方向?他们的代表工作是什么?",
"你的目标读者群体(投稿期刊/会议)是谁?他们看重什么?",
],
"When时间范围": [
"这个问题是新近出现的,还是长期未解决的老问题?",
f"{method}」相关技术近 2-3 年有哪些关键进展,改变了可行性?",
"你的数据/结论是否有时效性,会不会很快过时?",
],
"Where应用场景/验证环境)": [
f"你会在哪些数据集或真实环境上验证「{task}」?为什么选它们?",
f"结果能否泛化到「{domain}」之外的场景?边界在哪?",
"部署或复现需要什么条件(算力、数据可得性、伦理审批)?",
],
"How怎么做": [
f"用「{method}」解决的核心机制设想是什么?为什么它可能改善「{prop}」?",
"需要哪些数据/实验?最关键的一个对照实验怎么设计?",
"如何验证假设成立?需要哪些消融来排除替代解释?",
],
}
GAP_FRAMEWORK = {
"文献缺口": [
"哪些子问题已被反复研究、哪些几乎没人碰?",
"现有综述的结论分歧点在哪?是否有相互矛盾的结果未被调和?",
],
"方法缺口": [
"现有方法共同的失败模式是什么?是否都依赖某个不现实的假设?",
"是否存在「换个方法可能更好但没人试过」的组合?",
],
"应用缺口": [
"实验室结论与真实部署之间,有哪条「最后一公里」没人走通?",
"是否有理论成熟但缺乏落地验证的方向?",
],
"交叉缺口": [
"邻近领域有没有成熟工具,可迁移来解决本领域的难题?",
"两个看似无关的方向结合,是否会产生新问题?",
],
"时间缺口": [
"近期的新数据/新工具,是否让旧问题值得重做?",
"有没有因技术限制被搁置、如今可重启的老课题?",
],
}
def build_question_card(idea: str, slots: dict[str, list[str]]) -> dict[str, Any]:
"""生成研究契约卡(待填模板 + 自检要点)。"""
task = _first(slots["tasks"], "目标任务")
prop = _first(slots["properties"], "目标指标")
method = _first(slots["methods"], "拟用方法")
return {
"研究问题": f"(用一句可证伪的话写出):在 ____ 条件下,{method} 能否显著提升 {task}{prop}",
"核心假设": f"采用 {method} 会因为 ____机制而使 {prop} 相比基线提升 ____幅度",
"现有证据": "(列出支持该假设的已知工作/预实验,标注来源):",
"缺失证据": "(要支持假设还差哪些实验/数据/理论推导):",
"支持标准": f"{prop} 在 ____ 数据集上相对基线提升 ____ 且通过显著性检验,则支持假设。",
"证伪标准": f"若提升不显著、或在 ____ 场景下反而下降,则假设被推翻或需修正。",
"最小可行下一步": "(一周内能完成、能给出方向性证据的最小实验):",
}
def smart_check(slots: dict[str, list[str]]) -> dict[str, str]:
"""SMART 原则自检提示。"""
has_metric = bool(slots["properties"])
has_task = bool(slots["tasks"])
return {
"Specific具体": "问题是否聚焦到单一可回答的点?避免「研究 X 领域」这种泛题。",
"Measurable可测": ("已识别到量化指标,注意定义清楚测法。" if has_metric
else "未识别到明确指标,请先确定用什么数字衡量成败。"),
"Achievable可行": "现有资源(数据/算力/时间)是否支撑?最小实验能否一周内跑通?",
"Relevant相关": ("任务方向明确。" if has_task else "任务不够明确,先锁定要解决的具体任务。"),
"Time-bound有期限": "是否给关键里程碑设了时间点?开题/预实验/初稿的节点在哪?",
}
def forge(idea: str) -> dict[str, Any]:
"""主流程:拆题 + 缺口 + 契约卡 + 自检。"""
idea = idea.strip()
slots = parse_idea(idea)
return {
"idea": idea,
"slots": slots,
"five_w_one_h": build_5w1h(idea, slots),
"gap_framework": GAP_FRAMEWORK,
"question_card": build_question_card(idea, slots),
"smart_check": smart_check(slots),
}
def render_markdown(result: dict[str, Any]) -> str:
slots = result["slots"]
lines = [
f"# 研究念头拆解 — {result['idea']}",
"",
"> 本文档由 idea-forge 生成,把模糊念头拆成能下手的问题与一份研究契约卡。"
"标 ____ 处需你填写。",
"",
"## 一、识别到的概念槽位",
"",
f"- 方法:{(''.join(slots['methods']) or '(未识别,建议在念头中点明)')}",
f"- 任务:{(''.join(slots['tasks']) or '(未识别)')}",
f"- 指标/属性:{(''.join(slots['properties']) or '(未识别)')}",
f"- 领域:{(''.join(slots['domains']) or '(未识别)')}",
"",
"## 二、5W1H 拆题",
"",
]
for dim, qs in result["five_w_one_h"].items():
lines.append(f"### {dim}")
for q in qs:
lines.append(f"- {q}")
lines.append("")
lines += ["## 三、研究缺口分析框架", ""]
for cat, qs in result["gap_framework"].items():
lines.append(f"### {cat}")
for q in qs:
lines.append(f"- {q}")
lines.append("")
lines += ["## 四、研究契约卡", ""]
for k, v in result["question_card"].items():
lines.append(f"- **{k}**{v}")
lines.append("")
lines += ["## 五、SMART 自检", ""]
for k, v in result["smart_check"].items():
lines.append(f"- **{k}**{v}")
lines.append("")
lines += ["---", "", "由 idea-forge 生成。纯本地脚手架,不替你想 idea只帮你把念头理成能查的问题。"]
return "\n".join(lines)
def main(argv: list[str] | None = None) -> int:
p = argparse.ArgumentParser(prog="idea-forge", description="研究念头拆解器")
p.add_argument("--idea", help="研究念头(一句话)")
p.add_argument("--idea-file", type=Path, help="从文件读取念头")
p.add_argument("--format", choices=["markdown", "json"], default="markdown")
p.add_argument("--output", type=Path, help="输出文件")
args = p.parse_args(argv)
if args.idea_file:
idea = args.idea_file.read_text(encoding="utf-8")
elif args.idea:
idea = args.idea
else:
print("错误:请用 --idea 或 --idea-file 提供研究念头。", file=sys.stderr)
return 2
result = forge(idea)
out = (json.dumps(result, ensure_ascii=False, indent=2) if args.format == "json"
else render_markdown(result))
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(out, encoding="utf-8")
print(f"已写入 {args.output}")
else:
print(out)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,81 @@
"""idea-forge 单元测试。"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
import pytest
from forge import parse_idea, build_5w1h, build_question_card, smart_check, forge, render_markdown
class TestParseIdea:
def test_identifies_method_and_task(self):
s = parse_idea("用对比学习改进医学影像分割的小样本精度")
assert "对比学习" in s["methods"]
assert "分割" in s["tasks"]
assert "小样本" in s["properties"] or "精度" in s["properties"]
assert "医学" in s["domains"] or "影像" in s["domains"]
def test_empty_slots(self):
s = parse_idea("我想研究一些东西")
assert s["methods"] == []
assert s["tasks"] == []
class TestBuild5W1H:
def test_six_dimensions(self):
s = parse_idea("用强化学习做机器人控制")
w = build_5w1h("用强化学习做机器人控制", s)
assert len(w) == 6
for dim in ("What", "Why", "Who", "When", "Where", "How"):
assert any(dim in k for k in w.keys())
def test_questions_reflect_slots(self):
s = parse_idea("用扩散模型做图像生成")
w = build_5w1h("用扩散模型做图像生成", s)
joined = "\n".join(q for qs in w.values() for q in qs)
assert "扩散模型" in joined # 方法槽位嵌入了问题
assert "生成" in joined # 任务槽位嵌入了问题
class TestQuestionCard:
def test_card_fields(self):
s = parse_idea("用迁移学习提升分类准确率")
card = build_question_card("用迁移学习提升分类准确率", s)
for field in ("研究问题", "核心假设", "现有证据", "缺失证据",
"支持标准", "证伪标准", "最小可行下一步"):
assert field in card
def test_card_embeds_method(self):
s = parse_idea("用知识蒸馏压缩模型")
card = build_question_card("用知识蒸馏压缩模型", s)
assert "知识蒸馏" in card["核心假设"]
class TestSmartCheck:
def test_metric_detected(self):
s = parse_idea("提升召回率")
chk = smart_check(s)
assert "已识别到量化指标" in chk["Measurable可测"]
def test_metric_missing(self):
s = parse_idea("研究图神经网络")
chk = smart_check(s)
assert "未识别到明确指标" in chk["Measurable可测"]
class TestForgeAndRender:
def test_full(self):
r = forge("用对比学习改进医学影像分割的小样本精度")
assert "five_w_one_h" in r and "gap_framework" in r
assert "question_card" in r and "smart_check" in r
def test_render(self):
md = render_markdown(forge("用自监督学习做语音识别"))
assert "研究念头拆解" in md
assert "5W1H 拆题" in md
assert "研究契约卡" in md
assert "SMART 自检" in md
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-v"]))