news 2026/9/14 16:25:38

LifeOS Evals ViewResults 工作流:解读评估结果、检查饱和度与失败分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LifeOS Evals ViewResults 工作流:解读评估结果、检查饱和度与失败分析

LifeOS Evals ViewResults 工作流:解读评估结果、检查饱和度与失败分析

【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS

本文围绕 LifeOS 的 Evals 技能中的 ViewResults 工作流展开:讲清楚评估(eval)运行结束后结果落盘在哪里、如何用jq逐层读取运行摘要与逐次试错(trial)明细、如何用SuiteManager判定一个套件是否已经"饱和"并给出晋级建议,以及如何按固定模板产出结果报告。读完本文,你可以独立完成"跑完 eval → 检查结果 → 定位失败 trial → 判断套件是否需要晋级回归"这一完整闭环。

ViewResults 在 Evals 工作流中的定位

Evals 是 LifeOS 中一套"断言优先"(assertion-first)的 AI 评估框架,其完整能力由若干工作流组成。在 Evals 技能说明 的路由表中,ViewResults的触发语义是:"eval results"、"how did it score"、"show the last run"、"saturation"——即它专门负责 RunEval 之后的"看结果"环节,而不是再跑一次评估。

它解决三个具体问题:

  1. 读结果:从 canonical 结果存储中取出某次运行的 summary、逐 trial 分数与 grader 输出;
  2. 判饱和:当一个 capability(能力型)套件连续跑赢阈值时,判断它是否该"毕业"进入 regression(回归型)套件;
  3. 出报告:用统一模板把结果整理成可汇报的格式,并明确"当前技能没有内置趋势对比 CLI"这一边界。

结果落盘位置:Evals-Results 是唯一的权威存储

ViewResults 工作流首先定义了结果的存在位置(原文档的 "Where Results Live" 一节):

~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/<run-id>/results.json

每个results.json包含:运行摘要(run summary)、逐 trial 分数(per-trial scores)、grader 输出(grader outputs)和失败细节(failure details)LIFEOS/MEMORY/STATE/Evals-Results/目录是 canonical store(权威存储),工作流明确要求直接用标准工具查询它(jqrgcat),而不是依赖某个专用查看器。

这个设计与 Evals 的核心纪律一脉相承。SKILL.md 在 Doctrine 一节写着:"Never trust a score until you read transcripts—— 每次运行都会把完整 case transcript 持久化到MEMORY/STATE/Evals-Results/<suite>/<run>/run.json"。也就是说,ViewResults 不只是"看个分数",而是把"读原始输出"作为看结果的必经动作。

从源码结构看,这个存储布局由运行器自己负责写入。以 v2 的断言优先运行器 EvalRunner.ts 为例,每次运行结束会执行:

// 持久化完整 transcript(Anthropic 纪律)+ latest 状态 const dir = join(RESULTS_DIR, name); mkdirSync(join(dir, runId), { recursive: true }); writeFileSync(join(dir, runId, 'run.json'), JSON.stringify({ ...result, detail: caseResults }, null, 2)); writeFileSync(join(dir, 'latest.json'), JSON.stringify({ ...result, ts: new Date().toISOString() }, null, 2));

