
如果你最近在关注 AI 编程工具大概率会频繁看到两个英文词coding agent 和 harness。很多人以为它们是同一种东西的两种叫法实际上agent和harness解决的问题完全不同。agent是那个能“自己写代码”的智能体而harness是控制、编排、约束、观测这个智能体的整套工程框架。从标题看VT Code 正是这样一次尝试用自研的 coding-agent harness 去解决“让 AI 智能体真正可用、可管、可控”的问题。我写这篇文章的目的不只是介绍 VT Code 这个项目更是想帮你搞清楚一个更底层的问题当一个 AI 编程智能体从 Demo 走向生产环境时为什么需要一个 harness它到底管住了哪些事如果你想自己设计一个 harness第一版代码应该长什么样这篇文章会先拆解 coding-agent harness 的核心概念再给出一个最小可运行的原型实现最后落到工具链、安全边界和工程化建议上。无论你是想学习 agent 开发还是打算基于 DeepSeek、Codex 这类模型做自己的编程助手这都会是一篇值得收藏的实践参考。1. 这篇文章真正要解决的问题过去一年AI 编程的玩法发生了非常明显的变化。第一代工具是“问你一句给你一段代码”本质是套壳的代码补全。第二代工具升级成了“你给任务我动手改文件”典型代表是各种 coding agent。到了现在讨论重心已经从“模型能不能写代码”转移到了“agent 能不能在真实仓库中稳定完成一个多步任务”。但真实仓库和聊天窗口完全是两回事。一个 agent 要完成任务需要读取项目文件、搜索相关代码、修改多个文件、运行测试、根据报错再修复。这个过程不是一次模型调用能完成的而是一个“规划 - 行动 - 观察 - 再规划”的循环。循环听起来简单真正做起来全是问题模型调用的上下文越来越长、工具调用经常失败、agent 可能会在同一个错误上反复打转、修改文件时权限边界不清楚、出了问题不知道是哪一步导致的。这些问题的共同点是它们都不属于“模型能力”的范畴而是属于“工程控制”的范畴。也就是说你需要一个中间层把模型对工具、文件、进程的访问统一管理起来。这一层就是 harness。VT Code 的价值不在于它又是一个聊天界面而在于它尝试把“agent 执行过程”本身做成一个可编排、可复用、可观测的产品。从我看到的行业趋势来判断这类项目正在成为 AI 编程工具链里最值得关注的一个方向。2. harness 与 agent 的核心区别先用一个比喻讲清楚先给一个直觉。想象你的团队里来了一位能力很强的程序员但他性格鲁莽接到需求就动手改文件很快也经常把代码改坏。你不可能让他直接在生产仓库里自由操作。你会怎么做你会给他一台带权限控制的开发机先限定他只能访问哪些目录你会要求他每次改动先写方案你批准后再动手你会让他在独立的测试分支上执行最后 review 他的 patch他每执行一条命令你都有日志可以回溯发现他陷入死循环时你能随时叫停。这套“管理一个能力很强但需要约束的执行者”的方式就是 harness。它不是那个程序员而是那个程序员周围的“管理体系”。用技术语言来对应概念类比职责Agent智能体程序员理解任务、生成代码、调用工具Model模型程序员的大脑负责推理和生成Harness执行框架开发环境 管理制度编排循环、管理上下文、控制工具权限、记录日志、追加约束所以你可以看到很多项目中 agent 本身只是一个“循环逻辑”拿到任务调用模型模型返回决策执行行动观察结果继续循环。而这个循环本身需要放在 harness 里才能安全、稳定、可控制地运行。还有一个常见的误区把 harness 当成 Agent 框架的代名词。LangChain、LlamaIndex这类是面向应用开发的 agent 框架它们解决的是“如何调用模型、如何做检索、如何搭建应用”而 coding-agent harness 面向的是“如何让 agent 操作真实代码仓库”它必须处理文件系统、git、shell 命令、测试框架、代码搜索、patch 生成这一类开发环境中的具体问题。两者关注的层次不同不能混为一谈。3. 为什么需要 coding-agent harness开发场景的三个痛点聊概念太抽象我们还是回到真实场景。假设你已经接入了 DeepSeek 这类模型希望它自动帮你完成一个“添加新接口”的任务。没有 harness 时你会发现三个痛点。3.1 缺少状态管理模型不记得自己改过什么每次模型调用都是无状态的。它第一次调用读了一个文件第二次调用时如果不把文件内容重新放回上下文它就完全忘了。于是你要维护一个不断膨胀的消息列表把每次工具调用的结果都追加进去。很快上下文窗口被占满模型开始“忘记”最开始的需求。代码库越大这个问题就越严重。一个真实的 agent 在每个任务循环中可能产生几万甚至几十万 token 的上下文不做管理根本无法工作。harness 需要解决的正是上下文裁剪、摘要、结构化检索这一类问题。3.2 缺少工具权限约束agent 可能做出危险操作如果你直接把 shell 权限开放给 agent让它“自由发挥”它在一次错误决策中可能执行rm -rf、修改全局配置、或者向生产环境写入脏数据。模型本身不知道边界在哪里这就需要 harness 在工具调用层做白名单、路径约束、命令过滤和执行审批。这一点在自建 harness 时最容易被人忽略。很多人第一版 demo 就是“给模型一个run_command工具”然后发现 agent 跑着跑着把环境搞坏了。不是模型坏是 harness 没有做好约束。3.3 缺少可观测性出了问题无法排查Agent 是多步决策系统每一步都依赖前一步的结果。如果中间某一步产生了错误你很难判断是模型理解错了、工具调用格式错了、还是文件路径找错了。没有完整日志的 agent 就像没有控制台的 Web 应用出了 bug 只能靠猜。Harness 的价值在这里体现得非常明显把每一步的输入输出、token 消耗、工具调用时间、错误类型全部记录下来这既是排查问题的依据也是后续做评测和优化的数据基础。3.4 什么时候你其实不需要 harness当然不是所有场景都需要自己搭 harness。如果你只是用 AI 写点脚本、做做代码补全或者只需要一次性的单轮问答直接用聊天产品就够了。但如果你在做以下事情中的任意一件就应该认真考虑 harness开发自己的 coding agent希望它操作真实仓库需要多模型、多工具的编排与统一管理团队希望把 AI 编程能力集成到现有 CI/CD 流程中需要审计和记录 agent 的操作行为。4. VT Code 的设计定位与核心功能拆解回到 VT Code 项目本身。虽然目前公开材料不多从标题“My attempt at building a coding-agent harness”可以确认它是一个个人发起的 coding-agent harness 项目目标不是做一个普通聊天工具而是搭建一个能承载完整 agent 执行过程的框架。综合当前 coding-agent harness 这一类工具的通用设计思路一个成品 harness 通常至少要包含下面五个核心模块。这也可以作为你评估或设计 harness 时的检查清单。4.1 任务编排器负责把用户给出的自然语言任务转换成 agent 可执行的多步计划。它决定先读哪些文件、后改哪些文件、最后跑什么测试。好的编排器不是一次性生成一个完整计划而是边执行边调整因为初始计划往往是错的。4.2 上下文管理器这是 harness 和普通 Agent 框架最不一样的地方。它需要把仓库信息、任务要求、历史工具结果、当前 diff 状态统一组织成模型输入。为了控制 token 成本还要做基于相关性的上下文检索而不是把整个仓库都塞给模型。4.3 工具执行器工具是 agent 的“手”。读文件、搜索代码、执行命令、创建 patch、运行测试每个操作都要由 harness 暴露成可调用的工具。工具层是安全控制的关键位置所有工具的入参都要校验所有执行都要记录日志。4.4 循环控制与终止机制Agent 执行是一个循环。harness 必须定义循环的终止条件任务完成、达到最大步数、检测到死循环、用户主动中断。没有终止机制的 agent 程序就是一个可能持续消耗 token 的巨大风险。4.5 可观测性与审计日志把 agent 每一步动作、每个 token 消耗、每次工具调用结果全部记录下来。从项目演进的角度看日志不只是用于排查更是积累数据集、建立评测集的基础。对于一个自建项目来说第一版不需要做到完美。能跑通一个“读取仓库 - 生成修改方案 - 输出 patch”的最小流程就已经是一个合格的出发点了。5. 自己实现一个最小 coding-agent harness环境准备接下来我们动手实现一个最小可运行的 coding-agent harness。这里用 Python 编写因为它生态成熟、写起来快适合做原型。完整示例会演示一个最核心的能力让 agent 在循环中调用工具最终完成一个简单的仓库分析任务。5.1 环境要求操作系统Windows / macOS / Linux 均可Python 3.10 或以上版本一个支持 OpenAI 兼容接口的大模型 API。例如 DeepSeek 开放平台、OpenAI 等国内也有多家模型服务商提供兼容接口。为了方便演示下面统一按 OpenAI 兼容协议编写如果本地网络无法直连某些海外接口请替换为可访问的兼容服务并注意使用合规渠道和合法账户建议使用虚拟环境隔离项目依赖。5.2 目录结构vt_harness/ ├── main.py # 入口接收任务启动 harness ├── core.py # 核心执行循环 ├── tools.py # 工具注册与工具实现 ├── config.yaml # 模型与执行参数配置 └── requirements.txt # 依赖文件5.3 依赖安装pip install openai pyyaml如果你的模型服务商已经支持 OpenAI 兼容接口使用openai这个 SDK 就能统一调用不需要额外引入其他库。6. 最小 coding-agent harness 核心代码实现核心代码拆成三个文件工具注册模块、核心循环模块、入口模块。这样结构清晰也方便后续扩展。6.1 工具注册与实现tools.pyHarness 的第一步是建立工具注册表。Agent 只能调用注册过的工具这是一个基本的安全边界。# FILE: vt_harness/tools.py import os from pathlib import Path from typing import Callable, Dict # 全局工具注册表 TOOL_REGISTRY: Dict[str, Callable] {} def register_tool(name: str): 装饰器把函数注册为 agent 可用的工具 def decorator(func: Callable): TOOL_REGISTRY[name] func return func return decorator # 安全边界所有文件操作都限制在 workspace 目录内 WORKSPACE Path.cwd() / demo_repo WORKSPACE.mkdir(exist_okTrue) def _safe_resolve(path: str) - Path: 解决路径穿越问题禁止读取工作区以外的文件 target (WORKSPACE / path).resolve() if not target.is_relative_to(WORKSPACE.resolve()): raise PermissionError(f拒绝访问工作区外的路径: {path}) return target register_tool(list_files) def list_files(path: str .) - str: 列出指定目录下的所有文件 target _safe_resolve(path) files [] for item in sorted(target.iterdir()): files.append(str(item.relative_to(WORKSPACE))) return \n.join(files) if files else (空目录) register_tool(read_file) def read_file(path: str) - str: 读取文本文件内容 target _safe_resolve(path) if not target.is_file(): return f错误文件不存在 {path} return target.read_text(encodingutf-8, errorsignore) register_tool(run_command) def run_command(command: str) - str: 在演示环境中模拟执行命令。实际使用时建议接入沙箱。 allowed_prefixes (echo, pwd, ls, python) if not command.startswith(allowed_prefixes): return f错误命令不在白名单中{command} import subprocess result subprocess.run(command, shellTrue, capture_outputTrue, textTrue, timeout10) return result.stdout result.stderr这里有意做了一个简化的安全设计_safe_resolve保证路径不能跳出工作区run_command只允许少量命令前缀。真实项目中命令执行必须使用容器沙箱或虚拟机隔离仅仅做字符串过滤是不够的。6.2 核心循环core.py核心循环是 harness 的“心脏”。它的工作方式是把系统提示、任务描述、历史消息、工具调用结果组装成上下文交给模型然后模型决定是调用工具还是给出最终回答。如果是调用工具就解析工具名和参数执行把结果追加到上下文继续下一轮。# FILE: vt_harness/core.py import json from typing import Optional from openai import OpenAI from .tools import TOOL_REGISTRY SYSTEM_PROMPT 你是一名资深软件工程师运行在 coding-agent harness 中。 你可以调用以下工具 {tool_schema} 规则 1. 必须使用合法 JSON 格式调用工具{name: 工具名, arguments: {参数名: 参数值}} 2. 先分析任务再决定调用哪些工具。 3. 只有工具返回的结果不足以回答问题才继续调用。 4. 任务完成时用 summary 标记并直接输出最终结论。 class CodingAgentHarness: def __init__(self, model: str, base_url: str, api_key: str, max_steps: int 8): self.model model self.client OpenAI(base_urlbase_url, api_keyapi_key) self.max_steps max_steps self.messages: list[dict] [] self.history: list[dict] [] def _build_tool_schema(self) - str: lines [] for name, func in TOOL_REGISTRY.items(): doc (func.__doc__ or 无描述).strip().split(\n)[0] lines.append(f- {name}: {doc}) return \n.join(lines) def run(self, task: str) - str: self.messages [ {role: system, content: SYSTEM_PROMPT.format(tool_schemaself._build_tool_schema())}, {role: user, content: task}, ] for step in range(1, self.max_steps 1): print(f\n[step {step}] 调用模型...) resp self.client.chat.completions.create( modelself.model, messagesself.messages, temperature0.2, ) content resp.choices[0].message.content print(f[step {step}] 模型输出: {content[:200]}) # 检查是否完成任务 if summary in content: self.messages.append({role: assistant, content: content}) return content # 尝试解析工具调用 tool_call self._parse_tool_call(content) if not tool_call: # 模型没有输出合法工具调用追加提示让它纠正 self.messages.append({role: assistant, content: content}) self.messages.append({ role: user, content: 刚才的输出不是合法的工具调用请重新调用工具。, }) continue result self._execute_tool(tool_call) print(f[step {step}] 工具执行结果: {result[:200]}) self.messages.append({role: assistant, content: content}) self.messages.append({role: user, content: f工具结果\n{result}}) # 简单记录方便追踪 self.history.append({ step: step, tool_call: tool_call, result: result, }) return 错误达到最大运行步数任务未完成。 def _parse_tool_call(self, content: str) - Optional[dict]: 解析模型输出中的 JSON 工具调用 try: # 如果模型返回了 code block 包裹先去掉 cleaned content.strip().replace(json, ).replace(, ) data json.loads(cleaned) if name in data and arguments in data: return data except json.JSONDecodeError: pass return None def _execute_tool(self, tool_call: dict) - str: name tool_call.get(name) arguments tool_call.get(arguments, {}) if name not in TOOL_REGISTRY: return f错误未注册的工具 {name} try: func TOOL_REGISTRY[name] if isinstance(arguments, str): arguments json.loads(arguments) return func(**arguments) except Exception as exc: return f工具执行异常{type(exc).__name__}: {exc}这段代码是最简版本但它已经包含了一个 harness 最核心的骨架消息组装、循环控制、工具调用解析、执行结果回填。真实项目里你需要在此之上加入更多的错误恢复逻辑但思路是一致的。6.3 入口模块main.py入口模块负责读取配置、接收任务、启动 harness。# FILE: vt_harness/main.py import os import yaml from .core import CodingAgentHarness def load_config(path: str config.yaml) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def main(task: str): config load_config() model_cfg config[model] harness CodingAgentHarness( modelmodel_cfg[name], base_urlmodel_cfg[base_url], api_keyos.environ.get(model_cfg[api_key_env]), max_stepsconfig[agent][max_steps], ) result harness.run(task) print(\n FINAL RESULT ) print(result) if __name__ __main__: # 实际使用时改成命令行参数解析这里简单从 sys.argv 读取 import sys task sys.argv[1] if len(sys.argv) 1 else 请总结 demo_repo 目录下的文件结构 main(task)6.4 配置文件config.yaml配置文件提示了几个关键参数模型接入信息、最大步数、系统提示词。把安全相关参数和业务参数分离是生产级 harness 的好习惯这里先合并到一份配置里。# FILE: vt_harness/config.yaml model: provider: openai-compatible # 请替换为你实际使用的模型名称 name: your-model-name # 如果使用本地或国内模型服务请填写对应的兼容接口地址 base_url: https://api.example.com/v1 api_key_env: LLM_API_KEY agent: max_steps: 8 system_prompt: 你是一名资深软件工程师任务是在给定仓库中完成分析并输出结论。7. 运行结果与效果验证7.1 准备演示仓库在项目根目录下建一个简单的demo_repo放一个README.md和一份 Python 文件mkdir -p demo_repo echo # Demo Repo demo_repo/README.md echo print(hello harness) demo_repo/main.py7.2 运行 harness# 1. 安装依赖 pip install openai pyyaml # 2. 设置 API Key export LLM_API_KEYyour_api_key_here # 3. 运行任务 python -m vt_harness.main 分析 demo_repo 目录下的文件并告诉我 main.py 的作用7.3 预期输出运行成功时你会看到类似下面的流程[step 1] 调用模型... [step 1] 模型输出: {name: list_files, arguments: {path: .}} [step 1] 工具执行结果: README.md main.py [step 2] 调用模型... [step 2] 模型输出: {name: read_file, arguments: {path: main.py}} [step 2] 工具执行结果: print(hello harness) [step 3] 调用模型... [step 3] 模型输出: summary: demo_repo 目录包含 README.md 和 main.py 两个文件main.py 的作用是打印一行文本。 FINAL RESULT summary: demo_repo 目录包含 README.md 和 main.py 两个文件main.py 的作用是打印一行文本。判断成功的标准有四个Agent 能在不超过最大步数的情况下完成任务工具调用格式全程合法没有出现路径穿越、非法命令等安全违规最终输出能正确回答用户任务。如果运行失败优先检查以下三项LLM_API_KEY是否正确设置、base_url是否确实指向 OpenAI 兼容接口、模型名称是否与模型服务商提供的名称一致。对于使用国内模型服务的情况通常需要将base_url设置为服务商提供的兼容地址模型名称也以服务商文档为准。8. 常见问题与排查方法从自己写 harness 到搭建生产级 harness过程中会碰到很多类似的问题。这里列一份通用排查表当你基于任何模型或框架开发时都能用上。问题现象可能原因排查方式解决方案模型返回空内容上下文过长导致截断、API 限流查看 API 返回的 finish_reason 和 usage 字段精简历史消息接入上下文压缩工具调用格式解析失败模型没有返回合法 JSON打印原始输出检查是否被 markdown 包裹提示词增加格式示例或使用函数调用模式agent 在同一个错误上反复重试缺少失败识别和恢复机制分析每步工具结果查看是否重复相同动作加入“连续相同操作”检测并中断读取文件路径越界路径校验不严查看工具执行日志中的路径参数统一使用安全解析函数禁止外部直接拼路径上下文长度超限工具结果全部追加未做摘要查看消息列表的总 token 数对工具结果做截断、摘要或向量检索执行命令把环境弄坏命令白名单过宽或没有沙箱审查工具调用日志使用容器沙箱限制网络和文件系统权限大量 token 很快耗尽死循环或低效规划查看历史记录中的步数和调用次数设置更小的 max_steps增加成本预算不同模型表现差异大每个模型的格式遵循能力不同用同一任务对比多个模型针对模型微调提示词模板或固定采用函数调用协议这里有一个很容易被忽略的经验不要把“模型输出不符合预期”当成一个问题去修模型本身。更多时候问题出在 harness 的工具设计、上下文组织或错误恢复机制上。你把工具描述写得更清楚、把失败反馈写得更明确效果往往比换更大的模型更明显。9. 最佳实践与工程建议9.1 安全边界harness 的生命线如果你要自己做一个 coding-agent harness安全是第一优先级不是功能。至少应该做到以下几点文件系统层使用沙箱目录所有路径必须解析并校验禁止访问工作区外文件命令执行层只允许白名单命令复杂任务放到隔离容器或虚拟机中执行网络层建议默认禁止 agent 访问外网需要时可以按域名白名单放行成本控制设置最大步数、最大 token 预算、单次任务费用上限审批机制关键操作如 git push、生产部署、删除文件必须进入人工审批流程。9.2 可观测性数据要先留好很多 coding-agent harness 项目初期不重视日志等出问题再补代价会非常大。至少要为每个步骤记录这些信息完整输入输出消息工具名称、参数、耗时、返回结果每一步的 token 消耗模型名、温度、max_tokens 等参数最终成功还是失败失败发生在哪一步。这些日志不仅用于排查还是后续建立评测集的重要数据来源。你在调试中发现的失败案例经过整理就是最好的回归测试用例。9.3 评测驱动不发没有评测的 agent如果你打算把 agent 接入真实项目一定要先建一个小的评测集。不需要多10 到 20 个任务即可。这些任务应该覆盖读代码、定位问题、生成 patch、运行测试、多文件修改等场景。每次修改 harness 的提示词或工具设计后都跑一遍评测集确保没有引入回归问题。这是最容易被初学者跳过但价值极高的环节。9.4 工具设计的克制原则给 agent 的工具不是越多越好。每多一个工具模型的选择空间就变大出错概率也会提高。我的建议是第一版只提供最核心的 5 到 8 个工具每个工具的描述里写清楚“什么时候用”“不要做什么”工具的入参尽量简单不要暴露复杂对象工具之间职责要分离避免两个工具都能做同一件事。9.5 模型选择先兼容再优化接入新模型时先用统一的 OpenAI 兼容接口跑通流程再针对模型特点做提示词优化。以 DeepSeek 这类模型为例它们的 API 已经兼容 OpenAI 协议环境和代码改动很小你就可以快速对比不同模型在相同 harness 下的表现。评估模型时除了关注代码质量还要关注工具调用的格式遵循能力、上下文理解能力和失败恢复能力。10. 总结与后续学习方向VT Code 这个项目提醒我们AI 编程已经进入了“工程化”阶段。一个 coding-agent harness 要解决的不是“模型会不会写代码”而是“agent 在真实代码仓库里能不能被安全、稳定、可预期地使用”。从最小实现来看它至少包含任务编排、上下文管理、工具执行、循环控制和可观测性五个部分。从工程角度看安全边界、评测集、日志体系是决定一个 harness 能否从 Demo 走向生产的三个关键因素。如果你想继续深入这个方向可以按下面的路径学习先跑通本文的最小 harness改造成你自己的工具集加入 git 操作能力让 agent 能创建分支、生成 patch接入本地代码搜索引擎解决大仓库的上下文检索问题建立评测集开始用数据迭代提示词和工具设计再加入人工审批、沙箱执行、成本统计等生产级功能。最终你会发现harness 的复杂度会随着 agent 能力提升而快速增长但核心思路始终不变给强大的模型加一个安全可控的执行环境。如果你正在做类似的尝试希望这篇文章能让你少走一些弯路。欢迎收藏备用也欢迎在实践中遇到具体问题时回来对照检查。