前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8720
  • 用代码图谱压缩 AI 审查上下文
  • 可组合的工程级 Agent 技能集
  • 为语音 AI 构建可控运行模式
  • 用结构化输出构建可迁移审核器
  • 四个实验验证 AI 防护栏效果
  • LFM2.5 小模型实现端侧 Agent
  • 为客服 Agent 增设风险路由层
  • 生产级 AI 系统不能只靠 RAG
  • 阿里Wan 3.0开放公测
  • GPT-5.6向ChatGPT免费用户开放
  • 用金丝雀文件审计编程Agent越界
  • 为AI生成补丁建立合并门禁
  • 用4位本地模型远程编排开发任务
  • 在Agent与工具间加入权限代理
  • 拆解 vLLM 高吞吐推理架构
  • 用 Sanitizer 检验 AI 修复的 C++ 代码
  • 蚂蚁开源多智能体协作框架Avernet
  • VS Code与Cursor接入IDEA语言能力
  • 用可复现基准筛选AI编程模型
  • 用40行测试复现Agent越权调用
  • 为Agent工具链注入故障并接入CI
  • 先构建只读Agent再开放写权限
  • 用微型聊天机器人测试指令边界
  • 用大模型把每日日志变成故障摘要
  • 可替换的文档问答检索架构
  • 用 dsctl 自动化管理 DolphinScheduler
  • TONTOU 绕过 Spectre v2 防护
  • Apple 芯片本地跑模型:MLX 对比 llama.cpp
  • AI Agent 入侵暴露凭证复用风险
  • 百万Token让模型生成三维世界
  • 用证据排序缓解RAG位置偏差
  • AI写Rust通过编译后还要审什么
  • 分清Claude Code的上下文与记忆
  • 模型切换时保护分类数据语义
  • 下一代 Qwen 或对大客户按营收抽成
  • OpenAI 发布智能体插件开放规范
  • 生产级 LLM Agent 避坑实战
  • 卡帕西力推指环王成为大模型评测基准
  • 更换大模型前先测真实业务负载
  • GitHub Copilot企业可管控MCP服务器白名单
  • 阮一峰:国家为何需要开源软件
  • 低成本弹性部署多模态推理服务
  • AI 批量造站为何几乎赚不到钱
  • 用模型路由层降低供应商锁定
  • Bot 检测应转向代理授权验证
  • AI 应用生产化的成本与扩容指南
  • DeepSeek V4 Flash 基准与架构核验
  • 多模型路由的生产级设计思路
  • 英伟达开源 GPU 直连存储栈
  • Vercel Sandbox:给每个编程Agent独立隔离环境
  • MCP服务上线前的验证清单
  • 已加载 51 / 8720
8.0
热点
AI SCORE
技术实践2026-08-07 10:55

为Agent工具链注入故障并接入CI

dev.to · AI#Agent#故障注入#MCP
Editor brief · 编辑速览

文章从工具端以HTTP 200返回限流错误、导致Agent静默失败的事故出发,设计本地故障注入框架验证异常响应处理。测试可在免费环境持续运行,避免消耗生产配额并污染线上遥测。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

引发这一切的告警

两周前,我们的 coding-agent worker 开始返回 HTTP 200,但 diff 是空的。没有错误日志。队列深度正常。延迟正常。SLO 仪表盘一片绿色,但每一项任务都在悄无声息地生成空结果。

根本原因平平无奇:一个 MCP 工具端点开始返回 {"error": "rate_limited"},HTTP 状态码却仍是 200,而我们的 Agent 循环把所有 HTTP 200 都当成工具调用成功。随后,模型在缺失输出的情况下产生了幻觉,而不是让任务失败。此前从来没有人测试过:当工具欺骗 Agent 时,它会怎么做。

这篇文章介绍的是我本应在事故发生前进行的演练:为 Agent 工具边界故障搭建一套本地故障注入框架。它可以运行在免费基础设施上,因此持续放在 CI 中也不会产生成本。

为什么免费套餐才是这类演练的正确选择

针对付费生产模型端点进行故障演练有三个问题:消耗配额、污染生产遥测数据,而且账单一来,人们就会悄悄禁用这些演练。一个你负担不起、无法持续运行的演练,等同于没有演练。

披露声明:本文是 MonkeyCode 产品推广活动的一部分。

MonkeyCode 目前提供免费的模型访问和免费服务器选项,足以承载这类工作负载,因为演练的成本主要取决于故障注入期间的工具调用次数,而不是模型质量。你测试的是自己的编排代码——超时、重试、验证和熔断——而不是模型的推理能力。实际上,较小的免费模型在这里或许更合适:如果你的测试框架能够应对较弱模型生成的、更混乱的工具调用格式,它也就能应对更强的模型。如果你正在评估适合这种生产前准入检查的免费方案,可以从 monkeycode.dev 的免费套餐入手;下面的测试框架与 provider 无关,因此也可以把端点替换成你已经在使用的任何服务。

┌────────────┐   tool calls   ┌──────────────────┐
│ Agent loop │ ─────────────▶ │ Fault-injecting  │ ──▶ real tool (echo/fs mock)
│ (harness)  │ ◀───────────── │ tool proxy       │
└─────┬──────┘                └──────────────────┘
      │ model API
      ▼
┌──────────────────┐
│ Free model       │
│ endpoint         │
└──────────────────┘

这个 proxy 位于 Agent 与工具之间,负责注入预先声明的故障。整个过程完全不会触碰生产环境。

1 个 Agent 循环,3 个工具:read_file、write_file、run_tests

每种故障模式运行 40 项任务,每项任务都是一次小规模的编辑并测试任务

模型:免费套餐提供的任意模型;temperature 设为 0

