给 AI 发一张“科研上岗证”——这句话最近一直在我脑子里转。很多人看到“163 个技能秒变 AI 科学家”这种标题,第一反应是夸大其词,第二反应是“是不是又来推销模型”。但如果你自己搭过 scientific agent 就会发现,这个说法其实点到了一个很本质的工程转向:我们不再指望大模型靠“涌现”自动变成科学家,而是直接给它一套可注册、可调用、可审计的 Scientific Agent Skills,让它在具体任务里像持证上岗一样按流程操作。
K-Dense 这类项目也好,类似 SSP 的技能协议也好,背后的思路都差不多:把“科学家”这个职业抽象成一张岗位说明书,把岗位说明书拆成几百个原子操作,再让 Agent 根据需要去调用。这篇就来拆一拆,为什么“ 163 个技能”比“换一个更大的模型”更靠谱,以及如果你想给 AI 实验室里的模型也发一张上岗证,具体该怎么办。
1. 为什么说“上岗证”比“模型变聪明”更接近真实科研
1.1 模型不缺知识,缺的是稳定执行
先说一个很常见的现象。你用普通对话模型问“Dunnett 检验和 Bonferroni 校正的区别是什么”,它能答得头头是道。但你让它“读取这份实验数据,判断哪些组别与对照组有显著差异,并给出符合期刊要求的报告”,它可能吭哧吭哧跑出一段代码,却在多重比较、方差齐性、样本量这些问题上突然犯迷糊。
这不是模型变笨了,是因为科研任务本质上不是“单次问答”,而是一长串需要前后衔接的操作。阅读文献、提出假设、设计实验、清理数据、选择统计方法、跑分析、写结果、复现检查,任何一个环节都有大量隐性规则。普通对话模型擅长的是“根据上下文生成下一段文本”,而不是默认就能稳定执行一套长达几十步的科研流程。
技能的作用,就是把每一步操作固化成可运行、可校验的最小单元。模型不需要凭记忆猜“这里要不要做正态性检验”,因为技能描述里就写着“先做 Shapiro-Wilk”,如果数据明显非正态就切到非参数检验。换句话说,我们不让模型临场发挥,而是让它照着岗位操作手册干活。
1.2 技能不是给模型加知识,而是给模型加约束
有人会问:技能库里的内容,模型难道不知道吗?很多统计方法它确实知道,论文里怎么写它也大概知道。但“知道”和“稳定执行”之间的距离非常大。
举一个非常小的例子:判断两组数据是否有差异。没有技能的模型,可能直接给一个 t 检验结果。而一个设计良好的“两样本比较”技能,至少要在内部处理四件事:
- 每组样本量是否够用,太少就拒绝出结果;
- 正态性假设是否满足,不满足就该换非参数方法;
- 方差是否齐性,不齐要用 Welch 校正;
- 返回结果时是否附带效应量,而不是只丢一个 p 值。
这四步单独看都不难,但让模型在自由对话里每次都做到,几乎不可能。技能的价值,就是把这个“几乎不可能”变成“必然如此”。因为它不只是提示词,背后还有实际执行代码,每一层校验都写死在函数逻辑里,模型想跳过也跳不过去。
所以说“上岗证”这个比喻很准确。证件真正的意义不是告诉别人你有多聪明,而是告诉你哪些动作能做、哪些不能做、做了以后要按什么标准交付。
1.3 谁最需要这套东西
按我自己的观察,三类人最需要这种科研技能 Agent:
第一类是高校和研究所里真正做实验的人。他们每天产出大量数据,但是没精力把所有统计细节都学好,又不放心把数据直接丢给通用模型。第二类是药企、CRO、检测机构里的数据科学家,他们关心的不是模型能不能聊科研,而是能不能在合规流程里稳定输出可审计结果。第三类是 AI 应用开发者,想给垂直行业的客户交付“科研 Agent”能力,但是发现没有一套标准技能库,只能从零造轮子。
如果你属于这三类里任何一类,下面内容应该能直接落地。尤其是第三类,你会发现,做好一个技能库,比调一个“神仙模型”更实在,也更可控。
2. K-Dense 这类 Agent 技能库,内部到底按什么结构组织
2.1 163 个技能不可能全塞进提示词
很多人第一次听到“163 个技能”的第一反应是:模型上下文窗口能装得下这么多内容吗?
答案很明确:装不下,也不该装。你不可能在一个系统提示词里把 163 个技能全部写好,那会让模型不知道该选哪个。就算硬塞进去,真正执行一个任务时,也会因为指令太多导致路由混乱,甚至出现“这个技能好像是干这个的,但另一个技能描述也沾边”的情况。
所以 K-Dense 这类技能型 Agent,工程上普遍采用一个“注册表 + 路由 + 执行 + 审计”的结构。我把这套结构画成一句话来说:
- 技能注册表负责登记所有可用的技能,包括名字、版本、输入输出格式、依赖环境、测试用例;
- 技能路由负责根据用户问题,从注册表里挑出最相关的几个候选技能;
- 技能执行器负责真正调用代码或外部工具,并在执行前做参数校验;
- 审计日志负责记录每次调用、输入、输出和异常,方便事后再查。
换句话说,163 个技能不是放在模型的脑子里,而是放在一个外部能力库里。模型每次拿到任务,只先加载与任务相关的五六个技能定义,剩下的都留在注册表里,需要时再取。
2.2 一个技能长什么样:注册表里的最小单元
不管项目名字叫 K-Dense,还是封装成 SSP 这类技能协议,单个技能的核心结构都差不多。我会用下面这套最小字段来定义:
{ "skill_id": "s0037", "name": "compare_two_groups", "version": "1.2.0", "description": "对两组连续观测值做假设检验,先做正态性检验," "不符合正态则使用 Mann-Whitney U,否则使用 Welch t 检验。", "inputs": { "type": "object", "properties": { "group_a": {"type": "array", "items": {"type": "number"}}, "group_b": {"type": "array", "items": {"type": "number"}} }, "required": ["group_a", "group_b"] }, "outputs": { "type": "object", "properties": { "method": {"type": "string"}, "p_value": {"type": "number"}, "effect_size": {"type": "number"} } }, "policy": { "allow_internet": false, "approval_required": false } }这里最关键的不是 JSON Schema,而是description字段。因为模型不是靠读代码决定要不要调用这个技能的,它读的是自然语言描述。描述里如果只写“比较两组数据”,模型可能什么场景都往这里塞;但如果写清楚“先做正态性检验、非正态建议切换非参数方法”,路由准确率会大幅提升。
我自己每次设计技能时,都会先写 description,再写代码。如果 description 写不清楚一个技能在什么场景下用、什么场景下不用,这个技能就还不合格,不该进入注册表。
2.3 为什么这种结构更能防止翻车
科研场景最怕两件事:一是不知不觉用了错误方法,二是出了问题找不到原因。
单体模型直接输出分析结论的时候,它不会告诉你“我这一步为什么会选 ANOVA,为什么我认为方差齐性满足”,因为它压根没有真正检验方差,它只是在生成一串看起来合理的文本。而注册表加执行器这种结构,能把每一步都变成可以被质疑、被回滚、被审查的对象。
比如执行器接收到“比较 A 组与 B 组”的任务后,它会先做参数校验,发现 A 组只有两个样本,直接拒绝执行,同时返回“样本量不足,至少需要 3 个观测值”。这种硬约束,靠提示词很难做到,但写成代码就是一行 if 判定的问题。
我甚至见过有团队把这种技能库集成到电子实验记录本里,每个技能调用的前后状态都会被快照,之后论文被审稿人质疑分析过程时,团队能直接把当时的审计日志导出来,这种可追溯性在真实科研协作里非常重要。
3. “163 个技能”到底能从哪些抽屉里抽出来
3.1 六个抽屉覆盖完整科研管线
如果只是为了凑数量,163 这个数字一点也不稀奇。真正有价值的是技能覆盖的边界。我在梳理这类项目时,比较认可按科研流程把它们分成六个抽屉。
| 抽屉 | 覆盖环节 | 典型技能示例 |
|---|---|---|
| 文献与知识获取 | 阅读、检索、综述、溯源 | 从 PDF 抽取实验表格、识别引用关系、对比不同论文的方法表述 |
| 数据工程与清洗 | 拿到原始数据后的预处理 | 列名语义识别、缺失值模式提示、单位统一、离群值标记 |
| 统计推断与建模 | 实验数据分析 | 正态性检验、多组比较、生存分析、回归诊断、时间序列建模 |
| 实验设计与仿真 | 没做实验前先算清楚 | 样本量估算、随机分组方案生成、功效分析、敏感性分析 |
| 科学写作与可视化 | 把结果变成论文或报告 | 图表主题统一、方法段落生成、图片说明重写、参考文献格式化 |
| 质量与伦理审计 | 最容易被忽视的一环 | p 值多重比较检查、数据造假模式提示、可复现性检查、结论边界提示 |
这个分类方法不是唯一标准,但它能回答一个重要问题:为什么需要 163 个技能,而不是 20 个大功能。
因为科研不像写一个软件,只要把少数几个 API 做得够深刻就行。科研的复杂性在于分支条件非常多:数据类型不一样,样本量不一样,实验设计不一样,期刊要求不一样。一个“多组比较”的技能,可能要根据方差是否齐性、样本是否配对、是否需要校正等多种分支选择不同的路径。如果把这些分支都混在一个 300 行的函数里,维护成本会高到没人敢动。
3.2 为什么“小技能”比“大工作流”更好用
我看到不少团队刚做 Agent 时,喜欢先搭一个很大的流水线:检索文献、归纳结论、生成研究方案、生成代码、跑数据、写报告,一次全部跑完。
这种做法乍一看很专业,实际用起来特别脆。因为只要某一步出错,后面整个流程都白费。比如前端检索回来的文献标准不可控,输入到方案生成阶段,模型就可能生成一个基于错误前提的假设。
相比之下,小技能组成的大流程更容易调试。如果你发现“数据清洗”这一步里没有处理缺失值,你只需要改进那一个技能,而不需要推翻整个管线。技能可以像零件一样组合:先跑“缺失值检查”,再根据结果决定走“完整数据回归”还是“多重插补”。坏了哪个零件就换哪个零件,这是大而全的设计给不了的灵活性。
3.3 数量只是第一步,核心是技能之间的“互认协议”
163 个技能真正麻烦的地方不在开发,而在接口统一。如果每个技能输入输出格式各写各的,模型在技能之间做切换时会非常痛苦。
举个例子,数据清洗技能如果跑完输出的是 DataFrame,而统计建模技能默认接收 CSV 文件路径,那模型就要自己在中间做一次格式转换。这种转换一旦交给模型自由发挥,错误率会非常高。
所以成熟的技能库一定会有统一的数据中间格式。我一向建议,不同技能之间尽量通过文件、标准 JSON 或统一的表格对象传递数据,而不是靠在参数里传递一个大对象。这就像工厂里的传送带,每个工位只需要知道原料和标准包装长什么样,不关心前一个工位内部怎么运作。
4. 手把手造一个“能上岗”的科学技能
4.1 第一步:先写好输入输出契约
空谈概念没有意义,直接动手写一个最小技能最有用。我选择“两组连续数据比较”作为例子,因为这个场景既有统计判断,又有异常处理,非常适合展示一个技能该有的严谨度。
动手前先定义输入输出契约:
- 输入:
group_a、group_b两个数值数组,每组至少 3 个样本; - 过程:先做 Shapiro-Wilk 正态性检验,若任一组的 p 值小于 alpha,就改用 Mann-Whitney U 检验;否则使用 Welch t 检验;
- 输出:方法名、统计量、p 值、效应量 Cohen's d;
- 边界:样本量不足 3 或 scipy 未安装时,不返回空结果,而是返回明确错误码。
这个契约看着简单,但已经把“模型自由发挥”的空间压到很小了。
4.2 第二步:把技能写成可执行函数
下面是这个技能的核心实现。为了看清楚逻辑,我故意没有做太多工程魔法,只保留了最重要的分层判断。
import statistics try: from scipy import stats except ImportError: stats = None def compare_two_groups(group_a: list[float], group_b: list[float], alpha: float = 0.05) -> dict: """对两组连续观测值做假设检验。 流程: 1. 校验样本量; 2. Shapiro-Wilk 正态性检验; 3. 满足正态性 -> Welch t 检验; 4. 不满足正态性 -> Mann-Whitney U 检验。 """ if stats is None: return {"error": "scipy is not installed"} if len(group_a) < 3 or len(group_b) < 3: return {"error": "每组样本量至少为 3,拒绝执行统计推断"} # 正态性检验 _, p_a = stats.shapiro(group_a) _, p_b = stats.shapiro(group_b) if p_a < alpha or p_b < alpha: stat, p_value = stats.mannwhitneyu(group_a, group_b, alternative="two-sided") method = "Mann-Whitney U" else: stat, p_value = stats.ttest_ind(group_a, group_b, equal_var=False) method = "Welch t-test" # 计算 Cohen's d n1, n2 = len(group_a), len(group_b) mean1, mean2 = statistics.mean(group_a), statistics.mean(group_b) sd1, sd2 = statistics.stdev(group_a), statistics.stdev(group_b) pooled_sd = (((n1 - 1) * sd1 ** 2 + (n2 - 1) * sd2 ** 2) / (n1 + n2 - 2)) ** 0.5 cohens_d = abs(mean1 - mean2) / pooled_sd if pooled_sd > 0 else None return { "method": method, "statistic": float(stat), "p_value": float(p_value), "effect_size": cohens_d, "normal": p_a >= alpha and p_b >= alpha, }这里有一个特别容易踩的细节,就是 Cohen's d 计算。如果两组标准差都极小,pooled_sd可能接近 0,直接除会产生无穷大。所以我在返回前判断了分母,如果它太小就不给效应量,防止 Agent 拿到一个 Inf 后还煞有介事地写进报告。
4.3 第三步:让 Agent 学会在需要时调用它
有了函数,还要把它注册成模型可以感知的技能。不同 Agent 框架做法不一样,但思路都是把函数签名、参数说明、返回值结构转成模型能读的格式。
我在实际项目里的注册方式大致是把 JSON 描述放进候选工具列表,然后由路由层决定是否把当前问题的相关工具传给模型。
def route_and_call(user_query: str, available_skills: list[dict]): # 1. 用一个简单的关键词召回候选技能 if "两组" in user_query or "差异" in user_query or "compare" in user_query.lower(): candidates = [skill for skill in available_skills if skill["name"] == "compare_two_groups"] else: candidates = [] if not candidates: return { "error": "没有找到合适的技能,请明确你要比较的数据类型" } # 正常流程里:这里应该把 candidates 和参数结构交给模型, # 由模型解析用户数据并生成调用参数。 # 本文为演示,直接手动传入参数。 result = compare_two_groups( group_a=[1.2, 1.4, 1.3, 1.5], group_b=[2.2, 2.1, 2.4, 2.0] ) return result为了让这类技能真正在 Agent 里跑起来,我强烈建议你在本地把调用过程完整跑一遍。自己先扮演“调度模型”,用不同的测试数据反复调用这个函数,观察它会不会在某些边界条件下返回不理想结果。只有当你对技能的行为模式熟悉到一定程度,才能把它放心交给 AI 去自动调用。
5. 常见问题与踩坑记录
5.1 五个最常见的“技能上岗”问题
我把过去实操中遇到的高频问题整理成了一张排查表。这些问题大部分都不是模型笨导致的,而是技能库工程化不足导致的。
| 现象 | 可能原因 | 处置方式 |
|---|---|---|
| Agent 选了技能,但迟迟不调用 | 技能描述和用户问题表述不一致 | 检查 description 是否太短或太 API 化,改成说人话 |
| 技能返回结果被 Agent 无视 | 模型直接把代码写在对话里,没走工具链路 | 加强输出格式约束,禁止模型直接给出代码 |
| 小数据能跑通,换真实数据就报错 | 技能内部缺少异常捕获和类型检查 | 调用前增加 schema 校验,拒绝非预期输入 |
| 技能之间数据格式对不上 | 每个技能各自为政,没有统一中间格式 | 统一表格或 JSON 传输规范,避免传 DataFrame |
| 多跑几次结果不稳定 | 随机数种子没固定或随机采样没指定参数 | 在技能里固定随机种子并记录版本号 |
5.2 一个有争议的问题:163 个技能为什么也会越权
技能越多,Agent 可做的操作越多,这句话既是优势也是风险。
我见过一个比较激进的团队,把所有和“数据处理”相关的技能都给了 Agent 完整权限。结果模型在处理一个 CSV 时直接把原文件覆盖了,因为负责写入的技能没有做“是否允许覆盖原文件”的权限校验。对科研数据来说,这种事故非常要命。
所以我做技能库时的原则是:技能默认最小权限,只有明确需要外部读写的技能才开放文件权限;涉及到删除、覆盖、发送外部请求的操作,一律要求人审。哪怕这样会让自动化程度下降一点,我也愿意换确定性。
还有个有意思的问题是:技能库越大,模型在路由时越容易出幺蛾子。比如用户说“帮我看看这些数据行不行”,模型可能同时选中了“缺失值检查”“离群值识别”“正态性检验”三个技能,结果每个都跑了一遍,最后输出互相矛盾。这时候需要在路由层加入“互斥规则”,比如如果一个技能已经返回了数据不可信的结果,另一个技能就不应该继续执行。
5.3 科学技能评测不能只看“模型自评”
给技能库做评测,我觉得最忌讳的是用“模型觉得自己做得好不好”来当标准。模型在绝大多数情况下都会觉得自己每一步都很正确,你要做的是准备一批有确定答案的数据集。
比如我上面写的compare_two_groups,至少要准备几组测试数据:
- 一组符合正态分布的数据,期望输出 Welch t 检验;
- 一组明显右偏的数据,期望输出 Mann-Whitney U;
- 一组只有两个样本的数据,期望直接拒绝执行;
- 一组两组均值几乎相等的数据,期望 p 值很大,效应量接近 0。
把这些用例自动化之后,每次往技能库新增内容,都要跑一遍完整回归测试。技能函数和代码库一样,非常容易“改一个地方坏另一处”。没有回归测试,你根本不敢在 163 个技能上放心做持续迭代。
6. 直接落地的话,我会建议你从哪一步开始
6.1 先不要急着造 163 个技能
如果只是看到“163 个技能”就觉得自己也要做一个大型技能库,大概率会陷入一个很尴尬的局面:技能写了一大堆,但没有一个在真实任务里被稳定使用过。
我的建议正好相反。先找一两个自己每天都会做的重复分析任务,比如“临床数据基线表生成”“两组结果比较”“异常值检测”,把它们做成三五个高质量技能,先在实际项目里跑两个月。
这两个月里你一定会不断修复 bug、补边界条件、重写 description。等这几个技能的调用成功率达到 95% 以上,你再照着同一个流程去扩展其他技能。经验是,第一批技能的质量决定了后续整个技能库的底气。第一批做糙了,后面全在还债。
6.2 给技能写“上岗考题”,比写技能本身更重要
我会给每一个新技能配套至少三个测试用例,一个用来验证主路径,一个用来验证边界,另外一个用来验证异常输入。这些测试其实就是“上岗考题”。只有通过这些考题,技能才允许被 Agent 正式调用。
听起来很像考试,但科研 Agent 确实应该这样。毕竟真正的科研人员要拿学位、拿资质才能做实验、出报告;AI 要为科研负责,也应该先通过一关一关的能力验证。这也是我一直觉得“上岗证”这个比喻特别贴切的原因。
最后分享一个实际体会:你不需要一次性搞定所有科研方向,但如果你能把某个细分领域里高频、重复、容易出错的分析步骤都固化成经得起测试的技能,这个 Agent 在真实用户眼里,就已经比一个只会空谈方法学的通用大模型靠谱得多。技能库真正值钱的不是那份长长的清单,而是清单背后每一道经过检验的标准动作和约束边界。