【摘要】
你是否好奇像Claude Code、Cursor这样的AI编程助手是如何运作的?它们能读文件、跑测试、迭代修改——这些能力背后,依靠的正是一个强大的 Harness 系统。本文将从零开始,手把手教你构建一个自己的AI Agent Harness。我们将通过6大核心模块的实战代码,一步步搭建一个具备任务调度、工具调用、记忆管理和多Agent协作的轻量级执行环境。无论你是AI初学者还是后端开发者,都能通过本文掌握AI Agent工程化的核心思想。
关键词:AI Agent、Harness Engineering、Agent Loop、工具注册、任务编排、LangGraph、FastAPI
一、为什么需要Harness?
如果说大模型是“大脑”,那么Harness就是它的“手脚”和“神经系统”。
大语言模型(LLM)很聪明,能推理、能决策,但它有一个致命的局限:它活在文字里,碰不到真实世界。它不能读你磁盘上的文件,不能运行Python脚本,不能看到报错信息,也不能查git log。每次对话,它都只是在处理你输入的文字。
Harness就是给大模型“安装身体”的技术——让大模型变成能自主感知、决策、行动、反思的“智能体”。它的核心公式是:
Harness = 工具 + 知识 + 上下文管理 + 权限边界
打个比方:大模型是“司机”,Harness是“车”。你不需要教司机怎么开车,你只需要造一辆好车。
Harness工程包含三大支柱:
- 上下文工程 (Context Engineering):确保智能体在正确的时间获得正确的信息
- 约束与验证 (Constraints & Verification):限制智能体可执行的操作,验证任务完成质量
- 反馈与纠正 (Feedback & Correction):当智能体出错时进行自动修复
二、准备工作
2.1 技术栈/知识
- Python 3.8+:我们将使用Python作为主要开发语言
- 基础命令行操作:熟悉基本的CLI操作会让开发更顺畅
- LLM基础认知:了解大模型是什么以及大致工作原理
- API调用经验:了解RESTful API的基本概念
2.2 环境/工具
- Python 3.8+ 环境
- VS Code 或 PyCharm 等代码编辑器
- OpenAI API Key(或其他LLM提供商的API Key)
- Git(可选但推荐)
2.3 安装依赖
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
# 安装核心依赖
pip install openai langchain langgraph fastapi uvicorn streamlit
pip install python-dotenv pydantic sqlalchemy psycopg2-binary
三、核心概念:Harness的六大组件
在动手写代码之前,先了解一个完整的Harness需要哪些组件:
| 组件 | 作用 | 对应功能 |
|---|---|---|
| 工具库 (Tool Registry) | 给AI提供“手”,让它能操作外部系统 | 文件读写、命令执行、API调用 |
| 记忆系统 (Memory) | 给AI提供“长期记忆”,记住用户偏好和过往对话 | 向量存储、会话持久化 |
| 任务调度器 (Task Scheduler) | 管理任务分解和执行顺序 | Agent Loop、Todo List |
| 安全围栏 (Guardrails) | 限制AI的操作权限,防止越界行为 | 路径限制、操作审批 |
| 上下文管理 (Context Manager) | 防止对话上下文爆炸 | 滑动窗口、语义摘要 |
| 可观测性 (Observability) | 监控AI的一举一动 | 日志、追踪、指标 |
这六大组件共同构成了一个完整的Harness系统。接下来,我们将一步步实现它们。
四、架构设计
4.1 整体架构图

