跳到主要内容

Inspect AI — 这是什么 / 全景 / 阅读地图

30 秒导读: Inspect 是英国 AI 安全研究所(UK AI Security Institute, AISI)开源的大模型评测框架。你把一次评测写成一个 Task(数据集 + 求解步骤 + 打分器三件套),eval() 就替你把每条样本喂给模型、跑完、打分、聚合成指标,并把全过程记进一份能回放的日志。本章只做导航:讲清它是什么、大盘怎么转、去哪读细节——不钻底层。


1. 这是什么(零基础也能懂)

一句话定义: Inspect 是一个把"给大模型出一套题、让它答、然后自动判分"这件事标准化的 Python 框架。

它的口号写在自己的 README 里(README.md:3):a framework for large language model evaluations,由 UK AI Security Institute 创建。引用信息见 CITATION.cff(标题 "Inspect AI: Framework for Large Language Model Evaluations")。

解决谁的什么问题。 假设你想回答"GPT-5 和 Claude 谁更会做小学数学题""某模型会不会被诱导说危险内容"。手写脚本很快就乱:要拉数据集、要接不同厂商的 API、要处理多轮对话和工具调用、要判分、要重复多次取平均、要留证据以便复盘。Inspect 把这些都做成了可复用的部件,你只填三样东西。

三件套 = 一次评测的全部骨架:

部件你要提供什么白话
Dataset一批 Sample(input, target)题目 + 标准答案
Solver一串求解步骤(通常含 generate())怎么让模型作答
Scorer一个打分函数怎么判对错

它能做什么(功能一览):

  • 内置 prompt 工程、工具调用、多轮对话、模型判分(model-graded)等组件(README.md:5)。
  • 一套接口对接约三十家模型提供商(OpenAI / Anthropic / Google / 本地 vLLM…),换模型只改一个字符串。
  • 自带 200+ 现成评测,可直接对任意模型跑(README.md:9)。
  • 全程事件化记录 + 可视化回放(inspect view)。

用起来什么样(最小真实示例):

# 示意,非源码:一个最小的 Inspect 评测
from inspect_ai import Task, task
from inspect_ai.dataset import Sample
from inspect_ai.scorer import model_graded_fact # 让另一个模型判事实对错
from inspect_ai.solver import generate, system_message

@task # ① 注册成一个可被 eval 的 task
def theory_of_mind():
return Task(
dataset=[Sample(input="小明以为盒子里是糖…他会去哪找?", target="盒子")],
solver=[system_message("仔细推理后作答"), generate()], # ② 求解步骤链
scorer=model_graded_fact(), # ③ 打分器
)

命令行跑它(选模型只是换个字符串):

inspect eval theory_of_mind.py --model openai/gpt-4o
inspect view # 打开浏览器回放这次评测

一句话直觉: 把 Inspect 当成评测界的单元测试框架——Task 是一个测试套件,Sample 是一个测试用例,Solver 决定"怎么运行被测对象",Scorer 就是断言(assert),EvalLog 是带完整堆栈的测试报告。


2. 顶层全景(它大概怎么转)

先建静态心智模型,再看动态流水。

图 A:三件套的静态结构。 @task 把这三样注册成一个 Task

┌─────────────── Task(被 @task 注册)───────────────┐
│ │
Dataset ───► Solver 链 ───► Scorer
一批 Sample 求解步骤序列 拿最终状态
(input, target) (最后常是 generate()) 打出 Score

图 B:一条样本的一生(运行主线)。 怎么读:从上往下是一条样本被处理的顺序;每条箭头旁的编号是该阶段干的事。

eval(task)
│ ① 解析 task、建 Model、按 epochs 把数据集展开成 N×epochs 个 Sample,并发调度

每个 Sample
│ ② Solver 链依次执行,读写同一个 TaskState;generate() 调 Model 产出 output

TaskState(messages / output / store / scores)
│ ③ 每个 Scorer 拿"最终 TaskState + target"打分

Score(按 scorer 名存进 state.scores)
│ ④ 同一 Sample 的多个 epoch 分数,用 reducer 归并成一条(默认取均值)

归并后的样本分数
│ ⑤ Metric 跨样本聚合(accuracy、stderr…)

EvalLog(结果指标 + 全程事件 Transcript,可回放)

