ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AI编程中的Harness:让模型生成代码可控可验证

2026/9/2 15:25:04 拓冰建站 浏览量
AI编程中的Harness:让模型生成代码可控可验证 Harness 在 AI 编程领域的热度主要来自一个很直接的痛点大模型生成代码速度快但直接搬运到项目里并不让人放心。模型写出的代码可能语法正确但语义错误可能调用了不存在的接口可能随手修改了不应该动的配置文件也可能在同一个编译错误上反复打转。所谓 harness就是围绕编码模型搭建的一层工程化约束和控制层让代码生成过程可观察、可验证、可干预。这篇文章以“生成可控代码”为主线讨论几类能落地的 harness 实践并用一个最小项目说明从模型输出到验证反馈的闭合流程。Harness 并不是什么神秘工具。它在测试领域早就存在叫“测试夹具”作用是给被测对象提供稳定的运行环境。到了 AI 编程场景里它的角色变成给模型一个明确任务、一组受限工具、一套验证规则和一条失败后的反馈回路。社区里常见的做法是把 DeepSeek、Codex 这类编码能力较强的模型接入本地工具链时不直接执行模型输出而是先套一层 harness让模型在约束好的工作区里生成、修改、验证代码。这样做的目的只有一个让 AI 生成代码的过程像人工开发一样有任务单、有规范、有测试、有审查而不是把一段模糊输出直接当成交付物。下面从概念、控制维度、可落地实践、最小示例、参数调优、效果评估、常见问题和生产建议几个方向展开。1. 先搞清楚Harness 到底给编码模型加了一层什么1.1 从“模型直接输出”到“模型在框架内行动”没有 harness 时使用模型的典型流程是开发者把需求发给模型模型返回一段代码开发者复制到项目里手动编译、测试、修改。这个过程有两个问题。第一模型只负责生成不负责验证验证责任全部落在人身上。第二如果生成的代码有问题开发者需要手动把报错信息重新喂给模型多轮往复效率低且容易遗漏上下文。有了 harness 之后流程变成开发者只定义任务和验收条件harness 负责调用模型、接收输出、执行静态检查、运行测试、收集失败信息并把失败信息重新作为上下文发送给模型进行修复。整个过程被缩小到一个受控的执行循环里。模型不再直接接触真实项目目录而是先在一个临时工作区或受限目录中完成任务只有通过全部验证后diff 才会被提交到项目。这个转变的本质是“责任转移”。代码是否正确不再依赖模型一次性输出得对不对而是依赖验证器是否覆盖了关键问题。模型可以犯错但只要验证器能发现错误并且错误信息能有效地回到模型侧修复循环就可以收敛。Harness 解决的不是“模型能不能写对代码”而是“模型写错了能不能被及时拦住、及时修掉”。1.2 Harness 与 Agent 不是一回事很多人在实际项目里把 Harness 和 Agent 混在一起导致设计上出现偏差。Agent 强调的是自主决策模型可以根据目标自行规划步骤、调用工具、观察结果。Harness 强调的则是约束和管控它定义模型能做什么、不能做什么、做到什么程度算通过。两者的关系可以这样理解Harness 是 Agent 的“外壳 管理方”。一个 Agent 可以在 Harness 内部运行但它的每一步行动都要经过 Harness 的校验。模型认为自己有权限修改某个文件不等于它真的能修改Harness 会在文件系统层面对路径进行拦截。模型认为自己已经完成任务也不等于任务真的完成Harness 会执行测试套件来判断。对比项HarnessAgent设计目标约束、验证、可观测自主规划、执行复杂任务控制方式白名单、规则、验证器目标驱动、自我决策对错误的容忍错误必须被捕获并反馈错误可能被吞掉或继续执行核心产物可验证、可审计的交付物完成目标的过程和结果典型关系管理 Agent 的运行边界在 Harness 内完成任务设计 harness 时不要把“让模型更自主”当成目标。模型越自主越需要更强的边界。一个只知道“写代码”的模型如果被赋予了任意执行命令、任意访问网络的权限它可能在几秒内做出超出预期的事。可控的优先级永远高于智能。1.3 可控代码要控制的其实是四个环节可控制代码不是一个静态概念它分布在生成链路的四个环节里。输入环节要控制的是任务描述是否清晰、上下文是否完整、规则是否被模型看到。生成环节要控制的是模型参数的随机性、输出格式的合法性、生成结果是否被截断。执行环节要控制的是模型能否运行代码、能访问哪些路径、能调用哪些外部命令。验证环节要控制的是编译是否通过、测试是否通过、代码风格是否符合项目规范、是否存在明显安全隐患。这四个环节缺一个都会出现“看起来能跑但一进项目就出事”的情况。很多团队接了大模型写代码只做了生成环节的控制比如调低温度、限制输出长度却忽略执行环节和验证环节结果模型生成的代码破坏了本地环境或带上了不安全依赖。一个完整的 harness 至少要把这四个环节串起来输入有模板生成有格式约束执行有沙箱验证有测试。下面一节逐个展开。2. 生成可控代码的五个控制维度2.1 输入控制任务模板和上下文裁剪模型对输入非常敏感。同一个需求用一句话描述和用结构化的任务单描述输出质量差别很大。输入控制的核心是两件事一是把任务描述标准化二是把上下文裁剪到模型能有效处理的长度。任务模板的作用是固定输入结构。模板里至少应该包含任务目标、输入输出约定、技术栈约束、禁止事项、验收条件。模板不是限制开发者的表达而是让模型每次都收到同样完整的信息避免模型因为缺少关键约束而自由发挥。任务目标 用 Python 实现一个函数输入为整数 n返回第 n 个斐波那契数。 技术栈约束 - Python 3.10 - 只允许标准库 - 禁止使用递归方式 输出格式 必须输出 JSON包含 code、explanation、tests 三个字段。 验收条件 - 代码通过 pylint 检查 - 测试文件通过 pytest - 函数能正确处理 n0 和 n1上下文裁剪同样重要。把整个仓库的代码都塞进 prompt模型往往记不住早期内容还可能被无关文件干扰。常见做法是只把与任务相关的文件内容、函数签名、测试用例放进去。如果模型支持检索增强可以按相似度召回相关代码而不是无脑拼截上下文。2.2 工具控制白名单而不是自由发挥模型在生成代码过程中如果需要修改文件、执行命令、运行测试harness 必须明确告诉它可以用哪些工具。工具控制的基本原则是“默认拒绝白名单放行”。一个最小工具白名单配置可以这样设计{ allowed_tools: [ read_file, write_file, list_dir, run_command ], allowed_read_paths: [ ./src, ./tests, ./requirements.txt ], allowed_write_paths: [ ./src, ./tests ], allowed_commands: [ python, pytest, pylint, pip install ] }这里的逻辑不是告诉模型“你可以做什么”而是告诉 harness“模型只能做这些”。模型在 prompt 里可能说要删除某个目录harness 在执行层直接拒绝因为删除操作不在白名单内。把权限控制放在 harness 端而不是模型端这是很多失败实践的最重要分界线。永远不要依赖模型自己判断“这个文件该不该改”。2.3 执行控制沙箱、超时和资源限制即使有了工具白名单也建议把代码执行放到沙箱里。原因是模型生成的代码可能有难以预料的副作用占用大量内存、开启网络连接、修改全局配置。如果直接在开发机上执行清理成本非常高。常见的沙箱方案是 Docker 容器。把项目目录挂载进容器限制网络访问设置 CPU 和内存上限执行完自动销毁容器。这样即使模型生成的代码有问题影响范围也被限制在容器内。docker run --rm \ -v $(pwd)/workspace:/workspace \ -w /workspace \ --network none \ --memory 512m \ --cpus 1 \ --tmpfs /tmp:rw,noexec,nosuid,size128m \ python:3.11-slim \ bash -c pip install -r requirements.txt pytest tests/ -x这条命令里的关键参数每一个都有意义。--network none禁止容器访问网络防止模型生成的代码下载恶意依赖或外发数据。--memory 512m限制内存占用避免死循环代码拖垮宿主机。--cpus 1限制 CPU 使用。--rm保证任务结束后容器自动删除。--tmpfs把临时目录放到内存且不允许执行文件降低写入入侵风险。学习环境为了简单可以直接在本地执行但至少要使用临时目录。生产环境如果接受不了容器方案也要用操作系统级别的用户隔离和目录权限来做执行控制。2.4 验证控制编译、静态检查、测试三道关卡验证器是 harness 的裁判。没有验证器模型说“写完了”你无法判断真假。验证器应该分层设计每一层解决一类问题。第一层是结构验证。检查模型输出是否符合约定的 JSON 格式、字段是否齐全、代码是否能够被解析。这一层可以过滤掉大量格式错误。第二层是静态检查。包括编译、代码风格检查、静态类型检查。Python 项目可以用python -m compileall、pylint、mypyJavaScript 项目可以用tsc --noEmit、eslint。静态检查能发现语法错误、未使用变量、类型不匹配等问题执行速度快非常适合作为第一道自动化关卡。第三层是测试执行。运行项目的单元测试和集成测试验证代码行为是否符合预期。测试是判断代码是否“真的正确”的最可靠手段。如果项目测试覆盖不足harness 的可靠程度也会下降。#!/usr/bin/env bash set -e echo 1. 结构验证 python validate_schema.py generated_output.json echo 2. 静态检查 cd workspace python -m compileall src/ pylint src/ --errors-only mypy src/ --ignore-missing-imports echo 3. 测试执行 pytest tests/ -x --timeout30这三层验证的执行顺序不能乱。先做结构验证再做静态检查最后才跑测试。如果结构不对后面的检查都没有意义如果静态检查没过测试大概率也过不了。每一层失败后harness 都要收集失败信息返回给模型进行下一轮修复。2.5 修复控制有限轮次的失败反馈闭环模型第一次生成代码很可能无法通过全部验证这很正常。Harness 的价值在于把失败反馈给模型让模型有机会修正。但修复不能无限进行否则可能陷入死循环既浪费 token 又消耗时间。所以修复控制要有一个明确参数最大修复轮次。比如max_attempts 3意味着模型最多生成三次三次都失败就停止交给人工处理。def run_harness(task, max_attempts3): for attempt in range(1, max_attempts 1): prompt build_prompt(task, previous_feedback) output call_model(prompt) result validate_and_test(output) if result.passed: return result previous_feedback result.feedback print(f第 {attempt} 次修复失败原因{result.summary}) raise HarnessTimeout(超过最大修复轮次需要人工介入)这里的关键是previous_feedback。反馈信息必须简洁、结构化、可操作。直接把两万行 pytest 输出全部塞给模型模型反而不知道问题在哪。推荐的做法是保留错误类型、出错文件、出错行号、错误摘要以及最近的失败断言上下文。反馈质量直接决定修复能不能收敛。3. 五类可落地的 Harness 实践3.1 最小命令行 Harness把模型调用封装成校验流程最简单的一类 harness 适合个人开发者或学习场景用脚本封装“调用模型 - 生成文件 - 执行检查”的流程。它不涉及复杂的工作区权限控制也不依赖容器重点是把验证动作自动化。下面是一个最小 Python 示例的结构。call_model函数在实际项目中可以替换为任何模型的接口调用比如 OpenAI 兼容接口或本地模型服务。import json import subprocess import sys def call_model(prompt): # 实际项目替换为具体模型接口 # response client.chat.completions.create(...) # return response.choices[0].message.content return {} def generate(task: str) - dict: prompt_template 任务目标 {task} 输出格式 只输出 JSON不要输出解释性文字。 JSON 必须包含 code、explanation、tests 三个字段。 raw call_model(prompt_template.format(tasktask)) return json.loads(raw) def run_checks(payload: dict): with open(workspace/solution.py, w) as f: f.write(payload[code]) result subprocess.run( [pytest, workspace/, -x, --timeout30], capture_outputTrue, textTrue ) return result.returncode 0, result.stdout[-2000:] if __name__ __main__: task sys.argv[1] payload generate(task) passed, feedback run_checks(payload) print(PASSED if passed else feedback)这段代码的核心是“把验证动作从人工命令变成程序行为”。以后要调整检查项只需要改run_checks里的命令。要接入新的模型只需要替换call_model。最小 harness 的重点不是功能全面而是把生成到验证的链路建立起来形成闭环。3.2 工作区 Harness限制模型能改哪些文件第二个层级是工作区控制。当任务涉及多个文件模型需要读写项目代码时仅靠 prompt 约束不够。Harness 需要在文件系统层面强制执行路径白名单。一个可落地的思路是对所有文件操作做一层代理。模型不直接调用系统 API而是统一调用 harness 提供的read_file、write_file接口。每个接口在真正执行前都要检查请求路径是否在白名单内。ALLOWED_WRITE_PATHS { /workspace/src, /workspace/tests, } ALLOWED_READ_PATHS ALLOWED_WRITE_PATHS | { /workspace/README.md, /workspace/requirements.txt, } def safe_write(path: str, content: str): normalized str(Path(path).resolve()) if not any(normalized.startswith(p) for p in ALLOWED_WRITE_PATHS): raise PermissionError(f禁止写入路径: {path}) with open(normalized, w, encodingutf-8) as f: f.write(content)这种设计的优点是把权限判断从“模型的自我约束”变成“harness 的执行的强制约束”。即使模型在 prompt 里被诱导去修改config/app.conf只要路径不在白名单内操作就会抛错。工作区 harness 特别适合让模型修改一个已有项目的多个文件同时保护敏感配置不被覆盖。3.3 容器沙箱 Harness把依赖和副作用隔离起来当生成代码需要安装依赖、运行真实服务、连接数据库时本地工作区已经不够安全。这时候需要把整个环境装进容器做到“依赖隔离 副作用隔离”。容器沙箱 harness 的典型流程是生成代码并写入临时目录构建一个包含项目依赖的镜像启动容器挂载临时目录执行验证命令结束后销毁容器。这个流程适合集成到 CI 流水线中每次代码生成都在干净环境里验证。#!/usr/bin/env bash set -e TARGET_DIR$(mktemp -d) # 生成代码并写入 TARGET_DIR python generate_and_write.py $TARGET_DIR # 使用项目 Dockerfile 构建沙箱镜像 docker build -t code-harness-sandbox . # 执行验证 docker run --rm \ -v $TARGET_DIR:/app \ -w /app \ --network none \ --memory 1g \ --cpus 2 \ code-harness-sandbox \ bash -c pytest tests/ -x pylint src/容器方案的权衡是构建速度和隔离程度。每次构建镜像可能耗时较长所以一般把依赖安装分成两层基础依赖放在镜像构建阶段项目代码用挂载卷注入。这样代码变化不需要重新构建依赖层。生产环境可以在镜像仓库里缓存基础镜像把 build 时间控制在可接受范围内。3.4 测试驱动反馈 Harness让失败信息回到下一轮 prompt没有反馈的验证没有意义。测试驱动反馈 harness 的核心是把 pytest、eslint 等工具的输出转换成模型能理解的修复指令。一个常见错误是直接把原始测试输出全文喂给模型。原始输出包含大量路径、堆栈、无关警告模型很容易被干扰。正确做法是先结构化摘要哪条测试失败、失败类型是什么、期望值是什么、实际值是什么、关键堆栈在哪几行。上一轮结果 [1/3] test_fibonacci_value_0 - 失败 原因: assert fib(0) 0 实际: fib(0) 抛出了 IndexError 关键信息: 函数在 n0 时访问了 fib[-1] 请在不改变函数签名的情况下修复确保测试通过。这段反馈里包含了失败的测试名、断言、实际表现和关键线索。模型看到之后可以快速定位到边界条件处理缺失的问题。这样的反馈比“你的代码没有通过测试”有效得多。修复循环是否收敛很大程度上取决于反馈的信息密度。3.5 多 Agent 评审 Harness生成和审查分离更高一层的可控性来自“让另一个模型审查生成结果”。生成 agent 负责实现功能审查 agent 负责挑错。两者角色分离能减少生成 agent 自说自话的风险。多 agent 评审的流程并不复杂。先生成代码并运行基础测试再把代码、任务描述、测试结果一起交给审查 agent。审查 agent 不直接修改代码只输出审查意见包括正确性问题、边界条件、安全问题、可读性问题。生成 agent 根据意见修改第二轮再交给审查 agent 复核。这种方案的代价是 token 消耗增加延迟变高所以不适合每个小任务都使用。比较适合高风险代码例如数据库迁移、权限相关逻辑、支付金额计算、外部系统接口对接。评审 agent 的 prompt 也要单独设计不能简单复用生成 agent 的模板。审查时更看重“找问题”而不是“写代码”。4. 从零搭建一个最小可运行的 Harness 示例4.1 项目结构和运行环境下面用一个完整的 Python 小项目演示 harness 如何串起“生成 - 验证 - 修复”的闭环。项目结构如下harness-demo/ ├── runner.py ├── validator.py ├── prompt_builder.py ├── model_client.py └── workspace/ └── tests/ └── test_solution.py运行环境建议 Python 3.10 以上安装pytest、pylint。如果本地没有可用的模型接口可以把model_client.py里的模型调用替换成模拟函数先跑通整个 harness 流程再接入真实模型。pip install pytest pylint这里要说明一点真实项目里模型接口地址、API Key、模型名称都应通过环境变量注入而不是硬编码在代码里。学习环境为了减少配置成本可以先写在配置文件中但进入生产环境前必须外置化。4.2 任务模板和输出 JSON 约束prompt_builder.py负责把“任务 修复反馈”组装成模型输入。关键点是要求模型只能输出 JSON方便后续程序解析。def build_prompt(task: str, feedback: str | None None) - str: feedback_section if feedback: feedback_section f 上一轮验证失败请根据以下反馈修复代码 {feedback} return f 任务目标 {task} 约束 - 语言Python 3.10 - 只允许使用标准库 - 不要修改测试文件 - 函数签名必须保持为 fib(n) 输出要求 只输出一个 JSON 对象不要输出任何解释文字。 JSON 结构如下 {{ code: 完整的 Python 代码, tests: 针对该函数的 pytest 测试代码, explanation: 一句话说明实现思路 }} {feedback_section} 这里要求模型把代码和测试一起输出是为了让闭环更完整。测试代码虽然会覆盖预置的test_solution.py但模型给出的测试可以作为额外参考。实际项目中建议测试由人编写而不是模型编写否则模型的错误思路会渗透到测试里。模型自测通过不等于功能正确只有独立的测试套件才可信。4.3 验证器链从 JSON 解析到测试执行validator.py负责执行验证。它的输入是模型原始输出输出是一个验证结果对象包含是否通过、失败原因、完整反馈。import json import subprocess import tempfile from pathlib import Path class ValidationResult: def __init__(self, passed: bool, feedback: str): self.passed passed self.feedback feedback def validate(raw_output: str, workspace: Path) - ValidationResult: try: payload json.loads(raw_output) except json.JSONDecodeError as e: return ValidationResult(False, f模型输出不是合法 JSON{e}) if not all(k in payload for k in (code, explanation)): return ValidationResult(False, JSON 缺少 code 或 explanation 字段) src_dir workspace / src src_dir.mkdir(exist_okTrue) (src_dir / solution.py).write_text(payload[code], encodingutf-8) compile_result subprocess.run( [python, -m, compileall, str(src_dir)], capture_outputTrue, textTrue ) if compile_result.returncode ! 0: return ValidationResult(False, f编译失败{compile_result.stderr[-1500:]}) pytest_result subprocess.run( [pytest, str(workspace / tests), -x, --timeout30], capture_outputTrue, textTrue ) if pytest_result.returncode ! 0: return ValidationResult(False, pytest_result.stderr[-2000:] or pytest_result.stdout[-2000:]) return ValidationResult(True, 所有验证通过)注意验证器里使用了compileall作为第一道检查。它的执行速度比 pytest 快很多能在测试前就发现语法错误。如果编译失败就没有必要跑测试节省时间。这个顺序体现在代码里就是先编译、后测试。tempfile在代码里导入但未使用真实项目中应使用临时目录管理输出文件避免污染工作区。下面示例简化了路径管理实际生产代码要清理临时文件。4.4 修复循环把失败反馈送回模型runner.py是整个 harness 的主入口。它定义最大修复轮次循环调用模型、验证、反馈。import sys from pathlib import Path from model_client import call_model from prompt_builder import build_prompt from validator import validate def run(task: str, max_attempts: int 3): workspace Path(workspace).resolve() feedback None for attempt in range(1, max_attempts 1): print(f--- 第 {attempt} 轮 ---) prompt build_prompt(task, feedback) raw_output call_model(prompt) result validate(raw_output, workspace) if result.passed: print(验证通过) return print(f验证失败{result.feedback[:200]}) feedback result.feedback print(f连续 {max_attempts} 轮失败自动停止请人工介入。) sys.exit(1) if __name__ __main__: run(实现 fib(n)返回第 n 个斐波那契数n 为自然数要求处理 n0 和 n1 的边界。)修复循环有一个容易忽略的细节失败反馈不能无限累积。如果每一轮都把上一轮的完整输出拼进去prompt 会越来越长模型反而抓不住重点。一般建议只保留最近一次失败反馈或对多轮反馈做摘要压缩。可以在build_prompt里只保留feedback字段也就是最近一轮的验证结果。4.5 跑通流程后的预期结果如果接入了真实模型正常流程会看到类似这样的输出--- 第 1 轮 --- 验证失败test_fibonacci_value_0 失败fib(0) 抛出了 IndexError --- 第 2 轮 --- 验证失败test_fibonacci_value_1 失败fib(1) 返回 1期望 0 --- 第 3 轮 --- 验证通过如果没有接入模型可以先写一个模拟的call_model让它在第一轮返回固定 JSON用来测试 validator 和 runner 的逻辑是否正常。这也是一个很好的工程习惯在模型接入前先把 harness 自身链路验证好。def call_model(prompt: str) - str: return json.dumps({ code: def fib(n):\n if n 0:\n return 0\n if n 1:\n return 1\n return fib(n - 1) fib(n - 2), tests: def test_fib():\n assert fib(0) 0\n assert fib(1) 1, explanation: 使用递归实现但实际生产需要处理性能问题 })模拟模型的目的不是验证模型能力而是验证 harness 的 JSON 解析、文件写入、命令执行、结果判断是否正常。等 harness 链路稳定后再替换成真实模型接口排查范围会小很多。5. 关键参数怎么调调错了会有什么表现5.1 模型侧参数温度、采样和输出长度模型侧参数直接影响生成结果的可控性。下面是三个最常调整的参数。参数说明推荐值代码生成场景调大影响调小影响temperature控制随机性0 到 0.3更容易出现不常见写法也更容易出错输出更稳定、更保守top_p核采样控制候选词集合0.9 到 1.0多样性增加确定性增加max_tokens控制最大输出长度按任务预估留 20% 余量可能输出无关内容代码容易被截断代码生成场景建议把 temperature 调到接近 0。原因不是温度越低越聪明而是代码对确定性要求高。同样的需求模型如果每次输出不同实现验证成本会显著增加。低温度可以保证在同样输入下输出基本稳定便于复现和排除问题。max_tokens这个参数在 harness 里尤其重要。如果设置过小生成的代码会被强行截断validator 会报 JSON 解析错误。如果设置过大模型可能输出大量无关解释浪费 token。一个通用估算方法是按任务文件中可能出现的代码行数乘以每行约 10 到 15 个 token再加上 500 token 余量。5.2 Harness 控制参数harness 自身的参数比模型参数更影响可控性。这些参数通常集中在配置文件里而不是散落在代码中。参数默认值参考含义设置过大的表现设置过小的表现max_attempts3最大生成修复轮数长时间无效循环成本上升模型来不及修复可修复的错误timeout_seconds60单轮验证超时卡在死循环或慢测试中正常测试被误杀enabled_checkscompile, lint, test启用的验证器验证过严影响通过率漏掉关键错误allowed_write_paths按项目配置允许写入的目录模型可能改到敏感配置模型无法完成多文件任务max_attempts是最容易调错的一个参数。设成 1 等于不让模型修复设成 10 又可能让模型在错误方向上反复消耗。建议从 3 开始观察平均修复成功轮次后再调整。如果大部分成功任务都在第 2 轮修复成功而第 3 轮几乎不产生新进展就把最大值调回 3如果第 3 轮仍频繁成功可以提高到 5。timeout_seconds也需要按验证器的耗时单独设置。编译检查通常几秒内完成pytest 时间取决于测试规模集成测试可能需要几分钟。不要让一个统一超时同时约束所有验证器。更合理的做法是每个验证器都有单独的超时配置。5.3 安全边界和资源限制参数安全参数是生产环境不能省的。下面是一组必须显式配置的安全约束。security: forbidden_commands: - rm -rf - shutdown - sudo - curl - wget forbidden_packages: - subprocess - os.system max_output_size: 204800 max_runtime_seconds: 300 enable_network: false enable_audit_log: true api_keys_in_prompt: false这里的forbidden_commands和forbidden_packages是针对模型生成内容的静态过滤不能替代沙箱隔离但可以作为前端过滤。api_keys_in_prompt控制的是组装 prompt 时是否允许把 API Key、数据库连接串等敏感信息传给模型。默认应该是 false。任何情况下都不应该把生产密钥放进模型上下文因为模型输出可能被记录到日志、发送到外部服务或在不经意间被写入代码文件。6. 如何验证 Harness 真的让代码变可控了6.1 用通过率、轮次、越权次数三个指标评估搭建完 harness 后需要用数据判断它是否真的有效。三个核心指标值得长期记录。指标含义怎么统计信号解读首轮通过率不经过修复第一次生成就通过验证通过的样本数 / 总样本数越高说明任务模板和上下文越有效平均修复轮次从首次失败到最终通过的轮次数所有成功样本的轮次均值平均值越低反馈信息越有效越权次数模型尝试访问白名单外路径或命令的次数harness 拦截器日志计数数值高说明 prompt 约束或工具边界需要加强建议准备一个 20 到 50 个任务的评测集任务从简单到复杂分布。用相同的 harness 配置跑一遍记录通过、失败、越权和轮次。这个评测集的价值不只是验证当前 harness 状态它还能在更换模型版本、修改 prompt 模板时做回归对比避免“某次改动让整体效果变差”却没人发现。{ task_id: task_023, model: 模型标识, attempts: 2, passed: true, violation_count: 1, first_pass: false, duration_seconds: 45 }这类结构化记录可以累积成 CSV 或 JSONL后续用脚本统计。没有数据的 harness 优化都是凭感觉有了数据才知道该调参数还是该改 prompt。6.2 有 Harness 和无 Harness 的关键差异有 harness 和没有 harness使用模型的方式完全不同。可以用一张表说明差异。场景无 Harness 的典型行为有 Harness 的典型行为代码错误人工复制代码、手动运行、手动复制报错自动执行验证器失败信息结构化回流文件修改模型建议人工确认容易遗漏路径白名单强制执行越权直接拦截测试运行依赖人工执行时常跳过作为验证关卡自动执行失败处理模型可能重复同样的错误有限修复轮次内自动修正超限则介入审计几乎没有记录模型请求、工具调用、验证结果全部留痕这些差异不需要等到生产环境才会体现。即使是个人项目把 harness 脚本固定下来之后每次让模型生成代码都走同一套验证流程长期积累节省的时间非常可观。最明显的收益是“模型生成错误代码”时修复路径从“人来复述问题”变成“harness 自动把问题上下文带回给模型”。6.3 日志审计让每次生成和修复都可回溯日志是 harness 的“黑匣子”。生产环境的模型调用必须可审计。每条日志至少应该包含四个部分模型输入的摘要、模型输出的摘要、验证器的执行结果、harness 自身的决策。{ timestamp: 2026-08-22T10:15:33Z, task: 实现 fib(n), prompt_hash: abc123, model_output_hash: def456, validator: { compile: pass, pytest: fail, summary: test_fibonacci_value_0 失败 }, decision: feedback_to_model, token_count: 1234, duration_ms: 25400 }这里不建议把完整 prompt 和完整输出都直接写入日志因为可能包含敏感信息。更稳妥的做法是记录哈希值原始内容存入独立的、权限受限的存储里只有排查问题时才读取。日志的保留策略也要提前定义尤其是涉及代码生成的项目审计数据可能需要保留较长周期。7. 常见问题排查从现象倒推根因7.1 模型不按约定输出 JSON现象模型输出里混入了 Markdown 代码块、解释文字或多余字段导致 validator 在json.loads阶段直接失败。可能原因模型训练数据里代码类问题的回答习惯是包裹在 Markdown 代码块中。prompt 虽然要求“只输出 JSON”但模型的输出格式偏好可能覆盖指令。检查方式把模型原始输出保存下来查看是否带有json或“好的我将为你实现”这类前缀。解决方式在 validator 里先做一次净化。如果原始输出包含 Markdown 代码块提取其中的 JSON 内容如果包含说明文字尝试从第一个{截取到最后一个}。更根本的解决方法是把输出格式约束放到 prompt 的开头并且提供 in-context example。import re def extract_json(raw: str) - str: start raw.find({) end raw.rfind(}) if start -1 or end -1 or end start: raise ValueError(未找到 JSON 内容) return raw[start:end 1]这种净化方案不能作为偷懒的借口。它只是提高容错性真正解决问题的仍然是要让模型理解“输出 JSON 是硬约束不是建议”。7.2 修复循环不收敛同一个错误反复出现现象模型在上一轮已经看到fib(0)导致IndexError的反馈但新一轮代码仍然没有处理n0。可能原因有三个。第一反馈信息太弱模型没有理解错误根因。第二prompt 中任务约束被后续文字覆盖模型只记住了最新内容。第三温度设置过高模型每次都换一种实现方式没有针对反馈做局部修复。检查方式查看上一轮反馈文本是否在下一轮 prompt 中出现确认模型是否真的读到了反馈。然后对比相邻两轮的输出 diff看模型是“局部修改”还是“全量重写”。解决方式把反馈信息压缩成明确指令例如“请在函数开头增加 if n 0 的边界判断”。如果模型仍在全量重写调低 temperature并在 prompt 中明确要求“基于上一轮代码做最小修改不要重写整个函数”。另外要设置硬性最大轮次超过后停止并交给人工避免无效消耗。7.3 模型尝试修改白名单外的文件或执行危险命令现象harness 日志里出现 PermissionError模型试图写入config/database.yaml或尝试执行rm -rf。可能原因任务描述里没有明确禁止这些操作或者模型为了实现目标选择了破坏性方案。检查方式查看工具调用日志统计越权访问集中在哪些路径和命令。如果越权次数明显升高说明 prompt 中的约束信息不够醒目。解决方式第一层是加强 prompt在任务模板中增加“禁止修改的文件”和“禁止执行的命令”。第二层是执行层强制拦截路径不在白名单内直接拒绝。第三层是沙箱兜底即使前两层被绕过容器内也没有重要文件可破坏。预防建议不要把所有希望寄托在 prompt 上。模型可能因为上下文过长或对抗输入而忽略约束执行层的强制拦截才是可控性的底线。7.4 测试环境不稳定导致验证误判现象某轮生成的代码实际正确但 pytest 因为超时、依赖缺失、随机失败报了错harness 把反馈发给模型后模型在错误方向上反复修改。可能原因测试环境每次运行依赖系统状态。比如测试用了真实网络请求但网络不稳定或者测试依赖某个未被冻结版本的第三方库同一段代码在不同时间运行结果不同。检查方式在 validator 中重复执行同一段代码两次对比结果是否一致。检查requirements.txt或pyproject.toml是否锁定了所有依赖版本。查看 pytest 输出是否出现 timeout、connection error 等环境相关错误。解决方式固定依赖版本把测试环境换成 Docker 容器对网络测试使用 mock 或本地测试服务。同时区分 flaky 测试和真实失败可以在测试命令里标记--durations10或为已知 flaky 的用例单独配置重试规则。避免模型因为环境问题而修改正确代码。7.5 上下文过长导致规则被忽略现象prompt 里明明写了“只允许使用标准库”模型还是导入了numpy。可能原因模型可以处理的上下文长度有限。任务描述、仓库代码、历史反馈全部累积后早期约束被后续内容掩盖模型只关注最近的指令。检查方式打印发送给模型的完整 prompt查看约束语句在文本中的位置。如果出现在很长上下文的中部它被忽略的概率会明显增加。解决方式把最重要的约束放在 prompt 开头和结尾两个位置中间放背景材料。减小裁剪粒度只保留与当前任务直接相关的代码。历史反馈只保留最近一轮或使用摘要。更彻底的做法是启用模型服务端的系统提示词把硬性规则放在 system message 里而不是随任务内容一起传入。7.6 多 Agent 评审意见产生冲突现象生成 agent 和评审 agent 对某个问题的判断不一致。生成 agent 认为代码正确评审 agent 坚持要求修改两轮之后任务无法收敛。可能原因评审 agent 的判别标准不够具体它的判断依赖自身主观理解。尤其在没有测试用例覆盖的领域评审 agent 容易提出风格化、非必要的修改意见。检查方式记录评审 agent 输出的意见类型判断多少条属于正确性问题多少条属于风格问题。如果大多数是风格意见说明评审 prompt 需要更严格地限定“只报告会导致错误或安全隐患的问题”。解决方式给评审 agent 一个明确的等级分类。建议按严重级别输出阻断级别、重要级别、建议级别。只有前两级需要生成 agent 修复建议级别直接忽略。这样能减少意见冲突导致的循环。8. 生产环境落地的检查清单和扩展方向8.1 学习环境与生产环境的差异学习环境里跑通一个 harness 只需要几分钟但生产环境落地时要考虑的问题会多很多。下面这张表列出最常见的差异。维度学习环境生产环境配置硬编码在代码里配置外置化通过环境变量或配置中心注入模型 API本地模拟或测试账号固定模型版本关注兼容性执行环境本机目录Docker 容器或独立执行机权限一个开发账号独立服务账号最小权限日志打印到控制台接入集中日志平台设置保留周期监控无通过率、失败率、平均轮次、耗时指标回滚手动清理每个产物有唯一 ID记录版本密钥可能临时写在代码里从环境变量读取禁止进入 prompt 和日志学习环境的目的是快速看到效果可以容忍临时配置。生产环境的目的是稳定可维护必须从第一天就考虑可观测性和权限控制。把一个学习用 harness 直接搬到生产环境最常见的后果是密钥泄露、执行片段污染宿主机、模型版本升级后结果波动却无法定位原因。8.2 发布前检查清单在把 harness 接入正式项目前可以按下面这份清单逐项确认。模型版本是否固定是否记录了模型标识和 prompt 版本。max_attempts是否设置是否限制了总执行时间和总 token 消耗。执行环境是否隔离是否禁用了不必要的网络权限。白名单路径是否只覆盖任务需要的目录。敏感文件是否被排除在读取白名单之外。测试套件是否独立于模型生成内容测试是否由可信人员维护。验证器执行是否有超时控制是否有序排列。失败反馈是否结构化是否只保留有效信息。日志是否记录了任务 ID、模型输出哈希、验证结果和最终决策。API Key 等敏感信息是否不进入 prompt、不写入日志。是否有人工介入机制超限任务是否会被标记并推送。是否有评估集是否在修改 prompt 前后用同一套任务回归。这份清单不需要一次性全部满足。它可以作为从实验到生产的验收标准。每上线一个新场景先过一遍清单再开始引入模型生成流程能避免大量线上问题。8.3 可以继续深入的方向Harness 实践本身是一个可以持续扩展的方向。最直接的一个扩展是建立任务级评估集。把过去真实项目里的历史问题整理成测试用例每次调整模型、prompt 或 harness 参数时都跑一遍评估集通过率变化一眼可见。第二个方向是将 harness 接入 CI/CD 流水线。当开发者提交一个“由模型修改的 PR”时流水线自动运行 harness 的全部验证关卡包括编译、静态检查、测试、安全扫描。这样可以保证进入代码评审阶段的每个 PR 都已经是经过模型自验和工具验证的产物。第三个方向是让反馈循环更智能。当前许多 harness 只是把测试失败信息喂回给模型。更复杂的设计可以加入“问题定位器”先从堆栈中提取出错文件、行号、符号名再结合语法树分析给出更精确的修复建议。这种结构化反馈能显著减少模型盲目尝试的次数。第四个方向是横向对比不同模型在同一 harness 下的表现。同一个任务模板、同一套 validator、同一个评测集分别运行不同模型比较通过率、平均轮次、越权次数。这种对比可以为团队选型提供量化依据而不是单纯看演示效果。Harness 的核心价值不在于让模型显得更聪明而在于让模型生成的代码进入项目之前先经过一条有约束、有验证、有反馈、可回溯的生产线。把允许模型做什么、禁止模型做什么、怎样算通过、怎样算失败、失败后如何反馈写成配置harness 才真正开始帮你控制代码质量而不是替你写代码。