☰
AI辅助科研选题:大模型生成、去重聚类与多维打分的idea流水线
2026/10/26 10:35:53 网站建设 项目流程

刚进课题组的第一年,最难的往往不是跑通代码,而是回答“我到底要做什么”这个问题。标题里的“博一”点出了这类需求最典型的场景:文献读了一堆,脑子里零散的想法也不少,但真要落成一个能开题、能投稿、能撑三年的题目,总觉得差一口气。于是很多人开始把大模型当成许愿池,输入“帮我生成几个创新点”,拿回来十几条看起来很像论文摘要的句子,读着都通顺,落地全都不行。

这篇就围绕“用 AI 辅助产生研究 idea”这个主题,把我自己反复用过、也踩过坑的一套流程完整拆出来:怎么让模型稳定输出结构化的候选想法,怎么用脚本批量生成、去重、聚类、打分,怎么用文献接口做最近邻工作检查,以及哪些地方必须由人接手。适合刚进组的研究生、需要写开题报告的读者,也适合任何想用 AI 提升“选题效率”而不是“选题数量”的开发者。读完之后你应该能搭起一条属于自己的候选想法流水线,而不是每次从头对着对话框发呆。

1. 背景与核心概念

1.1 为什么“让 AI 给我想法”经常得到平庸结果

先把这个现象解释清楚,后面的方法才有意义。大模型本质上是一个在大量语料上训练出来的概率模型,它的输出天然趋向“最大公约数”——也就是语料里出现频次最高、被讨论最多、最不容易出错的方向。当你问“这个领域有什么值得做的问题”,它给你的答案往往就是那几条人人都会想到的主线,而且措辞非常像综述的引言部分。这不是模型不行,而是你的提问方式把它推向了“安全区”。

第二个原因是缺少约束。人类研究员在构思时会自动带上大量隐含条件:实验室有没有对应的设备、自己会不会那套方法、能不能拿到数据、半年内能不能出第一版结果。模型不知道这些,所以它给出的想法经常“正确但不可执行”,比如建议你在一个需要百万级标注数据的任务上做细粒度改进。

第三个原因是缺少可验证性。一句“结合对比学习提升表征质量”听起来是个方向,但它不是假设,没法证伪,也没法设计实验。真正能推进研究的想法,必须能落到“如果我这么做,预期看到什么现象;如果看到相反现象,说明什么”。这一点是后面所有提示词设计的核心。

理解了这三个失效原因,就能明白正确的做法不是“换一个更强的模型”,而是把人的约束条件、评价标准和流程控制注入进去,让模型在一个被限定的空间里发散。

1.2 好 idea 的三个可操作判据

“好”这个字太抽象,得拆成能打分的东西。我在实践里固定用三个维度,每个维度都能用 1 到 5 分粗略评估:

维度含义判断提问
新颖性与最近邻工作的差异是否说得清与最接近的三篇工作相比,差别具体在哪一行
可行性数据、算力、时间、技能是否齐备两周内能不能跑出第一个 baseline
价值解决之后谁会受益是改了一个指标,还是回答了一个开放问题

这三个维度之间存在真实的张力,而不是走形式。追求极端新颖,往往会掉进“没人关心”的坑;追求极端可行,容易变成“把 A 方法套到 B 数据集上”的增量工作。所以不要指望某一项拿满分,更合理的做法是三项都不低于 3 分,然后在某一项上做到 4 分以上。

除了这三项,还有一条隐藏门槛:可证伪性。如果一个想法无论实验结果如何都能自圆其说,那它就不是研究假设,而是营销话术。写下来的时候要能补上一句“如果观察到 X 现象,则假设被削弱”,这句话写不出来,说明想法还没成型。

1.3 AI 在选题流程中的三种角色

把模型当成“想法供应商”是最容易失望的用法,把它当成流程里的三个具体岗位则稳定得多:

  • 发散器:在给定约束下批量产出候选想法,追求数量和组合多样性,不追求质量。
  • 检索器:把候选想法翻译成检索式,帮你快速定位最近邻工作,判断这个方向是不是已经被做烂。
  • 批评者:扮演审稿人、工程师、竞争者三种视角,对想法做压力测试,指出最可能被攻击的地方。

这三种角色的提示词完全不同,甚至互相冲突:发散阶段需要高温度、鼓励奇怪组合;批评阶段需要低温度、鼓励挑刺。把它们混在一个对话里,模型会左右为难,最后给你一堆四平八稳的中间态。所以工程上正确的做法是拆成独立脚本、独立提示词、独立输出文件,中间用结构化数据衔接。

至于最终决策——选哪个方向、投入多少时间、要不要换题——这部分不要交给模型。它可以给你排序建议,但它不承担沉没成本,也不了解你的导师偏好和实验室资源。

2. 环境准备与工具选型

2.1 软硬件与运行环境

