Harness Engineering 背景介绍

1. 概念起源

"Harness"(套具/挽具) 这个词最早来自:

领域 含义

马具

控制马的装备,让马的力量被正确引导

电气工程

测试套具(Test Harness)- 连接被测系统与测试设备的中间层

软件测试

自动测试框架,负责执行测试用例并收集结果

在 AI Agent 领域,Harness 沿用了同样的隐喻:

Harness = 控制 AI 能力的"挽具",让 AI 的输出被正确引导和执行


2. 发展脉络

阶段一:纯 API 调用(2022-2023)

用户 → LLM API → 返回文本 → 结束

  • 模型直接输出结果
  • 无法执行真实操作
  • 应用场景有限
阶段二:工具调用 Function Calling(2023)

用户 → LLM API → 决定调用工具 → Harness 执行 → 返回结果 → 结束

  • 模型开始具备"调用工具"的能力
  • 但缺乏循环和状态管理
阶段三:完整 Agent Harness(2023-至今)

用户 → [模型思考 → Harness 执行 → 模型思考 → Harness 执行 → ...] → 完成

  • 循环直到任务完成
  • 完整的上下文管理
  • 多工具协作
  • 错误恢复机制

为什么需要 Harness?

核心问题:模型"不是"万能的
模型能做的 模型实际不能做的

理解自然语言

真正读取你的文件

生成代码

真正运行代码

推理和规划

真正访问互联网

写回答

真正修改数据库

Harness 的价值: 把"说"和"做"分离,模型负责决策,Harness 负责执行。

模型只负责"思考和决策",Harness 负责真正执行、管理上下文、控制循环。
┌─────────────────────────────────────────────────────────────┐
│                        Harness                               │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              工具注册表 (TOOL_FUNCTIONS)             │    │
│  │  - read_file()  - calculate()  - ...                │    │
│  └─────────────────────────────────────────────────────┘    │
│                            ▲                                  │
│                            │ 调用工具                          │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                  Agent Loop (循环)                    │    │
│  │  1. 发消息给模型                                       │    │
│  │  2. 模型决定: 调用工具 or 给出答案                     │    │
│  │  3. 如果调用工具 → Harness 执行 → 返回结果             │    │
│  │  4. 循环回到第1步                                      │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
                    ┌───────────────┐
                    │   AI Model    │
                    │   (Claude)    │
                    │  "思考和决策"  │
                    └───────────────┘

以下是代码示例:

"""
最小 Agent Harness 示例
核心思想:模型只负责"思考和决定调用什么工具",
harness 负责真正执行工具、管理上下文、控制循环。
"""

import json
from anthropic import Anthropic

client = Anthropic()  # 需要设置 ANTHROPIC_API_KEY 环境变量


# ---------- 1. 工具定义 ----------
# harness 里真正干活的函数。模型自己不能读文件、不能算数,
# 它只能"请求"harness 帮它做,这就是工具存在的意义。

def read_file(path: str) -> str:
    """读取一个文本文件的内容。"""
    try:
        with open(path, "r", encoding="utf-8") as f:
            return f.read()
    except Exception as e:
        return f"读取失败: {e}"


def calculate(expression: str) -> str:
    """计算一个数学表达式,比如 '2 * (3 + 4)'。"""
    try:
        # 生产环境别用 eval,这里只是演示。用 ast 或专用库更安全。
        result = eval(expression, {"__builtins__": {}}, {})
        return str(result)
    except Exception as e:
        return f"计算失败: {e}"


# 工具的注册表:把函数名映射到真正的 Python 函数
TOOL_FUNCTIONS = {
    "read_file": read_file,
    "calculate": calculate,
}

