ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Kiln Evals V2 创建流程的 Test Judge 侧栏:从线框图状态机到 Svelte 组件实现

Kiln Evals V2 创建流程的 Test Judge 侧栏:从线框图状态机到 Svelte 组件实现
  • 人工智能
  • AI 应用
  • AI 评测
  • RAG
  • 微调
  • 提示工程
  • 开发工具
  • 桌面应用

【免费下载链接】Kiln

Build, Evaluate, and Optimize AI Systems. Includes evals, RAG, agents, fine-tuning, synthetic data generation, dataset management, MCP, and more.

项目地址:https://gitcode.com/gh_mirrors/ki/Kiln
点击查看免费下载

本文围绕 Kiln 仓库中 Evals V2 创建流程(Create Eval)右侧共享的 Test Run / Test Judge 侧栏展开:先完整梳理设计线框图定义的面板框架、5 个 UI 状态与状态迁移规则,再对照app/web_ui/src/lib/components/eval_types/test_run/下的实际 Svelte 实现、eval_config_builder.svelte 的编排逻辑与后端test_v2_eval接口,说明这条“先测试、后保存”的评分器验证链路是如何落地的。读完后你可以掌握该侧栏的状态机设计、每个状态的组件构成,以及分数形状/取值范围校验等保存门控机制。

1. Test Run 面板的定位:创建流程中的共享右侧栏

设计文档 test_run_sidebar.md 首先明确了面板的定位:Test Run pane 是 Create Eval shell 第 2 步中所有 eval 类型共享的右侧栏,不同 eval 类型只有左侧表单不同,右侧栏结构完全一致。它的职责是让用户在保存之前,用一个真实的数据集样本测试评分器(scorer),并完整走过其生命周期:empty(空)→ pick input(选输入)→ running(运行中)→ results(结果);另外提供一个 "More options" 链接打开完整的数据集选择弹窗。

文档同时给出了一条重要的使用前提:这是一份wireframe 级规范,实现的是结构与内容;颜色、字号等视觉细节遵循 Kiln 设计系统,线框图中的状态指示符是灰度的,高保真版本才映射到语义色(success / neutral-skip / error)。项目总览 project_overview.md 也强调“代码是各 eval 类型行为的唯一事实来源”,线框图只是布局指引——这一原则直接体现在下文“线框 vs 实现”的差异上。

2. 共享面板框架与复用子组件

设计文档规定:除弹窗外,所有状态都渲染在同一个面板框架内——固定宽度的纵向面板、全高、约 18px 内边距、column flex 布局;Header为粗体Test Run标题并固定在顶部;Body弹性填充;Footer可选,用 1px 顶部分隔线 + 顶部内边距与主体隔开,承载该状态的主操作按钮与说明文字。

四个复用子组件在设计中的定义如下:

子组件设计定义实现中的对应物
SelectedInput标签为Input的高亮行,以 mono JSON 预览展示选中样本(如{"joke_topic": "cows"})+ 相对时间(2h ago),右对齐Change操作;results 状态可点,running 状态锁定(disabled/dimmed)test_run_input_card.svelte 的selected变体:Selected Test Run卡片 +Change链接,通过disabledprop 控制锁定
DatasetRow可选择的近期输入行:radio + 截断的 mono 输入预览 + 相对时间;选中态边框/填充更强;不展示模型信息同一文件的pick变体:quick-pick-card按钮,line-clamp-2截断两行 +title悬停全文
MoreOptions下划线More options链接 + 浅色说明— browse all dataset inputs,打开输入选择弹窗(State 5)test_run_browse_dialog.svelte,由卡片上的Change事件触发show()
StatusDot灰度状态字形:pass✓(实心)、fail✕(描边)、skip–(虚线描边)实现中结果区没有逐行状态点,跳过样本改用Warning组件 +skipped_reason呈现(见第 4 节)

3. 五个状态的逐一拆解

State 1 — Empty Dataset(无数据集样本)

设计:当任务还没有任何数据集输入时,Body 渲染Input分区标签 + 一个居中的大虚线占位卡片(图标、标题No sample inputs yet、正文Run your task to generate inputs in the dataset, then test against them here.、次级按钮Go to Run);Footer 是通栏 outline 按钮Save Without Testing——这是该状态下唯一的前进路径,会触发 "Save without testing?" 确认弹窗。

实现(eval_test_run_pane.svelte)保留了同样的信息结构:empty-state容器内是No sample inputs yet卡片与跳转/run的Go to Run按钮。而 “未测试先保存” 的确认弹窗被上移到了编排层 eval_config_builder.svelte 的confirm_save_dialog(标题Save Without Testing?,红色Save Anyway按钮),与线框图“Save Without Testing → 确认弹窗”的语义一致。

State 2 — Input Picker / Ready(选择输入并运行)