其中RESULTS_DIR被定义为~/.claude/LIFEOS/MEMORY/STATE/Evals-Results(见 EvalRunner.ts#L30-L31),每次运行额外还落一份latest.json方便取"最近一次"。ViewResults 文档里的results.json路径则对应更早的 v1 运行器(TrialRunner.ts)产生的按任务组织的结果文件,两者的目录骨架(<use-case>/<run-id>/+ 单个 JSON 大文件)是一致的。

五步执行流程

步骤 0:语音通知(可选,best-effort)

原文档开头给出一条工作流启动时的语音通知命令:

curl -s -X POST http://localhost:31337/notify \ -H "Content-Type: application/json" \ -d '{"message": "Running the ViewResults workflow in the Evals skill to display eval results"}' \ > /dev/null 2>&1 &

这里的localhost:31337是 LifeOS 的 Pulse 组件(menu-bar 应用 +:31337服务,见 INSTALL.md 组件表)。注意该命令以&挂到后台、并丢弃全部输出——它是纯通知,通知失败不影响工作流本身;如果当前环境没有安装 Pulse,这条命令静默失败即可。

步骤 1:列出某个 use case 的所有运行

# 按时间列出某个 use case 的全部运行(最新的在最上面) ls -1t ~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/ # 或者通过 SuiteManager 列出套件 bun run ~/.claude/skills/Evals/Tools/SuiteManager.ts list

第一条命令依赖结果目录" 一个子目录"的组织方式:ls -1t按修改时间倒序,天然就是"最近运行优先"。第二条则从套件定义侧入手—— SuiteManager.ts 的list子命令会扫描Suites/CapabilitySuites/Regression两个目录下的.yaml/.yml文件(源码注释明确说明.yml.yaml同等有效,见 SuiteManager.ts#L93-L99),输出形如🎯 <name> (N tasks)的列表。

步骤 2:查看最近一次运行的摘要

# 取最新一次运行的 results.json LATEST=$(ls -1t ~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/ | head -1) cat ~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/$LATEST/results.json | jq '.summary' # 或查看指定运行 cat ~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/<run-id>/results.json | jq '.summary'

<use-case>是占位符,实际是 use case / suite 名称(目录层级中的第一层)。jq '.summary'只取摘要字段,是最低成本的第一步:先确认"这是什么、结果如何",再决定是否下钻。

步骤 3:检查饱和度(capability → regression 晋级判断)

bun run ~/.claude/skills/Evals/Tools/SuiteManager.ts check-saturation <suite-name>

饱和度的具体算法见下文"饱和度机制"一节,这里先给出结论:该命令输出三行关键信息——Saturated: Yes/NoConsecutive above threshold: N/3Recommendation: <action>

步骤 4:查看逐 trial 分数或失败细节

# 逐 trial 摘要 cat .../results.json | jq '.trials[] | {trial: .trial_id, pass: .passed, score: .score}' # 只看失败的 trial cat .../results.json | jq '.trials[] | select(.passed == false)' # 查看某个 trial 的全部 grader 输出 cat .../results.json | jq '.trials[0].graders'

这三条jq是从"摘要级"下钻到"证据级"的关键:

  • 第一条把每个 trial 压成{trial, pass, score}三列,一眼看出"哪几次挂了、分数分布如何";
  • 第二条用select(.passed == false)过滤失败 trial——失败项里通常带有error字段与完整grader_results,是定位回归的第一现场;
  • 第三条取出单个 trial 的全部 grader 输出(每个 grader 的scorepassedreasoningdetails),这正是 SKILL.md 所说"读 transcript 再信分数"的落点。

字段名称有类型定义可查:TrialGraderResultEvalRun接口在 Types/index.ts 中定义,其中EvalRun聚合了pass_ratemean_scorestd_dev,以及两个关键可靠性指标pass_at_k(k 次里至少成功 1 次,衡量"能不能做到")与pass_to_k(k 次全部成功,衡量"是否稳定")。

步骤 5:按模板产出报告

工作流给定了一份固定的报告模板(完整继承自 ViewResults.md):

📋 SUMMARY: Evaluation results for <use-case> 📊 STATUS: | Metric | Value | |--------|-------| | Run ID | <run-id> | | Date | <date> | | Model | <model> | | Pass Rate | X% | | Mean Score | X.XX | 📖 STORY EXPLANATION: 1. Retrieved evaluation run from <date> 2. <N> trials evaluated against <use-case> criteria 3. <Key finding> 4. <Recommendation> 🎯 COMPLETED: Results retrieved for <use-case>, <pass-rate>% pass rate.

模板中每一格都能从结果 JSON 中取到对应字段:Run ID、Date(started_at/completed_at)、Model(model)、Pass Rate(pass_rate)、Mean Score(mean_score)。"STORY EXPLANATION" 一节要求用编号叙述交代数据来源、trial 数量、关键发现与后续建议——这是把一次冷查询变成可汇报结论的格式约束。v1 运行器的展示函数 TrialRunner.ts 的formatEvalResults生成的表格(Trials / Pass Rate / Mean Score / Std Dev / pass@k / pass^k + 逐 trial 状态 + Trial 1 的 grader 明细)与本模板字段高度对齐,可以视为同一信息结构的程序化版本。

饱和度机制:SuiteManager 的源码级解读

check-saturation是 ViewResults 工作流中唯一带"决策输出"的步骤。读 SuiteManager.ts 的checkSaturation可以完整还原其判定逻辑:

  1. 取历史:读取Evals-Results/<suite-name>/下所有以run_开头的运行目录(按名称排序取最近 10 个),逐个解析其run.json,抽出{date, rate}序列(datecompleted_at,缺失时退回started_atratepass_rate)。解析失败的运行静默跳过,不会中断检查。

  2. 定阈值threshold = suite.saturation_threshold ?? 0.95,即套件 YAML 未显式配置时默认 0.95。可参考仓库中现成的套件定义:core-behaviors.yaml 把回归套件压得很高(pass_threshold: 0.95saturation_threshold: 0.99),而 core-dispositions.yaml 用pass_threshold: 0.75并显式trials: 2(注释解释了为何取 2 而非 3:该套件在每次配置变更时都会触发,每次 trial 都有真实推理成本,2 次仍能保证 pass^k 表示"两次都稳定"而不是"碰巧过一次")。

  3. 判饱和recentAboveThreshold = history.slice(-3).filter(h => h.rate >= threshold),当且仅当最近 3 次运行全部达标recentAboveThreshold.length >= 3)时saturated = true

  4. 给建议recommended_action三选一——

    条件建议动作
    套件是 capability 型 且 已饱和graduate_to_regression(毕业进回归)
    已饱和但非 capability 型add_harder_cases(加更难的 case)
    未饱和keep(维持现状)

    返回结构SaturationStatus(见 Types/index.ts#L313-L319)包含suite_idpass_rate_historysaturatedconsecutive_above_thresholdrecommended_action五个字段,CLI 的check-saturation子命令直接打印其中三项。

"毕业"动作本身由graduate子命令执行(graduateSuite):把套件type改为regressionpass_threshold提到 0.95(回归套件要求更高),并把 YAML 文件从Suites/Capability/移到Suites/Regression/。这与 Evals 的整体教条一致——capability 套件是"要攀登的山丘"(起点可以低),regression 套件"瞄准接近 100%",通过的能力 case 应当晋升到回归里被永久守护(见 SKILL.md Doctrine 一节)。

SuiteManager的完整 CLI 面(无参数时打印帮助,源码见 SuiteManager.ts#L271-L298):

命令作用
create <name> -d "desc" [-t type] [--domain D]创建套件(type 默认capabilitycreateSuite中默认阈值:regression 0.95 / capability 0.70,saturation_threshold默认 0.95)
list [type]列出套件,可按类型过滤
show <name>套件详情 + 饱和度状态(含最近 5 次通过率列表)
add-task <suite> <task>向套件追加任务
check-saturation <name>ViewResults 步骤 3 使用的饱和检查
graduate <name>capability 套件晋级为 regression

读懂结果:pass^k、pass@k 与加权断言

查看 trial 分数时,真正要理解的是评分语义,而不是数字本身。v2 运行器 EvalRunner.ts 对每个 case 跑trials次,每次 trial 的分数是各断言的加权平均weighted()Σ(score×weight) / Σweight),score >= caseThreshold判为 passed,case 级阈值缺省时回落到套件的pass_threshold。两个 case 级指标的计算见 EvalRunner.ts#L169-L182:

  • pass_at_k:只要有任何一次 trial 通过即为 1,否则 0 —— 能力型问题"能不能做到";
  • pass_to_k:只有当passed === trials(全部通过)才为 1,否则 0 —— 可靠性问题"是否每次都做到"。

源码里有一段值得注意的修正注释(EvalRunner.ts#L174-L179):早期实现用passed/trials(均值)表示 pass^k,会把"3 次过 2 次"报成 67%,让一个 flaky(不稳定)的 case 看起来像是基本通过;修正后的语义是 2/3 直接记 0。注释明确说"这是语义修正,不是回归——历史数字不可直接比较"。所以当你用 ViewResults 对比新旧两次运行的 pass^k 数字时,跨实现版本的数字没有可比性,这一点源码已经提前声明了。

套件级结果(SuiteResult)把 case 级指标再平均:score是各 casemean_score的均值,pass_to_k/pass_at_k是各 case 指标的均值,passed = meanScore >= threshold。CLI 输出形如pass^k 100%, mean 83.3% over 2 trials,退出码 0 表示通过、1 表示回归——这让 ViewResults 看到的run.json/results.json里每个字段都能追溯到明确的计算来源。

从源码结构看,v1 与 v2 两套运行器的落盘字段有差异:ViewResults 文档中的jq '.trials[] ... .graders'路径对应 v1 的results.jsontrials[].graders即 v1 的grader_results,接口定义见 Types/index.ts 的 Trial/GraderResult);而 v2 的EvalRunner写入的是run.json(含detail明细数组,每个 case 的 trials 内含asserts数组与output原文)外加一份latest.json。因此在实际查看时,如果目录里是run.json而非results.json,把上面的jq字段路径换成对应结构(如.detail[].trials[].asserts)即可,"摘要 → 下钻失败项 → 读原文"的三层查看方法不变。

实操提示:结果目录的拼写一致性

有一个值得留意的细节:EvalRunner.ts#L30-L31 使用~/.claude/LIFEOS/MEMORY/STATE/Evals-Results(全大写LIFEOS),而 ViewResults 文档与 v1 工作流一致地使用同样的大写路径;但从源码结构看,SuiteManager.ts#L15-L16 的RESULTS_DIR是相对技能目录拼出来的.../LifeOS/MEMORY/STATE/Evals-Results(驼峰LifeOS)。在大小写敏感的文件系统(如 Linux)上,这两个路径解析到不同目录。如果你的check-saturation总报"未饱和"而 EvalRunner 明明每次都在跑,可以推断原因之一是两边看的不是同一棵目录树——先用ls确认结果实际落在哪个拼写的路径下,再决定从哪一侧读历史。

对比与趋势分析:当前技能的明确边界

原文档在 "Comparison and Trend Analysis" 一节给出了诚实的边界声明:当前技能没有内置趋势分析、回归检测或跨运行对比的 CLI。跨运行/跨模型对比被设计为"在results.json之上用jq或小型临时脚本按需编写"的使用场景;如果确实需要反复做趋势分析,文档建议的方向是"编写一个Tools/TrendReport.ts脚本并接入路由表"——并且明确说明该脚本尚不存在于仓库中

这一点在仓库中可以得到佐证:Tools/ 目录下有EvalRunner.tsSuiteManager.tsTrialRunner.tsFailureToTask.ts等 12 个脚本,但没有 TrendReport;跨模型对比的工作流 CompareModels.md 的 Step 4 也是同一模式——"每个模型跑一次EvalRunner,各自落一份结果 JSON,然后直接jq读取这些 JSON 做并排比较,本技能没有内置跨模型对比 CLI"。换句话说,ViewResults 的"看单次运行"是有工具支撑的,"看多次运行"则交给你的一行jq。一个最小可用的跨运行对比示例(基于目录组织方式,无需额外脚本):

# 某 use case 最近 10 次运行的 (run-id, summary) 序列 for d in $(ls -1t ~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/ | grep '^run_' | head -10); do f="$HOME/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/$d" json=$(ls "$f"/*.json | head -1) echo "$d $(jq -r '.summary // .summary' "$json" 2>/dev/null)" done

小结

ViewResults 工作流的产出边界在原文档 "Done" 一节有明确表述:结果从LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/<run-id>/results.json检查完成,并(可选地)通过SuiteManager.ts给出套件饱和度状态。把它落回到本文的要点上:

  1. 一个权威存储~/.claude/LIFEOS/MEMORY/STATE/Evals-Results/<use-case>/<run-id>/,用ls -1t找最近运行、用jq下钻;
  2. 三步下钻法jq '.summary'看摘要 →select(.passed == false)看失败 trial → 取单 trial 的 grader 输出读证据,贯彻"读 transcript 再信分数"的纪律;
  3. 饱和判定:最近 3 次运行全部 ≥saturation_threshold(默认 0.95)即饱和,capability 套件饱和后graduate晋级 regression 并把pass_threshold提到 0.95;
  4. 明确的边界:跨运行趋势/回归检测没有内置 CLI,TrendReport.ts尚未落盘——需要时基于results.json/run.json自行用jq或临时脚本实现。

关键参考文件:ViewResults.md、SKILL.md、SuiteManager.ts、EvalRunner.ts、TrialRunner.ts、Types/index.ts、core-dispositions.yaml。

【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 16:24:02

AI论文写作软件实测对比:8款工具功能评测与选题降重指南

专科生写论文&#xff0c;最磨人的从来不是查重率&#xff0c;而是面对空白文档根本不知道从哪下笔。我见过太多人把毕业论文拖到最后一个星期才动手&#xff0c;通宵三天还是憋不出两千字。这几年AI论文写作软件陆续冒出来&#xff0c;确实帮了不少忙&#xff0c;但网上一搜全…

作者头像 李华
网站建设 2026/9/14 16:23:51

用DeepSeek Harness从零构建可用AI Agent的完整实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 16:23:49

TransUnet二分类分割:解码器级Transformer融合原理与PyTorch实现

简介&#xff1a;本资源是一份基于Transformer架构的语义分割实战项目&#xff0c;面向计算机视觉初学者、算法工程师及医学影像、自动驾驶等领域的研究者&#xff0c;聚焦二分类像素级分割任务&#xff0c;解决传统CNN模型在长程依赖建模上的局限性。项目核心为TransUnet模型—…

作者头像 李华
网站建设 2026/9/14 16:21:08

消费级AR+AI双引擎架构:跨端融合与生态协同实践

1. 项目概述&#xff1a;这不是一个“AR眼镜AI模型”的简单拼接 “构建消费级ARAI双引擎&#xff1a;雷鸟基于腾讯云实现跨端融合与生态协同”——这个标题里藏着三个被多数人忽略的关键限定词&#xff1a; 消费级、双引擎、跨端融合 。它不是在讲某款AR眼镜搭载了多大参数的…

作者头像 李华
网站建设 2026/9/14 16:18:15

企业展厅大屏选型指南:LED与LCD的底层差异与工程实践

做了这么多年的企业展厅项目&#xff0c;被问得最多的一个问题就是“大屏到底选LED还是LCD”。每次听到这个问题&#xff0c;我都得先按住对方&#xff0c;别急着看报价单。2026年了&#xff0c;LED和LCD的技术边界确实在互相渗透——LCD拼接墙越做越窄缝&#xff0c;LED小间距…

作者头像 李华
网站建设 2026/9/14 16:18:06

Java TreeSet详解:红黑树实现与有序集合实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华