Agent Harness 实战指南:构建生产级 AI Agent 的"马具"框架
引言:为什么需要 Agent Harness?AI Agent 就像一匹骏马,能力强大但桀骜不驯。如果直接让它自由奔跑,很可能撞墙、跑偏,甚至失控。Agent Harness 就是这匹马的"马具"------一套标准化的约束和工具系统,让 Agent 在可控范围内高效完成任务。在实际生产中,AI Agent 面临三个核心问题:- 失控风险 :Agent 可能执行危险操作(如删除文件、调用外部 API 无限制)- 可观测性差 :难以追踪 Agent 的思考过程和决策依据- 资源浪费 :无限循环调用 LLM 导致成本爆炸Agent Harness 框架正是为了解决这些问题而生。它像一位经验丰富的驯马师,给 Agent 套上缰绳、马鞍和护腿,让它安全、高效地奔跑。## 核心概念:Agent Harness 的三大组件### 1. Tool Registry(工具注册中心)就像马具上的挂钩,所有工具都需要注册后才能使用。每个工具定义其名称、参数、安全级别和错误处理策略。### 2. Policy Engine(策略引擎)这是马具的"刹车系统",控制 Agent 的行为边界。包括:- 权限控制:哪些工具可以调用- 速率限制:每秒/分钟最大调用次数- 成本控制:单次对话最大 token 消耗### 3. Observation Layer(观测层)相当于马具上的 GPS 和心率监测器,记录 Agent 的每一步操作、推理过程和性能指标。## 实战:构建一个安全的代码生成 Agent让我们通过一个具体例子来理解 Agent Harness 的工作原理。我们将构建一个能够生成 Python 代码并安全执行的 Agent。### 环境准备首先安装必要的依赖:bashpip install openai pydantic python-dotenv### 代码示例 1:基础 Agent Harness 实现pythonimport asyncioimport jsonfrom typing import Dict, Any, Listfrom pydantic import BaseModel, Fieldfrom openai import AsyncOpenAI# 定义工具注册模型class Tool(BaseModel): name: str description: str parameters: Dict[str, Any] required_permissions: List[str] = ["read"] # 默认只读权限# Agent Harness 核心类class AgentHarness: def __init__(self, openai_api_key: str): self.client = AsyncOpenAI(api_key=openai_api_key) self.tools = {} # 工具注册表 self.policies = { # 策略引擎配置 "max_tool_calls": 5, # 最多调用5次工具 "max_tokens_per_call": 2000, # 每次调用最多2000 tokens "allowed_operations": ["read"], # 允许的操作类型 "rate_limit_per_minute": 10 # 每分钟最多10次调用 } self.observation_log = [] # 观测日志 self.call_count = 0 # 调用计数器 def register_tool(self, tool: Tool, handler: callable): """注册工具到 harness""" self.tools[tool.name] = { "definition": tool, "handler": handler } print(f"[Harness] 注册工具: {tool.name}") def _check_permissions(self, tool_name: str) -> bool: """策略引擎:检查权限""" tool = self.tools.get(tool_name) if not tool: return False # 检查操作是否在允许列表中 for perm in tool["definition"].required_permissions: if perm not in self.policies["allowed_operations"]: print(f"[Policy] 拒绝操作: {tool_name} 需要 {perm} 权限") return False return True def _check_rate_limit(self) -> bool: """策略引擎:检查速率限制""" if self.call_count >= self.policies["rate_limit_per_minute"]: print("[Policy] 达到速率限制") return False return True async def execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]: """执行工具调用,包含 harness 控制""" # 1. 权限检查 if not self._check_permissions(tool_name): return {"error": f"权限不足:{tool_name}"} # 2. 速率检查 if not self._check_rate_limit(): return {"error": "速率限制已触发"} # 3. 调用计数 self.call_count += 1 # 4. 记录观测日志 observation = { "step": len(self.observation_log) + 1, "action": f"调用工具 {tool_name}", "parameters": parameters, "timestamp": asyncio.get_event_loop().time() } self.observation_log.append(observation) # 5. 执行工具 try: tool = self.tools[tool_name] result = await tool["handler"](**parameters) # 更新观测日志 observation["result"] = result observation["status"] = "success" return result except Exception as e: observation["status"] = "error" observation["error"] = str(e) return {"error": f"工具执行失败: {str(e)}"} async def process_agent_request(self, user_input: str) -> str: """处理 Agent 请求,包含完整的 harness 控制""" print(f"\n[Agent] 用户输入: {user_input}") # 构建系统提示,告诉 LLM 可用工具和限制 tools_description = "\n".join([ f"- {name}: {tool['definition'].description}" for name, tool in self.tools.items() ]) system_prompt = f"""你是安全编码助手,使用以下工具完成任务:{tools_description}规则:1. 每次只能调用一个工具2. 最多调用 {self.policies['max_tool_calls']} 次3. 生成代码后必须用 execute_code 工具执行并返回结果4. 如果遇到错误,尝试修复后重新执行""" # 调用 LLM response = await self.client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input} ], tools=[self._build_openai_tool_format()], tool_choice="auto", max_tokens=self.policies["max_tokens_per_call"] ) # 处理 LLM 响应 message = response.choices[0].message if message.tool_calls: for tool_call in message.tool_calls: tool_name = tool_call.function.name parameters = json.loads(tool_call.function.arguments) result = await self.execute_tool(tool_name, parameters) print(f"[Harness] 工具执行结果: {result}") return str(result) return message.contentdef build_openai_tool_format(self): """将工具转换为 OpenAI API 格式""" # 实现略,用于演示 pass### 代码示例 2:安全代码执行工具现在让我们实现一个安全的代码执行工具,包含沙箱保护:pythonimport astimport sysimport ioimport contextlibimport signalfrom typing import Optionalclass SafeCodeExecutor: """安全的 Python 代码执行器,防止危险操作""" def __init__(self, max_execution_time: int = 5): self.max_execution_time = max_execution_time def _check_code_safety(self, code: str) -> bool: """静态代码安全检查""" try: tree = ast.parse(code) except SyntaxError as e: print(f"[Safety] 语法错误: {e}") return False # 黑名单:禁止的操作 forbidden_imports = {'os', 'subprocess', 'shutil', 'sys'} # sys 部分可用 forbidden_functions = {'eval', 'exec', 'compile', '__import__'} forbidden_attributes = {'__subclasshook__', '__init_subclass__'} for node in ast.walk(tree): # 检查 import 语句 if isinstance(node, ast.Import): for alias in node.names: if alias.name in forbidden_imports: print(f"[Safety] 禁止导入: {alias.name}") return False # 检查 from ... import 语句 elif isinstance(node, ast.ImportFrom): if node.module in forbidden_imports: print(f"[Safety] 禁止导入: {node.module}") return False # 检查函数调用 elif isinstance(node, ast.Call): if hasattr(node.func, 'id'): if node.func.id in forbidden_functions: print(f"[Safety] 禁止函数调用: {node.func.id}") return False # 检查属性访问(如 os.system) elif hasattr(node.func, 'attr'): if node.func.attr in forbidden_attributes: print(f"[Safety] 禁止属性访问: {node.func.attr}") return False return True async def execute_code(self, code: str, timeout: Optional[int] = None) -> Dict[str, Any]: """安全执行代码,返回结果""" # 1. 安全检查 if not self._check_code_safety(code): return {"error": "代码包含不安全的操作"} # 2. 准备执行环境 local_vars = {} stdout_capture = io.StringIO() stderr_capture = io.StringIO() # 3. 设置超时 actual_timeout = timeout or self.max_execution_time def timeout_handler(signum, frame): raise TimeoutError(f"代码执行超时({actual_timeout}秒)") # 4. 执行代码(捕获输出) try: # 设置信号处理器(仅限 Unix) if sys.platform != 'win32': signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(actual_timeout) with contextlib.redirect_stdout(stdout_capture), \ contextlib.redirect_stderr(stderr_capture): exec(code, {"__builtins__": __builtins__}, local_vars) # 取消闹钟 if sys.platform != 'win32': signal.alarm(0) return { "stdout": stdout_capture.getvalue(), "stderr": stderr_capture.getvalue(), "locals": {k: v for k, v in local_vars.items() if not k.startswith('_')}, "status": "success" } except TimeoutError as e: return {"error": str(e)} except Exception as e: return {"error": f"执行错误: {str(e)}"} finally: stdout_capture.close() stderr_capture.close()# 主程序:使用 Agent Harnessasync def main(): # 初始化 harness harness = AgentHarness(openai_api_key="your-api-key-here") # 注册安全代码执行工具 safe_executor = SafeCodeExecutor(max_execution_time=10) code_tool = Tool( name="execute_code", description="安全执行 Python 代码并返回结果", parameters={ "type": "object", "properties": { "code": {"type": "string", "description": "要执行的 Python 代码"} }, "required": ["code"] }, required_permissions=["read", "write"] # 代码执行需要读写权限 ) harness.register_tool(code_tool, safe_executor.execute_code) # 注册一个只读工具(示例) def read_file_handler(path: str) -> Dict[str, Any]: try: with open(path, 'r') as f: content = f.read() return {"content": content, "status": "success"} except Exception as e: return {"error": str(e)} read_tool = Tool( name="read_file", description="读取文件内容(只读)", parameters={ "type": "object", "properties": { "path": {"type": "string", "description": "文件路径"} }, "required": ["path"] }, required_permissions=["read"] # 只读权限 ) harness.register_tool(read_tool, read_file_handler) # 处理用户请求 result = await harness.process_agent_request("写一个 Python 函数计算斐波那契数列第 n 项,并执行验证") print(f"\n最终结果: {result}") # 打印观测日志 print("\n=== 观测日志 ===") for log in harness.observation_log: print(f"步骤 {log['step']}: {log['action']}") print(f" 参数: {log.get('parameters', {})}") print(f" 状态: {log.get('status', 'unknown')}") print()if __name__ == "__main__": asyncio.run(main())## 最佳实践:生产级 Harness 配置### 1. 分层权限控制python# 权限级别PERMISSIONS = { "read": 1, # 只读操作 "write": 2, # 写入操作 "execute": 3, # 执行操作 "admin": 4 # 管理操作}# 不同 Agent 角色的权限agent_roles = { "assistant": ["read"], # 助手只能读取 "developer": ["read", "write"], # 开发者可以读写 "admin": ["read", "write", "execute", "admin"] # 管理员全权限}### 2. 成本控制策略pythonclass CostController: def __init__(self, budget: float = 10.0): self.budget = budget self.spent = 0.0 def can_proceed(self, estimated_cost: float) -> bool: return self.spent + estimated_cost <= self.budget def record_cost(self, cost: float): self.spent += cost if self.spent > self.budget * 0.8: print(f"[Cost] 警告:已使用预算的 {self.spent/self.budget*100:.1f}%")### 3. 错误恢复机制pythonclass RetryHandler: def __init__(self, max_retries: int = 3): self.max_retries = max_retries async def execute_with_retry(self, tool_func, *args, **kwargs): for attempt in range(self.max_retries): try: return await tool_func(*args, **kwargs) except TemporaryError as e: wait_time = 2 ** attempt # 指数退避 print(f"[Retry] 第 {attempt+1} 次重试,等待 {wait_time} 秒") await asyncio.sleep(wait_time) except PermanentError: raise## 总结Agent Harness 框架就像给 AI Agent 配上了专业的马具,让它从"野马"变成"战马"。通过本文的实战,我们学到了:1. 安全第一 :Tool Registry 和 Policy Engine 确保 Agent 在可控范围内行动2. 可观测性 :Observation Layer 让每一步操作都可追踪、可审计3. 成本可控 :通过速率限制、token 预算等策略避免资源滥用4. 容错机制 :重试、回滚、降级策略保证系统稳定性在实际生产环境中,Agent Harness 还需要考虑:- 多 Agent 协同 :多个 Harness 实例如何共享状态和资源- 动态策略 :根据实时负载调整限制参数- 审计日志:完整的操作记录用于合规和调试记住:好的工具不是限制创造力,而是让创造力在安全边界内自由发挥。就像真正的马具,既约束了马的行动,又放大了它的力量。