这套流程对硬件要求很低,绝大部分计算发生在远端 API 上,本地只负责编排、解析和简单统计。下面是我常用的基线环境,具体版本请按你所在实验室的实际情况调整,不必强行对齐:

项目建议说明
操作系统macOS / Ubuntu / Windows + WSL2脚本均为跨平台 Python,路径用 pathlib 处理
Python3.9 及以上用到 f-string、pathlib、类型注解等特性
依赖管理venv 或 conda避免污染系统环境
主要库openai、requests、numpy其余用标准库完成
编辑器VS Code / PyCharm需要能方便地查看 JSONL 文件

如果你的课题涉及本地跑模型(比如做嵌入向量),再额外准备一块显卡;但这篇文章的流程不依赖本地推理,纯 API 编排就能跑通。这一点很重要:很多同学卡在“我机器不行”上,其实选题阶段的信息处理根本用不到大算力。

2.2 工具组合与职责划分

把工具按职责分清楚,能避免后面互相打架:

环节工具职责
批量发散大模型对话接口按约束生成结构化候选想法
文献检索arXiv API、OpenAlex 等公开接口拉取候选文献元数据
去重聚类本地 Python 脚本把语义相近的想法合并
打分排序大模型 + 规则加权多维度评分并排序
人工复核你自己 + 笔记工具最终判断与修改

这里要强调一点:不要让模型直接做“去重”。模型判断两条想法是否重复时很不稳定,同一个问题问两次可能给出相反结论。去重这种确定性任务交给代码,用可解释的相似度算法做,结果可复现、可调阈值。

2.3 项目目录结构

建议一开始就把目录定好,后面每加一个环节只新增文件,不打乱结构:

idea-lab/ ├── .env # 密钥,不提交到版本库 ├── .gitignore ├── prompts/ │ ├── diverge.txt # 发散模板 │ ├── critique.txt # 批评模板 │ ├── experiment.txt # 实验设计模板 │ └── onepager.txt # 一页纸开题模板 ├── pipeline/ │ ├── generate.py # 批量生成 │ ├── dedupe.py # 去重聚类 │ ├── score.py # 多维打分 │ └── arxiv_search.py # 文献检索 └── data/ ├── ideas.jsonl # 原始候选 ├── clusters.json # 聚类结果 └── scored.jsonl # 打分结果

用 JSONL(每行一个 JSON 对象)而不是一个大 JSON 数组,是因为追加写入方便、出错时不会整个文件损坏、也便于用命令行工具快速抽查。这个习惯在处理几百条候选想法时非常省事。

2.4 密钥与配置管理

密钥管理是最容易被忽略的一环。最低要求是:密钥放环境变量或.env,.gitignore里排除.env,脚本里绝对不硬编码。如果多人协作,用各自的密钥,不要共享。

# 文件路径:.gitignore .env data/ __pycache__/ *.pyc
# 文件路径:.env(示例,请替换为你的真实配置) LLM_API_KEY=your_api_key_here LLM_BASE_URL=https://api.openai.com/v1 LLM_MODEL=your-model-name
# 文件路径:pipeline/config.py import os from pathlib import Path def load_env(path: str = ".env") -> None: """极简 .env 加载器,避免额外依赖。""" p = Path(path) if not p.exists(): return for line in p.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) os.environ.setdefault(key.strip(), value.strip()) load_env() API_KEY = os.environ.get("LLM_API_KEY", "") BASE_URL = os.environ.get("LLM_BASE_URL", "https://api.openai.com/v1") MODEL = os.environ.get("LLM_MODEL", "")

这段代码只做了最小职责:读文件、设环境变量、暴露三个配置项。用setdefault是为了让外部传入的环境变量优先,方便在 CI 或临时实验中覆盖配置。生产或正式项目里建议直接用python-dotenv,这里手写只是为了让脚本零额外依赖、方便复制。

3. 核心方法论:把“灵感”改造成“可验证假设”

3.1 想法的四层结构

一条能推进的候选想法,至少要能拆成四层。这个结构不是形式主义,它直接决定了你能不能设计实验、能不能被审稿人接受:

  1. 现象层:观察到什么异常或空白。比如某类方法在长尾样本上系统性退化。
  2. 假设层:用一句话解释原因,且必须可证伪。比如“退化源于特征归一化在小样本上的方差放大”。
  3. 干预层:你打算改什么。比如替换归一化方式或加一个方差校正项。
  4. 证据层:预期看到什么、如何度量。比如在三个长尾数据集上指标提升且方差下降。

绝大多数“看起来不错”的 AI 生成想法,只写了第 3 层。它告诉你“加一个模块”,但没告诉你为什么加、加了之后预期怎样。这就是为什么我把输出格式强制定义为包含 hypothesis、evidence_needed、risk 等字段的 JSON——逼着模型把四层补齐,哪怕补得不好,也便于人快速判断它到底缺哪一层。

