上篇我们搭了第一个 Agent,60 行跑通了「思考→调用工具→生成回答」的循环。
但如果你真的拿去用几天,会发现一堆问题:
- 搜到一半 API 超时,整个程序崩了
- 模型连续调用工具 10 次不回答,Token 烧光
- 搜索工具返回了乱码,Agent 开始胡说八道
- 跑了一个小时,不知道中间发生了什么
这些问题的根因是:你的 Agent 只有『应用程序』级别的代码(调用模型→返回结果),没有『操作系统』级别的能力来管资源、管异常、管边界。
这个『操作系统』就是 Harness。
今天这篇是系列第 02 篇,也是第二阶段的开门之作。我们从零给上篇的 Agent 装上 Harness,让它能扛住真实场景。
一、Harness 到底管什么
Harness 不是某个复杂框架,而是一组让你 Agent 可靠运行的工程机制。类比真实操作系统:
没有 Harness 的 Agent 就像没有操作系统的裸机------能通电,但随时蓝屏。
二、从第一行代码看问题
回顾上篇 Agent 的核心逻辑:
ini
# ======================================================================
# [问题] 上篇代码没有任何保护措施
# ======================================================================
response = client.chat.completions.create(...) # 网络超时?直接崩
msg = response.choices[0].message # response 为 None?崩溃
if msg.tool_calls:
result = web_search(args["query"]) # 搜索抛异常?直接崩
# 没有最大循环限制 -> 模型可以无限调工具,Token 烧光
# 没有日志 -> 跑完不知道它做了什么
# 没有降级 -> 工具挂了,整个 Agent 也挂了
在实际场景中,上面每一行都可能崩。
三、Harness 核心组件实现
我们从最常用的 5 个组件开始,逐个加到 Agent 身上。
1. 重试机制(Retry)
网络抖动、API 限流是家常便饭。重试要带退避(Backoff),避免把服务器打炸:
python
import time
import random
# ================================================================
# 组件 1: 重试机制(Retry)-- 网络抖动?给我再试一次
# ================================================================
def retry_on_fail(func, max_retries=3, base_delay=1):
"""
带指数退避(Exponential Backoff)的重试包装器
参数:
func: 要执行的函数
max_retries: 最大重试次数(默认 3 次)
base_delay: 初始等待秒数(每次翻倍)
效果:
失败 1 次等 1s,失败 2 次等 2s,失败 3 次等 4s ...
"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise
delay = base_delay * (2 ** attempt) + random.uniform(0, 0.5)
print(
f"[attempt {attempt + 1}/{max_retries}] "
f"failed: {e}, retry in {delay:.1f}s..."
)
time.sleep(delay)
2. 超时控制(Timeout)
模型或工具可能无响应,不能无限等下去。用 signal 或 Thread 加超时:
python
import signal
# ================================================================
# 组件 2: 超时控制(Timeout)-- 不能无限等下去
# ================================================================
class TimeoutError(Exception):
"""自定义超时异常"""
pass
def _timeout_handler(signum, frame):
"""SIGALRM 信号处理函数:直接抛异常"""
raise TimeoutError("操作超时")
def with_timeout(func, timeout_sec=30):
"""
给任意函数加超时保护(基于 SIGALRM)
参数:
func: 要执行的函数
timeout_sec: 超时秒数(默认 30s)
返回:
函数执行结果,超时则返回 None
"""
signal.signal(signal.SIGALRM, _timeout_handler)
signal.alarm(timeout_sec)
try:
return func()
except TimeoutError:
print(f"[timeout] operation exceeded {timeout_sec}s, terminated")
return None
finally:
signal.alarm(0)
3. 最大步数限制(Max Steps)
最容易被忽略的陷阱------模型可能陷入「思考→调工具→再思考→再调工具」的死循环。必须设硬上限:
python
# ================================================================
# 组件 3: 最大步数限制(Max Steps)-- 防死循环的保险丝
# ================================================================
def run_with_step_limit(agent_func, max_steps=5):
"""
限制 Agent 最大执行步数,防止陷入死循环
参数:
agent_func: Agent 单步执行函数,返回 {"done": bool, ...}
max_steps: 最大允许步数(默认 5 步)
返回:
正常完成 -> Agent 结果
超出限制 -> 错误字典 + 部分结果
"""
last_result = None
for step in range(max_steps):
last_result = agent_func()
if last_result and last_result.get("done"):
return last_result
print(f"[max_steps] reached limit ({max_steps}), force terminating")
return {
"error": "max_steps_exceeded",
"partial": last_result,
}
4. 日志追踪(Logging)
不知道 Agent 做了什么,等于没有调试能力。每步操作留下结构化记录:
python
import json
from datetime import datetime
# ================================================================
# 组件 4: 日志追踪(Logging)-- 每一步都看得见
# ================================================================
class AgentLogger:
"""结构化 Agent 日志器,记录每一步操作"""
def __init__(self):
self.logs = []
def log(self, event, detail):
"""
记录一条日志。
event: 事件类型(如 TOOL_CALL / MODEL_RESPONSE / TIMEOUT)
detail: 事件详情(字符串或字典)
"""
entry = {
"timestamp": datetime.now().isoformat(),
"event": event,
"detail": detail,
}
self.logs.append(entry)
if isinstance(detail, dict):
detail_str = json.dumps(detail, ensure_ascii=False)
else:
detail_str = str(detail)
print(f"[log] [{event}] {detail_str[:100]}")
def dump(self):
"""导出完整日志(列表形式)"""
return self.logs
# ===== 使用示例 =====
logger = AgentLogger()
logger.log(
"TOOL_CALL",
{"tool": "web_search", "query": "AI news 2026"},
)
logger.log(
"MODEL_RESPONSE",
{"tokens": 256, "finish_reason": "stop"},
)
5. 降级策略(Fallback)
核心工具挂了不等于整个 Agent 要崩。准备好降级方案:
python
# ================================================================
# 组件 5: 降级策略(Fallback)-- 工具挂了,Agent 不能挂
# ================================================================
def safe_tool_call(func, fallback_result=None):
"""
工具调用安全包装:重试 -> 失败 -> 降级
参数:
func: 要执行的工具函数
fallback_result: 降级时的替代返回值(可选)
执行链:
func -> 重试(最多 3 次)-> 降级 -> 返回友好提示
"""
try:
return retry_on_fail(func)
except Exception as e:
print(f"[fallback] tool call failed ({e}), using fallback")
if fallback_result is not None:
return fallback_result
return "(工具暂不可用,请稍后再试)"
四、组装:带 Harness 的 Agent
现在把上面 5 个组件装进上篇的 Agent,升级为带 OS 的版本:
python
# ======================================================================
# Harness Agent -- 完整可运行版本(带操作系统)
# ======================================================================
# 比上篇多了:重试、超时、步数限制、日志追踪、降级策略
# 复制后替换 API Key 即可运行
# ======================================================================
import json
import random
import requests
import signal
import time
from datetime import datetime
from openai import OpenAI
# ======================================================================
# 第一步:配置
# ======================================================================
client = OpenAI(
api_key="your-api-key-here",
base_url="https://api.deepseek.com/v1",
)
MODEL = "deepseek-chat"
# ======================================================================
# 第二步:装配 Harness 组件
# ======================================================================
# ---------- 组件 1: 日志追踪 ----------
class AgentLogger:
"""结构化日志器,记录 Agent 每一步操作"""
def __init__(self):
self.logs = []
def log(self, event, detail):
entry = {
"timestamp": datetime.now().isoformat(),
"event": event,
"detail": detail,
}
self.logs.append(entry)
print(f"[log] [{event}] {str(detail)[:80]}")
def dump(self):
"""导出完整日志(列表形式)"""
return self.logs
harness_log = AgentLogger()
# ---------- 组件 2: 重试机制 ----------
def with_retry(func, retries=3, delay=1):
"""指数退避重试:失败后等待时间呈指数增长"""
for i in range(retries):
try:
return func()
except Exception as e:
if i == retries - 1:
raise
wait = delay * (2 ** i) + random.uniform(0, 0.5)
harness_log.log(
"RETRY",
f"attempt {i + 1}: {e}, retry in {wait:.1f}s",
)
time.sleep(wait)
# ---------- 组件 3: 超时控制 ----------
class TimeoutError(Exception):
"""自定义超时异常"""
pass
def _signal_handler(signum, frame):
"""SIGALRM 信号处理函数:直接抛异常"""
raise TimeoutError("操作超时")
def with_timeout(func, sec=30):
"""SIGALRM 超时保护"""
signal.signal(signal.SIGALRM, _signal_handler)
signal.alarm(sec)
try:
return func()
except TimeoutError:
harness_log.log("TIMEOUT", f"exceeded {sec}s")
return None
finally:
signal.alarm(0)
# ---------- 组件 4: 安全调用(重试 + 超时 + 降级) ----------
def safe_call(func, fallback="(工具暂不可用)"):
"""安全调用链:超时 -> 重试 -> 降级"""
try:
return with_timeout(lambda: with_retry(func))
except Exception as e:
harness_log.log("FALLBACK", str(e))
return fallback
# ======================================================================
# 第三步:定义 Agent 可用工具
# ======================================================================
TOOLS = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "搜索互联网获取最新信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词",
},
},
"required": ["query"],
},
},
},
]
def web_search(query):
"""执行互联网搜索,返回前 3 条结果摘要"""
url = "https://api.bocha.cn/v1/web-search?query=" + query
resp = requests.get(url, timeout=15)
results = resp.json().get("results", [])
lines = [
f"{r['title']}: {r['snippet']}"
for r in results[:3]
]
return "\n".join(lines)
# ======================================================================
# 第四步:组装 Harness Agent
# ======================================================================
def run_agent(user_input, max_steps=5):
"""
带 Harness 的 Agent 主循环。
工作流程:
用户输入
-> 模型思考
-> 是否调工具?
-> 是 -> 安全调用 -> 再思考
-> 否 -> 返回最终答案
"""
messages = [
{
"role": "system",
"content": "你是一个资讯助手,使用 web_search 获取最新信息。",
},
{
"role": "user",
"content": user_input,
},
]
for step in range(max_steps):
harness_log.log("STEP", f"step {step + 1}/{max_steps}")
# --- 安全调用模型(自动重试 + 超时) ---
response = safe_call(
lambda: client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
tool_choice="auto",
)
)
if response is None:
return json.dumps(
{"error": "模型调用失败"},
ensure_ascii=False,
)
msg = response.choices[0].message
messages.append(msg)
harness_log.log(
"FINISH_REASON",
msg.finish_reason or "none",
)
if not msg.tool_calls:
return msg.content
for tc in msg.tool_calls:
harness_log.log(
"TOOL_CALL",
f"{tc.function.name}({tc.function.arguments})",
)
if tc.function.name == "web_search":
args = json.loads(tc.function.arguments)
result = safe_call(
lambda: web_search(args["query"]),
fallback="搜索暂时不可用,请稍后再试",
)
messages.append(
{
"role": "tool",
"tool_call_id": tc.id,
"content": result,
}
)
harness_log.log("MAX_STEPS", f"reached {max_steps} steps limit")
return "已达到最大执行步数,请简化问题后重试。"
# ======================================================================
# 第五步:运行!
# ======================================================================
if __name__ == "__main__":
answer = run_agent("今天AI圈有什么大新闻?")
print()
print("=" * 50)
print("Agent 回答:")
print(answer)
print()
print("=" * 50)
print("Harness 运行日志:")
print(json.dumps(harness_log.dump(), ensure_ascii=False, indent=2))
运行效果
正常运行时输出一切如常。但当遇到 API 超时、工具报错、模型死循环时,Harness 会:
- 自动重试(日志里看到 RETRY)
- 超时中断(TIMEOUT 并返回降级结果)
- 超过 5 步自动终止(MAX_STEPS)
- 每一步都有日志可回溯
这就是『操作系统』级别的保障------你的 Agent 再也不会因为一次网络抖动就全盘崩溃。
五、Harness 组件全景图
至此你的 Agent 已经从『裸机』升级到了『带 OS 的电脑』------不会随便蓝屏了。
六、写在最后
很多团队踩过的坑:模型选最强的,Harness 写最弱的。结果最强模型跑在最差的系统上,体验还不如弱模型配好 Harness。
记住:
Harness 决定了 Agent 在生产环境下能存活多久,模型决定了它回答的质量上限。两者缺一不可。
今天加装了重试、超时、步数限制、降级策略、日志追踪 5 个组件。下一篇,我们解决让每个开发者肉疼的问题------Token 成本,第 03 篇见。
- 第03篇: 上下文工程------吃透 Context,Token 成本降 80%
- 第04篇: 工具调用------让 Agent 真正『动手干活』
- 第05篇: Agent 记忆系统------从『转头就忘』到『过目不忘』
提示:📌 欢迎来到「智能体开发实战」第二阶段!
第一阶段我们用 60 行代码跑通了第一个 Agent,建立了直觉。从这篇开始,我们进入真正的工程世界------每个组件都是生产级的,每一行代码都能直接用。
今天这篇文章的代码不到 120 行,但给 Agent 装上了真正的『操作系统』。这些组件不是一次性的------你会反复使用它们,直到它们变成你的肌肉记忆。
想在生产环境偷懒?直接用 LangGraph / CrewAI 等框架也行。但理解底层原理,出了问题才知道去哪修。
下一篇:上下文工程,带你吃透 Context,把 Token 成本打下来!
提示 :本文由 码农大坚果 出品,欢迎转发分享,转载请注明出处。
参考: LangChain、Anthropic、花叔 Harness Engineering PDF、知识库 concepts/ai-agent-harness | 整理 by 码农大坚果