设计:数据集已有样本,Body 依次是:分区标签Input · recent dataset items;一叠 4 个DatasetRow(第一条默认选中),示例数据为{"joke_topic": "cows"}(2h ago,选中)、{"joke_topic": "kubernetes"}(yesterday)、{"joke_topic": "startup valuations"}(yesterday)、{"joke_topic": "cows and dogs"}(3d ago);列表下方的More options链接;通栏主按钮Run。设计还特别标注:会话中第一次 Run(或 Save)对 code eval 会先弹出 "Trust this code?" 信任门。Footer 是Results分区标签 + 写着Run to see scores的虚线占位框。

实现里 Ready 态({:else if selected_run}分支,见 eval_test_run_pane.svelte)为:选中的TestRunInputCard+ReferenceDataField(见第 4 节)+ 通栏Run Test按钮(LLM judge 未选模型/算法时通过disabled={is_llm_judge && !can_submit_llm}禁用)+ 上一次的test_error展示。线框图中“展示 4 行候选”被简化为“1 个选中卡片 + Change 打开浏览弹窗”,这正是 project_overview.md 里对设计稿的修正指令(主屏只保留 selected + 少量备选,浏览动作移入宽弹窗)。

信任门由编排层实现:run_test()收到skipped_reason === "code_eval_not_trusted"时弹出TrustCodeDialog并记录pending_trust_action = "test";保存路径handle_submit()则先调checkAddCodeTrust预检,未信任时记录pending_trust_action = "save"后弹窗,授权成功后grant_trust_and_retry()不阻塞地续跑被挂起的 test/save(见 eval_config_builder.svelte)。

State 3 — Running(评分器执行中)

设计:顶部是 Change被锁定(changeable={false})的SelectedInput;居中堆叠 spinner、标题Running…、说明Executing the scorer on your input、outlineCancel按钮(始终可用——code 与 LLM judge 的运行可能很长);无 Footer。

实现与线框几乎逐句对应(eval_test_run_pane.svelte):test_loading为真时渲染disabled={true}的选中卡片、loading-spinner、Running...、Executing the scorer on your input与cancel-run按钮。取消的实际机制在编排层:run_test()每次请求前创建AbortController,cancel_test()调用abort()并同步复位test_loading;请求返回时catch对AbortError静默处理,不算错误(见 eval_config_builder.svelte)。

State 4 — Results(pass / fail / skipped 结果预览)

设计:测试完成,展示每个 score 的预览结果(尚未保存)。要点包括:

  • 顶部SelectedInput,Change可用;
  • Scores 头部行:左侧Scores标签,右侧斜体preview · not saved;
  • Score 行:StatusDot+ 粗体 score名+ 浅色 score类型子标签 + 右对齐值;skip 行的值用浅色文字。示例数据:
    • Helpfulness—five_star (1–5)—4 / 5—pass
    • Faithfulness—pass_fail—Pass—pass
    • Tone match—pass_fail · needs reference—Skipped—skip
  • 行下方的内联注释(斜体、柔和 callout):"Tone match" skipped: no reference data on this item — skips are excluded from averages, shown via tooltip.;
  • Footer:图例行(Used/Skipped/Fail三种字形带标签)+Run again(默认按钮)+ 通栏主按钮Save Eval;
  • 关键语义:成功的测试才启用 Save;服务端校验“所有已声明的 score 都被返回”——缺失 = warning,类型错误 = error。

实现的 Results 分支(eval_test_run_pane.svelte)在保留上述骨架的同时补充了若干线框图中没有、但源码结构中真实存在的元素:

  • Scores头部右侧为Preview only · not saved,分数行以name → value的等宽文本行呈现,Run Again按钮内嵌于面板;
  • 若结果为跳过(test_result.skipped_reason),用Warning展示skipped_detail || skipped_reason(例如缺少 reference data、缺 trace 的场景);
  • ToolCallLogTable展示评分代码对其他工具的内部调用(“在父进程而非沙箱执行,按 eval item 计费”);
  • 两个校验告警位:test_shape_warning(Score Shape Mismatch)与test_score_range_warning(Score Out of Range),二者都会把该次测试判定为“不通过”,从而阻断直接保存。

“缺失/wrong type = warning/error”这条服务端契约在前端由 test_run_shape.ts 的validate_result_shape()落地:把返回的 scores 与 eval 声明的output_scores对比,缺失 key 与多余 key 都使测试失效(多余 key 往往意味着代码评分器里残留了占位键名,会在真实 run 时过不了分数校验)。而“值超出 rating 类型范围”(如five_star返回6.0,合法区间1.0–5.0;pass_fail为0.0–1.0;pass_fail_critical为-1.0–1.0)由后端test_v2_eval返回score_range_errors字段上报,编排层将其拼成test_score_range_warning并置passed = false。设计依据见 architecture.md 第 13 节的 D16 说明。