3.2 四段式提示词骨架

我试过很多写法,最后稳定下来的是一个四段式骨架:角色、素材、约束、输出格式。四段缺一段,输出质量就会明显下滑:

  • 角色:告诉模型它现在是谁,以及评判标准。比如“你是一位熟悉该领域近三年工作的审稿人,偏好机制解释而非指标堆叠”。
  • 素材:给出锚点,通常是两三篇最相关的论文要点,或者你观察到的具体现象。没有素材,模型只能从语料里捞高频内容。
  • 约束:硬性条件,包括数据可得性、算力上限、时间预算、必须包含的元素。约束越具体,输出越可用。
  • 输出格式:明确字段名和类型,最好直接给一段示例 JSON。这一条对后续脚本解析至关重要。

关键经验是:不要在同一段提示词里既要求“大胆创新”又要求“严格可行”。这两件事会互相抵消。正确做法是分两轮,第一轮只发散,第二轮用批评模板筛,中间用文件衔接。

3.3 五种约束注入手法

约束不只是限制,它本身就是产生新意的工具。同一个锚点论文,换一种约束,出来的想法差别非常大。我常用的五种:

手法具体做法效果
换场景把方法迁移到资源受限场景容易产出工程型创新
加约束限定单卡、限定两周、限定公开数据淘汰空想,逼出务实方案
跨领域迁移引入其他学科的解释框架新颖性来源,但需验证合理性
反向提问假设主流结论不成立会怎样容易找到被忽略的边界条件
极端条件数据量趋近于零或特征维数极高暴露方法的隐含假设

实际使用时不要一次上五种,而是把每种约束单独跑一遍,生成五组候选,再去重合并。这样做的另一个好处是:你能清楚看到自己偏好的约束类型,从而在后续迭代中把精力集中在最有产出的那一两类上。

需要提醒的是,跨领域迁移和反向提问容易产出“听起来很妙但站不住”的想法。这类想法的价值在于启发,不在于直接采用,必须经过第 5 节的文献检查才能进入下一步。

3.4 三视角评审:把自我感觉换成外部标准

人对自己的想法的评价极不可靠,尤其是刚有点兴奋的时候。所以我固定用三个视角做压力测试,每个视角单独一轮,不混在一起:

  • 审稿人视角:攻击新颖性和实验充分性。典型问题包括“与某工作的区别是什么”“只在一个数据集上验证够不够”。
  • 工程师视角:攻击可行性。典型问题包括“数据从哪来”“显存够不够”“复现需要多久”。
  • 竞争者视角:假设别人也在做同一个方向,他们最容易先做出什么,你的优势在哪。

每一轮都要求模型输出“最致命的三个问题”,而不是“总体评价”。这个约束很关键:模型天生喜欢给平衡的、带鼓励的评价,强制它只输出攻击点,才能拿到真正有用的信息。拿到三个问题之后,再回到想法本身修改,通常两三轮之后,想法会从模糊变具体,或者干脆被淘汰——被淘汰也是好事,早淘汰比做完实验再淘汰便宜得多。

4. 完整实战:搭建 idea 生成与筛选流水线

4.1 定义数据契约

先定契约再写代码,能省掉大量返工。下面是我用的字段集合,用 JSON 对象描述,每条候选想法一行:

{ "title": "一句话标题,不超过20字", "phenomenon": "观察到的具体现象或空白", "hypothesis": "一句可证伪的假设", "intervention": "你打算改动什么", "novelty_angle": "与最近邻工作的差异点", "evidence_needed": ["需要的数据", "需要的对照实验"], "metric": "预期观察的指标", "risk": "最可能失败的原因", "constraint": "生成时使用的约束标签", "anchor": "生成时使用的锚点论文标识", "model": "生成所用模型标识" }

后面三个字段是元信息,用于追溯。没有constraint和anchor,你过两天回来根本不知道自己当时是怎么问出来的,也没法复现。这一点在候选想法积累到上百条时尤其重要,因为你的判断标准会随时间变化,需要能回看原始上下文。

4.2 撰写发散提示词模板

模板放在prompts/diverge.txt,用占位符{domain}、{anchor_paper}、{constraint}做替换。注意这里刻意要求“只输出 JSON”,避免模型加一堆解释性文字影响解析:

你是一位在 {domain} 方向有多年经验的科研合作者。你的任务是在给定约束下提出候选研究想法。 【锚点材料】 {anchor_paper} 【硬约束】 {constraint} 【要求】 1. 提出 3 个互不相同的候选想法,不要在同一思路下变体。 2. 每个想法必须包含一个可证伪的假设,写清"如果观察到什么现象则假设被削弱"。 3. 新颖性角度必须指向与锚点材料的具体差异,禁止使用"提升性能""增强鲁棒性"这类空泛表述。 4. 不得虚构论文、数据集或指标数值。不确定的地方写"需核实"。 5. 只输出 JSON,不要任何解释性文字。 【输出格式】 {"ideas": [{"title": "", "phenomenon": "", "hypothesis": "", "intervention": "", "novelty_angle": "", "evidence_needed": [], "metric": "", "risk": ""}]}