三个词记住整张图:Solver 出 output,Scorer 出 Score,聚合出 Metric,全部留痕在 EvalLog


3. 部件一句话职责表(各在哪个子包)

src/inspect_ai/ 下每个子包对应上图的一块。想深挖某块时,照下表跳。

部件一句话职责所在子包 / 关键文件
Dataset提供样本集合,每条是 Sample(input, target, …)dataset/(_dataset.py)
Solver可组合的求解步骤,改写 TaskState;generate() 是最常用一步solver/(_solver.py)
TaskState一条样本求解中的可变状态:消息历史、模型 output、store、scoressolver/_task_state.py:139
Scorer拿"最终 TaskState + Target"打出 Scorescorer/(_scorer.py)
Metric / ReducerScore → 跨样本聚合指标;多 epoch 分数归并scorer/_metric.pyscorer/_reducer/
Model统一模型接口,背后挂各家 Providermodel/(_model.py)
Tool模型可调用的函数(@tool)tool/(_tool.py)
Agent多轮"想—调工具—再想"循环(如 react),可当 solver 或 toolagent/(_react.py)
LogEvalLog 结构 + 事件 Transcript 的读写log/(_log.py_transcript.py)
Sandbox隔离执行环境,给工具/不可信代码用util/_sandbox/(environment.py:92)
Registry装饰器注册表:@task/@solver/@scorer/@tool 等靠它按名查找_util/registry.py

4. 对外入口与主线(从哪进去读代码)

包的门面在 src/inspect_ai/__init__.py 它把散在各 _ 私有子包里的东西汇成公开 API(__init__.py:5-24)。你在用户代码里 from inspect_ai import ... 拿到的,基本都在这份导出清单里。

四个最该认识的公开符号:

符号作用定义处
eval / eval_async评测总入口:编排一切,返回 EvalLog_eval/eval.py:109_eval/eval.py:392
eval_set批量/断点续跑一组 task_eval/evalset.py:120
Task / @task三件套的容器 / 注册装饰器_eval/task/task.py:61_eval/registry.py:98
get_model"provider/name" 字符串拿到统一 Modelmodel/_model.py:1653

主线一句话: @task(声明) → eval()(编排,_eval/eval.py:109) → task_run()(单个 task 的运行,_eval/task/run.py:324) → task_run_sample()(单条样本,_eval/task/run.py:1037)。样本函数里两处是全片的心脏:

  • state = await plan(state, generate) —— 跑 Solver 链(_eval/task/run.py:1333)。
  • score_result = await scorer(state, Target(sample.target)) —— 逐个 Scorer 打分(_eval/task/run.py:1529)。

打完分后 eval_results(...) 做 epoch 归并 + Metric 聚合(_eval/task/run.py:784,实现于 _eval/task/results.py:88)。

这条链的每一环,下面各章会各展开一节。


5. 阅读地图(建议顺序与选章指引)

本组共 7 章(含本章)。推荐顺序 = 编号顺序,正好顺着"图 B"从上游走到下游。

讲什么什么时候读它
index.md(本章)是什么 / 全景 / 路由先读,建立心智模型
01-eval-loop.md主循环:eval() → 一条样本打完分的完整调度想搞懂"谁在什么时候调用谁"
02-solver-taskstate.mdSolver 协议、链式组合、TaskState 数据模型想写自定义求解步骤 / 理解状态如何流动
03-model-layer.mdModel/ModelAPI 抽象、Provider 注册、generate想接新模型 / 理解一次 generate 内部
04-tools-agents.md@tool 定义、工具调用回环、react 等 Agent做工具使用 / 多轮智能体评测
05-scorer-metrics.mdScorer/Score、epoch reducer、Metric 聚合想自定义判分 / 理解指标怎么算出来
06-log-transcript-sandbox.mdEvalLog 结构、事件 Transcript、Sandbox、可观测性想读日志 / 做复盘 / 跑沙箱工具

选章速查(带着问题来):

  • "换个模型怎么就能跑?" → 03
  • "我的 solver 里改的东西下一步怎么看得到?" → 02(TaskState)。
  • "同一题跑 5 次,最后那个数怎么来的?" → 05(reducer + metric)。
  • "评测崩在半路,怎么查?" → 01(错误处理)+ 06(Transcript)。
  • "让模型用工具、开子智能体?" → 04