保存门控在编排层handle_submit()(eval_config_builder.svelte):若配置使用了reference_data而当前配置版本没有一次通过的测试,弹Test Required对话框强制先测;否则走Save Without Testing?确认。所谓“当前配置版本”由config_version计数器实现——任何一次表单编辑(含 reference data 修改)都会config_version++并使last_tested失效,测试请求发出前捕获版本号,编辑在飞行期间发生也会使返回结果作废。

State 5 — Input Picker Modal("More options" 宽弹窗)

设计:由More options链接打开的居中对话框(宽约 620px,带背景遮罩),比内联列表更宽、信息更全。结构为:

  • Header:标题Choose an input+ 副标题Pick a dataset item to test this scorer against.+ 右上角关闭(✕)按钮;
  • 搜索框:通栏输入,带搜索图标,placeholderSearch inputs…;
  • 表格:大写浅色列头Input preview·Tags·Created;行为 radio + 截断 mono 预览 + tag chip(或None)+ 相对创建时间,首行默认选中。示例数据:{"joke_topic": "cows"}·manual_run· 2h ago(选中)、{"joke_topic": "kubernetes"}·synthetic_batch_1· yesterday、{"joke_topic": "startup valuations"}·eval_candidate· yesterday、{"joke_topic": "cows and dogs"}·manual_run· 3d ago、{"joke_topic": "why does the cow"}· None · 5d ago;
  • Footer:左侧结果计数(248 inputs · showing 5),右侧Cancel(默认)+Use input(主按钮)。

实现为 test_run_browse_dialog.svelte:Dialog width="wide",标题Choose Dataset Sample,副标题保留设计稿原文Pick a dataset item to test this scorer against.;正文复用TaskRunPicker表格(含 Input / Output / Created 列与分页),并额外提供Add Manual Example入口。与线框图的两处刻意差异在 project_overview.md 中有明确裁定:弹窗内不做搜索(no search in modal)、且同时展示 output 而非仅 input(设计稿只画了 input)。

Add Manual Example由 manual_example_dialog.svelte 实现:复用共享的AddExampleDialog(manual_only={true},提交按钮Use Example),提交时构造一个临时的合成 run(output.source.type = "human"、model_type = "manual",无 trace),明确提示“This example won't be saved to your dataset”,并对 input/output 双空做了 no-op 保护,避免产生无内容的测试对象。

4. 状态迁移与面板组件树

设计文档给出的状态迁移图完整保留如下:

Empty ──(Save Without Testing)──▶ "Save without testing?" modal Picker ──(Run)──▶ [Trust gate, 1st time, code only] ──▶ Running ──▶ Results Picker ──(More options)──▶ Input Picker Modal ──(Use input)──▶ Picker (new input selected) Running ──(Cancel)──▶ Picker Results ──(Run again)──▶ Running Results ──(Change)──▶ Picker / Input Picker Modal Results ──(Save Eval)──▶ eval saved

实现中这套迁移由“事件上抛 + 编排层状态”驱动:面板组件dispatch出select/run/cancel/updateReferenceData/runAgain等事件,eval_config_builder.svelte 把它们分别接到select_task_run/run_test/cancel_test/ reference-data 更新 /run_test。其中select_task_run()会清空test_result、last_tested与各类告警——即线框图中 “Use input → Picker(新选中)” 同时意味着回到 Ready 态且不残留旧结果。组件树的静态结构是:面板 →TestRunInputCard(两种 variant)+ReferenceDataField+TestRunBrowseDialog(内部再挂ManualExampleDialog与共享SeeAllDialog)。

编排层还有一条设计文档未展开、但决定默认选中项的隐性逻辑:load_task_runs()拉取fetchTaskRuns后调用 test_run_selection.ts 的select_default_test_run()——优先选带 trace 的最新 run。原因写在注释里:step_count_check、tool_call_check等依赖执行轨迹的 judge 遇到无 trace 的样本会 skip(missing_trace),而空数组[]在 JS 中是 truthy,会被当真 trace 发出去、打出“0 步/0 工具调用”的误导性分数,因此宁可退回到“最新 run”也不选空 trace。

Reference Data 字段:实现相对线框图的增量

线框图的 State 4 用Tone match — needs reference暗示了 reference data 的参与,而实现中它是 Ready 与 Results 两态都会出现的独立控件 reference_data_field.svelte。它呈现为Reference Data行 + 可点链接(值为None、JSON key 摘要如a, b +2 more、非法 JSON 提示,或Missing x, y的红色缺失字段提示);点开是宽对话框,用 Name/Value 行编辑器录入任意合法 JSON,保存时做 key 非空与去重校验。对话框内的说明卡按usage_mode(llm_judge/reference_field/optional/code)区分文案,并明确区分“judge 必需的 key(缺了会被 skip 拒跑)”与“prompt 额外读取的 key(缺失会绕开渲染而非 skip)”——这一细节对应编排层judge_reference_signals把server_reference_keys与 prompt 解析出的 key 分开统计的逻辑。