第 4 条是最关键的一条。不加这条,模型会非常自然地编造出“在某某数据集上提升 3.2 个点”这种看起来专业的假数据,而这类假数据一旦进入你的笔记,很容易在后期被误当成真实结论引用。

4.3 编写批量生成脚本

下面是完整的生成脚本骨架,依赖openai1.x 版本的客户端写法。注意 SDK 版本差异较大,如果你用的是更早的版本,client.chat.completions.create这部分需要按官方文档调整。

# 文件路径:pipeline/generate.py # 依赖:pip install openai import json import time from pathlib import Path from openai import OpenAI from config import API_KEY, BASE_URL, MODEL client = OpenAI(api_key=API_KEY, base_url=BASE_URL) PROMPT_PATH = Path("prompts/diverge.txt") OUT_PATH = Path("data/ideas.jsonl") DOMAIN = "你的研究方向,例如图神经网络在分子性质预测中的应用" ANCHORS = [ "锚点论文A:问题设定、方法要点、主要结论、局限性", "锚点论文B:问题设定、方法要点、主要结论、局限性", ] CONSTRAINTS = [ "限定单卡消费级显卡可训练规模,两周内可跑出baseline", "必须使用公开数据集,且数据规模不超过10万样本", "要求给出机制层面的解释,不接受纯指标堆叠", "从其他学科的解释框架切入,需说明合理性", "反向思考:假设该方向的主流假设不成立,会观察到什么", ] def build_prompt(anchor: str, constraint: str) -> str: template = PROMPT_PATH.read_text(encoding="utf-8") return template.format(domain=DOMAIN, anchor_paper=anchor, constraint=constraint) def call_llm(prompt: str, temperature: float = 1.0) -> str: resp = client.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": "你是一名严谨的科研合作者,只输出符合要求的 JSON。"}, {"role": "user", "content": prompt}, ], temperature=temperature, response_format={"type": "json_object"}, ) return resp.choices[0].message.content def main() -> None: OUT_PATH.parent.mkdir(parents=True, exist_ok=True) total = 0 with OUT_PATH.open("a", encoding="utf-8") as f: for constraint in CONSTRAINTS: for anchor in ANCHORS: prompt = build_prompt(anchor, constraint) try: raw = call_llm(prompt) data = json.loads(raw) except Exception as exc: print(f"[skip] constraint={constraint[:12]} err={exc}") time.sleep(2) continue for item in data.get("ideas", []): item["constraint"] = constraint item["anchor"] = anchor item["model"] = MODEL f.write(json.dumps(item, ensure_ascii=False) + "\n") total += 1 time.sleep(1) print(f"done, wrote {total} ideas -> {OUT_PATH}") if __name__ == "__main__": main()

几个设计点值得说明。第一,用追加模式打开文件,中途失败不会丢已有结果。第二,异常处理单独包住每次调用,某一次解析失败不影响整体。第三,sleep(1)是为了避开接口限流,如果你的配额较高可以调小,反之调大。第四,response_format参数并非所有模型都支持,如果你的接口报错,去掉这一行并在提示词里加强“只输出 JSON”的要求即可。

按 5 个约束 × 2 个锚点计算,一轮跑完大约得到 30 条候选。想扩量就加锚点,不建议加温度——温度过高会让输出变得发散但离题。

4.4 去重与聚类

30 条候选里通常有 10 条左右是同一个意思的不同说法。用代码做去重,比让模型判断可靠得多。下面是一个不依赖第三方分词库的简易实现,用字符级 token 的 Jaccard 相似度做贪心聚类:

# 文件路径:pipeline/dedupe.py import json import re from pathlib import Path def tokens(text: str) -> set: text = (text or "").lower() return set(re.findall(r"[a-z0-9]+|[\u4e00-\u9fa5]", text)) def jaccard(a: str, b: str) -> float: ta, tb = tokens(a), tokens(b) if not ta or not tb: return 0.0 return len(ta & tb) / len(ta | tb) def load(path: str): lines = Path(path).read_text(encoding="utf-8").splitlines() return [json.loads(line) for line in lines if line.strip()] def cluster(items, threshold: float = 0.35): clusters = [] for item in items: text = f"{item.get('title','')} {item.get('hypothesis','')}" placed = False for c in clusters: if jaccard(text, c["rep_text"]) >= threshold: c["members"].append(item) placed = True break if not placed: clusters.append({"rep_text": text, "rep": item, "members": [item]}) return clusters def main() -> None: items = load("data/ideas.jsonl") clusters = cluster(items) clusters.sort(key=lambda c: len(c["members"]), reverse=True) out = [] for idx, c in enumerate(clusters, start=1): out.append({ "cluster_id": idx, "size": len(c["members"]), "representative": c["rep"], "members": [m.get("title", "") for m in c["members"]], }) Path("data/clusters.json").write_text( json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8" ) print(f"{len(items)} ideas -> {len(clusters)} clusters") if __name__ == "__main__": main()

