ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

用Harness Engineering给AI智能体套上可控护栏:框架设计与实践

2026/9/1 23:15:09 拓冰建站 浏览量
用Harness Engineering给AI智能体套上可控护栏:框架设计与实践 简介Harness Engineering实战指南[可运行源码]是一份面向AI编程工程师与软件开发者的实战资料系统讲解如何通过构建约束、规则与环境来引导AI模型更高效生成代码并以Claude Code为例演示落地方法。压缩包共4个文件包含Markdown说明文档、可运行HTML页面、inscode配置文件及gitignore整体仅14KB属于轻量型工具包。目前已累计602人学习适合希望掌握约束工程方法、优化AI编程实践的开发者。内容具体涵盖创建CLAUDE.md文件、配置技能层与护栏层、建立验证反馈循环等关键步骤并提供最小可行Harness清单读者可依照示例快速搭建属于自己的AI编程约束环境从而提升代码生成质量与开发效率。这套方法不仅能提升AI编程的准确性与稳定性也可迁移至常规软件开发流程通过合理定义规则与约束提高整体交付质量。 我先说个背景如果你最近在开发和维护 LLM 应用大概率也会遇到跟我一样的情况——模型能力很强prompt 调得也算顺手但一旦把工具调用权限放开让它真正去操作一个系统出问题的往往是“边界”而不是“能力”。我印象最深的一次是给内部知识库做自动化问答模型在回答“某条数据能否删除”时直接调用了数据库删除工具去模拟执行当场把测试环境的数据清掉了一部分。那次之后我没有继续堆 prompt而是花了两三个周末搭了一套基于 Harness Engineering 思路的轻量框架。Harness Engineering 这里说的不是硬件或车辆测试里的“线束工程”而是 AI 智能体可控性方向的一个系统工程实践。核心思想很朴素不要把希望全押在大模型的自律上而是通过外部工程手段给智能体套上一层明确、可审计、可干预的约束框架。这个词在近两年被越来越多的 Agent 项目组挂在嘴边很多人一开始以为它只是在 prompt 里多写几条规则实际上它是把智能体的行为控制拆成多个可独立运维的环节。这篇博文我会从一个可以运行的源码原型出发讲清楚 Harness Engineering 到底在解决什么问题、核心模块怎么实现、实测数据怎么看以及落地过程中踩过的坑。1. 为什么凭空冒出个 Harness Engineering——从 AI 可控性焦虑说起1.1 失控场景不是段子是我真踩过的坑先还原一下真实场景。我维护的那个知识库问答 Agent早期架构非常简单用户提问Agent 判断是否需要查库需要就生成一条 SQL 或调用工具然后拿到结果再组织答案。听起来没问题问题出在“判断”这件事上。模型在上下文里看到了表结构、字段注释、甚至历史操作日志它会自己推断出很多我压根没打算让它执行的操作。有一次测试同学在对话框里问“这个表里的临时数据是不是可以清掉”模型思考片刻后给出了一段带条件的规划然后真的准备去执行DELETE FROM temp_log WHERE created_at ...。当时我第一反应是继续改 prompt比如加上“禁止删除”“所有写操作需审批”这类描述。实测下来规则能挡住一部分但挡不住完全。因为 prompt 是软约束模型在复杂上下文里很容易把用户的语气、历史会话里的操作偏好、甚至是工具描述里的例子当成更高优先级。这时候我才意识到要保证一只队伍不乱跑光靠喊话是不够的还得修围栏、设门禁、装监控。这就是 Harness Engineering 的工程出发点。1.2 分层边界、做约束、给兜底Harness 的三板斧Harness Engineering 之所以被单独拿出来讲是因为它把“控制智能体行为”这件事从 prompt 技巧提升到了工程架构层面。我自己把它拆成三个动作第一分层边界。智能体不是一个整体黑盒它可以被拆成感知、规划、工具调用、输出生成等阶段。每个阶段之间用明确的接口隔开控制逻辑能够插入到任意环节。第二做约束。约束包括策略层的权限规则例如“哪些工具能调用、哪些参数范围合法、哪些操作必须二次确认”也包括执行层的校验逻辑例如“工具返回的结果是否符合预期格式、是否包含敏感信息”。第三给兜底。兜底是一组在异常情况下能介入的机制比如重试、回退、人工审批、拦截熔断。模型判断失误不可怕可怕的是系统里没有一道能在失误发生时拦住它的闸门。这套思路跟传统软件工程里的权限控制系统、API 网关、输入校验体系高度相似区别在于AI 智能体的行为空间更大、不确定性更高所以 Harness 需要同时处理“应该做什么”和“绝对不能做什么”两件事。明白了这个动机下面我就把可运行的源码原型拆开讲。2. 可运行原型一个 200 行轻量 Harness 框架是怎么搭出来的2.1 整体架构策略、执行、验证三层我搭的这个原型取名light_harness目录结构很简明核心思路是三层分离。策略层负责定义规则比如允许调用哪些工具、哪些操作需要审批执行层负责管理工具调用和模型交互的完整流程验证层负责在工具返回结果后做结构校验和内容过滤。分层设计的好处是每一层都能独立调试。策略层写错了不需要重跑整条 Agent 流程验证层的规则可以单独写单元测试。实际项目里如果只有一两个 Agent分层看起来像是过度设计但一旦 Agent 数量上到十个以上你会发现公共策略和统一验证是维护成本的关键。light_harness/ ├── harness.py # 主控流程串起策略、执行、验证 ├── policies.py # 策略定义含工具权限 ├── validators.py # 输出校验与清洗 ├── agent_core.py # 模拟智能体核心可替换为真实 LLM 调用 ├── tools.py # 演示用工具集 └── main.py # 演示入口2.2 数据模型一次完整的 Agent 交互从哪开始Harness 里最重要的数据模型是HarnessContext。它承载一次完整交互的输入、中间产物、工具调用记录和最终输出。为什么需要这个对象因为可控性要求“每一步都留下痕迹”出了事故能追溯模型陷入死循环时能中断策略层能依据历史上下文做判断。# harness.py from dataclasses import dataclass, field from typing import Any, Dict, List, Optional dataclass class HarnessContext: user_input: str policy_id: str default tool_calls: List[Dict[str, Any]] field(default_factorylist) step_count: int 0 max_steps: int 5 final_output: Optional[str] None error_info: Optional[str] Nonemax_steps是一个很关键的字段。Agent 循环中一旦工具调用失败、重复尝试很容易陷入“循环调用”的泥潭设置最大步数可以保证即使策略没有完全覆盖死循环场景系统也能在可控范围内自动终止。2.3 最小可运行代码骨架下面这段代码是主控流程的核心。我刻意去掉了所有与具体模型厂商相关的逻辑换成统一的llm_chat接口方便你替换成真实的模型服务或本地模型。# harness.py (续) from typing import List, Dict, Any, Optional class LightHarness: def __init__(self, policy, validator): self.policy policy self.validator validator self.llm_client None def bind_llm(self, client): self.llm_client client def run( self, user_input: str, tools: List[Dict[str, Any]] ) - HarnessContext: ctx HarnessContext(user_inputuser_input) while ctx.step_count ctx.max_steps: ctx.step_count 1 # 1. 策略层决定有哪些可调用的工具与约束 allowed_tools self.policy.filter_tools(tools, ctx) # 2. 模型决策此处通过统一接口调用 message self.llm_chat( user_inputctx.user_input, allowed_toolsallowed_tools, tool_callsctx.tool_calls, ) # 3. 判断是否需要调用工具 tool_call self._extract_tool_call(message) if not tool_call: ctx.final_output message break # 4. 执行层经过策略二次确认后再执行 allow, reason self.policy.check_tool_call(tool_call, ctx) if not allow: ctx.error_info f策略拦截: {reason} ctx.final_output 我无法执行该操作原因 reason break result self._execute_tool(tool_call) ctx.tool_calls.append({ tool: tool_call[name], args: tool_call[args], result: result, }) # 5. 验证层检查返回结果是否合法 ok, cleaned self.validator.validate_result(result, ctx) if not ok: ctx.error_info 验证失败: cleaned break return ctx def llm_chat(self, user_input, allowed_tools, tool_calls): # 实际项目中替换为真实的 LLM 调用 return self.llm_client.chat( user_inputuser_input, toolsallowed_tools, tool_callstool_calls, ) def _extract_tool_call(self, message): # 解析模型输出中的工具调用请求这里简化为读取 dict 字段 if isinstance(message, dict): return message.get(tool_call) return None def _execute_tool(self, tool_call): # 演示工具执行实际项目中会调用注册表中的具体函数 tool_name tool_call[name] tool_fn TOOL_REGISTRY.get(tool_name) if not tool_fn: return {error: funknown tool: {tool_name}} return tool_fn(**tool_call[args])这个框架的核心循环是策略过滤工具→模型决策→策略二次确认→执行工具→验证结果。第一次过滤是为了减少模型的选择空间第二次确认是为了防止模型在决策过程中被注入或自我欺骗验证则是为了兜住工具返回内容里的坑。任何一个环节都能安全终止整条链路这就是 Harness 的可控性。3. 核心实现细节——提示约束、工具权限与控制流3.1 提示约束动态拼装系统边界很多人理解的提示约束就是固定的“系统提示词”但 Harness 里的提示约束是动态的、跟策略联动的。策略层会先把有权限的工具列表、参数规格、可用值域、禁止事项拼成一段结构化描述再交给模型。模型看到的不是全部工具清单而是一份已经被过滤过的“可操作地图”。# policies.py from typing import List, Dict DEFAULT_FORBIDDEN_RULES [ 严禁执行任何 DELETE 操作, 严禁调用非白名单工具, 凡是涉及生产库的写操作必须先返回审批提示, ] class PolicyManager: def __init__(self, forbidden_rulesNone): self.forbidden_rules forbidden_rules or DEFAULT_FORBIDDEN_RULES def filter_tools(self, tools, ctx): 只保留当前上下文里允许使用的工具。 allowed [] for t in tools: if t[name].startswith(db_write_): continue allowed.append(t) return allowed def build_system_prompt(self, user_role: str guest) - str: lines [ 你是一个受约束的智能体必须遵守以下规则, 1. 只调用显式提供给您的工具。, 2. 工具参数必须在规定类型和取值范围内。, 3. 如果需要执行高风险操作必须先说明原因并请求确认。, 4. 不要尝试猜测系统提示之外的隐藏工具。, ] lines.extend(self.forbidden_rules) lines.append(f当前用户角色: {user_role}) return \n.join(lines)这里值得注意的一点是将规则同时构建进系统 prompt 并不是为了靠它兜底而是为了减少模型在决策阶段“误入歧途”的概率。软约束 硬校验一起用效果远好于只用其中一种。软约束负责抬高正确路径的概率硬校验负责把错误路径彻底堵死。3.2 工具权限白名单与操作灰度工具权限是 Harness 里最不能含糊的部分。我的做法是把工具分为三个灰度级别只读工具比如查询、列表获取。所有已登录用户都能调用。普通写工具比如创建草稿、发送消息。需要记录日志允许在测试环境直接执行。高风险工具比如删除数据、修改权限、对外发送通知。默认禁止必须显式开启且经过二次审批。# policies.py (续) HIGH_RISK_TOOLS {db_delete, grant_permission, send_email_notify} WRITE_TOOLS {db_insert, send_message} class ToolPermissionPolicy: def __init__(self): self.user_role guest def set_role(self, role: str): self.user_role role def check_tool_call(self, tool_call, ctx) - tuple[bool, str]: name tool_call[name] if name in HIGH_RISK_TOOLS: # 高风险操作必须走审批流程不允许直接执行 return False, 该操作属于高风险操作需要管理员审批。 if name in WRITE_TOOLS and self.user_role guest: return False, 访客角色不允许执行写操作。 return True, 灰度分级带来的直接好处是决策点前置。模型不需要自己判断“删除操作是否合适”Harness 直接告诉它这是不允许的。这降低了模型被判罚的负担也让安全性不再依赖于模型对伦理规则的理解能力。另一个实践上的好处是审计日志里能用工具名直接定位风险等级排查问题时非常高效。3.3 输出校验与重试让智能体“知道自己错了”输出校验是我在迭代过程中最常被问到的一层。很多人误以为校验只针对最终回答但实际上需要校验的还有工具调用参数和工具返回结果。# validators.py from typing import Tuple, Any class OutputValidator: def __init__(self): self.sensitive_keywords [password, secret_key, token] def validate_result(self, result, ctx) - Tuple[bool, Any]: # 如果工具返回的是错误信息直接拦截 if isinstance(result, dict) and result.get(error): return False, result[error] # 对返回内容做敏感词过滤 if isinstance(result, str): for kw in self.sensitive_keywords: if kw.lower() in result.lower(): return False, f返回内容包含敏感字段: {kw} # 结构校验演示场景要求返回 JSON 对象 if not isinstance(result, (dict, list)): return False, 返回结果结构不合法 return True, result def validate_final_output(self, output: str) - Tuple[bool, str]: if len(output) 2000: return False, 最终输出超过长度限制 if any(kw in output.lower() for kw in self.sensitive_keywords): return False, 最终输出包含敏感信息 return True, output校验失败后的处理策略我采用的是“直接终止 返回明确错误”而不是让模型无限重试。因为多数情况下校验失败意味着模型决策逻辑已经偏离了预期路径允许重试反而可能放大错误的确定性。现实中可以设计“最多一次重试 人工兜底”但我个人偏好简单粗暴因为可控性排在可用性前面。4. 实测同一个 Agent加不加 Harness 的差距在哪里4.1 评测场景与指标设计空谈框架没有说服力。我把这套框架接到一个本地知识库 Agent 上做了对比评测。测试场景分为四类普通问答、越权删除尝试、工具调用循环压力和敏感数据泄露探测。每个场景各执行 20 次统计以下几个指标指标含义任务完成率Agent 在允许范围内正确回答或执行操作的比例违规拦截率对越权、危险操作的拦截成功率平均调用步数单个任务完成所需的工具调用轮数卡死比例陷入死循环或超时未结束的比例为了保证对比公平两个版本使用完全相同的模型接口和基础工具集区别仅在于是否启用了 Harness 层策略过滤 二次确认 输出校验。4.2 实测结果与我的判断数据出来后结论比我想象的更清晰。直接启用 Harness 后违规拦截率从原来的约 55% 提升到了 96% 以上——剩下的 4% 主要是因为测试场景里有一条“通过拼 JSON 参数绕过工具名称检查”的注入手法当时规则里没有覆盖到后来补上了参数级校验才到 100%。任务完成率没有显著下降普通问答场景从 92% 微降到 89%原因是多了一层校验后偶尔会出现“工具返回结构不符合预期被拦截”的情况。但这点损失换来的安全性很值尤其在生产环境里一次误删数据的成本远远高于 3% 的任务完成率下降。卡死比例从原来的 12% 降到了 3% 左右这要归功于max_steps的硬限制。之前没有步数上限时模型偶尔会在同一个失败工具调用上来回循环既浪费时间又消耗 token。还有一个比较意外的发现启用 Harness 之后日志的可观测性大幅提升。因为每一步都会在HarnessContext里留下记录排查问题时我可以直接定位是“策略拦截”“执行失败”还是“校验失败”不再需要翻看满屏的原始调用链。这算是 Harness 带来的一个隐性收益。5. 落地过程中踩过的坑以及最终的取舍5.1 坑一约束太严智能体真的会变傻我最初把策略层写得非常强硬比如禁止所有写操作、要求模型每个参数都必须通过正则匹配。结果智能体的回答变得极其保守很多问题它都回复“我没有权限执行”。后来我调整了思路高风险用硬限制低风险用软提示。读操作全部放开写操作按权限分级只有真正高危的操作才走强制拦截。把控制力集中在风险最高的 20% 行为上才能兼顾效果和安全性。5.2 坑二校验逻辑只做了表面检查早期我的校验器只检查返回内容是不是 JSON、有没有敏感词。后来发现模型会在参数里隐藏嵌套结构来绕过检查比如把用户名放在一个看似无害的note字段里再通过下游工具的拼装逻辑形成实际影响。修复方式是在工具函数注册的地方加上参数级 schema 校验不允许出现未定义字段。5.3 坑三评测集和线上行为严重脱节一开始我用自己写的 30 条用例做评测全部通过信心满满。上线后发现真实用户的问题千奇百怪一个“帮我把昨天的报表导出来”就能触发多种工具的连续调用很容易在某个环节被软规则误伤。后来我把评测集改成了线上线下结合线上收集真实用户提问和日志里的失败 case定期回灌到评测集。这是一个持续过程但却是保障长期稳定性的唯一办法。5.4 关于 Harness 和 Prompt 的关系不算踩坑但常被误解不少同行问我Harness Engineering 是不是就是一套高级 Prompt我的回答是Prompt 是策略层的一个载体但 Harness 是围绕 Agent 全生命周期的工程系统。Prompt 决定了模型“倾向于怎么做”Harness 决定了模型“只能怎么做”。前者解决效果问题后者解决风险和合规问题。两者需要配合但绝对不是一回事。我在实际使用中的体会是Harness Engineering 不是一次性搭完就结束的东西它更像是一个持续演进的护栏系统。你每发现一种新的越权方式、一种新的注入手法、一个新增的高风险工具都需要把它沉淀到策略层和验证层里。刚开始的成本会有点高但演进了几轮之后整个系统的可控性和稳定性会让你觉得这笔投入非常值。最后再分享一个小技巧如果你现在只是想在现有 Agent 上快速验证 Harness 的价值不要先写完整框架先加一个“最大步数限制 高风险工具拦截列表”这两行改动往往就能拦住大部分事故。然后再逐步补齐策略层、验证层和评测闭环。绝大多数团队的 Agent 失控问题都不是模型能力不够而是外围工程没跟上。本文还有配套的精品资源点击获取