☰
Kiln Deep CR Cleanup 实战指南:从代码评审积压到分阶段修复的工程化治理
2026/10/15 18:26:16 网站建设 项目流程
  • 人工智能
  • 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 仓库中 Deep CR Cleanup 项目总览 与 实施计划 为核心,系统还原一次针对 evals_v2 深度代码评审(deep code review)遗留问题的"积压分诊(triage)→ 决策锁定 → 分阶段实施"完整治理流程。你将掌握:如何把数百条评审意见按正确性、API 一致性、安全加固、前端类型安全、数据模型语义、测试质量、依赖清理七类分批决策;如何区分 FIX / SKIP / DOC-ONLY / DROPPED / SUPERSEDED 五种处置结论;以及其中涉及 V2 评测系统底层实现的全部关键代码证据,可直接用于你自身项目的代码评审清理实践。

一、背景:什么是 Deep CR Cleanup

deep-cr-cleanup是 Kiln 仓库中的一个规格项目(spec project),由两份文档组成:

  • project_overview.md:充当功能规格与架构规格,逐条记录每个评审发现项的处置决策;
  • implementation_plan.md:将决策转化为 16 个"单一关注点(single-concern)"的提交阶段。

它的任务定义非常明确(见 project_overview.md 开篇):处理 evals_v2 深度代码评审(reviews/projects/evals_v2/)中"中度(moderate)"与"轻度(mild)"级别的发现项——这些是四个关键修复提交(tool_call_check 的expected_args、V1 共存测试、Issue 1 V2 新生成、Issue 2 EvalInput 执行)未覆盖的剩余部分。四个关键问题已修复并提交在scosman/evals_v2分支上,不属于本规格范围。

整个积压通过"逐批(batch-by-batch)分诊"完成:共 7 个批次(Batch 1–7),每一条记录用户最终决策(FIX / SKIP / 选择的具体选项)。7 个批次全部决策完毕,分诊(triage)已完成。其中仅有一项(RAG punt)在规格之外已提前实施,因此实施计划为它专门设计了Verification(验证)阶段,只确认它是否符合既定目标、把缺口记为后续事项,而不重复实现。

1.1 给编码 Agent 的实施准则

project_overview.md 中有一节专门写给后续实施 Agent 的"Implementation guidance",是理解整个项目调性的关键:

  1. 读代码后允许"顶回去"(push back):如果按原样实现某个决策会导致质量回退、破坏不变量、违背现有设计,或与代码实际行为相悖,应当停下来提出质疑,而不是硬改。不要为了满足一行清单项而降低代码质量。
  2. 决策是"方向"而非"强制令":质量门槛依然是"非常高的代码质量"。若清单项与质量门槛冲突,以质量门槛为准,并标记冲突。
  3. 简明地呈现质疑:说明(原决策是什么 / 代码实际表现 / 为什么改动有害 / 建议的替代方案),交由人类重新决策。
  4. 这不等于可以偷懒:只允许在真实的正确性/质量问题(以代码为证据)上质疑,不允许因为嫌麻烦而悄悄跳过工作。

这四条准则贯穿了后续 16 个实施阶段,也解释了为什么下面会看到大量"SKIP(经代码确认非问题)"的结论——分诊过程本身就是"清单项 vs 代码事实"的反复对质。

二、七批积压分诊全览:处置结论的类型学

整个 project_overview.md 的主体是 7 个批次的决策记录。在深入每个批次之前,先建立结论类型学——整个文档出现的处置标记有五种:

处置标记含义示例
FIX按清单项修复,通常附带具体实现方向Batch 1 的required_varNone 处理
SKIP经代码确认非问题/无价值,不改动Batch 2 的freeze_support()
DOC-ONLY代码行为是有意为之(通常优于规格),只改文档记录code_eval_validate_scores严格模式
DROPPED (non-issue)清单项本身站不住脚,被代码事实推翻"missingValidationError" 测试
SUPERSEDED被其他任务(RAG punt)覆盖,转入对应追踪scoring_utilsstrict标记

批次与实施计划阶段的映射关系大致为:Batch 1→Phase 2/6,Batch 2→Phase 9/10,Batch 3→Phase 11/12,Batch 4→Phase 13/14,Batch 5→Phase 3/4/5,Batch 6→Phase 7/8/15,Batch 7→Phase 16。下面逐批展开。

三、Batch 1:正确性缺陷(Correctness bugs)