这里的 token 化方式非常粗糙,中文按单字切、英文按单词切。它能抓住“用词高度重叠”的重复,但抓不住“换了说法”的重复。如果你的候选量大、语义重复多,可以换成嵌入向量加余弦相似度,阈值通常设在 0.8 左右。先用简单版跑通,觉得不够再升级,不要一上来就上复杂方案。

输出的clusters.json里,size大于 1 的簇值得重点关注:它们要么是真正的高频共识方向(可能已被做烂),要么是模型反复回到的“安全区”。反而size为 1 的孤立想法更值得看一眼,因为那往往是约束注入真正起作用的产物。

4.5 多维打分与排序

打分用批评模板,让模型按固定 rubric 输出数值,本地做加权。评分项固定为四项,权重代表你的个人偏好,可以按课题组风格调整:

# 文件路径:pipeline/score.py import json from pathlib import Path from openai import OpenAI from config import API_KEY, BASE_URL, MODEL from dedupe import jaccard client = OpenAI(api_key=API_KEY, base_url=BASE_URL) WEIGHTS = {"novelty": 0.35, "feasibility": 0.30, "value": 0.25, "clarity": 0.10} RUBRIC = """请以严格审稿人的身份为该研究想法打分,1-5分,整数。 novelty:与最接近的已有工作的差异是否具体、可验证 feasibility:数据、算力、时间、技能是否现实可得 value:解决后能回答的具体问题或带来的实际收益 clarity:假设是否可证伪、实验是否可设计 只输出 JSON:{"novelty":0,"feasibility":0,"value":0,"clarity":0,"fatal_flaw":"最致命的一个问题"} """ def score_one(item: dict) -> dict: payload = json.dumps(item, ensure_ascii=False) resp = client.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": "你只输出合法 JSON,不给任何额外说明。"}, {"role": "user", "content": RUBRIC + "\n【候选想法】\n" + payload}, ], temperature=0.2, response_format={"type": "json_object"}, ) return json.loads(resp.choices[0].message.content) def main() -> None: clusters = json.loads(Path("data/clusters.json").read_text(encoding="utf-8")) results = [] for c in clusters: item = c["representative"] try: s = score_one(item) except Exception as exc: print(f"[skip] cluster={c['cluster_id']} err={exc}") continue total = sum(WEIGHTS[k] * float(s.get(k, 0)) for k in WEIGHTS) results.append({ "cluster_id": c["cluster_id"], "size": c["size"], "title": item.get("title", ""), "scores": s, "weighted": round(total, 3), "item": item, }) results.sort(key=lambda r: r["weighted"], reverse=True) out = Path("data/scored.jsonl") with out.open("w", encoding="utf-8") as f: for r in results: f.write(json.dumps(r, ensure_ascii=False) + "\n") for r in results[:10]: print(f"{r['weighted']:.2f} {r['title']}") if __name__ == "__main__": main()

温度设成 0.2 是为了让评分稳定,同一批想法跑两次结果差异不大。如果你发现某条想法两次评分差距超过 1 分,说明它的描述本身有歧义,这本身就是一个有价值的信号——多半是假设写得不清楚。

这里没有做严格的一致性校验,实际使用时建议人工抽查前 10 条,看看模型给的分数和你自己的直觉差多少。如果系统性偏高,说明 rubric 里的措辞还不够严格,可以加上“多数想法应在 2 到 3 分之间”这类校准语句。

4.6 运行与结果解读

完整跑一遍的流程是:

# 文件路径:项目根目录 python pipeline/generate.py python pipeline/dedupe.py python pipeline/score.py

预期输出大致如下:

done, wrote 30 ideas -> data/ideas.jsonl 30 ideas -> 14 clusters 3.85 基于方差校正的长尾样本表征学习方法 3.62 跨域迁移中的特征对齐失效边界分析 3.41 小样本条件下的归一化选择准则 ...

接下来是整条流水线里最重要的一步:不要只看第一名。我的实际经验是,排在第 2 到第 5 位的候选往往比第一名更值得做,因为第一名通常是模型眼里的“标准答案”,也就是大家都想到的方向,新颖性分数高但实际已被大量工作覆盖。第 2 到第 5 位经常是约束注入产生的边缘组合,做的人少,可行性也还过得去。