6. 巧妙之处速览(这套设计好在哪)

三个反复出现的设计模式,是读全库前值得先记住的"母题"。后续各章会落到具体代码。

① 装饰器注册表(Registry)——按名字解耦。 @task/@solver/@scorer/@tool/@metric/@modelapi 都是同一套 registry 的马甲。装饰器把函数连同参数登记进全局表(_util/registry.py:78registry_add),之后命令行的一个字符串("theory_of_mind""openai/gpt-4o")就能反查出实现并携带参数重建。好处:用户代码、命令行、日志三方都只靠名字打交道,实现可插拔。以 @task 为例,包装器还会顺手记下 task 的源文件路径与参数(_eval/registry.py:120-160)。

② 统一 Model 抽象——一套接口驱动约三十家。 ModelAPI(抽象基类,model/_model.py:186)定义"一次 generate 长什么样",各家 Provider 各实现一份;providers.py 里用 @modelapi 登记了 27 个入口(model/_providers/providers.py,如 openai/anthropic/google/vllm…)。外层再包一个 Model(model/_model.py:601)统一处理重试、缓存、用量统计。所以上层 Solver/Scorer 永远只面对同一个 generate(),不关心背后是哪家。

③ 事件化 Transcript——过程即数据。 样本运行时,每个关键动作(模型调用、工具调用、打分、报错…)都作为一个事件写进 Transcript(log/_transcript.py:380),例如打分那步会 _event(ScoreEvent(...))(_eval/task/run.py:1541)。评测因此天然可回放、可审计——inspect view 显示的就是这条事件流。这也是"评测框架"和"随手写的脚本"的关键分水岭。


7. 边界与说明(本章不覆盖什么)

  • 本章是导航层,只给心智模型与跳转表;每个部件的真实机制、数据结构、坑,都在对应编号章里展开,此处不重复。
  • 引用锚点截至 sourceCommit: 64e0ff05。行号可能随上游漂移,优先用符号名 grep 定位(见下方代码地图)。
  • 前端 Web UI(_view/ts-mono 子模块)与命令行细节不在本组范围内。

8. 代码地图(全局导航索引)

一张跳转表:想从哪块下钻,直接按"符号名"到克隆里 grep(比行号抗漂移)。

主题文件路径符号名
公开 API 门面src/inspect_ai/__init__.py导出清单(evalTasktaskget_model…)
评测总入口src/inspect_ai/_eval/eval.pyeval / eval_async
批量续跑src/inspect_ai/_eval/evalset.pyeval_set
task 注册装饰器src/inspect_ai/_eval/registry.pytask / task_register
三件套容器src/inspect_ai/_eval/task/task.pyTask / Epochs
单个 task 运行src/inspect_ai/_eval/task/run.pytask_run
单条样本运行src/inspect_ai/_eval/task/run.pytask_run_sample(内含 planscorer 调用)
结果聚合src/inspect_ai/_eval/task/results.pyeval_results
Solver 协议src/inspect_ai/solver/_solver.pySolver / Generate / generate
样本状态src/inspect_ai/solver/_task_state.pyTaskState
Scorer 协议src/inspect_ai/scorer/_scorer.pyScorer / scorer
分数 / 指标src/inspect_ai/scorer/_metric.pyScore / Metric / metric
epoch 归并src/inspect_ai/scorer/_reducer/registry.pycreate_reducers / reducer_log_name
统一模型层src/inspect_ai/model/_model.pyModel / ModelAPI / get_model
提供商注册src/inspect_ai/model/_providers/providers.py@modelapi(27 个入口)
工具定义src/inspect_ai/tool/_tool.pyTool / tool
Agent 循环src/inspect_ai/agent/_react.pyreact
日志结构src/inspect_ai/log/_log.pyEvalLog / EvalSample / EvalResults
事件 Transcriptsrc/inspect_ai/log/_transcript.pyTranscript / transcript
沙箱src/inspect_ai/util/_sandbox/environment.pySandboxEnvironment
注册表内核src/inspect_ai/_util/registry.pyregistry_add / registry_create

下一步:读 01-eval-loop.md,把"图 B"的每条箭头落到真实调用栈上。