
1. 为什么 Agent 开发需要“驾驶工程”1.1 从大模型到 Agent2024 年到 2026 年大模型应用已经从“单轮对话”走向“多步骤任务执行”。你会发现单纯调用大模型 Chat 接口只能获得一段文字但真实业务需要的往往是“查询用户信息 → 分析当前学习进度 → 调用一个代码检测工具 → 生成个性化建议 → 自动更新学习记录”这样的完整闭环。这不再是一段 Prompt 能解决的而是需要一个“程序化调度 模型决策 工具执行 结果反馈”的运行时环境。也正因如此Agent 成了 AI 领域的高频词。但很多开发者最初的 Agent 实现其实就是把多个工具函数塞进 System Prompt再用函数调用Function Calling循环执行。这个方案在小 Demo 里没问题一旦工具变多、上下文变长、用户需求复杂工程上很快会遇到上下文溢出、工具调用超时、Agent 陷入死循环、问题难以定位。这时“Harness”这个概念出现在很多 AI Agent 开源项目和技术分享中。它英文原意是“马具、挽具”也有“操纵装置”的意思。在 AI 工程语境里Harness 可以直译为“驾驶系统”或“调度框架”专门负责把模型、工具、上下文、记忆、反馈编排到一起。1.2 Harness 到底是什么用一句话概括Harness 是封装 Agent 运行时的一整套调度与执行框架它决定了 Agent 如何感知上下文、如何调用工具、如何反思和进化。为了便于理解我们可以做一个类比对象比喻对应模块大模型发动机LLM API文档库与数据库燃料和配件RAG、检索、KV 存储RAG 与工具基础设施Tool Executor、Context RetrieverHarness驾驶舱与自动驾驶系统Agent Runtime、编排循环一个典型的 Harness 通常包含以下几个模块工具注册与执行Tool Registry / Execution Provider统一定义工具、参数校验、执行结果返回。上下文组装Context Assembly把系统提示词、用户画像、检索资料、工具描述、对话历史组装成模型输入。反思与修正Self-Reflection让模型观察执行结果判断是否完成任务必要时重新生成下一步动作。计划与执行循环Plan-Execute Loop先规划再执行再观察直到满足终止条件。安全与审批Guardrails / Human-in-the-loop敏感操作加人工审批限制危险工具的默认执行权限。社区里也出现了 codex harness、deepseek harness 这类围绕特定模型的开源封装项目。虽然它们实现细节不同但核心思想是一致的把“怎么调用模型”升级为“如何设计 Agent 的运行系统”。1.3 Harness Engineering、Prompt Engineering、Context Engineering 的区别很多读者会问这不是换了个名字继续做 Prompt 吗这里需要区分三个概念Prompt Engineering关注“如何写一段更好的指令”比如提示词结构、少样本示例、角色设定。Context Engineering关注“如何选择、组织、刷新模型接收到的全部上下文”比如上下文压缩、记忆管理、检索增强。这个概念在 2025 年被提到得越来越多核心是把模型上下文窗口当成一种稀缺资源来精打细算。Harness Engineering关注“如何设计 Agent 的执行循环、工具调度、记忆和进化机制”它比 Prompt 和 Context 的范围更大是一个系统工程。一句话总结Prompt 是“台词”Context 是“剧本素材”Harness 是“舞台调度系统”。三者层层递进缺少任一环Agent 都很难稳定工作。1.4 自我进化 Agent 的三个关键问题“自我进化”听起来很玄乎工程落地其实就是三件事记录把每一轮交互、用户反馈、工具执行结果沉淀到结构化记忆里。分析从失败案例、高分案例中提取可复用策略。更新把新策略写回策略库或上下文模板中并且支持回滚和灰度。自我进化的目标不是让模型参数“变强”而是让 Agent 系统逐步积累领域经验。举个例子一个学习助手第一次给用户讲解 Python 循环时可能太抽象用户反馈“听不懂”后系统自动把“先举生活例子再给代码”写入教学策略下次讲解就采用新策略。这就是最朴素的进化闭环。2. “学习助手”需求分析与系统拆解2.1 功能需求本文案例是一个“面向编程初学者的学习助手”核心功能如下回答问题支持 Python、Java、SQL 等常见编程语言的基础问题。讲解代码对用户粘贴的代码逐行解释并指出易错点。记录进度保存用户学习过的话题、练习正确率、易错知识点。个性化反馈根据用户历史学习记录动态调整讲解风格和练习难度。自我进化根据用户每次反馈自动更新教学策略并将策略写入系统记忆。一句话总结它不是一个“聊天机器人”而是一个有记忆、有策略、能基于反馈持续优化的 Agent 系统。2.2 系统架构整体架构分为五层我用下面这个简化的分层流程来说明用户输入 ↓ [1. Harness 调度核心] ↓ [2. 上下文组装器] ← 从记忆仓库读取用户画像、复习历史、推荐策略 ↓ [3. LLM 决策层] → 决定是直接回答还是调用工具还是进入反思修正 ↓ [4. 工具执行层] → 代码运行、知识检索、题库生成、进度更新 ↓ [5. 反馈与进化层] → 用户点赞/点踩 → 更新策略与记忆 ↓ 返回结果这里的关键设计是LLM 不直接操作数据库和业务逻辑而是通过工具接口访问外部资源。所有工具调用都由 Harness 调度统一记录日志方便后续分析和进化。2.3 技术选型为了让案例不依赖特定云服务也方便你在本地运行我采用以下技术栈开发语言Python 3.10LLM 接入OpenAI 兼容协议也可以用 DeepSeek、Qwen 等提供兼容接口的模型本地存储JSON 文件便于观察和调试工具调用自研轻量级 Tool Registry不引入重依赖命令行交互标准 input / print这种选型的好处是“逻辑透明”。你不需要搭一套复杂的 MCP 或函数调用框架也能把 Harness 的核心原理跑通后续迁移到 MCP、LangGraph 等成熟框架时思路完全可以复用。2.4 项目目录设计我建议按以下目录组织项目learning-assistant-harness/ ├── main.py # 命令行入口 ├── harness.py # Harness 调度核心 ├── context_builder.py # 上下文组装器 ├── llm.py # LLM 客户端封装 ├── memory.py # 用户记忆与持久化 ├── tools.py # 工具注册与执行 ├── evolution.py # 自我进化策略模块 ├── data/ │ └── user_memory.json # 用户画像和记忆文件 └── requirements.txt # 依赖清单代码不多但每个文件承担一个清晰的职责。下面我们先拆解核心原理再进入完整代码开发。3. 核心原理拆解上下文工程与 Harness3.1 上下文工程决定 Agent 上限和单轮 Prompt 不同Agent 的上下文是“动态变化的”。每一轮工具调用后的结果、每一条用户反馈都可能改变下一轮模型的输入。上下文工程要解决的核心问题是如何用有限的上下文窗口传递最关键的决策信息以学习助手为例每一轮对话的上下文组装顺序大致如下系统提示词固定角色、行为边界、安全约束。动态策略从 user_memory.json 中读取“教学策略片段”由进化模块维护。用户画像用户昵称、学习阶段、最近学习主题、易错点。对话历史摘要近三轮左右的对话摘要替换完整历史防止上下文爆炸。工具描述告诉模型当前有哪些工具可用参数分别是什么。当轮用户输入最新问题或代码。这种组装不是简单拼接而是一个“预算管理”过程。我在工程实现中会用 token 预算控制先放最重要的字段超限时丢弃最早的对话历史。3.2 Harness 工作流从指令到执行的闭环Harness 内部是一个循环核心流程可以概括为五个步骤接收输入拿到用户消息。组装上下文调用 Context Builder 生成模型请求。模型决策LLM 返回两种结果——直接回答或者请求调用某个工具。执行工具Harness 校验参数、执行工具、把结果追加到消息列表。循环判断若 LLM 判定任务已完成或达到最大轮次则输出最终回答否则回到第 2 步。每一步都对应日志记录。调试 Agent 时最重要的就是能完整看到“模型看到了什么、调用了什么、结果是什么”。3.3 自我进化机制把经验沉淀成策略自我进化需要一个“策略库”。在学习助手里策略库就是一个结构化的教学策略字典{ version: 3, max_difficulty: 5, explain_style: life_example_first, use_quizzes: true, recent_lessons: [while_loop, list_comprehension], user_feedback_tags: [prefers_simple_explanation] }进化模块的工作方式是用户点击“这个讲解很有帮助”时将策略权重 1用户点击“太难了/没听懂”时触发record_negative_feedback并记录失败关键词当某个关键词累计反馈超过阈值进化模块会向 LLM 发送一次“策略优化请求”让模型生成新的教学策略片段新策略写入前会保存旧策略备份便于回滚。这就是一个最小可用的“自我进化链路”。4. 完整开发实战命令行学习助手4.1 环境准备与项目初始化建议使用 Python 3.10 以上版本创建虚拟环境后安装requestsmkdir learning-assistant-harness cd learning-assistant-harness python -m venv venv source venv/bin/activate pip install requests如果你的模型需要额外 SDK比如 OpenAI SDK就额外安装。为了降低环境差异本次示例默认使用 HTTP 调用 OpenAI 兼容接口你只需要替换base_url、api_key、model即可。4.2 LLM 客户端封装文件llm.py我封装一个简单的LLMClient核心是提供chat()方法。因为不同服务商的兼容接口参数不完全一样所以这里只保留最通用的字段并允许通过options透传。import urllib.request import json import os class LLMClient: def __init__(self, base_urlNone, api_keyNone, modelNone): self.base_url base_url or os.getenv(LLM_BASE_URL, https://api.example.com/v1) self.api_key api_key or os.getenv(LLM_API_KEY, your-api-key) self.model model or os.getenv(LLM_MODEL, your-model-name) def chat(self, messages, temperature0.3): payload { model: self.model, messages: messages, temperature: temperature, } req urllib.request.Request( urlf{self.base_url}/chat/completions, datajson.dumps(payload).encode(utf-8), headers{ Content-Type: application/json, Authorization: fBearer {self.api_key} }, methodPOST, ) with urllib.request.urlopen(req, timeout60) as resp: data json.loads(resp.read().decode(utf-8)) return data[choices][0][message][content]说明这里用urllib.request是为了避免额外 SDK 依赖。真实项目中你完全可以用 OpenAI SDK、Anthropic SDK 或 LangChain只要把chat()的返回值保持字符串即可。4.3 用户记忆模块文件memory.py记忆模块负责读写data/user_memory.json。核心功能包括读取用户画像、添加学习记录、记录反馈标签。import json import os from datetime import datetime class MemoryStore: def __init__(self, pathdata/user_memory.json): self.path path os.makedirs(os.path.dirname(path), exist_okTrue) self.data self._load() def _load(self): if os.path.exists(self.path): with open(self.path, r, encodingutf-8) as f: return json.load(f) return { user_profile: { name: 小明, level: beginner, favorite_topics: [], weak_points: [], recent_topics: [] }, teaching_strategy: { version: 1, explain_style: step_by_step, use_quizzes: True, max_examples: 2 }, memory_episodes: [] } def save(self): with open(self.path, w, encodingutf-8) as f: json.dump(self.data, f, ensure_asciiFalse, indent2) def get_profile(self): return self.data[user_profile] def get_strategy(self): return self.data[teaching_strategy] def update_strategy(self, new_strategy): old dict(self.data[teaching_strategy]) new_strategy[version] old.get(version, 1) 1 new_strategy[_backup] old self.data[teaching_strategy] new_strategy self.save() def add_episode(self, etype, content): self.data[memory_episodes].append({ type: etype, content: content, ts: datetime.now().isoformat() }) if len(self.data[memory_episodes]) 200: self.data[memory_episodes] self.data[memory_episodes][-200:] self.save()在这个模块里“记忆”被明确分成了三层用户画像、教学策略、事件流。这样的设计可以让后续“进化策略”只更新teaching_strategy而不会污染用户历史记录。4.4 工具注册与执行文件tools.py工具层是 Harness 中最容易被低估的部分。我采用一个简单的注册表模式工具名称 → 描述 执行函数。这样模型可以通过函数名调用工具Harness 也能统一校验。# 工具注册表 TOOL_REGISTRY {} def register_tool(name, description, handler): TOOL_REGISTRY[name] { description: description, handler: handler } def get_tool_descriptions(): return [ f- {name}: {meta[description]} for name, meta in TOOL_REGISTRY.items() ] def run_tool(name, args_str): if name not in TOOL_REGISTRY: return f[工具错误] 未知工具: {name} handler TOOL_REGISTRY[name][handler] try: return handler(args_str) except Exception as e: return f[工具执行异常] {str(e)} # ---- 具体工具实现 ---- def _web_search(query): # 这里演示一个占位实现真实项目可改为 RAG 检索或搜索 API return f关于「{query}」的参考要点先掌握核心概念再动手写最小示例最后做练习巩固。 def _calc_study_plan(args_str): # 输入格式: topic,days try: topic, days args_str.split(,) daily_minutes 30 plan f针对《{topic.strip()}》的 {int(days)} 天计划每天学习 {daily_minutes} 分钟前 2 天看概念中间 2 天做练习最后 1 天总结。 return plan except Exception: return 参数格式错误示例topic,7 def _show_quiz(topic): return f练习题《{topic}》中下面哪段代码会输出 [1, 2, 3] A. range(1,4) B. range(3) C. [1,2,3].append(4)。答案A register_tool(web_search, 搜索知识点相关资料参数为查询关键词, lambda q: _web_search(q.strip())) register_tool(calc_study_plan, 根据学习主题和天数生成学习计划参数格式topic,days, _calc_study_plan) register_tool(show_quiz, 生成一道当前主题的练习题参数为主题名, lambda t: _show_quiz(t.strip()))这是一种“轻量级 Function Calling”实现。如果你使用 OpenAI 官方 Function Calling 格式可以在工具描述中补充 JSON Schema让模型以结构化参数请求工具原理是相同的。4.5 上下文组装器文件context_builder.py上下文组装是上下文工程的核心实现。我通过一个build_messages()方法把“系统提示词 教学策略 用户画像 最近事件 工具描述 当前输入”拼成一个 messages 列表。from memory import MemoryStore from tools import get_tool_descriptions SYSTEM_TEMPLATE 你是一位经验丰富的编程学习助手负责辅导初学者学习编程知识。 请遵守以下原则 1. 始终使用中文回答 2. 讲解时先给结论再给示例最后给练习建议 3. 如果用户代码有问题先指出错误再给出正确写法 4. 必须明确说明可用的工具但只有在确有必要时才调用工具 5. 严禁编造工具执行结果工具结果未返回时不得假装已执行 当前教学策略 {strategy} 当前用户画像 {profile} 可用工具 {tools} class ContextBuilder: def __init__(self, memory: MemoryStore): self.memory memory def build_messages(self, user_input: str): profile self.memory.get_profile() strategy self.memory.get_strategy() tools_desc \n.join(get_tool_descriptions()) system_prompt SYSTEM_TEMPLATE.format( strategystrategy, profilejson.dumps(profile, ensure_asciiFalse, indent2), toolstools_desc ) messages [ {role: system, content: system_prompt} ] # 追加最近 3 条记忆事件帮助模型感知上下文 episodes self.memory.data[memory_episodes][-3:] for ep in episodes: messages.append({role: user, content: f[历史事件] {ep[content]}}) messages.append({role: user, content: user_input}) return messages代码里值得注意的点是我使用了“近 3 条事件 系统提示词”的方式而不是把全部历史对话原样带进上下文。这是上下文工程里常见的信息压缩策略防止上下文无限膨胀。4.6 Harness 调度核心文件harness.py现在进入最重要的部分。Harness 的职责包括调用上下文构建器、请求模型、解析模型输出中的工具调用意图、执行工具、把结果回传给模型直到模型给出最终答案。import json from llm import LLMClient from context_builder import ContextBuilder from tools import run_tool class LearningAssistantHarness: def __init__(self, llm: LLMClient, context_builder: ContextBuilder): self.llm llm self.context_builder context_builder self.max_iterations 5 def execute(self, user_input: str) - str: messages self.context_builder.build_messages(user_input) iteration 0 while iteration self.max_iterations: iteration 1 # 第一步模型决策 model_output self.llm.chat(messages, temperature0.2) # 第二步判断是否需要调用工具 tool_call self._parse_tool_call(model_output) if tool_call is None: # 模型给出了直接回答返回给用户 return model_output tool_name, tool_args tool_call print(f[Harness] 第 {iteration} 轮调用工具: {tool_name}({tool_args})) # 第三步执行工具 tool_result run_tool(tool_name, tool_args) # 第四步将工具结果追加到消息列表让模型继续决策 messages.append({role: assistant, content: f我想使用工具: {tool_name}({tool_args})}) messages.append({role: user, content: f[工具结果] {tool_result}请根据结果继续回答若已完成任务则直接输出最终回答。}) return 抱歉当前任务步骤较多已达到最大执行轮次我为你输出当前进度摘要。 def _parse_tool_call(self, output: str): 简化解析识别形如 TOOL_CALL: tool_name(args) 的文本。 真实项目中可替换为 Function Calling 结构化解析。 if TOOL_CALL: in output: try: part output.split(TOOL_CALL:)[1].strip() name part.split(()[0].strip() args part.split((, 1)[1].rsplit(), 1)[0].strip() return name, args except Exception: return None return None这里有一个很关键的设计我通过TOOL_CALL:这样的纯文本协议让模型表达调用意图而不是直接依赖平台的 Function Calling。这样做的好处是“对模型提供方零依赖”任何支持中文对话的模型都能运行坏处是需要自己处理格式解析真实生产项目建议换成结构化工具调用或 MCP 协议。4.7 自我进化模块文件evolution.py进化模块的核心是“根据用户反馈更新策略”。这里我采用规则 少量 LLM 再总结的方式当用户连续两次给负面反馈时自动调整策略中的explain_style。当用户提问“太难”时记录 weak_points。每次策略更新前使用update_strategy()保存旧策略备份。from memory import MemoryStore class EvolutionEngine: def __init__(self, memory: MemoryStore, llmNone): self.memory memory self.llm llm # 可选用于高级策略生成场景 def record_feedback(self, feedback: str, question: str ): feedback 可以是 positive / negative / too_hard / too_simple self.memory.add_episode(feedback, f{feedback} | {question}) profile self.memory.get_profile() if feedback in (negative, too_hard): if question: profile[weak_points].append(question) # 去重并最多保留 20 个弱点 profile[weak_points] list(dict.fromkeys(profile[weak_points]))[-20:] self._maybe_evolve_strategy() self.memory.save() def _maybe_evolve_strategy(self): episodes self.memory.data[memory_episodes] negative_count sum( 1 for ep in episodes[-10:] if ep[type] feedback and ep[content].startswith((negative, too_hard)) ) strategy self.memory.get_strategy() # 连续负面反馈较多时自动降低讲解抽象度 if negative_count 2: strategy[explain_style] life_example_first strategy[max_examples] min(strategy.get(max_examples, 2) 1, 5) self.memory.update_strategy(strategy) self.memory.add_episode(evolution, 策略更新负面反馈较多改为生活化示例优先)这里的进化逻辑虽然简单但已经形成闭环用户反馈 → 记忆写入 → 策略更新 → 下次上下文组装采用新策略。真实项目还可以把“弱项复习提醒”“推荐下一步学习路径”纳入进化引擎。4.8 主入口文件main.py主入口负责组装各个模块并提供一个简单的命令行交互环境。import os from llm import LLMClient from memory import MemoryStore from context_builder import ContextBuilder from harness import LearningAssistantHarness from evolution import EvolutionEngine def main(): memory MemoryStore() llm LLMClient() context_builder ContextBuilder(memory) harness LearningAssistantHarness(llm, context_builder) evolution EvolutionEngine(memory, llm) print(你好我是编程学习助手。输入问题开始学习输入 quit 退出。) print(常用反馈命令/positive /negative /too_hard /too_simple\n) while True: user_input input(\n你: ).strip() if user_input.lower() in (quit, exit): print(再见) break if user_input.startswith(/): evolution.record_feedback(user_input[1:]) print(已记录反馈后续将优化讲解策略。) continue answer harness.execute(user_input) print(f\n助手: {answer}) if __name__ __main__: main()运行方式很简单python main.py4.9 运行与验证启动后可以尝试以下交互流程你: 什么是 Python 的 while 循环 助手: 先给结论再给示例...如果模型没有正确调用工具你可以通过增加 prompt 示例来引导。比如在系统提示词中加入当你需要搜索资料时请输出 TOOL_CALL: web_search(关键词)然后在下一行输出最终回答。运行日志中能看到每个工具调用步骤这是学习 Harness 排错的关键。5. 常见问题与排查思路问题现象常见原因解决思路工具执行超时网络请求慢、模型返回慢、Execution Provider 未响应设置超时时间增大超时阈值检查网络将工具执行改为异步模型一直不调用工具系统提示词中工具描述不清晰增加明确指令给出一个 TOOL_CALL 调用示例模型调用不存在的工具工具名拼写错误或参数格式不合法使用结构化 Function Calling增加参数 Schema 校验上下文越来越长每轮都拼接完整历史做摘要压缩只保留最后 N 轮和关键记忆自我进化后回答质量反而下降策略更新过度拟合负面反馈设置回滚机制进化前保存策略快照对策略更新加人工审批出现“the agent execution provider did not respond in time”类似报错工具执行系统响应超时或者子进程阻塞检查工具进程是否被锁增加执行超时控制把耗时操作切到独立任务队列以上问题大部分都能通过“日志 超时控制 记忆隔离”解决。我建议在开发阶段为 Harness 的每一轮都打印以下信息当前轮次模型输入的消息条数和 token 估算模型是否请求工具工具执行结果是否达到终止条件有了这些日志Agent 就不再是“黑盒”。6. 最佳实践与工程建议6.1 上下文工程设计要点优先级排序系统提示词优先动态策略其次用户画像再次历史摘要最后。当 token 不足时优先裁剪历史摘要和早期对话。动态策略独立教学策略不写在固定 System Prompt 里而是作为独立 JSON 保存。这样可以在不修改代码的前提下调整策略也为 A/B 测试留出空间。工具描述精简工具描述不要写成长篇文档模型只需要知道“工具名 参数 返回值含义”即可。6.2 安全边界与最小权限学习助手涉及代码讲解可能还会执行用户代码。这里必须强调任何代码执行工具都必须放在沙箱或只读环境中生产环境不允许用本地开发环境直接运行用户代码。我建议的安全设计代码解释工具默认只读禁止写文件。高风险工具需要人工确认。记忆文件按用户隔离避免用户 B 读取到用户 A 的学习记录。Agent 的进化策略更新必须保留备份支持一键回滚。6.3 可观测性与日志记录Harness 工程和传统后端开发的关键差异在于Agent 的“异常”不一定是崩溃更多是“逻辑方向偏移”。比如模型连续调用同一工具 5 次或工具结果明明失败却仍然输出“成功”。这些情况必须依赖完整的 trace 日志来判断。日志至少需要包含每次 LLM 请求的完整消息体工具调用与返回结果策略版本号反馈事件的来源6.4 Agent 开发学习路线如果你想往更深的 Agent 工程方向学习建议按以下路径推进掌握基础 LLM API 接入与 Prompt 设计。理解上下文工程包括记忆压缩、RAG、上下文缓存。实现一个最小 Harness理解工具调度和循环机制。学习生产级 Agent 框架如 LangGraph、MCP 等。研究自我进化机制包括策略搜索、指令蒸馏、失败样例回放。关注安全与评估建立 Agent 自动化评测集。7. 总结与下一步通过本文的案例我们完成了基于 Harness 思路的学习助手开发核心收获有三点第一Harness 不是一套神秘的高深架构而是一种工程化思维把模型、工具、记忆、反馈看作一个可编排的系统明确每一步的输入和输出。第二上下文工程是 Agent 质量的“隐藏杠杆”同样的模型上下文组织得好不好效果差距很大。第三自我进化必须建立在结构化记忆和可控回滚的基础上否则系统会越改越偏。下一步你可以先把这个命令行学习助手跑起来观察日志中每一轮模型决策的变化然后尝试把工具层替换成真实的 RAG 检索或代码沙箱最后再引入 Function Calling 和 MCP 协议把 Harness 从“最小实现”升级成“生产可用方案”。代码里还有很多可以扩展的设计点比如把数据存储从 JSON 换成 SQLite、增加用户权限、加入评估集来量化进化效果。希望这篇文章能成为你进入 Harness 工程与 Agent 开发的起点。如果过程中遇到问题欢迎在评论区交流我也会继续分享 Agent 实战中的踩坑经验。