另外,一定要打开scored.jsonl看fatal_flaw字段。这一栏里的内容比分数本身有用得多,很多想法是被一个具体问题否决的,而这个问题能不能绕过,往往决定了它最后的命运。

5. 文献支撑:让 idea 站得住

5.1 用 arXiv API 拉取候选文献

想法再多,不落回文献就是空谈。arXiv 提供公开的查询接口,返回 Atom XML,用标准库就能解析,不需要额外依赖:

# 文件路径:pipeline/arxiv_search.py import urllib.parse import urllib.request import xml.etree.ElementTree as ET NS = {"atom": "http://www.w3.org/2005/Atom"} def search(query: str, max_results: int = 20) -> list: params = { "search_query": query, "start": 0, "max_results": max_results, "sortBy": "relevance", } url = "http://export.arxiv.org/api/query?" + urllib.parse.urlencode(params) with urllib.request.urlopen(url, timeout=30) as resp: raw = resp.read() root = ET.fromstring(raw) items = [] for entry in root.findall("atom:entry", NS): items.append({ "title": (entry.findtext("atom:title", default="", namespaces=NS) or "").strip(), "summary": (entry.findtext("atom:summary", default="", namespaces=NS) or "").strip(), "published": entry.findtext("atom:published", default="", namespaces=NS), "link": entry.findtext("atom:id", default="", namespaces=NS), }) return items if __name__ == "__main__": for item in search('all:"graph neural network" AND all:"molecular property"', 10): print(item["published"][:10], item["title"])

查询语法遵循 arXiv 的字段约定,all:表示全字段,ti:表示标题,abs:表示摘要,多个条件用AND、OR、ANDNOT连接。构造检索式时有个小技巧:直接让模型把你的候选想法翻译成三条不同的 arXiv 查询语句,然后三条都跑一遍,取并集。单一查询容易漏掉换了术语的同类工作。

接口有访问频率限制,连续调用之间要留间隔,批量跑的时候加一个time.sleep(3)比较稳妥。

5.2 生成文献映射表

拿到文献元数据之后,让模型做一件它擅长且低风险的事:把候选文献归类到“解决什么问题、用什么方法、结论是什么、局限在哪”四栏里。这一步的输出是表格,不是结论,人只需要扫一眼就能判断自己的方向有没有被覆盖。

你是文献整理助手。下面是若干篇论文的标题和摘要。 请为每篇输出一行:问题 | 方法 | 主要结论 | 明确写出的局限 要求: 1. 只依据给出的摘要内容,摘要未提及的写"未提及",禁止推测。 2. 结论部分不得编造具体数值。 3. 输出为 Markdown 表格,不要额外解释。

“摘要未提及的写未提及”这条约束非常重要。不加它,模型会基于标题和常识补全,看起来表格很完整,实际上全是推测。而这份表格是要用来判断新颖性的,一旦掺了推测,判断就失去意义。

5.3 最近邻工作检查与 gap 判断

把映射表和你的候选想法放在一起,问三个问题:

  1. 有没有一篇工作的问题设定和我完全一样?如果有,我的差异点还成立吗。
  2. 有没有一篇工作已经指出了我要解释的那个现象?如果有,我的假设是不是别人的已知结论。
  3. 我的“空白”是真的空白,还是只是我没检索到?换三组术语再查一遍。

第三点是最容易出错的。同一个问题在不同社区里有不同叫法,长期退化、分布偏移、尾部性能下降,说的可能是同一件事。判断 gap 时,宁可假设它已经被做过,然后去找差异,也不要假设没人做过。前者最多多花一天检索,后者可能让你在开题后被指出“这个早有人做了”。

一个实用的判定标准:如果你能用一句话说清“我和最近的那篇工作的差别在于 X,而 X 会导致 Y 上的不同表现”,这个想法就有继续做的价值;如果说不清,或者只能说“我们组合了两种方法”,那多半是增量工作,需要重新设计角度。

6. 常见问题与排查思路

6.1 高频问题速查表

问题现象常见原因解决思路
返回内容无法被 JSON 解析模型加了说明文字或代码围栏强化“只输出 JSON”约束,或用正则截取第一个{到最后一个}
想法高度雷同锚点太少、约束太弱增加锚点数量,把约束写得可量化
想法天马行空无法落地缺少可行性约束加入算力、数据、时间三项硬限制
调用频繁报错触发接口限流增大sleep间隔,加指数退避重试
评分波动大温度过高或描述有歧义温度降到 0.2,重写假设使表述唯一
文献表格出现不存在的结论未限制“只依据摘要”加约束并在人工复核时抽查
聚类结果不理想相似度算法过于粗糙换嵌入向量,或调低阈值到 0.25

6.2 三个典型故障的详细排查