这一批处理的是评测链路中最容易"悄悄出错"的地方——缺失数据、错误屏蔽、空输出校验。

3.1required_var与 extract:None也应按"跳过"处理(FIX)

v2_eval_helpers.py 中的check_reference_key()是本决策的核心证据。看当前代码(v2_eval_helpers.py#L114-L141):

def check_reference_key( reference_key: str, eval_input: EvalTaskInput, ) -> tuple[Any, SkippedReason | None, str | None]: if eval_input.reference_data is None: return (None, SkippedReason.missing_reference_key, f"No reference_data; need key '{reference_key}'") if reference_key not in eval_input.reference_data: return (None, SkippedReason.missing_reference_key, f"reference_data missing key '{reference_key}'") value = eval_input.reference_data[reference_key] if value is None: return (None, SkippedReason.missing_reference_key, f"reference_data key '{reference_key}' is None") return value, None, None

注意第 135 行的if value is None分支:当前代码已把None值视为"缺失引用数据"并返回missing_reference_key跳过原因。这正是 Batch 1 #1 决策"treatNone(not justUndefined) as skip"落地后的形态——缺失引用数据应当产生干净的 skip,而不是把None当作垃圾值喂给 judge。

同文件中的extract_value()(v2_eval_helpers.py#L49-L83)也体现了同一语义:表达式解析结果若为Undefined或None(第 77 行),均返回extraction_failed跳过;而extract_output_value()(第 86-111 行)则区分"缺 trace → 数据可用性跳过"与"缺输出字段 → 确定性检查的 FAIL"两种语义。Skip 只保留给缺 ground-truth / 引用数据的场景——这是整个 V2 评测语义的基石。

3.2_filter_output_to_score_keys:无匹配键时应当报错而非静默透传(FIX)

原清单项指向v2_eval_llm_judge.py:180-197的_filter_output_to_score_keys:当输出键与评分键无一匹配时,当前行为是"静默把未过滤的输出透传下去",应当改为抛出一个清晰的 "no recognized score fields (expected X, got Y)" 错误。

不过文档同时明确给出一个开放问题(open question):该项可能随 RAG 模板删除一起消失——这正是实施计划 Phase 1 验证阶段的职责("Resolve Batch-1 #2's open question: does_filter_output_to_score_keysstill exist?")。看当前仓库的 v2_eval_llm_judge.py,文件已不再包含_filter_output_to_score_keys,说明该函数确实随 RAG punt 被移除;LLM judge 的打分现在走 scoring_utils.py#L175-L197 的build_llm_as_judge_score():

def build_llm_as_judge_score(run_output, score_from_token_fn): scores: EvalScores = {} if not isinstance(run_output.output, dict): raise ValueError("LLM as Judge output must be a dictionary") for metric, value in run_output.output.items(): token_score = score_from_token_fn(f"{value}") if token_score is None: raise ValueError( f"No score found for metric: {metric}. The LLM failed to follow the scoring rubric/instructions/schema." ) scores[metric] = token_score return scores

注意该函数没有strict标志参数——这正是 Batch 1 #4"SUPERSEDED by the RAG punt"的直接证据:RAG 删除任务把 V2llm_judge恢复为离散打分(allow_float_scores=False,见 v2_eval_llm_judge.py#L171-L173 的build_score_schema(self.eval, allow_float_scores=False)),V1 与 V2 共用原始的严格打分器,不再有分支与标志。

3.3 V1validate_output_fields的output is not None守卫(FIX)

清单项:eval.py的validate_output_fieldsV1 分支应加output is not None守卫——因为 V1 EvalRun 若output=None当前能通过校验,随后在别处触发AttributeError。

当前代码已落地该守卫,eval.py#L1058-L1059:

if self.output is None and self.skipped_reason is None: raise ValueError("V1 EvalRun requires output to be set")

即:非跳过的 V1 EvalRun 必须有output。同段代码还体现了full_trace校验(第 1043-1050 行:非跳过、evaluation_data_type == EvalDataType.full_trace的写入必须携带 trace,历史文件豁免)与final_answer校验(第 1061-1065 行:final_answer运行不应携带 trace)。

3.4 其余结论:DOC-ONLY 与 DROPPED

  • DOC-ONLY(保持代码不动):code_eval的_validate_scores(v2_eval_code_eval.py#L126-L160)采用严格键精确匹配(actual_keys != expected_keys直接RuntimeError),与规格的宽松规则(缺失→None / 多余→忽略)有意分歧。决策:保留严格行为,把"严格校验是 Kiln 的既定行为"记录到components/27 §2.2。当前代码还能看到对 bool 打分的拒绝(第 136-139 行:Score '...' returned a bool. Use a float)、非有限数拒绝(第 154-157 行)等安全加固,这些都印证了"严格"是经过深思熟虑的。
  • DROPPED(非问题):"missingValidationError"测试项:request.properties类型为V2EvalConfigProperties,FastAPI 对畸形 body 返回 422;且 pydantic v2 的ValidationError继承自ValueError,现有except ValueError已能捕获内部构造错误并返回 400。无需任何改动。

四、Batch 2:API 一致性(对应 Phase 9/10)

这一批处理eval_api.py中 API 契约与错误语义的偏差。

4.1 类型化 trust 端点响应(FIX 2.1)

原问题:trust 端点返回dict[str, bool],OpenAPI/TS 契约是一个开放的布尔映射,而非封闭对象。决策:改为命名模型CodeEvalTrustResponse(BaseModel){ trusted: bool }并重新生成 web_ui schema。

当前代码(eval_api.py#L539-L542)已落地:

class CodeTrustResponse(BaseModel): """Response indicating whether code is trusted for a project in this session.""" trusted: bool

两个端点都返回该模型(eval_api.py#L3329-L3356):

@app.post("/api/projects/{project_id}/add_code_trust", ...) async def add_code_trust_endpoint(project_id) -> CodeTrustResponse: project = project_from_id(project_id) add_code_trust(str(project.path)) return CodeTrustResponse(trusted=True) @app.get("/api/projects/{project_id}/add_code_trust", ...) async def check_add_code_trust_endpoint(project_id) -> CodeTrustResponse: project = project_from_id(project_id) return CodeTrustResponse(trusted=has_add_code_trust(str(project.path)))

前端的 trust 门禁逻辑则集中在 trust_code_dialog.svelte 与 eval_config_builder.svelte:当测试返回skipped_reason === "code_eval_not_trusted"且该 eval 类型requiresTrust时,弹出 trust 对话框,用户授权后重试 pending action(test 或 save)。

4.2 泄漏型 400 消息收敛(FIX 2.2,选项 a)

get_eval_progress与get_eval_config_score_summary在 V2 eval 上返回 400 时,消息里泄漏了实现细节 "This eval does not have a V1 eval set filter"。决策:保留"单资源→400 / 列表聚合→跳过"的正确拆分,只把两处消息改为类型无关的 "This endpoint isn't supported for this eval type."。列表端点(eval_results_summary、run_config_eval_scores)行为不变。

4.3 V2 校验 400 消息形状统一(FIX 2.3)

create_eval_config(eval_api.py#L2336-L2396)对 V2 类型不应再暴露原始的 pydanticValidationError字符串,而应返回与 V1 缺失字段错误同形状的干净 400,并补一个快速测试。同时注意该端点内嵌的 trust 门禁(第 2387-2393 行):保存 code eval 即"接纳新代码为永久可信",因此保存前必须已有本会话的代码 trust,否则返回 403。

4.4 两处 SKIP:spawn 相关项(2.4/2.5)

dev_server.py:25的freeze_support()与set_start_method("spawn")排序问题都被判为SKIP:开发模式从不冻结(PyInstaller),且 sandbox_worker.py 的文档注释明确子进程由kiln_ai.tools.sandbox_bridge.run_bridged_child通过显式get_context("spawn")生成,与全局默认无关,make_app()在 import 时也不 spawn。因此两者均为无害冗余,保持不动。

4.5 其余两项

  • FIX(2.6):trust 端点中的project_from_id、v2_eval_code_eval懒导入移到模块顶层(两者都不慢)。当前 eval_api.py#L90 已将project_from_id提升到顶层导入区。
  • SKIP(2.7):POST 端点返回 200 而非 201——这是全文件既有约定,单独改一个端点反而制造新的不一致,超出本分支评审范围。

五、Batch 3:code_eval 与安全加固(对应 Phase 11/12)

这一批围绕"用户自写 Python 评分器"的安全边界展开,是全部 7 批中改动最微妙的部分。

5.1 序列化完整 code_eval 执行(FIX 3.1)

原问题:_spawn_lock只包住p.start(),过于狭窄;规格 §4.6/§5.1 要求序列化整个code_eval 执行。决策:用模块级asyncio.Lock包住CodeEvalAdapter.evaluate()中的await run_in_executor(...)(让等待中的 code_eval 以协程挂起,而不是占用 executor 线程阻塞最多 300 秒),同时保留原有的窄threading.Lock包住p.start()以应对 PyInstaller 的 spawn 竞争(#7410)。

代码证据在 v2_eval_code_eval.py#L95-L100:

res = await run_bridged_child( target=execute_scorer_bridged, args=(props.code, inputs), timeout_s=float(props.timeout_seconds), server=server, )

execute_scorer_bridged(sandbox_worker.py#L24-L130)是子进程入口:双队列桥接协议(requests child→parent、responses parent→child)、重定向 stdout/stderr、exec(compile(...))后要求用户代码必须定义score()(且至少接受output或trace之一)、支持同步与async def score(经call_entrypoint透明 await)、并捕获SystemExit防止sys.exit()杀死子进程前不留结果。这些机制共同构成了"沙箱子进程 + 工具白名单 + 超时 + 崩溃/错误结构化上报"的执行链路。

5.2 超时输入的客户端校验(FIX 3.2)

code_eval_form.svelte的超时输入需加min={1} max={300},让非法值立即得到客户端反馈,而不是等到保存时才报服务器错误。当前表单默认值timeout_seconds: 180(见 code_eval_form.svelte),合法范围正是 1–300 秒。

5.3 信任对话框措辞(FIX 3.3)

这是安全沟通上的一次"反向修复":删除 trust 对话框中一切暗示有保护机制的安抚性语言(如 "Code evals execute Python in a sandboxed subprocess. While basic safeguards are in place, …"),完全不要描述执行方式(不提 "sandboxed"/"isolated"/"subprocess"/"safeguards"),只保留并强化直白的 "runs arbitrary Python code on your machine" 警告。因为该对话框唯一的问题是"你信任这段代码在你的机器上运行吗?"——任何暗示有保护机制的说法都会软化风险认知。

当前 trust_code_dialog.svelte 的文案即为决策落地后的形态:

This project wants to run Python code on your machine. Only proceed if you trust the eval code and this project. Never paste code from a stranger or the internet.

标题为 "Trust Code and Project?",按钮为 "I Trust this Code"——没有出现任何 "sandbox" 等字眼。

5.4 docstring 与行为对齐(FIX 3.4)

five_star的 docstring 声称 "clamped"(钳制),但代码实际是抛ValueError。决策:修正 docstring 以匹配(正确)的抛错行为。当前 eval_helpers.py#L133-L144:

@staticmethod def five_star(rating: int | float) -> float: """Validate and return a 1-5 star rating as a float. Raises ValueError if *rating* is not a number or is outside [1, 5]. """ if not isinstance(rating, (int, float)) or isinstance(rating, bool): raise ValueError(f"rating must be a number, got {type(rating).__name__}") # NaN compares False against both range bounds, so it needs its own check. if not math.isfinite(rating) or rating < 1 or rating > 5: raise ValueError(f"rating must be between 1 and 5, got {rating}") return float(rating)

六、Batch 4:前端类型安全与 UI(对应 Phase 13/14)

6.1 死三元表达式收敛(FIX 4.1)

formatEvalConfigName(formatters.ts#L335-L339)中 V2 的 compact/非 compact 两个分支输出相同,应折叠为单一表达式。

6.2 类型化表单契约EvalTypeFormApi(FIX 4.2 + 4.4)

原问题:V2 表单组件上用any/SvelteComponent<any>声明命令式 API。决策:定义一个共享接口,命名为EvalTypeFormApi——{ getProperties(): V2EvalConfigProperties; validate?(): string | null }——并让 registry 的createFormComponent按它做类型化,收窄调用点,移除eslint-disable。

当前代码已落地,见 registry.ts#L56-L59:

/** * Imperative API exposed by V2 eval-type form components via `export function`. * Used to retrieve form state from the parent page via `bind:this`. */ export interface EvalTypeFormApi { getProperties(): V2EvalConfigProperties validate?(): string | null }

各表单组件通过export function getProperties()/validate()实现该接口,例如 exact_match_form.svelte#L21-L36 会根据source是reference_key还是expected_value清除对方字段,并做必填校验。

6.3 其余前端清理

  • SKIP(4.3):LLM-judge 测试不对称——代码确认是有意为之:is_llm_judge渲染自己的<LlmJudgeForm>(V1 风格"配置→保存→运行 eval"),内联的 "Test Your Judge" 面板只服务确定性类型(即时、无模型调用)。
  • FIX(4.5):删除exact_match_form.svelte中死的"value_expression"联合成员(当前 exact_match_form.svelte#L10-L16 的 properties 里已没有该成员;注意contains_form.svelte仍在使用value_expression,该类型并未全局删除)。
  • FIX(4.6):7 处重复的as-cast 属性提取去重为共享的extractV2Props<T>(eval_config, expectedType)类型守卫工具——已在 registry.ts 实现,并被 code_eval_result.svelte#L11、contains_result.svelte#L13 等结果组件使用:$: props = extractV2Props(eval_config, "code_eval")。
  • FIX(4.7):tool_call_check_result.svelte应导入 schema 的components["schemas"]["ToolCallSpec"],而非本地镜像类型。
  • SKIP(4.8):set_check_form.svelte的命令式 null 与响应式派生之间的张力无害(source 为reference_key时 textarea 隐藏)。
  • SKIP(4.9):model_name/provider_name(展示)与combined_model_name(ID)的命名差异与既有AvailableModelsDropdown约定一致,重命名只会扩散改动面。
  • SKIP(4.10):eval_config_to_ui_name中不可达的|| eval_config_type回退无害,satisfies已保证全覆盖。

七、Batch 5:数据模型语义与规格对齐(对应 Phase 3/4/5)

7.1evaluation_data_type默认值优于规格(DOC-ONLY 5.1)

代码默认值EvalDataType.final_answer(eval.py#L1403-L1406:evaluation_data_type: EvalDataType | None = Field(default=EvalDataType.final_answer, ...))比规格更好:磁盘上省略该字段的 V1 eval 会按真正的 V1 行为加载,而不是含糊的None。决策:保留代码,更新规格15_v1_v2_coexistence.md:215以匹配并记录向后兼容理由。

7.2 调度器重命名(FIX 5.2)

eval_adapter_from_type重命名为legacy_eval_adapter_from_type,以标识旧路径;V2 分支的错误指向v2_eval_adapter_from_config。当前 registry.py#L37-L52:

def legacy_eval_adapter_from_type(eval_config: EvalConfig) -> type[BaseEval]: """Legacy dispatch -- returns a BaseEval subclass for g_eval/llm_as_judge. For v2, raises NotImplementedError (v2 adapters use v2_eval_adapter_from_config). """ match eval_config.config_type: case EvalConfigType.g_eval: return GEval case EvalConfigType.llm_as_judge: return GEval case EvalConfigType.v2: raise NotImplementedError( "V2 eval configs should use v2_eval_adapter_from_config(), not legacy_eval_adapter_from_type()" ) case _: raise_exhaustive_enum_error(eval_config.config_type)

与之对称的 V2 调度器v2_eval_adapter_from_config(registry.py#L55-L73)读取properties.type,经_V2_ADAPTER_MAP(exact_match / pattern_match / contains / set_check / tool_call_check / step_count_check / llm_judge / code_eval 八类)查表实例化,未注册类型走NotImplementedError(对应type_not_available跳过路径,见 eval_runner.py)。

7.3 引用数据的 UI 门禁 TODO(FIX 5.3,TODO-gate)

这是整个规格中唯一有意保留 TODO的地方,属于硬性发布前门禁:V2.0 中没有任何已接线(wired)的 UI 来源能填充reference_data,因此在每个暴露reference_key/reference_data 源的 UI 入口(如exact_match_form.svelte、set_check_form.svelte的 source 选择器)添加 TODO,内容必须是:发布前要么把 reference_data 填充接线进 UI,要么移除该选项。SDK/代码路径不受影响,这只是 UI 完整性门禁。CI 的 no-TODO-on-main 规则使该 TODO 成为硬性的发布前拦截。

代码侧可见证据:reference_data_keys()(eval.py#L411-L435)对V2EvalConfigProperties联合类型做穷尽匹配,返回各 judge 需要的引用数据键——新增 V2 类型若不在其中会直接ty类型检查失败;而 registry.ts 中有SHOW_REFERENCE_DATA_UI开关控制 exact_match / contains 等表单是否展示 reference_data 来源(explainer 文案随开关切换),正是该门禁的运行时开关。

7.4 其余数据模型项

  • FIX(5.4):删除CodeEvalProperties.validate_code中死的except SyntaxError: pass。当前 eval.py#L357-L360 已将 SyntaxError 转为带上下文的ValueError。
  • FIX(5.6):澄清dataset_id/eval_input_id互斥错误消息。当前 eval.py#L982-L989:
@model_validator(mode="after") def validate_input_source(self) -> Self: if (self.dataset_id is None) == (self.eval_input_id is None): raise ValueError( "Exactly one of dataset_id (V1 TaskRun source) or " "eval_input_id (V2 EvalInput source) must be set" ) return self
  • SKIP(5.7):EvalInputFilter镜像DatasetFilter是规格 §4.2 的有意设计,已有文档。
  • FIX(5.8):用显式属性类型元组替换脆弱的双重get_args解包。当前 eval.py#L397-L408 已定义V2_PROPERTY_TYPES: tuple[type[BaseModel], ...](八个属性类),并声明"必须与V2EvalConfigProperties联合完全一致"。
  • FIX-light(5.9):在 adapter/runner 初始化时缓存output_scores,避免build_binary_scores每条目调用parent_eval()(磁盘 I/O)。build_binary_scores位于 v2_eval_helpers.py#L16-L26,对空输出分数表返回{}。
  • → Batch 6(5.5/5.10):skipped_reason保持str(前瞻兼容),补测试断言 runner 路径产出合法SkippedReason值;V1-dict-带-properties.type:"exact_match"的判别联合误路由往返测试并入 Batch 6 的 6.4。

八、Batch 6:测试质量与去重(对应 Phase 7/8/15)

这一批是测试内部事项(test-internal),用户委托并采纳建议。已在更早提交解决的:Phase-9a 关键项(never+expected_args测试,提交7e73c3f)、Phase-9b 关键项(API V1 共存测试,提交42050a2);rag_judge_templates冒烟测试因文件被 RAG punt 删除而判 MOOT。

  • FIX(6.1):把重复的StubV2Eval/SkippingStubV2Eval(散布于test_v2_dispatch_and_contract.py与test_eval_runner.py)抽到共享 conftest/helper。
  • FIX(6.2):去重test_registry.py中重叠的 dispatch 测试与精确重复项,并与 5.2 的重命名协调。
  • FIX(6.3):为 6 个确定性匹配器测试文件共用的_make_config/_inp样板建立共享 conftest fixture 工厂。
  • FIX(verify+fill)(6.4):模型层 V1 共存——确认42050a2覆盖后补缺口:(a) V1 EvalRun 携带新可选字段→None,(b) V1 配置走 legacy runner 端到端,(c) V1config_type=None加载+运行;同时吸收 5.10(误路由往返)。文档明确标注这是A0.1 V1 回归防护中最有价值的条目。
  • FIX(6.5):test_g_eval_raises_when_provider_lacks_logprobs从run_until_complete改为@pytest.mark.asyncio。
  • SKIP(6.6):create_eval_config 页面测试的重 mock 是页面级集成测试固有成本,只是不再增加 mock 数量。
  • FIX-light(6.7):把 exact_match 的"带/不带 eval_config 打分"渲染器测试模式扩展到其余 7 个结果渲染器。
  • FIX(6.8):test_nothing_persisted应断言真实的数据模型目录,而不是无关的tmp_path(避免空洞通过)。
  • SKIP(6.9/6.10):_coerce_to_set直接测试保持现状(公开evaluate()已覆盖);匹配器 pass/fail 测试参数化属于低价值的机械改动。
  • FIX(6.11):sandbox_worker 超时测试sleep(60)/2s 改为sleep(10)/1s(失败时更快,余量相同)。
  • FIX-light(6.12):为test_g_eval_data/增加 provenance README(pickle 夹具不透明)。
  • FIX-light(6.13):用it.each参数化v2_eval_api.test.ts中 8 个重复的错误路径测试。
  • FIX(6.14):registry.test.ts的数量断言从硬编码toHaveLength(8)改为从 expected-types 数组派生。

九、Batch 7:依赖与琐碎清理(对应 Phase 16)

  • FIX(7.1):移除冗余的codemirror伞包并重新解析 lockfile。分诊时已验证:编辑器只用 5 个细粒度@codemirror/*包(在 code_editor.svelte 动态导入,版本钉在 package.json#L52-L56 的@codemirror/commands、@codemirror/lang-python、@codemirror/language、@codemirror/state、@codemirror/view),伞包在src中无处导入,删除后可去掉约 4 个未使用的传递依赖(@codemirror/autocomplete、lint、search、crelt),且不影响正在工作的编辑器。
  • SKIP(7.2):约 30 个无关的"peer": truelockfile 标记是 npm 元数据重组噪声;7.1 的npm install反正会重新解析 lockfile。

十、实施计划:16 个单一关注点阶段

implementation_plan.md 将全部决策组织为16 个阶段,每阶段一个单一关注点提交(编码 Agent → 规格感知 CR → 提交)。核心原则与阶段对照如下:

阶段关注点对应 Batch 条目
Phase 1验证已完成的 RAG punt(只验证,不重做)Already-completed work + Batch 1 #2 开放问题
Phase 2后端正确性守卫Batch 1 #1/#2/#3 + 5.4
Phase 3重命名 legacy 调度器5.2 + 6.2
Phase 4数据模型健壮性5.8 / 5.9 / 5.6
Phase 5引用数据 UI TODO 门禁5.3
Phase 6规格文档对齐(仅文档)1d / 5.1
Phase 7后端测试卫生6.1 / 6.3 / 6.5 / 5.5 / 6.12
Phase 8V1 共存测试守卫(A0.1 回归防护)6.4(吸收 5.10)
Phase 9类型化 trust 响应 + schema 重新生成2.1
Phase 10API 一致性修复2.2a / 2.3 / 2.6 + 6.8
Phase 11code_eval 执行序列化(最微妙,独立提交)3.1 + 6.11
Phase 12code_eval UI 与文档打磨3.2 / 3.3 / 3.4
Phase 13前端类型化表单契约4.2 / 4.4
Phase 14前端清理4.1 / 4.5 / 4.6 / 4.7
Phase 15前端测试卫生6.7 / 6.13 / 6.14
Phase 16依赖清理 + 全项目检查7.1

贯穿所有阶段的横切规则(implementation_plan.md 明确列出):

  1. 遵循 overview 的 "Implementation guidance"——先读代码,宁可顶回去也不要为满足清单项而降低质量;
  2. 除 5.3 有意的 reference-data UI 门禁 TODO 外,不留任何 TODO;
  3. 提交前保证项目自动化检查全绿——注意 eval 测试套件需要禁用沙箱(日志 fixture 写入~/.kiln_ai/logs/);
  4. 代码改动的测试放在该改动所在阶段;独立的测试卫生阶段覆盖不与某个修复绑定的覆盖率/去重工作。

十一、对自身工程的启示:如何复制这套治理流程

Deep CR Cleanup 的实践价值不止于 Kiln 本身,其方法论可以直接迁移到任何经历过大规模代码评审的工程:

  1. 先分类,再处置:把评审积压按"正确性 / API 一致性 / 安全 / 前端类型 / 数据模型 / 测试 / 依赖"分批,避免一次性淹没在意见海里。
  2. 五种结论类型要齐全:FIX 不是唯一出路。SKIP(代码证明非问题)、DOC-ONLY(行为有意优于规格)、DROPPED(清单项本身不成立)、SUPERSEDED(被更高层任务覆盖)各有用武之地——本仓库 7 批 60+ 条目中近三分之一以 SKIP/DOC-ONLY/DROPPED 收场,这本身就是质量的信号:评审意见必须接受代码事实的检验。
  3. 用"验证阶段"处理"已提前完成"的工作:唯一已落地项(RAG punt)不重做,而是通过 Phase 1 验证清单确认删除干净(V1 与main字节级一致 A0.1、V2 恢复离散打分、严格打分器回归、libs/app/specs无 RAG 残留、specs/projects/rag_templates/project_overview.md记录删除 SHA),缺口记为 follow-up。
  4. 微妙改动独立成阶段:Phase 11(执行序列化)因涉及asyncio.Lock与窄threading.Lock的双锁共存而单独提交,便于回滚与评审聚焦。
  5. 发布前门禁要落到代码里:5.3 用"CI no-TODO-on-main"规则把"UI 暴露了无法填充的功能"变成硬性拦截——比口头约定可靠得多。

如果你想在本地验证本文引用的任何一处实现,可以按 README.md 的方式搭建 Kiln 环境,然后直接阅读上述源码路径;本文所有代码引用均为当前仓库中确认存在的实现,行号对应 project_overview.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
点击查看免费下载

相关推荐

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

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

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

立即咨询