简介在AI代理技术领域从简单的Demo验证到复杂的工程化落地面临着可靠性、可控性和安全性的核心挑战。其原理在于通过系统化的工程方法为AI代理构建一个具备确定性和可重复性的执行环境从而确保其在复杂任务中的稳定表现。这一实践的技术价值在于将AI代理从易失控的探索者转变为在明确规则内高效工作的执行者大幅提升了自动化任务的可靠性和安全性。应用场景广泛覆盖代码生成与修复、自动化测试、文档编写等软件开发环节以及需要长链条、多步骤执行的各类业务流程自动化。本文聚焦的Harness Engineering理念正是通过容器化沙箱、工具规范化、状态管理等关键技术为AI代理提供了一套完整的工程化解决方案其中Docker容器和LangChain工具链是实现环境隔离与行为可控的核心组件有效解决了代理在真实任务中常见的失忆、死循环和权限失控等问题。1. 从“玩具”到“工程”为什么我们需要Harness Engineering如果你和我一样在过去一年里尝试过各种AI代理框架从AutoGPT到LangChain再到BabyAGI你大概率会经历一个相似的循环一开始你被它们“自主完成任务”的愿景所吸引兴致勃勃地搭建环境跑通一个Demo。Demo很酷比如让它自动搜索资料、写个总结。然后你信心满满地给它一个稍微复杂点的真实任务比如“帮我分析一下这个开源项目的Issue并写一个修复PR的草稿”。接下来事情就开始失控了。代理可能会陷入死循环不停地调用同一个工具或者生成一堆语法正确但逻辑混乱的代码更常见的是在执行了几个步骤后它“失忆”了忘记了最初的目标是什么。最终你得到的不是一个可用的成果而是一堆需要你手动清理的“半成品”和破碎的日志。这个问题的核心在于我们之前搭建的大多是一个“玩具”环境。它能在受控的、简单的、预设好的路径下工作但一旦面对真实工程任务的复杂性、不确定性和长链条依赖就会立刻崩溃。这就像教一个小朋友在平地上骑带辅助轮的自行车然后直接把他扔到了山地越野赛道上。Harness Engineering正是为了解决这个问题而生的理念和实践。它不是一个具体的框架或工具而是一整套工程方法论和基础设施旨在为AI代理构建一个可靠、可观测、可控制的“缰绳”Harness环境。这个环境的核心目标是让AI代理能够像一名合格的软件工程师一样在真实、复杂、动态的工程上下文中可靠地完成从需求理解、任务分解、工具调用、代码执行到结果验证的全流程。简单来说Harness Engineering要做的是把AI代理从一个容易“迷路”和“闯祸”的探索者变成一个在明确规则和护栏内高效工作的执行者。它关注的不再是“代理能做什么”而是“我们如何确保代理在复杂环境中每次都做对至少是可控地做错”。这涉及到环境隔离、状态管理、工具规范化、可观测性、错误处理与回滚、以及人类监督介入点设计等一系列工程实践。接下来的内容我将带你从零开始构建一个具备Harness Engineering核心思想的AI代理环境。我们将以“让AI代理自动为一个Python项目添加README文件、运行单元测试并修复基础错误”这个真实工程任务为例贯穿始终。你会发现重点不在于使用最炫酷的框架而在于如何设计一个健壮的系统。2. 环境基石构建安全、隔离且资源可控的沙箱任何可靠工程系统的起点都是一个定义清晰、边界明确、资源可控的执行环境。对于AI代理来说一个“全能”的系统权限是灾难的开始。我们的第一步就是为它打造一个专属的“工作间”。2.1 为什么需要沙箱不止于安全很多人认为沙箱只是为了安全防止恶意代码。这没错但对于Harness Engineering沙箱更核心的价值在于确定性和可重复性。状态隔离代理在上一个任务中安装的包、修改的环境变量、产生的临时文件绝不能污染下一个任务。每个任务都应在全新的、纯净的环境中开始。资源限制防止代理陷入死循环或无节制地占用CPU/内存导致宿主机瘫痪。必须限制其CPU时间、内存上限、磁盘空间和网络访问。故障 containment当代理执行出错比如rm -rf /这样的危险命令时破坏范围应被严格限制在沙箱内不影响宿主系统和其他任务。行为审计所有在沙箱内的操作文件读写、命令执行、网络请求都应被完整记录便于事后复盘和调试。2.2 容器化使用Docker作为首选沙箱方案在众多隔离技术中Docker容器是平衡了轻量性、易用性和功能性的最佳选择。我们不会直接让代理操作宿主机而是让它在一个Docker容器内工作。首先我们准备一个基础的Dockerfile。这个镜像不宜过大应只包含最必要的运行环境和工具。# Dockerfile.agent-base FROM python:3.11-slim # 安装系统级基础工具 RUN apt-get update apt-get install -y \ git \ curl \ wget \ build-essential \ rm -rf /var/lib/apt/lists/* # 设置工作目录 WORKDIR /workspace # 创建一个非root用户提升安全性 RUN useradd -m -s /bin/bash agent USER agent # 预设环境变量 ENV PATH/home/agent/.local/bin:${PATH}构建基础镜像docker build -t ai-agent-base:latest -f Dockerfile.agent-base .接下来是关键的一步我们如何让AI代理与这个容器交互一个朴素的想法是让代理直接执行docker run和docker exec命令。但这非常危险等同于赋予了代理在宿主机上运行任意容器的能力。正确的做法是我们提供一个安全的容器管理服务。我们可以编写一个简单的Python服务使用Docker SDK来管理容器的生命周期。这个服务暴露安全的API给AI代理调用。# container_manager.py import docker import uuid import threading import time from typing import Dict, Optional class ContainerManager: def __init__(self): self.client docker.from_env() self.active_containers: Dict[str, docker.models.containers.Container] {} self.lock threading.Lock() def create_workspace(self, image: str “ai-agent-base:latest”) - str: 创建一个新的工作容器返回容器ID container_id str(uuid.uuid4())[:8] try: # 限制资源1核CPU512MB内存10秒CPU时间限制 container self.client.containers.run( image, command“tail -f /dev/null”, # 保持容器运行 detachTrue, namef“agent-workspace-{container_id}”, working_dir“/workspace”, mem_limit“512m”, cpuset_cpus“0”, # 绑定到0号CPU cpu_period100000, cpu_quota100000, # 限制为1个核心 network_disabledFalse, # 可根据需要禁用 read_onlyFalse, # 根文件系统只读这里我们给workspace写权限 volumes{ # 可以挂载一个临时卷用于持久化workspace内容 f“agent-temp-{container_id}”: {“bind”: “/workspace”, “mode”: “rw”} }, user“agent” ) with self.lock: self.active_containers[container_id] container print(f“Created workspace container: {container.id[:12]} for session {container_id}”) return container_id except docker.errors.APIError as e: print(f“Failed to create container: {e}”) raise def exec_command(self, container_id: str, cmd: str, timeout: int 30) - Dict: 在指定容器内执行命令并返回结果 if container_id not in self.active_containers: return {“error”: “Container not found or inactive”} container self.active_containers[container_id] try: # 使用低层级的exec_create和exec_start以便获取退出码 exec_id self.client.api.exec_create( container.id, cmd, user“agent”, workdir“/workspace”, environment{“PYTHONUNBUFFERED”: “1”} ) output self.client.api.exec_start(exec_id[‘Id’], detachFalse, streamFalse, demuxTrue) # 获取执行状态 inspect_data self.client.api.exec_inspect(exec_id[‘Id’]) exit_code inspect_data[‘ExitCode’] # 处理输出 (stdout, stderr) stdout, stderr output stdout stdout.decode(‘utf-8’) if stdout else “” stderr stderr.decode(‘utf-8’) if stderr else “” return { “exit_code”: exit_code, “stdout”: stdout, “stderr”: stderr, “cmd”: cmd } except Exception as e: return {“error”: f“Execution failed: {str(e)}”} def cleanup(self, container_id: str): 停止并移除容器及其卷 if container_id in self.active_containers: container self.active_containers.pop(container_id) try: container.stop(timeout5) container.remove(vTrue) # 删除关联的卷 print(f“Cleaned up container for session {container_id}”) except docker.errors.APIError as e: print(f“Error during cleanup: {e}”)注意以上是一个简化示例。在生产环境中你需要考虑更多比如使用--read-only挂载根文件系统只给/workspace或/tmp写权限使用Seccomp或AppArmor配置文件进一步限制系统调用以及为网络访问设置白名单。这个ContainerManager就是AI代理与Docker容器之间的安全桥梁。代理通过调用create_workspace获得一个沙箱通过exec_command在沙箱内执行命令。所有危险操作都被封装在这个服务内部代理无法直接触碰宿主机的Docker守护进程。3. 工具规范化为AI代理定义清晰、可靠的操作接口有了安全的沙箱下一步是定义AI代理在这个沙箱里能做什么、不能做什么。我们不能让它随意执行任何Shell命令。相反我们需要提供一套规范化、可预测、带约束的工具Tools。3.1 从原始命令到结构化工具直接给代理一个bash终端是最糟糕的设计。工具的设计原则是高内聚、低风险、强反馈。以我们的“处理Python项目”任务为例代理可能需要以下工具文件操作工具读文件、写文件、列出目录。禁止直接使用cat、vim等。代码执行工具运行特定的、允许的命令如python -m pytest、black代码格式化、isort导入排序。版本控制工具封装git的特定子命令如git statusgit diffgit addgit commit。禁止git push --force等危险操作。包管理工具运行pip install -r requirements.txt禁止pip install任意包可通过白名单或虚拟环境策略控制。我们使用LangChain的Tool抽象来定义这些工具。每个工具都是一个Python函数它内部会调用我们前面创建的ContainerManager服务。# agent_tools.py from langchain.tools import Tool from container_manager import ContainerManager import json manager ContainerManager() # 全局管理器实例 current_container_id None # 当前会话的容器ID def init_workspace(): global current_container_id current_container_id manager.create_workspace() return f“Workspace initialized with ID: {current_container_id}” def read_file(filepath: str) - str: 读取指定文件的内容。 if not current_container_id: return “Error: Workspace not initialized.” result manager.exec_command(current_container_id, f“cat {filepath}”) if result.get(“exit_code”) 0: return result[“stdout”] else: return f“Failed to read file: {result.get(‘stderr’, ‘Unknown error’)}” def write_file(filepath: str, content: str) - str: 将内容写入指定文件。注意这会覆盖原有内容。 if not current_container_id: return “Error: Workspace not initialized.” # 先将内容写入一个临时命令中使用echo和重定向 # 注意这里需要对content进行适当的转义简易版用base64 import base64 encoded_content base64.b64encode(content.encode(‘utf-8’)).decode(‘utf-8’) cmd f“echo {encoded_content} | base64 --decode {filepath}” result manager.exec_command(current_container_id, cmd) if result.get(“exit_code”) 0: return f“Successfully wrote to {filepath}” else: return f“Failed to write file: {result.get(‘stderr’, ‘Unknown error’)}” def run_pytest(test_path: str “.”) - str: 在指定路径运行pytest测试。 if not current_container_id: return “Error: Workspace not initialized.” # 先确保pytest已安装可以在容器构建时预装 result manager.exec_command(current_container_id, f“python -m pytest {test_path} -v”) output result.get(“stdout”, “”) “\n” result.get(“stderr”, “”) exit_code result.get(“exit_code”, -1) return f“Exit Code: {exit_code}\nOutput:\n{output}” def get_git_status() - str: 获取当前git仓库的状态。 if not current_container_id: return “Error: Workspace not initialized.” result manager.exec_command(current_container_id, “git status”) return result.get(“stdout”, “”) result.get(“stderr”, “”) # 将函数封装成LangChain Tool read_file_tool Tool( name“read_file”, funcread_file, description“Useful for reading the contents of a file. Input should be the full file path.” ) write_file_tool Tool( name“write_file”, funcwrite_file, description“Useful for writing content to a file. Input should be a JSON string with ‘filepath’ and ‘content’ keys.” ) run_pytest_tool Tool( name“run_pytest”, funcrun_pytest, description“Useful for running Python tests with pytest. Input can be a specific test file or directory path.” ) get_git_status_tool Tool( name“get_git_status”, funcget_git_status, description“Useful for checking the status of the git repository in the current workspace.” )实操心得工具的描述description至关重要。AI代理尤其是大语言模型依赖这些描述来理解何时以及如何使用工具。描述要尽可能精确说明输入格式和工具的副作用。对于write_file这类有破坏性的工具必须在描述中明确警告“这会覆盖原有内容”。3.2 工具使用的约束与验证仅仅提供工具还不够我们还需要在工具被调用时进行验证。例如write_file工具应该禁止写入某些系统路径如/etc,/bin。我们可以在工具函数内部添加校验逻辑。def write_file(filepath: str, content: str) - str: # 路径安全检查 forbidden_paths [‘/etc’, ‘/bin’, ‘/usr’, ‘/lib’, ‘/home/agent/..’] import os abs_path os.path.abspath(filepath) for forbidden in forbidden_paths: if abs_path.startswith(forbidden): return f“Error: Writing to system path ‘{forbidden}’ is not allowed.” # ... 其余执行逻辑此外对于run_pytest我们可能想限制其最长运行时间防止无限循环的测试。这可以在ContainerManager.exec_command的timeout参数中实现并在超时后强制终止进程。4. 状态管理与记忆让AI代理记住目标与上下文AI代理在长链条任务中“失忆”是常见问题。Harness Engineering需要一个显式的、持久化的状态管理系统来维护任务上下文。这个状态至少包括最终目标用户最初提出的任务描述。已执行步骤一个按时间顺序排列的行动历史Action History包括使用的工具、输入、输出和观察结果。当前工作状态例如当前正在处理哪个文件、测试失败了几次、处于任务分解的哪个子阶段等。环境快照关键文件的哈希值、重要的中间结果等。我们不必自己从头造轮子可以利用LangGraph这类框架来管理有状态的、多步骤的工作流。LangGraph将执行过程建模为一个图Graph节点是工具调用或LLM决策边是状态转移的条件。状态State是一个可变的字典在整个图执行过程中传递。下面我们定义一个简单的状态结构并构建一个包含“规划-执行-检查”循环的图。# agent_graph.py from typing import TypedDict, Annotated, List import operator from langgraph.graph import StateGraph, END from langchain_core.messages import HumanMessage, AIMessage, ToolMessage from langchain_openai import ChatOpenAI # 假设使用OpenAI from agent_tools import read_file_tool, write_file_tool, run_pytest_tool, get_git_status_tool, init_workspace # 1. 定义状态结构 class AgentState(TypedDict): messages: Annotated[List, operator.add] # 消息历史LangGraph专用语法 final_goal: str # 最终目标 current_subtask: str # 当前子任务 iteration_count: int # 循环迭代次数用于防止无限循环 # 2. 初始化LLM和工具 llm ChatOpenAI(model“gpt-4”, temperature0) tools [read_file_tool, write_file_tool, run_pytest_tool, get_git_status_tool] llm_with_tools llm.bind_tools(tools) # 3. 定义图节点函数 def planner_node(state: AgentState) - AgentState: 规划节点根据当前状态和最终目标决定下一步做什么。 # 构建给LLM的提示 history state[‘messages’][-5:] # 只看最近几条历史 prompt f“”” 你是一个AI工程助手。你的最终目标是{state[‘final_goal’]}。 你当前正在处理的子任务是{state.get(‘current_subtask’ ‘尚未开始’)}。 以下是最近的执行历史 {history} 请思考下一步应该做什么。你可以 1. 使用已有的工具读文件、写文件、运行测试、检查git状态。 2. 如果当前子任务已完成或无法进行提出一个新的子任务。 3. 如果最终目标已达成请输出‘TASK_COMPLETE’。 请只输出你的思考结果和下一步行动计划不要输出其他内容。 “”” human_msg HumanMessage(contentprompt) # 这里我们让LLM进行“思考”但不直接调用工具。思考结果会添加到消息历史。 ai_msg AIMessage(content“I need to first understand the project structure by reading the main Python files and the existing test files.”) return {“messages”: [ai_msg]} def executor_node(state: AgentState) - AgentState: 执行节点根据规划节点的决定调用具体的工具。 last_message state[‘messages’][-1] if isinstance(last_message, AIMessage) and last_message.tool_calls: # 如果上一步的AI消息包含了工具调用请求则执行 tool_calls last_message.tool_calls tool_messages [] for tool_call in tool_calls: tool_name tool_call[‘name’] tool_input tool_call[‘args’] # 找到对应的工具并执行 for tool in tools: if tool.name tool_name: result tool.invoke(tool_input) tool_messages.append(ToolMessage(contentstr(result) tool_call_idtool_call[‘id’])) break return {“messages”: tool_messages} else: # 如果上一步只是“思考”则让LLM根据思考结果决定调用哪个工具 # 这里简化处理直接让LLM基于整个历史决定下一步动作 response llm_with_tools.invoke(state[‘messages’]) return {“messages”: [response]} def checker_node(state: AgentState) - AgentState: 检查节点评估工具执行的结果判断子任务是否完成或是否出错。 last_message state[‘messages’][-1] # 检查工具执行结果中是否包含错误 if isinstance(last_message, ToolMessage): content last_message.content if “Error:” in content or “Failed” in content: # 执行出错需要将错误信息反馈给规划器 error_msg HumanMessage(contentf“The last tool execution failed: {content}. Please analyze the error and adjust the plan.”) return {“messages”: [error_msg], “current_subtask”: “Handling Error”} # 检查是否达成了最终目标的某些条件例如测试全部通过 # 这里需要根据具体任务定义检查逻辑例如解析pytest输出 if “Exit Code: 0” in content and “passed” in content.lower(): # 测试通过可以进入下一个阶段 pass # 默认情况下继续循环 return {“current_subtask”: “Continue”, “iteration_count”: state.get(“iteration_count” 0) 1} # 4. 构建图 workflow StateGraph(AgentState) # 添加节点 workflow.add_node(“planner” planner_node) workflow.add_node(“executor” executor_node) workflow.add_node(“checker” checker_node) # 设置边和条件流转 workflow.set_entry_point(“planner”) workflow.add_edge(“planner” “executor”) workflow.add_edge(“executor” “checker”) # 从checker节点根据状态决定下一步 def decide_next(state: AgentState): iteration state.get(“iteration_count” 0) if iteration 10: # 防止无限循环 return END last_msg_content str(state[‘messages’][-1].content) if state[‘messages’] else “” if “TASK_COMPLETE” in last_msg_content: return END # 如果有错误跳回规划器重新规划 if “Handling Error” state.get(‘current_subtask’): return “planner” # 否则继续标准循环检查 - 规划 return “planner” workflow.add_conditional_edges(“checker” decide_next) # 编译图 app workflow.compile()这个图定义了一个简单的“规划-执行-检查”循环。planner负责思考executor负责调用工具checker负责评估结果并决定下一步。状态AgentState在整个过程中流转记录了所有的交互历史。通过add_conditional_edges我们实现了基于执行结果的动态路径选择。踩坑实录在早期版本中我让LLM在planner节点直接输出要调用的工具和参数。这经常导致格式错误或工具选择不合理。后来改为两阶段先让LLM在planner进行“纯思考”输出一个自然语言计划然后在executor节点再将整个历史包含这个计划给LLM让它生成格式严格的工具调用。这样大大提高了工具调用的准确率。5. 可观测性与控制为人类监督者装上“仪表盘”一个黑盒的、自主运行的AI代理是令人不安的。Harness Engineering强调可观测性和人类在环。我们需要实时知道代理在做什么、做得怎么样并在关键节点有能力介入。5.1 日志与追踪所有工具调用、LLM的输入输出、状态变更都必须被详细记录。这不仅是调试的需要也是审计和后续优化的数据基础。我们可以利用LangSmith或简单的结构化日志来实现。import logging import json from datetime import datetime def log_execution(step_type: str, data: dict): timestamp datetime.utcnow().isoformat() log_entry {“timestamp”: timestamp, “step”: step_type, “data”: data} # 输出到控制台和文件 print(json.dumps(log_entry indent2)) with open(“agent_execution.log” “a”) as f: f.write(json.dumps(log_entry) “\n”) # 在工具函数和节点函数中插入日志 def read_file_with_log(filepath: str) - str: log_execution(“tool_call_start” {“tool”: “read_file” “input”: filepath}) result read_file(filepath) log_execution(“tool_call_end” {“tool”: “read_file” “output_preview”: result[:200]}) return result5.2 检查点与人工审批对于关键操作比如向主分支提交代码、安装新的系统级依赖、删除重要文件等不能完全自动化。我们需要在流程中设置检查点暂停执行等待人类审批。我们可以在状态图中加入一个human_review节点。当执行流到达此节点时它会将当前上下文和待执行的操作摘要发送到一个预定义的接口如Slack消息、邮件或一个简单的Web界面并阻塞等待响应。# 模拟一个人类审批节点 def human_review_node(state: AgentState) - AgentState: 请求人工审批。 pending_action state.get(“pending_action”) # 例如{“type”: “git_commit” “message”: “Fix bug in module X”} # 1. 将审批请求发送到外部系统这里用打印模拟 print(f“[HUMAN REVIEW REQUIRED] Action: {pending_action}. Approve? (yes/no)”) # 2. 在实际系统中这里会轮询一个数据库或消息队列等待审批结果 # 3. 为简化我们模拟一个输入 import sys # 注意在实际自动化流程中不能这样阻塞等待控制台输入 # response input(“Approve? (yes/no): “).strip().lower() response “yes” # 模拟自动批准 if response “yes”: return {“human_approved”: True, “messages”: [HumanMessage(content“Human approved the action.”)]} else: return {“human_approved”: False, “messages”: [HumanMessage(content“Human rejected the action. Please revise the plan.”)]} # 在条件边中可以将特定状态如准备提交代码路由到human_review_node5.3 实时状态监控与中断一个基本的Web仪表盘可以展示当前任务目标、已完成的步骤、最近的操作日志、资源使用情况CPU/内存、以及一个“紧急停止”按钮。当代理行为异常时监督者可以手动中断任务。实现上可以将状态图的运行封装在一个异步任务中并通过一个WebSocket服务将状态实时推送到前端。“紧急停止”按钮触发一个标志位在下一次节点执行前检查该标志如果为真则清理容器并终止图执行。6. 实战演练组装一个完整的Harness并运行任务现在让我们把前面所有的部件组装起来并运行开篇提到的那个任务“自动为一个Python项目添加README文件、运行单元测试并修复基础错误”。假设我们有一个简单的Python项目结构如下/workspace ├── calculator.py ├── test_calculator.py └── requirements.txt其中test_calculator.py里有一个故意写错的测试。6.1 任务启动与初始化首先启动我们的容器管理服务和初始化工作空间。# main.py from container_manager import ContainerManager from agent_tools import init_workspace from agent_graph import app, AgentState # 初始化 manager ContainerManager() init_workspace() # 这会设置全局的 current_container_id # 准备初始状态 initial_state: AgentState { “messages”: [ HumanMessage(content“Please help me with this Python project. First, add a comprehensive README.md file. Then, run the existing unit tests and fix any basic errors you find. The project is in the current /workspace directory.”) ], “final_goal”: “Add README.md and ensure all unit tests pass.”, “current_subtask”: “Inspect project structure”, “iteration_count”: 0 } # 将项目文件复制到容器中模拟已有项目 # 这里省略了实际的文件复制代码假设容器内已有项目文件 print(“Starting the AI agent with harness...”)6.2 运行代理并观察我们以流式的方式运行状态图并打印出关键步骤。# 流式运行方便观察 for step in app.stream(initial_state, stream_mode“values”): step_name, step_state next(iter(step.items())) # 获取节点名和状态 print(f”\n Step: {step_name} ) last_msg step_state[‘messages’][-1] if step_state[‘messages’] else None if last_msg: print(f“Last Message Type: {type(last_msg).__name__}”) content last_msg.content # 如果是工具消息可能很长只预览 if hasattr(last_msg, ‘tool_calls’) and last_msg.tool_calls: print(f“AI decided to call tools: {last_msg.tool_calls}”) elif isinstance(last_msg, ToolMessage): print(f“Tool Result (preview): {content[:300]}...”) else: print(f“Content: {content}”) # 检查是否应该人工介入例如准备写README或提交代码时 if step_state.get(“current_subtask”) “Propose README content”: # 这里可以触发一个审批流程 print(“[ACTION] Agent is ready to write README.md. Would you like to review the content first? (Simulated Auto-Approval)”) # 模拟批准继续执行 pass if step_state.get(“iteration_count” 0) 15: print(“[WARNING] Iteration limit reached. Stopping.”) break6.3 预期执行流程与关键节点在一个设计良好的Harness中代理的执行流程应该是可预测的规划节点LLM分析任务决定先读取项目文件calculator.py,test_calculator.py来理解项目。执行节点调用read_file_tool获取文件内容。检查节点确认文件读取成功更新状态。规划节点基于代码内容构思README的结构。可选人工审批节点将生成的README草案提交给人类审核。在我们的简化流程中可能跳过。执行节点调用write_file_tool创建README.md。规划节点决定运行测试。执行节点调用run_pytest_tool。检查节点解析测试输出发现失败。将错误信息反馈。规划节点分析测试失败原因比如断言错误决定修改源代码。执行节点调用read_file_tool读取有问题的测试文件分析后调用write_file_tool修复它。执行节点再次调用run_pytest_tool。检查节点确认测试通过。判断最终目标README已添加测试通过已完成。流程结束。在整个过程中所有的操作都被限制在容器内工具调用被严格限定状态被完整记录你可以在日志中看到每一步的输入输出。如果代理在修复测试时试图执行rm -rf /它会被容器权限和工具白名单阻止。如果它陷入“运行测试-失败-改代码-运行测试”的死循环iteration_count会触发终止条件。6.4 事后分析与复盘任务结束后无论是成功还是被终止我们都有完整的日志agent_execution.log和容器内的最终文件状态。我们可以分析效率代理用了多少步完成任务有没有不必要的来回工具使用合理性代理是否选择了正确的工具参数是否正确LLM推理质量规划节点的决策是否合理错误处理遇到错误时代理的恢复策略是否有效这些数据是迭代优化你的Harness、工具描述以及提示词Prompt的宝贵资产。7. 超越基础Harness Engineering的进阶思考构建一个能跑通的Harness只是起点。要让AI代理真正可靠地处理多样化的真实工程任务还需要考虑更多。7.1 动态工具注册与发现我们的工具列表是硬编码的。在更复杂的场景中你可能需要代理根据任务动态“发现”或“请求”新工具。例如代理在处理一个前端项目时可能需要npm run build工具。这可以通过一个“工具注册表”和“工具请求协议”来实现。代理可以查询注册表或提出需求由Harness系统在安全检查后动态地将一个新工具本质上是另一个安全的封装函数加载到其可用工具列表中。7.2 多容器与分布式任务有些任务可能需要多个服务协同比如需要同时运行后端API和前端服务进行端到端测试。Harness可以扩展为管理多个容器并定义容器间的网络通信规则。代理可以拥有操作多个“工作单元”的能力但同样需要通过规范化的工具进行例如“在容器A中启动服务X”“从容器B向容器A的端口Y发送请求”。7.3 长期记忆与知识库对于跨会话的任务需要将关键信息如项目架构决策、已解决的难题持久化到向量数据库或知识库中。当代理在新会话中处理同一项目时可以先查询知识库获取上下文避免重复劳动或犯同样的错误。这需要将Harness与外部存储系统连接并设计有效的信息检索与更新机制。7.4 测试与验证套件最可靠的Harness自身也需要被充分测试。你应该为你的Harness系统编写单元测试和集成测试模拟各种代理行为包括恶意和异常行为确保沙箱隔离有效、工具约束起作用、状态管理正确。可以建立一个“挑战任务集”包含各种边缘案例定期用你的Harness运行评估其成功率和安全性。构建一个成熟的Harness Engineering体系是一个与AI代理能力共同进化的过程。它没有终极的完美形态只有针对特定场景、特定可靠性要求的不断权衡与精进。这套体系的终极目标不是取代人类工程师而是成为人类工程师手中一件强大、可控、可预测的超级工具。通过今天从0到1的搭建我希望你收获的不仅仅是一套可运行的代码更是一种让AI在真实世界中安全、有效工作的工程化思维。本文还有配套的精品资源点击获取