第一个是 JSON 解析失败。这在接入不同模型时极常见,有些模型即使开了response_format也会在前后加一句“好的,以下是结果”。排查顺序是:先打印原始返回的前 200 个字符,确认是不是格式问题;如果是,加一个容错解析函数,用raw.find("{")和raw.rfind("}")截取中间部分再json.loads;仍然失败就检查提示词里是否给了完整的示例结构。

第二个是生成结果“看起来都差不多”。根本原因通常是锚点材料太少,模型的输出空间被限制在锚点附近。解决办法是增加锚点的多样性,比如选一篇本方向的、一篇相邻方向的、一篇方法论的,让模型有更多可组合的素材。另一个办法是加一条约束:“禁止使用锚点材料中出现过的核心术语”,强迫它换角度描述。

第三个是想法落地时发现数据拿不到。这是最伤的问题,因为要推翻重来。预防办法是在生成阶段就把“数据来源”作为必填字段,并在提示词里明确“必须使用公开可下载的数据集,且给出数据集名称”。如果模型给不出具体名称,只写“需核实”,那这条想法在可行性上就要扣分。养成这个习惯之后,能省掉大量“想得很美但没数据”的返工。

7. 可复用提示词模板库

7.1 发散模板

上面 4.2 节的模板已经完整可用,这里补充一个变体,用于“从一个模糊方向出发”的阶段。区别是把锚点材料换成你自己的观察记录:

你是一位该方向的资深研究者。我目前只有一个模糊的观察,需要你帮我把它拆成可验证的候选假设。 【我的观察】 {observation} 【已知条件】 {known_conditions} 【要求】 1. 给出 3 种可能的解释,每种解释都要能对应一个可证伪的实验。 2. 明确指出这三种解释中哪一种最容易被现有数据否定。 3. 不得引用不确定的文献,需要引用时写"需核实"。 4. 只输出 JSON:{"explanations":[{"explanation":"","test":"","expected_if_true":"","expected_if_false":""}]}

这个模板产出的东西不是“想法”,而是“解释竞争”。它的好处是把研究从“我要提出一个新方法”转向“我要区分几种可能”,后者更容易做出扎实的工作,也更难被审稿人一句“增量改进”打掉。

7.2 批评模板

你是该领域顶会的审稿人,风格严格,只关注能否被推翻。 【候选想法】 {idea_json} 【任务】 1. 给出三个最致命的问题,每个问题必须指向具体的缺失证据或逻辑跳跃。 2. 对每个问题,说明作者需要补充什么实验才能回应。 3. 判断该想法属于以下哪一类:可继续 / 需重构 / 建议放弃,并给出一句话理由。 4. 不要给鼓励性评价,不要复述想法内容。

第 4 条是必须的。不写这条,模型有相当高的概率先来一段“这是一个有价值的方向”,把真正有用的批评稀释掉。

7.3 实验设计模板

想法通过批评之后,进入实验设计。这一步最容易出问题的地方是对照组不完整:

你是实验设计顾问。请为该假设设计一套最小可验证实验。 【假设】 {hypothesis} 【资源约束】 {resources} 【要求】 1. 列出必要对照组,说明每组排除什么替代解释。 2. 给出主指标与辅助指标,说明为什么选它们。 3. 指出该实验最可能的失败模式,以及失败后应如何调整假设。 4. 估算完成时间,按周为单位。 5. 输出 JSON:{"groups":[],"metrics":[],"failure_modes":[],"weeks":0}

“每组排除什么替代解释”这一条是核心。很多实验做完发现结论站不住,就是因为对照组只是“不加模块”,而没排除“参数量增加”这个替代解释。让模型在动手前把这件事想清楚,比做完再补实验便宜得多。

7.4 一页纸开题模板

最后一步是把选中的想法压缩成一页纸。这个动作本身就有筛选作用:写不满一页说明还没想清楚,写超过两页说明还没收敛。

请把以下材料压缩成一页纸研究计划,严格控制在 600 字以内。 【材料】 {selected_idea_and_evidence} 【结构要求】 1. 问题:一句话说明要解决什么,以及为什么现在值得解决。 2. 现状:两句话说明最接近的工作做到了哪一步。 3. 假设:一句话,可证伪。 4. 方法:三句话,说明干预手段与关键设计。 5. 验证:两句话,说明数据集、对照与指标。 6. 风险:一句话,说明最可能失败的原因与备选方案。 禁止使用"显著提升""有效解决""具有重要意义"这类空话。

最后那句禁止清单很关键,它逼着输出落到具体动作上。写完这一页纸,你对这个题目的把握程度会明显不一样。

8. 最佳实践与工程建议

8.1 可复现:记录种子、版本与提示词

候选想法是会贬值的资产。今天觉得一般的想法,三个月后可能有新价值,但前提是你还记得它是在什么条件下生成的。所以每条记录都要带上:使用的模型标识、提示词版本、约束标签、生成时间。最省事的做法是把提示词文件也纳入版本管理,每次改动提交一次,记录里写 git 短哈希。