超时预算:每次工具调用 30 秒,每项任务 5 分钟

成功标准:任何未获得已验证工具输出的任务,都不得报告成功

# fault_proxy.py — sits between agent loop and tool implementations
import random, time, json

FAULTS = {
    "none":            lambda r: r,
    "http200_error":   lambda r: {"error": "rate_limited"},        # the incident
    "empty_body":      lambda r: {},
    "slow":            lambda r: (time.sleep(45), r)[1],             # exceeds 30s timeout
    "truncated_json":  lambda r: json.dumps(r)[:12],                 # unparseable
    "wrong_schema":    lambda r: {"result": None, "extra": "x"},
}

class ToolProxy:
    def __init__(self, tools, fault="none", seed=0):
        self.tools, self.fault = tools, fault
        random.seed(seed)

    def call(self, name, args):
        raw = self.tools[name](**args)
        return FAULTS[self.fault](raw)

测试框架中的断言(真正重要的部分)

这场演练并不是简单地“让 Agent 使用故障工具运行”。演练的关键在于断言:你的编排层能够检测出每一种故障模式。

# drill.py — expected behavior under each fault
EXPECT = {
    "http200_error":  "job_failed_tool_error",    # must NOT be treated as success
    "empty_body":     "job_failed_validation",
    "slow":           "job_failed_timeout",
    "truncated_json": "job_failed_parse",
    "wrong_schema":   "job_failed_validation",
}

def run_drill(agent, fault, jobs=40):
    outcomes = {"verified_success": 0, "silent_success": 0, "correct_failure": 0}
    for _ in range(jobs):
        result = agent.run_job(fault=fault)
        if result.ok and result.tool_outputs_verified:
            outcomes["verified_success"] += 1
        elif result.ok:                      # 200 + garbage = the incident class
            outcomes["silent_success"] += 1
        elif result.failure_class == EXPECT.get(fault):
            outcomes["correct_failure"] += 1
    return outcomes

准入条件只有一行:在每一种故障模式下,silent_success 都必须为 0。其他一切都只是调优细节。

修复我们这次事故的验证 shim

def call_tool_verified(proxy, name, args, timeout=30):
    try:
        resp = proxy.call(name, args)          # raises on timeout
    except TimeoutError:
        return ToolResult.fail("timeout")
    if isinstance(resp, str):
        try:
            resp = json.loads(resp)
        except json.JSONDecodeError:
            return ToolResult.fail("parse")
    if "error" in resp:
        return ToolResult.fail("tool_error", detail=resp["error"])
    if not SCHEMAS[name].is_valid(resp):
        return ToolResult.fail("validation")
    return ToolResult.ok(resp)

四项检查:超时、解析、错误字段和 schema。整个修复就这么简单。进行这场演练,是为了证明这个 shim 始终正确接入系统,没有被绕过。

每项任务需要记录的遥测字段

记录以下字段,确保 CI 中发生回归时能够调试:

job_id, fault_mode, tool_name, latency_ms, http_status,
parse_ok, schema_ok, error_field_present, outcome_class, verified

当 http_status=200 时出现 outcome_class=silent_success,正是我们最初那次事故的精确特征。

预期输出(以下标记为预期结果,请在本地运行确认)

fault=http200_error  verified=0  silent=0  correct_failure=40  PASS
fault=empty_body     verified=0  silent=0  correct_failure=40  PASS
fault=slow           verified=0  silent=0  correct_failure=40  PASS
fault=truncated_json verified=0  silent=0  correct_failure=40  PASS
fault=wrong_schema   verified=0  silent=0  correct_failure=40  PASS
fault=none           verified=40 silent=0  correct_failure=0   PASS

换用其他免费模型后,你得到的数字会有所不同——尤其是在 truncated_json 模式下,较弱的模型有时会以颇具创造性的方式重试。这种差异没有问题;唯一不可妥协的指标是 silent=0。

这场演练针对 mock 和免费端点运行;回滚方式就是删除 CI job,除此之外,生产环境中不存在任何相关改动。

把验证 shim 和演练代码放在同一个 repo 中,并让它们遵循同一套 PR 流程。没有配套演练的 shim 迟早会腐化失效。

如果你把它接入共享的免费服务器,请把并发上限设为 1~2。免费套餐使用的是共享容量,而故障注入循环恰恰是最容易触发 rate limit 的那类工作负载。

局限性,以及哪些人不应该这样做

免费模型不能替代生产模型的实际行为。这场演练验证的是编排机制,而不是输出质量。质量评估需要使用真实模型和真实评估数据。

免费套餐会发生变化。固定测试框架的行为,确保 provider 侧的变更会让 CI 明确失败,而不是悄悄跳过演练。

如果 Agent 的工具会对共享系统产生副作用,例如部署或写入工单,请使用 mock——对真正具有副作用的工具进行故障注入,正是演练变成事故的方式。

不要把它当作唯一的安全保障。针对队列时长和截止期限余量实施准入控制——我此前写过相关内容——能够阻止系统过载;这场演练则用于阻止静默产生错误结果。两者缺一不可。

运维层面的启示

我们的事故并不是模型故障,而是缺少一个四行代码的验证 shim。之所以从未有人测试它,是因为大家认为测试它“要花钱”。把演练迁移到免费基础设施上之后,这个借口也就不存在了。对于任何在生产环境中运行 Agent 的团队,我都会问这样一个问题:当工具返回 HTTP 200,但响应体中包含错误时,你的 pipeline 能否做到静默成功次数为零——而且你能否在本周通过 CI 证明这一点?

对于后续操作,你可以考虑屏蔽此人和/或举报滥用行为。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
用40行测试复现Agent越权调用
下一篇
先构建只读Agent再开放写权限