5. 后端支撑:test_v2_eval 接口

侧栏的每次Run Test最终落到 studio server 的接口(eval_api.py):

  • POST /api/projects/{project_id}/tasks/{task_id}/evals/{eval_id}/test_v2_eval:请求体携带 eval 配置(properties,或 LLM judge 的llm_judge_builder_input)与eval_input(由前端build_eval_input()组装:final_message取样本 output、task_input、trace、reference_data),服务端即时执行一次评分并返回TestV2EvalResponse;
  • 同一文件中的test_v2_eval_draft端点则支持尚未保存的 eval 草稿测试——用草稿的output_scores构造内存中的临时 eval,使创建流程在 save 之前就能验证 judge;
  • 响应中的score_range_errors(eval_api.py)承载第 4 节所述的取值范围校验结果,且分数本身仍会随响应返回,让面板可以展示违规值本身(Preview only · not saved区照常渲染,同时给出Score Out of Range告警并锁住 Save)。

请求链路上,前端区分两类 API:LLM judge 走testV2EvalLlmJudge(携带 model、provider、g_eval、judge/system prompt、judge_instructions),其余类型走testV2Eval(携带表单getProperties()得到的 properties);code eval 保存时会额外把reference_keys放入 properties,因为客户端最清楚用户评分代码读取了哪些 reference 字段(见 eval_config_builder.svelte 的注释)。

6. 测试保障与线框-实现差异清单

面板的状态机有专门的组件测试 eval_test_run_pane.test.ts(约 2200 行,Vitest + jsdom,用 dialog/form 等 stub 隔离依赖),按State 1: Empty dataset等小节逐状态断言渲染内容;architecture.md 第 11 节列出的目标还包括:信任流程“点 Trust 立即关弹窗并进入 Running”、取消Save Without Testing?不留卡死的保存 spinner(B1 回归)、参考数据 JSON 合法/非法路径、手动示例生成 ephemeral run 等。

最后汇总线框图与当前实现的主要差异,便于对照阅读源码:

设计线框当前实现依据
面板标题Test RunTest Judge+ 副标题Test your judge on real data before saving.eval_test_run_pane.svelte
选中行只展示 Input 的 JSON 预览同时展示 Input 与 Output(截断 + SeeAll 全文)test_run_input_card.svelte;project_overview.md 明确“design md only shows input, but you're right we need to show both”
Ready 态内联 4 个DatasetRow+ 4 行候选1 个选中卡片 + Change 打开宽弹窗浏览同上(“Main screen: just shows selected option + 2 other options”)
弹窗含Search inputs…搜索框弹窗无搜索,保留分页表格project_overview.md:“no search in modal”
逐行StatusDot(pass/fail/skip 字形)跳过样本以Warning+skipped_reason/skipped_detail呈现eval_test_run_pane.svelte
Footer 承载Save Eval主按钮Save 按钮位于左栏表单底部,面板内保留Run Again;未测试时由Save Without Testing?/Test Required两个对话框门控eval_config_builder.svelte
—(线框未涉及)新增Score Shape Mismatch、Score Out of Range告警、ToolCallLogTable、Reference Data 编辑器同上及 reference_data_field.svelte

7. 小结

Test Judge 侧栏是 Kiln Evals V2 创建流程中“先验证、后保存”理念的核心载体:设计文档 test_run_sidebar.md 用“共享面板框架 + 5 个状态 + 状态迁移图”把空数据集、选输入、运行中、结果预览与浏览弹窗完整约束为一个类型无关(type-agnostic)的状态机;实现侧则以 eval_test_run_pane.svelte 为纯展示层、eval_config_builder.svelte 为唯一编排者,叠加了 trace 优先的默认样本选择、AbortController 取消、配置版本化的“测试有效性”判定、分数形状与取值范围双重校验,以及 code eval 的信任门——这些在仓库源码与 architecture.md 测试策略中均有对应实现和回归测试可查。

  • 人工智能
  • AI 应用
  • AI 评测
  • RAG
  • 微调
  • 提示工程
  • 开发工具
  • 桌面应用

【免费下载链接】Kiln

Build, Evaluate, and Optimize AI Systems. Includes evals, RAG, agents, fine-tuning, synthetic data generation, dataset management, MCP, and more.

项目地址:https://gitcode.com/gh_mirrors/ki/Kiln
点击查看免费下载

相关推荐

上一篇:从0到1:Cane配置文件(.cane)完全解析与最佳实践
下一篇:Git仓库瘦身终极指南:BFG Repo-Cleaner快速清理大型文件

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进