4.2 目录结构
my_harness/
├── src/
│ ├── agents/ # Agent实例
│ │ ├── base_agent.py
│ │ └── agents/ # 具体Agent实现
│ ├── core/ # 核心Harness组件
│ │ ├── loop.py # Agent循环
│ │ ├── tools.py # 工具注册
│ │ ├── memory.py # 记忆系统
│ │ ├── guardrails.py # 安全围栏
│ │ └── context.py # 上下文管理
│ ├── api/ # API接口
│ │ ├── routes.py
│ │ └── models.py
│ └── utils/ # 工具函数
├── evals/ # 评估测试
├── config/ # 配置文件
├── .env # 环境变量
└── requirements.txt
五、核心模块实战
5.1 模块一:最小Agent循环(心脏)
这是整个Harness最核心的部分——一个while循环,驱动模型不断调用工具,直到它自己判断不再需要为止。
# src/core/loop.py
import json
from typing import List, Dict, Any, Optional
from openai import OpenAI
from .tools import ToolRegistry
class AgentLoop:
"""Agent循环:模型思考 → 调用工具 → 观察结果 → 继续思考"""
def __init__(self, model: str = "gpt-4", max_iterations: int = 20):
self.client = OpenAI()
self.model = model
self.max_iterations = max_iterations
self.tool_registry = ToolRegistry()
self.messages: List[Dict[str, Any]] = []
def run(self, user_input: str) -> str:
"""执行Agent循环"""
# 初始化对话
self.messages = [{"role": "user", "content": user_input}]
iteration = 0
while iteration < self.max_iterations:
iteration += 1
print(f"[Iteration {iteration}] 正在思考...")
# 调用LLM,带上可用工具的定义
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
tools=self.tool_registry.get_openai_tool_definitions(),
tool_choice="auto"
)
assistant_message = response.choices[0].message
self.messages.append(assistant_message.model_dump())
# 检查是否需要调用工具
if not assistant_message.tool_calls:
# 没有工具调用,任务完成
return assistant_message.content or "任务完成"
# 执行所有工具调用
for tool_call in assistant_message.tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f" 🔧 调用工具: {tool_name}({arguments})")
# 通过工具注册表执行
result = self.tool_registry.execute(tool_name, arguments)
# 将工具执行结果添加到消息列表
self.messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "达到最大迭代次数,任务未完成"
核心要点:
- 什么时候停?大模型自己决定,不需要写任何判断逻辑
- 整个纠错过程完全由模型自己完成——命令失败了,它会自己换一条命令重试
5.2 模块二:工具箱(安全围栏)
光有循环不够。只有一个bash命令行,AI什么都能干,太危险了。我们需要做两件事:注册允许的工具 + 加围栏限制操作范围。
# src/core/tools.py
import subprocess
import os
from typing import Dict, Any, Callable, List
from pathlib import Path
class ToolRegistry:
"""工具注册表 — 管理AI能用的所有工具"""
def __init__(self, workspace_root: str = "."):
self.workspace_root = Path(workspace_root).resolve()
self._tools: Dict[str, Dict[str, Any]] = {}
self._register_default_tools()
def _register_default_tools(self):
"""注册默认工具"""
# 工具1: 读文件
self.register(
name="read_file",
description="读取文件内容",
parameters={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"]
},
handler=self._read_file
)
# 工具2: 写文件
self.register(
name="write_file",
description="写入内容到文件",
parameters={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"content": {"type": "string", "description": "写入内容"}
},
"required": ["path", "content"]
},
handler=self._write_file
)
# 工具3: 执行命令
self.register(
name="execute_command",
description="执行shell命令",
parameters={
"type": "object",
"properties": {
"command": {"type": "string", "description": "要执行的命令"}
},
"required": ["command"]
},
handler=self._execute_command
)
def register(self, name: str, description: str, parameters: Dict, handler: Callable):
"""注册一个工具"""
self._tools[name] = {
"description": description,
"parameters": parameters,
"handler": handler
}
def _read_file(self, path: str) -> str:
"""读取文件(带围栏检查)"""
full_path = self._resolve_path(path)
if not full_path:
return f"错误:路径 {path} 超出工作区范围"
try:
with open(full_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return f"读取文件失败: {e}"
def _write_file(self, path: str, content: str) -> str:
"""写入文件(带围栏检查)"""
full_path = self._resolve_path(path)
if not full_path:
return f"错误:路径 {path} 超出工作区范围"
try:
full_path.parent.mkdir(parents=True, exist_ok=True)
with open(full_path, 'w', encoding='utf-8') as f:
f.write(content)
return f"文件已写入: {path}"
except Exception as e:
return f"写入文件失败: {e}"
def _execute_command(self, command: str) -> str:
"""执行命令(带安全限制)"""
# 危险命令黑名单
dangerous = ['rm -rf', 'sudo', 'dd if=', 'mkfs', ':(){ :|:& };:']
for d in dangerous:
if d in command.lower():
return f"错误:命令 '{command}' 包含危险操作,已拒绝执行"
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
return result.stdout if result.stdout else result.stderr
except subprocess.TimeoutExpired:
return "命令执行超时(30秒)"
def _resolve_path(self, path: str) -> Path | None:
"""路径解析+安全检查:确保文件操作不出工作区"""
target = (self.workspace_root / path).resolve()
# 围栏检查:不能访问工作区之外的文件
if self.workspace_root not in target.parents and target != self.workspace_root:
return None
return target
def get_openai_tool_definitions(self) -> List[Dict]:
"""生成OpenAI格式的工具定义"""
return [
{
"type": "function",
"function": {
"name": name,
"description": info["description"],
"parameters": info["parameters"]
}
}
for name, info in self._tools.items()
]
def execute(self, name: str, arguments: Dict) -> str:
"""执行已注册的工具"""
if name not in self._tools:
return f"错误:未知工具 '{name}'"
return self._tools[name]["handler"](**arguments)
核心要点:
- 加新工具完全不用改循环代码,往工具箱里多注册一条就行
- 路径围栏:只能操作项目目录里的文件,防止智能体越界访问系统文件
- 命令黑名单:自动拦截危险操作
5.3 模块三:记忆系统
AI需要记住用户偏好和过往对话。这里实现一个简单的向量记忆系统。
# src/core/memory.py
import json
from pathlib import Path
from typing import List, Dict, Any, Optional
from datetime import datetime
class MemorySystem:
"""记忆系统 — 短期会话记忆 + 长期向量记忆"""
def __init__(self, storage_path: str = "./memory"):
self.storage_path = Path(storage_path)
self.storage_path.mkdir(exist_ok=True)
self.session_memory: List[Dict] = [] # 会话级记忆
self.long_term_memory: List[Dict] = [] # 长期记忆
self._load_long_term()
def add_session_memory(self, role: str, content: str):
"""添加会话记忆"""
self.session_memory.append({
"role": role,
"content": content,
"timestamp": datetime.now().isoformat()
})
def get_session_context(self, limit: int = 10) -> List[Dict]:
"""获取最近的会话上下文"""
return self.session_memory[-limit:]
def add_long_term_memory(self, content: str, tags: List[str] = None):
"""添加长期记忆"""
memory_item = {
"id": len(self.long_term_memory),
"content": content,
"tags": tags or [],
"timestamp": datetime.now().isoformat()
}
self.long_term_memory.append(memory_item)
self._save_long_term()
def search_long_term(self, query: str, limit: int = 5) -> List[Dict]:
"""搜索长期记忆(简单的关键词匹配,生产环境可替换为向量检索)"""
query_lower = query.lower()
results = []
for item in self.long_term_memory:
if query_lower in item["content"].lower():
results.append(item)
elif any(tag in query_lower for tag in item["tags"]):
results.append(item)
if len(results) >= limit:
break
return results
def clear_session(self):
"""清空会话记忆"""
self.session_memory = []
def _load_long_term(self):
"""加载持久化的长期记忆"""
memory_file = self.storage_path / "long_term.json"
if memory_file.exists():
with open(memory_file, 'r') as f:
self.long_term_memory = json.load(f)
def _save_long_term(self):
"""保存长期记忆"""
memory_file = self.storage_path / "long_term.json"
with open(memory_file, 'w') as f:
json.dump(self.long_term_memory, f, indent=2)
5.4 模块四:上下文管理(防止信息腐烂)
当对话轮次太多时,上下文会越来越臃肿。需要通过滑动窗口和摘要来管理。
# src/core/context.py
from typing import List, Dict, Any
from openai import OpenAI
class ContextManager:
"""上下文管理器 — 滑动窗口 + 自动摘要"""
def __init__(self, max_tokens: int = 8000, summary_threshold: int = 10):
self.max_tokens = max_tokens
self.summary_threshold = summary_threshold # 超过10轮触发摘要
self.client = OpenAI()
self.summary_cache = None
def compress(self, messages: List[Dict]) -> List[Dict]:
"""压缩过长的上下文"""
if len(messages) <= self.summary_threshold:
return messages
# 如果已经有摘要,返回摘要+最近消息
if self.summary_cache:
return [
{"role": "system", "content": f"之前的对话摘要:{self.summary_cache}"},
*messages[-5:] # 保留最近5条
]
# 生成摘要
to_summarize = messages[:-5] # 摘要前N条
summary = self._generate_summary(to_summarize)
self.summary_cache = summary
return [
{"role": "system", "content": f"之前的对话摘要:{summary}"},
*messages[-5:]
]
def _generate_summary(self, messages: List[Dict]) -> str:
"""调用LLM生成对话摘要"""
try:
response = self.client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "请将以下对话浓缩为一句话摘要,保留关键信息。"},
*messages
],
max_tokens=200
)
return response.choices[0].message.content
except Exception:
return f"共 {len(messages)} 轮对话"
5.5 模块五:任务编排与多Agent协作
通过LangGraph实现复杂任务的编排和子Agent分工。
# src/core/orchestrator.py
from typing import TypedDict, Annotated, List
from langgraph.graph import StateGraph, END
from langgraph.checkpoint import MemorySaver
import operator
# 定义状态结构
class AgentState(TypedDict):
messages: Annotated[List[dict], operator.add]
task_list: List[str]
current_task: str
completed_tasks: List[str]
class TaskOrchestrator:
"""任务编排器 — 管理多Agent协作和任务分解"""
def __init__(self, agent_loop):
self.agent_loop = agent_loop
self.graph = self._build_graph()
self.checkpointer = MemorySaver()
def _build_graph(self):
"""构建任务流程图"""
workflow = StateGraph(AgentState)
# 定义节点
workflow.add_node("plan", self._plan_node) # 任务分解
workflow.add_node("execute", self._execute_node) # 执行
workflow.add_node("verify", self._verify_node) # 验证
# 定义边
workflow.set_entry_point("plan")
workflow.add_edge("plan", "execute")
workflow.add_conditional_edges("execute", self._should_continue)
workflow.add_edge("verify", END)
return workflow.compile(checkpointer=self.checkpointer)
def _plan_node(self, state: AgentState) -> AgentState:
"""将用户需求分解为任务清单"""
user_message = state["messages"][-1]["content"]
# 调用LLM进行任务分解
prompt = f"""
请将以下用户需求分解为具体的任务清单,每项任务用 - 开头:
用户需求:{user_message}
请输出任务清单:
"""
response = self.agent_loop.client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
# 解析任务清单
tasks = [line.strip("- ").strip() for line in response.choices[0].message.content.split("\n") if line.strip().startswith("-")]
return {
"task_list": tasks,
"current_task": tasks[0] if tasks else "",
"completed_tasks": []
}
def _execute_node(self, state: AgentState) -> AgentState:
"""执行当前任务"""
current = state["current_task"]
if not current:
return state
# 使用子Agent执行当前任务
result = self.agent_loop.run(f"请执行以下任务:{current}")
# 记录完成状态
completed = state.get("completed_tasks", []) + [current]
task_list = state["task_list"]
next_idx = len(completed)
next_task = task_list[next_idx] if next_idx < len(task_list) else ""
return {
"completed_tasks": completed,
"current_task": next_task
}
def _should_continue(self, state: AgentState) -> str:
"""判断是否继续执行"""
if state["current_task"] and len(state["completed_tasks"]) < len(state["task_list"]):
return "execute"
return "verify"
def _verify_node(self, state: AgentState) -> AgentState:
"""验证任务完成质量"""
# 调用评估Agent进行验收
return state
def run(self, user_input: str) -> dict:
"""执行编排任务"""
initial_state = {
"messages": [{"role": "user", "content": user_input}],
"task_list": [],
"current_task": "",
"completed_tasks": []
}
final_state = self.graph.invoke(initial_state)
return final_state
5.6 模块六:可观测性(日志与追踪)
使用Langfuse实现全链路追踪,记录每一轮对话和工具调用。
# src/utils/observability.py
from langfuse import Langfuse
from functools import wraps
from datetime import datetime
import json
# 初始化Langfuse
langfuse = Langfuse(
public_key="your-public-key",
secret_key="your-secret-key",
host="http://localhost:3000"
)
def trace_agent(trace_name: str):
"""装饰器:自动追踪Agent执行过程"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
trace = langfuse.trace(name=trace_name)
span = trace.span(name=func.__name__)
try:
result = func(*args, **kwargs)
span.update(output=json.dumps(result)[:500])
return result
except Exception as e:
span.update(level="ERROR", status_message=str(e))
raise
finally:
span.end()
return wrapper
return decorator
class Observability:
"""可观测性管理"""
@staticmethod
def log_tool_call(tool_name: str, arguments: dict, result: str, duration_ms: float):
"""记录工具调用日志"""
langfuse.trace(
name=f"tool.{tool_name}",
metadata={
"arguments": arguments,
"result": result[:200],
"duration_ms": duration_ms
}
)
@staticmethod
def log_conversation(user_message: str, assistant_response: str, model: str):
"""记录对话"""
langfuse.trace(
name="conversation",
metadata={
"model": model,
"user_message": user_message[:500],
"response": assistant_response[:500]
}
)
六、集成与部署
6.1 组装完整的Agent系统
# src/agent.py
from core.loop import AgentLoop
from core.tools import ToolRegistry
from core.memory import MemorySystem
from core.context import ContextManager
from core.orchestrator import TaskOrchestrator
from utils.observability import trace_agent
class MyAgent:
"""完整的Agent系统 — 集成了所有Harness组件"""
def __init__(self, workspace: str = "./workspace"):
self.loop = AgentLoop(workspace_root=workspace)
self.memory = MemorySystem()
self.context_manager = ContextManager()
self.orchestrator = TaskOrchestrator(self.loop)
@trace_agent("agent.chat")
def chat(self, user_input: str) -> str:
"""处理用户输入"""
# 1. 加载相关长期记忆
relevant_memories = self.memory.search_long_term(user_input)
if relevant_memories:
context = f"用户之前的偏好:{relevant_memories[0]['content']}"
self.loop.messages.insert(0, {"role": "system", "content": context})
# 2. 执行Agent循环
response = self.loop.run(user_input)
# 3. 保存到记忆
self.memory.add_session_memory("user", user_input)
self.memory.add_session_memory("assistant", response)
return response
def execute_workflow(self, user_input: str) -> dict:
"""执行复杂工作流(多步骤+多Agent)"""
return self.orchestrator.run(user_input)
# 使用示例
if __name__ == "__main__":
agent = MyAgent(workspace="./my_project")
# 简单对话
result = agent.chat("帮我创建一个README.md文件,内容是'# My Project'")
print(result)
# 复杂工作流
workflow_result = agent.execute_workflow(
"帮我写一个Python脚本,读取data.csv文件,计算平均值,并输出报告"
)
print(workflow_result)
6.2 API服务化
# src/api/routes.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from src.agent import MyAgent
app = FastAPI(title="AI Agent Harness API")
agent = MyAgent()
class ChatRequest(BaseModel):
message: str
session_id: str = None
class ChatResponse(BaseModel):
response: str
session_id: str
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
"""对话接口"""
try:
response = agent.chat(request.message)
return ChatResponse(response=response, session_id=request.session_id or "default")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/workflow")
async def workflow(request: ChatRequest):
"""复杂工作流接口"""
try:
result = agent.execute_workflow(request.message)
return {"status": "success", "result": result}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health():
return {"status": "healthy"}
# 启动服务
# uvicorn src.api.routes:app --reload --port 8000
七、测试与优化
7.1 基础测试
# tests/test_harness.py
import pytest
from src.core.loop import AgentLoop
from src.core.tools import ToolRegistry
def test_agent_loop():
"""测试Agent循环的基本功能"""
loop = AgentLoop()
result = loop.run("请说'你好世界'")
assert result is not None
assert len(loop.messages) > 0
def test_tool_registry():
"""测试工具注册与执行"""
registry = ToolRegistry(workspace_root="./test_workspace")
# 测试文件写入
result = registry.execute("write_file", {"path": "test.txt", "content": "hello"})
assert "文件已写入" in result
# 测试文件读取
result = registry.execute("read_file", {"path": "test.txt"})
assert "hello" in result
def test_path_safety():
"""测试路径围栏安全性"""
registry = ToolRegistry(workspace_root="./test_workspace")
result = registry.execute("read_file", {"path": "../../etc/passwd"})
assert "超出工作区范围" in result
7.2 性能优化建议
- 缓存LLM响应:对于重复的问题,缓存结果减少API调用
- 流式响应:使用stream=True提升用户体验
- 异步处理:将长时间运行的任务放到后台队列
- 上下文压缩:当对话超过阈值时自动触发摘要
# 缓存示例
from functools import lru_cache
class CachedAgentLoop(AgentLoop):
@lru_cache(maxsize=100)
def _cached_completion(self, messages_hash: str):
pass # 实现缓存逻辑
八、总结与进阶
8.1 本文回顾
我们从零开始构建了一个完整的Harness系统,包含:
| 模块 | 功能 | 代码行数 |
|---|---|---|
| Agent Loop | 驱动思考→行动循环 | ~60行 |
| Tool Registry | 工具注册+安全围栏 | ~120行 |
| Memory System | 会话+长期记忆 | ~70行 |
| Context Manager | 滑动窗口+摘要 | ~40行 |
| Task Orchestrator | 多Agent协作 | ~90行 |
| Observability | 追踪日志 | ~50行 |
8.2 下一步进阶方向
- 向量化记忆:用PostgreSQL+pgvector或ChromaDB替代简单的关键词匹配
- MCP协议支持:接入Model Context Protocol标准,让工具互通
- 自修复机制:从工具调用失败中学习,自动生成校验器防止复发
- 生产级部署:使用Kubernetes + Redis + 消息队列实现高可用
- 评估Harness:构建独立的评估Agent,自动验证任务完成质量
8.3 推荐学习资源
- GitHub: learn-claude-code — 12章节完整Harness教程
- LangGraph官方文档 — 多Agent编排框架
- OpenAI Harness Engineering — 官方实践指南
参考资料
- AI Agent Harness Engineering 入门实战 — CSDN博客
- 2小时速通 Harness 工程 — CSDN博客
- 如何构建你自己的 Harness — 腾讯云开发者社区
- Building a Production-Ready AI Agent Harness — DEV Community
- 从0搭建一个Claude Code — 知乎专栏
超级会员免费看

427

被折叠的 条评论
为什么被折叠?