# 工具的 schema:告诉模型有哪些工具、怎么调用、参数是什么。
# 这段描述写得好不好,直接影响模型用不用得对工具。
TOOL_SCHEMAS = [
    {
        "name": "read_file",
        "description": "读取指定路径的文本文件内容",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "文件路径"}
            },
            "required": ["path"],
        },
    },
    {
        "name": "calculate",
        "description": "计算一个数学表达式并返回结果",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "要计算的表达式,如 '2 * (3 + 4)'",
                }
            },
            "required": ["expression"],
        },
    },
]


# ---------- 2. Agent Loop ----------
# 这是 harness 的心脏。

def run_agent(user_task: str, max_turns: int = 10) -> str:
    # messages 就是"上下文"。harness 的核心工作之一
    # 就是决定这个列表里放什么、怎么增长。
    messages = [{"role": "user", "content": user_task}]

    for turn in range(max_turns):
        # (a) 把当前上下文 + 工具列表发给模型,让它决定下一步
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            tools=TOOL_SCHEMAS,
            messages=messages,
        )

        # (b) 把模型这一轮的输出加回上下文(关键:保持对话连续)
        messages.append({"role": "assistant", "content": response.content})

        # (c) 判断模型是想调用工具,还是已经给出最终答案
        if response.stop_reason != "tool_use":
            # 模型没有要求调工具 → 认为任务完成,返回文本
            final_text = "".join(
                block.text for block in response.content
                if block.type == "text"
            )
            return final_text

        # (d) 模型要求调用一个或多个工具 → harness 真正执行它们
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                fn = TOOL_FUNCTIONS[block.name]
                print(f"  [调用工具] {block.name}({block.input})")
                result = fn(**block.input)          # 真正执行
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,        # 对应上模型的请求 id
                    "content": result,
                })

        # (e) 把工具执行结果作为一条 user 消息喂回去,进入下一轮
        messages.append({"role": "user", "content": tool_results})

    return "达到最大轮次,未完成任务。"


# ---------- 3. 跑起来 ----------
if __name__ == "__main__":
    answer = run_agent("帮我算一下 (128 + 256) * 3 等于多少")
    print("\n最终回答:", answer)

1. 工具定义(Lines 13-70)
 

TOOL_FUNCTIONS = {
    "read_file": read_file,
    "calculate": calculate,
}

为什么需要工具?

  • 模型本身不能真正读取文件、做计算
  • 它只能"请求"Harness 帮它做
  • 工具 = 模型与真实世界交互的桥梁
    TOOL_SCHEMAS = [
        {
            "name": "read_file",
            "description": "读取指定路径的文本文件内容",
            "input_schema": {...}
        },
        ...
    ]

    Schema 的重要性: 描述写得越好,模型用工具越准确。

2. Agent Loop(Lines 73-118)

这是 Harness 的心脏:

for turn in range(max_turns):
    # (a) 发消息给模型,让它决定下一步
    response = client.messages.create(...)
    
    # (b) 把模型输出加回上下文
    messages.append({"role": "assistant", "content": response.content})
    
    # (c) 判断是调用工具还是给出答案
    if response.stop_reason != "tool_use":
        return final_text  # 完成!
    
    # (d) Harness 真正执行工具
    for block in response.content:
        if block.type == "tool_use":
            result = fn(**block.input)  # 执行!
    
    # (e) 把结果喂回去,进入下一轮
    messages.append({"role": "user", "content": tool_results})

3. 运行示例(Lines 122-125)

answer = run_agent("帮我算一下 (128 + 256) * 3 等于多少")
执行流程:

模型收到任务 → "我需要调用 calculate 工具"
Harness 执行 → calculate("(128 + 256) * 3") → "1152"
模型收到结果 → 给出最终回答:"答案是 1152"

总结

Harness Engineering 的兴起是 LLM 应用成熟的必然结果:

  1. 从玩具到生产力 - LLM 需要真正做事,而不只是说话
  2. 安全与可控 - 把危险操作隔离在 Harness 层
  3. 可靠性保障 - 模型可能出错,Harness 需要兜底
  4. 工程化需求 - 可测试、可监控、可迭代
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