ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent上线前自动校验脚本:从本地跑通到线上稳定的五层检查与自愈实践

Agent上线前自动校验脚本:从本地跑通到线上稳定的五层检查与自愈实践 1. 从本地跑通到线上翻车Agent 项目最隐蔽的坑做 Agent 开发的人大概都经历过这种落差感。本地调试的时候Agent 每一步推理都清清楚楚工具调用返回正常多轮对话上下文也没丢你甚至录了个屏发到群里配文跑通了。结果一部署到线上用户第一条消息进来Agent 就开始胡言乱语工具调用参数缺字段或者干脆卡在某个循环里出不来日志里只留下一句冷冰冰的agent execution terminated due to error.。这个现象在 Agent 圈子里太普遍了普遍到很多人已经把它当成玄学。但我要说的是它一点都不玄。本地环境和线上环境之间横着一堆你平时不会注意的变量模型版本可能被静默更新、工具接口的返回结构在真实流量下会冒出你没见过的字段、上下文长度在长会话里会悄悄逼近上限、并发一上来状态就串了。这些问题在本地单条测试里几乎不可能暴露因为本地测试的输入是你精心构造的干净数据而线上用户给的是野生数据。我自己的项目就吃过这个亏。一个基于 Python 的 Agent 服务本地用几十条测试用例跑得漂漂亮亮上线第二天开始零星报错第三天错误率飙到 15%。排查了两天才发现问题出在一个工具调用的返回值上——本地测试时那个接口永远返回一个固定结构的 JSON线上偶尔会返回一个带null字段的变体而我的解析代码没做空值判断直接抛异常整个 Agent 链路就断了。痛定思痛之后我花了一周时间用 Python 写了一套自动校验脚本。这套脚本不干别的就是在 Agent 上线前和运行中自动检查那些本地跑通、线上翻车的高发环节。它帮我把线上错误率从 15% 压到了 1% 以内更重要的是它让我对 Agent 的行为有了可观测的抓手而不是每次出问题都靠猜。这篇文章就把这套脚本的设计思路、核心实现和踩坑经验完整拆开讲。不管你是刚入门 Agent 开发还是已经在做 Agent 框架与编排这套校验思路都能直接拿去用。我会尽量说人话把每个为什么这么设计讲清楚让你不只是抄代码而是真正理解 Agent 上线前到底该校验什么。2. 为什么本地跑通不等于线上可用Agent 特有的四类脆弱点在写脚本之前得先想明白一件事为什么普通 Web 服务的本地跑通基本能保证线上可用而 Agent 不行答案在于 Agent 的执行链路比普通服务长得多而且中间充满了不确定性。我把这些不确定性归纳成四类脆弱点校验脚本就是围绕这四类来设计的。2.1 模型输出的非确定性同一个输入两次结果可能不同普通函数你给它同样的输入它永远返回同样的输出。但 Agent 依赖的大模型不是这样。即使你把 temperature 设成 0不同批次的推理、不同的上下文长度、甚至服务端的负载均衡策略都可能让输出产生细微差异。这个差异在单轮对话里可能无所谓但在多轮 Agent 里会被放大——第一步的微小偏差经过后面几步的工具调用和推理可能演变成完全不同的执行路径。我遇到过一个典型案例Agent 需要从用户消息里抽取一个日期参数本地测试时模型稳定输出2024-01-15这种标准格式线上偶尔输出2024年1月15日。我的下游代码只认第一种格式第二种直接解析失败。这种问题你靠人工测试根本测不出来因为它是概率性的。所以校验脚本的第一要务是对模型的结构化输出做格式校验。凡是 Agent 要拿去调工具、做判断的输出都必须经过一层 schema 校验格式不对就重试或降级而不是让它带着错误格式往下走。2.2 工具返回的野生数据线上接口永远比文档复杂本地开发时你调用的工具接口往往是你自己 mock 的或者是在理想条件下测试的。线上真实接口的返回可能包含文档里没写的字段、可能在某些条件下返回空数组、可能因为限流返回一个错误结构而不是正常数据。Agent 的工具调用层如果没做防御性解析这些野生数据就会变成异常。这里有个经验永远不要相信工具接口的返回结构是稳定的。校验脚本要对每个工具的返回做结构校验至少要检查关键字段是否存在、类型是否正确、是否为空。发现异常时要么给 Agent 返回一个明确的错误信息让它自己处理要么直接中断并记录而不是让一个残缺的数据流进后续推理。2.3 上下文与状态的隐性漂移长会话里的定时炸弹Agent 的多轮对话会不断往上下文里追加内容。本地测试通常只跑三五轮线上用户可能聊几十轮。上下文一长问题就来了早期的重要信息被淹没、token 数逼近上限导致截断、状态变量在多轮之间没有正确传递。这些问题的表现往往是Agent 突然忘了之前说过的话或者开始重复之前的动作。校验脚本需要监控上下文的增长趋势和关键状态的完整性。比如每轮对话后检查 token 数是否超过阈值、检查关键状态字段是否还在、检查是否出现了重复的工具调用模式。这些检查能在问题爆发前给你预警。2.4 并发下的状态污染单线程测试永远发现不了本地测试基本是单请求串行执行线上是并发。如果你的 Agent 实现里用了全局变量、类属性、或者共享的缓存对象来存状态并发一上来就会串。A 用户的对话历史可能被 B 用户的请求覆盖工具调用的中间结果可能互相污染。这类问题最阴险因为它不是每次都出现而是高并发时才暴露而且日志里往往看不出明显异常。校验脚本要在每次请求开始时生成一个唯一的 trace id并在整个执行链路里传递确保每个请求的状态是隔离的。同时可以在关键节点做状态快照发现状态被意外修改时立即报警。把这四类脆弱点想清楚之后校验脚本的骨架就出来了它不是一个单点检查而是一套覆盖输入-推理-工具-状态-输出全链路的检查机制。3. 校验脚本的骨架设计五个检查层怎么排很多人写校验脚本容易犯一个错把所有检查堆在一起出问题了不知道是哪一层挂的。我的做法是把校验分成五个层次每层职责单一从外到内依次执行。这样出问题时日志里能直接定位到是哪一层拦截的。3.1 输入层校验把脏数据挡在门外输入层是 Agent 的第一道关。用户输入、上游系统传来的参数、定时任务触发的 payload都要在这里过一遍。检查内容包括必填字段是否存在、字段类型是否正确、字符串长度是否超限、是否包含明显的注入风险内容。这里有个细节值得说Agent 的输入校验不能太严否则会把正常但格式多样的用户输入挡掉。比如用户可能用自然语言描述需求你不能要求它必须是 JSON。所以输入层校验的重点是结构完整性和安全边界而不是格式统一性。对于自然语言输入主要检查长度和是否包含异常字符对于结构化输入才做严格的 schema 校验。from pydantic import BaseModel, Field, ValidationError from typing import Optional, List class AgentInput(BaseModel): session_id: str Field(..., min_length1, max_length128) user_message: str Field(..., min_length1, max_length8000) history: Optional[List[dict]] Field(default_factorylist) metadata: Optional[dict] Field(default_factorydict) def validate_input(raw: dict) - tuple[bool, str, Optional[AgentInput]]: try: parsed AgentInput(**raw) return True, ok, parsed except ValidationError as e: return False, finput validation failed: {e.errors()}, None用 Pydantic 做输入校验的好处是它把校验规则和数据结构定义在一起改起来不容易漏。而且错误信息很详细能直接告诉你哪个字段出了问题。3.2 推理层校验盯住模型输出的结构推理层校验的核心是结构化输出校验。Agent 在每一步推理后通常会输出一个结构化的决策比如调用哪个工具、传什么参数或者给出最终回答。这个结构必须被校验因为它是后续所有动作的依据。我的做法是给每种决策定义一个 schema模型输出后先解析再校验。解析失败或校验不通过就触发重试机制把错误信息作为反馈重新喂给模型让它修正输出。重试最多两到三次还不行就降级到兜底逻辑。from pydantic import BaseModel, Field from typing import Literal, Optional class ToolCall(BaseModel): tool_name: str Field(..., min_length1) arguments: dict Field(default_factorydict) reasoning: Optional[str] None class AgentDecision(BaseModel): action: Literal[tool_call, final_answer, ask_user] tool_call: Optional[ToolCall] None answer: Optional[str] None def validate_decision(raw_text: str) - tuple[bool, str, Optional[AgentDecision]]: import json try: data json.loads(raw_text) except json.JSONDecodeError as e: return False, fdecision is not valid json: {e}, None try: decision AgentDecision(**data) except ValidationError as e: return False, fdecision schema mismatch: {e.errors()}, None if decision.action tool_call and decision.tool_call is None: return False, action is tool_call but tool_call is missing, None if decision.action final_answer and not decision.answer: return False, action is final_answer but answer is empty, None return True, ok, decision这段代码里有个容易被忽略的点action和实际字段的一致性校验。模型可能输出action: tool_call但忘了填tool_call字段这种半结构化输出光靠 schema 是拦不住的得加业务逻辑校验。3.3 工具层校验给每个工具调用加护栏工具层是 Agent 最容易翻车的地方。我的校验策略是调用前校验参数、调用后校验返回。调用前检查参数是否满足工具的要求比如必填参数是否齐全、参数类型是否正确、参数值是否在允许范围内。调用后检查返回结构是否符合预期关键字段是否存在。这里有个实用技巧给每个工具定义一个契约包括输入 schema 和输出 schema以及超时时间和重试策略。校验脚本根据契约自动检查不用为每个工具手写检查逻辑。from dataclasses import dataclass, field from typing import Callable, Any dataclass class ToolContract: name: str input_schema: type output_schema: type timeout_seconds: float 10.0 max_retries: int 2 required_output_fields: list field(default_factorylist) def validate_tool_call(contract: ToolContract, args: dict) - tuple[bool, str]: try: contract.input_schema(**args) except ValidationError as e: return False, ftool {contract.name} input invalid: {e.errors()} return True, ok def validate_tool_result(contract: ToolContract, result: Any) - tuple[bool, str]: if result is None: return False, ftool {contract.name} returned None if isinstance(result, dict): for f in contract.required_output_fields: if f not in result: return False, ftool {contract.name} missing field: {f} try: contract.output_schema(**result) if isinstance(result, dict) else contract.output_schema(result) except Exception as e: return False, ftool {contract.name} output schema mismatch: {e} return True, ok3.4 状态层校验防止上下文和状态悄悄跑偏状态层校验关注的是 Agent 的记忆和进度。每轮对话后检查上下文 token 数、检查关键状态字段、检查是否出现异常循环。我通常会设几个阈值token 数超过模型上限的 80% 就预警超过 90% 就强制截断或摘要同一个工具连续调用超过 5 次就判定为循环中断并记录。状态校验还有一个重要职责是隔离性检查。每次请求开始时记录一个状态快照请求结束时对比确保没有意外的全局状态修改。这个检查在并发场景下特别有用。3.5 输出层校验最后一道关别让坏结果流出去输出层是 Agent 给用户的最终回答。这里要检查的是回答是否为空、是否包含明显的错误信息、是否符合业务规则比如不能泄露敏感信息、不能给出超出权限的建议。输出层校验是最后一道防线即使前面都过了这里也要再拦一次。五层校验的顺序不能乱因为每一层都依赖上一层的输出。输入层过了才有推理推理层过了才有工具调用工具层过了状态才更新状态更新了才有输出。这个顺序保证了问题能在最早的可能点被拦截减少无效计算。4. 把校验脚本接进 Agent 执行链路三种接入方式对比脚本写好了怎么接进 Agent 是个关键问题。接得不好要么侵入性太强把原有代码改得面目全非要么覆盖不全漏掉关键节点。我试过三种接入方式各有适用场景下面把对比和选择逻辑讲清楚。4.1 装饰器方式侵入性最小适合快速接入装饰器方式是在 Agent 的关键函数上加一层包装函数执行前后自动跑校验。这种方式对原有代码改动最小适合已经上线的项目快速接入。import functools import time import uuid def with_validation(layer: str): def decorator(func): functools.wraps(func) def wrapper(*args, **kwargs): trace_id kwargs.get(trace_id) or str(uuid.uuid4()) start time.time() try: result func(*args, **kwargs) elapsed time.time() - start log_validation(trace_id, layer, success, elapsed) return result except Exception as e: elapsed time.time() - start log_validation(trace_id, layer, error, elapsed, str(e)) raise return wrapper return decorator with_validation(reasoning) def run_agent_step(state, trace_idNone): ...装饰器的优点是干净缺点是它只能包住函数边界函数内部的中间状态它看不到。所以装饰器适合做粗粒度的监控细粒度校验还得靠显式调用。4.2 中间件方式适合框架化的 Agent 项目如果你的 Agent 是基于某个框架比如 LangChain、AutoGen 这类搭的那中间件方式最合适。框架通常提供了 hook 点你可以在这些点上挂校验逻辑不用改业务代码。中间件的核心是拦截-校验-放行或阻断。每个 hook 点拿到当前的状态和即将执行的动作跑校验通过就放行不通过就返回错误或触发降级。这种方式的好处是校验逻辑和业务逻辑彻底解耦校验规则可以独立迭代。4.3 独立校验服务适合多 Agent 协作的复杂系统当系统里有多个 Agent 互相调用时把校验做成一个独立服务更合适。每个 Agent 在执行关键步骤前通过 RPC 或 HTTP 调用校验服务拿到校验结果再决定下一步。这种方式的好处是校验规则集中管理多个 Agent 共享同一套标准缺点是增加了一次网络调用有延迟开销。三种方式的对比接入方式侵入性覆盖粒度适用场景延迟开销装饰器低函数级快速接入、单 Agent极低中间件中步骤级框架化项目、多步骤 Agent低独立服务高全链路多 Agent 协作、复杂系统中我的建议是先用装饰器快速接入把关键节点的监控跑起来等项目稳定了再逐步迁移到中间件方式把校验规则细化。独立服务是最后的选择除非你确实有多 Agent 协作的需求否则没必要为了架构而架构。4.4 接入时最容易忽略的trace id 的贯穿传递不管用哪种方式trace id 的贯穿传递都是必须的。没有 trace id你根本没法把一次请求在各个校验层的日志串起来。我见过太多项目日志里每条记录都挺详细但就是串不成一条完整的链路排查问题时只能靠时间戳猜。trace id 要在请求入口生成然后通过参数或上下文对象一路传下去。Python 里可以用contextvars做上下文传递避免每个函数都显式传参。import contextvars trace_id_var contextvars.ContextVar(trace_id, defaultNone) def set_trace_id(trace_id: str): trace_id_var.set(trace_id) def get_trace_id() - str: return trace_id_var.get() or unknown用contextvars的好处是它在异步场景下也能正确隔离不会出现 A 请求的 trace id 串到 B 请求里的情况。这一点在 Agent 这种大量使用异步调用的场景里特别重要。5. 上线前必跑的校验清单从冒烟到压测的完整流程脚本接好了接下来是上线前怎么跑。我的做法是分三个阶段冒烟校验、回归校验、压测校验。每个阶段的侧重点不同跑通了才能上线。5.1 冒烟校验用最小用例验证链路通畅冒烟校验的目标是确认整条链路能跑通不追求覆盖所有分支。我会准备一组最小用例覆盖 Agent 的几种基本行为单轮问答、单次工具调用、多轮对话、异常输入。每个用例跑一遍看五层校验是否都通过trace id 是否完整。冒烟校验的用例要少而精通常 5 到 10 条就够。跑一次控制在几分钟内这样每次改代码都能快速跑一遍。我习惯把冒烟校验做成一个命令行脚本改完代码顺手跑一下心里有底。SMOKE_CASES [ {session_id: smoke-1, user_message: 你好, history: []}, {session_id: smoke-2, user_message: 帮我查一下今天的天气, history: []}, {session_id: smoke-3, user_message: 继续刚才的话题, history: [{role: user, content: 你好}]}, {session_id: smoke-4, user_message: , history: []}, ] def run_smoke_tests(agent_fn): results [] for case in SMOKE_CASES: ok, msg, parsed validate_input(case) if not ok: results.append((case[session_id], input_rejected, msg)) continue try: output agent_fn(parsed) results.append((case[session_id], passed, output)) except Exception as e: results.append((case[session_id], failed, str(e))) return results注意最后一条用例空消息。这条用例预期是被输入层拦截的如果它没被拦截反而跑进了推理层说明输入校验有漏洞。5.2 回归校验用历史故障用例防止问题复发回归校验的用例来自历史故障。每次线上出问题排查修复后把那个场景做成一条回归用例加进去。这样下次改代码时如果又把老问题改回来了回归校验会立刻发现。我的回归用例库现在有几十条每条都对应一个真实踩过的坑。比如工具返回 null 字段、模型输出日期格式不对、上下文超过 80% 阈值、并发下状态串了。这些用例跑一遍大概十几分钟但能拦住绝大多数低级错误。回归校验的关键是用例要能复现问题。如果一条用例只是跑一下不报错那它拦不住任何东西。好的回归用例应该能精确触发当初那个故障条件比如构造一个返回 null 字段的 mock 工具看校验脚本是否正确拦截。5.3 压测校验并发下暴露状态污染和性能瓶颈压测校验是上线前的最后一道关。用并发工具模拟真实流量观察 Agent 在高并发下的表现。重点看三件事错误率是否随并发上升、响应时间是否稳定、状态是否隔离。我通常用locust或asyncio写压测脚本从 10 并发逐步加到 100 并发每档跑 5 分钟记录错误率和 P99 延迟。如果错误率在某个并发档突然上升说明那里有并发问题通常是状态污染或资源竞争。import asyncio import aiohttp async def stress_worker(session, url, worker_id, results): payload {session_id: fstress-{worker_id}, user_message: 测试消息, history: []} try: async with session.post(url, jsonpayload, timeout30) as resp: results.append(resp.status) except Exception as e: results.append(ferror: {e}) async def run_stress(url, concurrency, duration): results [] async with aiohttp.ClientSession() as session: end_time asyncio.get_event_loop().time() duration while asyncio.get_event_loop().time() end_time: tasks [stress_worker(session, url, i, results) for i in range(concurrency)] await asyncio.gather(*tasks) return results压测时有个细节要注意每个并发请求要用不同的 session_id否则你测的是同一个会话的并发而不是多会话并发。多会话并发才能暴露状态污染问题。5.4 三阶段校验的执行顺序和通过标准三个阶段不是随便跑的有明确的顺序和通过标准阶段执行时机通过标准耗时冒烟校验每次代码提交后全部用例通过无异常几分钟回归校验每次发版前全部历史故障用例通过十几分钟压测校验每次发版前错误率 1%P99 延迟 阈值半小时冒烟校验频率最高所以必须快。回归校验和压测校验频率低一些但每次发版前必跑。三个都过了才允许上线。6. 校验脚本自身的坑别让护栏变成新的故障源写校验脚本的时候很容易陷入一个误区觉得校验越严越好。实际上校验脚本本身也可能成为故障源。我踩过几个坑分享出来让你少走弯路。6.1 校验逻辑抛异常护栏自己先倒了最常见的问题是校验代码本身抛异常。比如你在校验工具返回时假设返回是个 dict结果返回是个 list你的result.get(field)直接抛AttributeError。这时候校验没起到保护作用反而把正常流程搞挂了。解决办法是给校验逻辑本身加一层异常保护。校验函数内部用 try-except 包住任何异常都转成校验失败的结果而不是往外抛。def safe_validate(validate_fn, *args, **kwargs): try: return validate_fn(*args, **kwargs) except Exception as e: return False, fvalidator crashed: {e}这个包装看起来简单但能救命。校验脚本的定位是保护者它自己绝对不能成为故障点。6.2 校验开销过大把 Agent 拖慢了第二个坑是校验太慢。比如你在校验里做了复杂的正则匹配、或者调用了外部服务、或者对大数据结构做了深拷贝。这些操作在单次请求里可能只增加几十毫秒但 Agent 一次执行要跑好几步累积起来就很可观了。我的经验是校验逻辑要尽量轻量能在内存里做的别调外部服务能用简单判断的别上复杂算法。如果某个校验确实很重考虑异步执行或者采样执行不要每条请求都跑。6.3 误报和漏报的平衡阈值怎么定校验的阈值定得太松会漏报定得太紧会误报。误报多了团队就会对告警麻木最后校验形同虚设。漏报多了校验就没意义。我的做法是先松后紧用数据调阈值。上线初期阈值设松一点收集一周的校验数据看看正常请求的指标分布然后根据分布把阈值调到能拦住异常但不误伤正常的水平。比如 token 数阈值先设 90%观察一周发现正常请求最高到 85%那就调到 88%。6.4 校验日志的存储和查询出问题时能快速定位校验日志如果只是打到控制台出问题时根本查不过来。我的做法是把校验日志结构化后存到一个可查询的地方每条日志包含 trace id、校验层、校验结果、耗时、错误信息。查询时按 trace id 或时间范围过滤几秒钟就能定位到问题。日志量大的话注意做采样和滚动清理别让日志把磁盘写满了。关键错误日志全量保留正常通过的日志可以采样。7. 从校验到自愈让 Agent 在异常时自己兜底校验脚本的终极形态不是发现问题就报警而是发现问题能自愈。报警只是第一步让 Agent 在异常时自动降级、重试、切换策略才是真正减少人工介入的关键。7.1 重试策略什么情况该重试什么情况不该不是所有失败都值得重试。模型输出格式错误重试可能有用因为模型有概率输出正确格式。工具调用超时重试也可能有用因为可能是网络抖动。但如果是参数校验失败、权限不足这类确定性错误重试多少次都一样只会浪费资源。我的重试策略是只对非确定性错误重试且重试次数有限。模型输出格式错误重试 2 次工具超时重试 1 次其他错误直接降级。重试时把上次的错误信息作为反馈带上提高成功率。7.2 降级方案重试不行时的兜底路径重试都失败了怎么办得有降级方案。降级方案的设计原则是保证基本可用牺牲高级功能。比如工具调用失败降级到用模型的知识直接回答模型输出格式错误降级到用规则解析上下文超限降级到摘要压缩。降级方案要提前设计好不能等出问题了临时想。每个关键步骤都应该有一个最坏情况下也能返回点什么的兜底逻辑。7.3 熔断机制连续失败时主动切断如果某个工具连续失败继续调用只会拖垮整个 Agent。这时候需要熔断连续失败 N 次后主动切断对该工具的调用直接走降级路径过一段时间再试探性恢复。熔断器的实现可以用一个简单的计数器加时间窗口。连续失败超过阈值就打开熔断之后一段时间内直接拒绝调用时间窗口过后进入半开状态允许少量请求试探成功了就关闭熔断。import time class CircuitBreaker: def __init__(self, failure_threshold5, recovery_timeout60): self.failure_threshold failure_threshold self.recovery_timeout recovery_timeout self.failures 0 self.last_failure_time 0 self.state closed def call(self, fn, *args, **kwargs): if self.state open: if time.time() - self.last_failure_time self.recovery_timeout: self.state half_open else: raise RuntimeError(circuit breaker is open) try: result fn(*args, **kwargs) if self.state half_open: self.state closed self.failures 0 return result except Exception as e: self.failures 1 self.last_failure_time time.time() if self.failures self.failure_threshold: self.state open raise熔断机制配合校验脚本使用效果最好。校验脚本负责发现异常熔断器负责在异常持续时切断两者配合能让 Agent 在部分依赖不可用时依然保持基本可用。7.4 自愈的边界哪些问题不该自动处理自愈不是万能的有些问题必须人工介入。比如模型输出持续不符合业务规则、工具返回的数据涉及敏感信息、Agent 的行为出现安全风险这些情况应该直接中断并报警而不是自动降级继续跑。我的原则是技术性异常可以自愈业务性和安全性异常必须人工介入。技术性异常比如超时、格式错误、网络抖动这些自愈是安全的。业务性异常比如输出内容违规、调用越权工具这些自愈可能掩盖问题必须让人看到。8. 一些实战中的零碎经验最后分享几个零碎但实用的经验都是踩坑踩出来的。第一个经验校验脚本要能单独运行。不要把它和 Agent 主流程绑死要能脱离 Agent 单独跑测试用例。这样你改校验规则时不用启动整个 Agent 就能验证。第二个经验校验规则要版本化。每次改校验规则记录改了什么、为什么改。因为校验规则会影响 Agent 的行为出问题时你需要知道是哪条规则导致的。第三个经验别追求一次到位。校验脚本是迭代出来的先覆盖最常出问题的几个点跑起来再逐步加。一上来就想做全链路校验往往做不完就放弃了。第四个经验关注校验的误伤率。定期看校验拦截的请求里有多少是真正有问题的有多少是误报。误报率高就调阈值别让校验成为业务的阻碍。第五个经验把校验结果反馈到开发流程。校验发现的常见问题应该反过来推动代码规范。比如发现模型输出格式错误频繁就在 prompt 里加强格式约束发现工具返回结构不稳定就推动工具方修复接口。校验不只是防守还能推动上游改进。这套校验脚本我用了大半年线上错误率从最初的 15% 降到了 1% 以内而且大部分错误在触发用户感知前就被拦截或自愈了。更重要的是它让我对 Agent 的行为有了信心——不再是跑通了就上线出问题再救火而是上线前就知道哪些地方可能出问题并且有预案。Agent 开发本来就是个和不确定性打交道的过程校验脚本就是你和不确定性之间的那层缓冲。
返回列表