AI Harness实战:构建大模型多步任务执行的工程框架

发布时间:2026/9/1 20:05:53
AI Harness实战:构建大模型多步任务执行的工程框架 简介企业级DevOps与AI工程化实践者可借助这份可运行源码包快速梳理Harness平台渐进式落地路径清晰拆解试点、扩展、深化、优化四个不可跳跃阶段并理解“价值驱动而非全面替代现有工具”的推广原则。包内还收录OpenAI基于Codex与GPT-5实现0行手写代码构建百万行系统的实验要点以及持久执行、闭环测试、架构约束、运行策略四大技能的具体约束方式。资源共3个文件以HTML演示页为主配合inscode运行配置与gitignore工程约束整体仅10KB轻量便于快速启动和对照学习。已有229人学习浏览适合正在评估企业级AI原生软件交付平台的架构师与平台工程师。通过源码可直接查看Harness工程实践解析的可视化呈现理解与Cursor模式的差异及Agent-First开发建议并借鉴花旗银行、Ancestry、Ulta Beauty等真实案例的量化收益与落地经验。 最近在整理AI编程工具链的时候我发现“harness”这个词出现得越来越频繁。不管是deepseek harness、codex harness还是通用的harness engineering大家都在讨论同一个问题怎么把大模型从“单次问答”变成“能稳定完成多步任务”的工程系统。我花了两周时间搭了一套可运行源码把整个harness的调度、上下文管理、工具调用全部跑通今天把完整思路和实现细节拆开讲。先说明这套源码能解决什么问题如果你只是调API做单轮对话你不需要harness但如果你想让模型自己规划步骤、调用工具、读取文件、执行命令、根据报错自我修正那你就需要一个harness来充当“脚手架”。它可以理解为给模型配了一间有工具、有规则、有记录的工作间让它在里面按流程干活而不是漫无目的地瞎猜。这套源码适合正在做AI Agent、自动化编码助手、或者想深入理解模型工具调用机制的开发者也适合想在大模型API之上做二次封装的产品团队。1. Harness到底是什么从一次“失控”的Agent调试说起1.1 一个真实场景没有harness的AI编码有多痛我之前做过一个自动修bug的实验直接让模型读取报错日志、修改代码、再跑测试。表面上看三步就能闭环实际跑起来全是问题。最常见的情况是模型在第二步改了A文件但B文件的依赖没同步更新测试还是挂接着模型又根据同样的报错改了C文件结果把原本正确的逻辑也改崩了。整个过程没有约束、没有状态记录、没有中间产物留痕模型就像蒙着眼睛在迷宫里乱撞。后来我意识到缺的不是模型能力而是一套工程框架来约束模型的行动边界和行动顺序。这就是harness的核心价值它把“模型自由发挥”变成“模型在框架内按步骤执行”每一步做什么、调用什么工具、拿到什么结果、要不要回溯都由harness来编排和记录。1.2 Harness的三层职责调度、封装、留痕我总结下来一个可用的harness至少要承担三层职责。第一层是调度也就是Agent主循环决定当前该调模型还是该调工具判断任务是否完成处理连续调用的终止条件。第二层是封装把文件读写、命令执行、代码搜索这些能力封装成模型可以调用的工具接口并在工具层做权限过滤、超时控制、输出截断。第三层是留痕完整记录每一次模型输出、每一次工具结果方便回溯和调试。这三层缺一不可。没有调度模型会陷入死循环没有封装工具调用不可控没有留痕出了问题连复现都做不到。我在这套可运行源码里把这三层拆成了独立模块方便单独替换和扩展。2. 可运行源码的整体架构设计2.1 模块划分从入口到执行整套源码我用Python编写核心架构分成五个模块main.py负责命令行入口和配置加载harness/core.py实现了Agent主循环harness/tools.py注册了所有可调用工具harness/context.py管理上下文窗口harness/llm.py封装了各家模型API的统一接口。这种划分方式是我反复调整后确定的。一开始我把所有逻辑塞在一个文件里改起来确实快但一旦加新工具、换模型提供方整个文件就变得没法维护。后来参考了几种主流Agent框架的目录设计最终选择按职责拆每个模块只做一件事。如果你只是想跑通流程main.py和core.py足够如果要接入自己的业务场景重点改tools.py和context.py就行。2.2 数据流全景一条任务如何被跑完我画过一张数据流图代码里有docs/dataflow.md整个流程是这样的用户输入任务后主循环先把任务写进消息列表然后调用LLM接口让模型决定是“给出最终回答”还是“调用某个工具”。如果是调用工具harness会从消息里解析出工具名和参数执行对应函数把结果拼成一条新的系统消息回灌给模型。模型看到工具结果后要么继续调工具要么给出最终结论。这个“模型决策-工具执行-结果回灌”的循环是整个harness的心脏对应代码里就是core.py中的for循环和while判断。实际运行时我会设置一个最大迭代次数的上限防止模型陷入无限循环同时记录每次迭代的消耗和耗时方便后面做性能调优。3. 核心实现拆解动手写一个最小Harness3.1 主循环Agent状态机主循环我用一个简洁的while循环实现核心状态就三种thinking模型思考/决策、acting执行工具、done完成。每次迭代开始时从消息列表里取出最新一条模型输出如果它的tool_calls字段有内容就进入acting状态执行工具否则判断消息里是否包含最终答案标记如果有就置为done。state thinking max_iterations 20 for step in range(max_iterations): if state done: break response llm.chat(messages) if response.get(tool_calls): state acting for call in response[tool_calls]: tool_result tools.execute(call[name], call[arguments]) messages.append(format_tool_message(call[id], tool_result)) state thinking else: final_answer extract_final_answer(response) messages.append(format_assistant_message(final_answer)) state done这里有几个细节很容易踩坑。一是tool_calls的解析格式OpenAI兼容接口和Anthropic接口不完全一样最好统一在llm.py里做适配二是工具结果回灌时每条工具调用的id必须对应上模型请求里的tool_call_id否则模型会混淆三是最大迭代次数不能设太小我实测一个复杂的多文件重构任务通常在15~20次工具调用内完成设太小会截断任务设太大又容易死循环20是个相对安全的起始值。3.2 上下文窗口管理如何避免上下文爆炸这是harness工程里最头疼的问题之一。每执行一次工具调用、回灌一次结果消息列表就会膨胀。如果任务涉及几十次工具调用token消耗和延迟都会指数级上升。我在context.py里实现了三层管理策略。第一层是结果截断工具返回超过2000字符时只保留头部和尾部摘要中间用[truncated N chars]标记。第二层是历史裁剪超过一定轮数的历史消息用一次性的“摘要消息”替换摘要由模型生成保留前文的关键决策信息。第三层是缓存复用重复的只读操作结果比如多次读取同一个文件直接查缓存不再重复调用工具。实测下来一个原本会用掉5万token的任务三层策略叠加后可以压到1.5万token以内而且模型的任务完成质量没有明显下降。这里的关键是摘要生成本身也会消耗token所以要设置合理的历史裁剪阈值我一般以“最近10轮消息完整保留更早的做摘要”为基准。3.3 工具注册与权限控制工具系统我采用装饰器注册模式。写一个新工具只需定义函数并加上register_tool装饰器harness会自动收集函数名、参数描述和帮助信息生成模型可识别的tools schema。这个设计是从FastAPI的路由注册思路借来的比手动维护一份函数列表方便很多。register_tool(nameread_file, description读取指定路径的文件内容) def read_file(path: str, max_chars: int 2000): with open(path, r, encodingutf-8) as f: content f.read() return content[:max_chars]权限控制部分我放在工具执行前的统一入口。每个工具声明自己需要的权限等级只读、读写、命令执行。模型请求调用工具时harness会检查当前任务的授权范围比如我只允许Agent在workspace目录下写文件禁止修改其他路径命令执行默认禁用手动开启才生效。这个安全层非常关键不加限制的话模型一旦生成一条危险命令后果不可控。4. 实操过程把源码跑起来并接入DeepSeek/Codex4.1 环境准备与依赖安装源码依赖不多核心库只有openai、anthropic和pyyamlPython版本要求3.10以上。我建议用虚拟环境隔离别直接装到全局环境里。git clone https://github.com/yourname/ai-harness.git cd ai-harness python -m venv .venv source .venv/bin/activate pip install -r requirements.txt依赖装完后复制.env.example为.env填入模型API的key和base_url。源码里默认走OpenAI兼容协议这让我能同时接入DeepSeek、通义千问等国内模型和OpenAI自家的服务只要它们提供兼容接口。4.2 配置模型提供方我做了config.yaml来统一管理模型参数里面可配置项包括provider、model_name、temperature、max_tokens和api_base。接入DeepSeek时把provider设为openai_compatibleapi_base填DeepSeek的官方接口地址即可接入Codex相关工作流时走的是CLI调起方式在这套源码里我封装了一个codex_cli工具直接调用Codex的命令行接口做代码生成和修改。llm: provider: openai_compatible model_name: deepseek-chat api_base: https://api.deepseek.com/v1 temperature: 0.2 max_tokens: 4096 tools: enabled: - read_file - write_file - run_command - codex_cli command_permission: warn max_working_directory: ./workspace这里说一个经验temperature设低一点模型在工具调用参数生成上会更稳定。我试过0.7的温度结果模型经常把文件名参数名写错导致工具解析失败降到0.2之后这类问题少了八成。4.3 运行第一个示例任务源码里带了一个示例任务让Agent分析指定目录下的代码结构找出所有函数定义并生成一份索引文档。跑起来很简单python main.py --task 分析 workspace 目录下的所有 Python 文件列出每个文件的函数名和行号保存为 INDEX.md我第一次跑这个任务时Agent依次调用了list_dir、read_file、write_file三个工具共迭代6次耗时约8秒。整个过程在终端里可以看到每一步的工具调用日志这就是harness留痕的价值所在——一旦不符合预期能立刻定位到是哪一步出了问题。生成完索引文档后我还让Agent基于索引自查遗漏它发现汇总时漏了一个工具文件又补了一次读取。5. 常见问题与排查技巧实录5.1 高发问题速查表我在调试这套源码的过程中遇到了下面这些高频问题也逐一确认了解决方式整理成表格供你参考。现象根因解决方案模型反复调用同一个工具不停止工具结果没有改变状态Agent死循环检查tool_call_id是否回传正确在工具结果中加入明显的“完成标记”提示词工具参数解析报错模型返回参数JSON格式不合法或字段名被改写为每个工具提供更完整的schema描述将temperature调低在解析时用retry机制重新让模型修正参数上下文窗口超限历史消息未裁剪token累积调低max_history_rounds阈值启用摘要压缩工具结果截断长度调小模型拒绝继续执行工具中途某条工具结果出现异常格式模型困惑在异常工具结果中加入format_error标记并附带重试建议权限拦截误伤正常操作工具权限声明过严按任务类型动态调整授权范围仅对高风险操作命令执行保持严格限制5.2 几条用代码换来的教训第一别让Agent一次性读太多文件。模型在看到一个目录里有几十个文件时往往会选择“全部读取”策略这会让上下文瞬间爆掉。我在list_dir工具结果里主动做了文件数量上限和文件大小排序提示让Agent优先读更可能相关的文件实测这个改动让任务成功率提升明显。第二工具返回结果里一定要带上机器可读的状态码和处理建议。比如run_command工具执行成功后我在返回消息里拼接了[STATUS: SUCCESS]执行失败时除了返回exit code还会生成一句“可能的修复方向”。模型对这类结构化信息的理解能力远强于纯文本输出后处理步骤的准确性也好很多。第三适时给Agent“上强度”。有些任务需要多个文件联调修改模型往往改完A文件就说完成了。我在工具层加了一个可选的“自检提醒”让它完成修改后主动运行一次测试或搜索一次相关引用这相当于给Agent加了一道质量门禁。初始开发时我觉得这是多余的实际用下来发现它避免了很多半成品结果被直接交付的情况。再分享一个实操中很实用的技巧如果你想复用这套源码接入自己的业务优先改工具层而不是主循环。主循环的“模型-工具”协作模式是通用的而业务差异基本集中在“工具能做什么”和“配置允许什么”上。我后续接入了代码搜索、接口文档生成等场景都是新增几个工具函数、改几行配置就完成了主循环完全没有动过。本文还有配套的精品资源点击获取