# 文件路径:项目根目录 git init git add prompts/ pipeline/ .gitignore git commit -m "init: idea pipeline v1" git rev-parse --short HEAD

这样做的收益在三个月后会非常明显:当你想复现某一批候选时,能准确回到当时的提示词版本,而不是对着一个已经改了五遍的模板猜。

8.2 幻觉与引用治理

这条要单独强调,因为它是把 AI 用在科研场景里最大的风险。三条硬规则:

  1. 任何具体数值、数据集名称、论文标题,都必须人工核实后才能进入笔记。
  2. 提示词里固定加“不确定的写需核实”,并且要求模型标注不确定处。
  3. 文献表格只允许基于给定摘要生成,禁止模型自行补充背景。

还有一条容易被忽略:不要让模型“帮你写参考文献列表”。它生成的条目格式可能完全正确,但内容可能是拼接出来的。参考文献只能从检索结果里复制,不能生成。

8.3 数据与合规边界

如果你处理的文献包含未公开数据、合作方材料或受限制的内容,不要直接粘贴到第三方接口。判断标准很简单:这段内容能不能公开放在网上?不能,就不要上传。涉及人体、临床、隐私数据的课题,还要额外确认所在机构的数据管理规定,走正规审批流程。

另外,生成的候选想法如果最终形成了论文的核心贡献,署名与贡献声明按学术规范处理,工具的使用情况按期刊要求披露。这部分不要图省事,很多期刊已经有明确的 AI 使用披露要求。

8.4 时间盒与节奏控制

这套流水线最大的风险不是技术问题,是时间失控。生成 30 条候选只要几分钟,但如果你开始逐条读、逐条改、逐条查文献,一天很容易搭进去,最后什么也没定下来。我的做法是给每个环节设时间盒:

环节时间盒产出
生成30 分钟30 到 50 条候选
初筛30 分钟保留 8 条
文献检查半天保留 3 条
批评与重构半天保留 1 到 2 条
一页纸1 小时定稿

关键在于“初筛 30 分钟”。这段时间里只看标题、假设、致命问题三栏,不深究、不查文献、不纠结。人的判断在快速扫读时反而更准,一旦进入细节就容易陷入局部最优,为一个明显不行的想法找理由。

9. 学习路线与风险提示

9.1 两周可执行清单

如果你准备马上动手,可以按这个顺序推进,每一步都有明确产出物,不至于停在“看完了但不知道从哪开始”:

第 1 到 2 天:确定领域和三个锚点材料,把diverge.txt改写成符合你方向的语言,跑通生成脚本拿到第一批 30 条候选。第 3 天:跑去重和打分,人工挑出 8 条,重点看fatal_flaw字段。第 4 到 6 天:对 8 条逐一做文献检索,用映射表判断是否已被覆盖,砍到 3 条。第 7 到 8 天:对 3 条做三视角批评,该重构的重构,能合并的合并。第 9 到 10 天:为剩下的 1 到 2 条写实验设计,明确对照组和失败模式。第 11 到 14 天:写一页纸,拿给同组的人读,看对方能不能在三句话内听懂你要做什么。

这个节奏的重点是每个阶段都在做减法。多数人选题失败不是因为想法少,而是因为一直没做决定,手里的候选越攒越多,一个都不敢投入。

9.2 三个容易翻车的点

第一个是把生成数量当成进展。跑出 200 条候选不会让选题变容易,只会让筛选变累。真正的进展是“我能用一句话说清这个方向和最近那篇工作的差别”。

第二个是过度依赖打分排序。分数只是排序工具,不是决策依据。一个 3.6 分的想法如果你有独特的数据或技能优势,实际价值可能远超 4.2 分的那个。打分的作用是帮你把明显不行的先排除掉,剩下的必须靠人判断。

第三个是跳过最近邻检查。这是代价最高的一步,也是最容易被跳过的一步,因为查文献枯燥、费时间、还经常查不到理想结果。但恰恰是这一步决定了你的想法是“新的”还是“你以为新的”。宁可多花两天把检索式换三种写法各查一遍,也不要带着一个有风险的前提进入实验阶段。

最后留一个实操建议:把这套流水线的输出当成“待办清单”而不是“答案清单”。模型能帮你把发散的成本降到很低、把遗漏的角度补上一些,但选题这件事最终取决于你对问题的理解深度,以及你愿意为一个方向投入多久。工具负责让你更快地看到更多可能性,判断哪一条值得走三年,还是得你自己来。

如果这套流程在你的方向上跑出了不错的结果,可以把prompts/目录单独抽出来做成自己的模板库,按方向分类积累。用上几次之后你会发现,最有价值的不是某一次生成的候选,而是那套被反复打磨过的约束条件——它其实是你对自己研究品味的显式记录。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询