AIMock:AI技术栈Mock套件
当我们开始构建 AI 应用时,每次测试运行都会调用真实的 LLM API,消耗大量 token,而且因为某个提供商那周调整了响应格式,测试就会随机失败。所以我们构建了 LLMock,将其开源,以为这样就万事大吉了。
但现实是,2026 年一个单一的 Agent 请求在返回响应之前可能会涉及六到七个服务:LLM、MCP 工具服务器、向量数据库、重排器、Web 搜索 API、内容审核层,以及通过 A2A 通信的子 Agent。
大多数团队只 Mock 了其中一个。其余六个都是真实的、非确定性的,悄悄地让你的测试套件变成一纸空谈。这就是我们构建 AIMock 的原因。
AIMock 通过一个配置文件就能 Mock 你整个 Agent 技术栈。它还做了其他 Mock 工具做不到的事情:记录真实的 API 响应并将其作为固件回放,每天运行漂移检测以便在你的用户之前发现提供商的变更,以及让你注入故障来验证你的应用能否优雅地处理它们。
零依赖,全部基于 Node.js 内置模块构建。文档。
npm install @copilotkit/aimock
AIMock 是开源的,完全免费。
1、2026 年的 AI 应用调用的远不止 LLM
一个真实的 Agent 请求看起来是这样的:
User message
→ LLM decides to use a tool
→ Tool call via MCP (file system, database, calendar)
→ RAG retrieval from Pinecone or Qdrant
→ Web search via Tavily
→ Cohere reranker to sort results
→ Back to LLM with full context
在你的测试环境中,每一个都是一个真实的网络调用。每一个都可能失败,返回略微不同的结果,或者消耗你的 token。
我们研究了市面上所有的工具。有些能处理 LLM Mock,有些能处理某个协议,但没有一个覆盖了完整的技术栈。你需要将三到四个库拼接在一起,每个库都有自己的配置格式,而且仍然会有漏洞。
以下是 AIMock 与其他方案的对比。
2、AIMock 介绍:Mock 你整个 Agent 技术栈
它 Mock 了你的 AI 应用通信的一切。以下是它包含的内容:
- LLMock :11 个提供商,完整的流式传输、工具调用、推理模型
- MCPMock :本地 MCP 服务器,支持完整的 JSON-RPC 2.0、会话管理、工具、资源、提示
- A2AMock :Agent 卡发现、消息路由、Agent 之间的 SSE 流式传输
- VectorMock :用于确定性 RAG 检索的 Mock 向量数据库
- 服务 :搜索、重排和内容审核。这些是大家都忘记 Mock 的 API。
除此之外,AIMock 还做了其他 Mock 工具做不到的三件事:
- 漂移检测 :每天对真实提供商 API 运行检测,在 24 小时内发现响应格式的变化,赶在你的用户之前
- 录制与回放 :代理真实的 API 调用,将其保存为固件,在 CI 中永远回放它们而不再触碰真实 API
- 混沌测试 :注入 500 错误、格式错误的 JSON 和中途断开连接,以验证你的应用能处理故障
用一个配置文件在一个端口上运行所有这些:
{
"llm": { "fixtures": "./fixtures/llm", "providers": ["openai", "claude", "gemini"] },
"mcp": { "tools": "./fixtures/mcp/tools.json" },
"a2a": { "agents": "./fixtures/a2a/agents.json" },
"vector": { "path": "/vector", "collections": [] }
}
使用 AIMock CLI 快速开始:
npx aimock --config aimock.json --port 4010
让我们简要介绍每一个。
3、LLMock
LLMock有完整的流式传输、工具调用、跨所有主流提供商的推理能力.
LLMock 在真实的端口上运行一个真实的 HTTP 服务器,而不是进程内的补丁。机器上的任何进程都可以访问它:你的 Next.js 应用、Agent 工作进程、LangGraph 进程,任何能发起 HTTP 请求的程序。
它原生支持 11 个提供商:OpenAI、Claude、Gemini、Bedrock、Azure、Vertex AI、Ollama、Cohere、OpenRouter 和 Anthropic Azure。所有提供商都支持推理模型。
任何兼容 OpenAI 的端点,如 Mistral、Groq、Together AI、vLLM 都可以开箱即用。完整的流式传输、工具调用、结构化输出、扩展思考、多轮对话和 WebSocket API 全部内置。
AIMock 内部处理转换,所以一种固件格式适用于所有提供商。
使用 vitest 的编程式 API,注册一个固件并对响应进行断言。beforeAll 为整个测试套件启动一次 Mock 服务器,afterAll 将其关闭,mock.on() 注册一个将用户消息映射到确定性响应的固件。
import { LLMock } from "@copilotkit/aimock";
import { describe, it, expect, beforeAll, afterAll } from "vitest";
let mock: LLMock;
beforeAll(async () => {
mock = new LLMock();
await mock.start();
});
afterAll(async () => {
await mock.stop();
});
it("non-streaming text response", async () => {
mock.on({ userMessage: "hello" }, { content: "Hello! How can I help?" });
const res = await fetch(`${mock.url}/v1/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: "hello" }],
stream: false,
}),
});
const body = await res.json();
expect(body.choices[0].message.content).toBe("Hello! How can I help?");
expect(body.object).toBe("chat.completion");
expect(body.id).toMatch(/^chatcmpl-/);
});
将你现有的 OpenAI 客户端指向 Mock URL 即可。你的代码不需要做其他任何更改。无需 API 密钥,无需网络调用,每次都返回相同的响应。完整的提供商文档在这里。
4、MCPMock
MCPMock用于测试工具集成的 Mock MCP 服务器。
MCP 是 Agent 调用工具的方式。如果你的 Agent 使用 MCP,那么你的测试套件中的每次工具调用都在访问一个真实的服务器,具有真实的延迟,而且你无法控制返回的内容。
MCPMock 提供一个通过 JSON-RPC 2.0 完整支持 MCP 协议的本地服务器。你的 Agent 连接它的方式与连接真实 MCP 服务器完全一样。你可以控制返回的内容。
import { MCPMock } from "@copilotkit/aimock/mcp";
const mcp = new MCPMock();
mcp.addTool({
name: "search",
description: "Search the web",
inputSchema: { type: "object", properties: { query: { type: "string" } } },
});
mcp.onToolCall("search", (args) => {
const { query } = args as { query: string };
return `Found 3 results for "${query}"`;
});
const url = await mcp.start();
// point your MCP client at `url`
在真实的 Agent 测试中,你的 LLM 和 MCP 工具服务器一起运行。将 MCPMock 挂载到 LLMock 上,这样它们共享一个端口:
import { LLMock, MCPMock } from "@copilotkit/aimock";
const llm = new LLMock({ port: 5555 });
const mcp = new MCPMock();
mcp.addTool({ name: "calc", description: "Calculator" });
mcp.onToolCall("calc", (args) => "42");
llm.mount("/mcp", mcp);
await llm.start();
// MCP available at http://127.0.0.1:5555/mcp
它支持:
- 工具 - 注册返回字符串或丰富内容的处理程序
- 资源 - 你的 Agent 可以通过 URI 读取的静态文件和数据
- 提示 - 带参数的可重用提示模板
- 会话管理 - 完整的
Mcp-Session-Id生命周期,符合 Streamable HTTP 规范
完整文档在这里。
5、A2AMock
A2AMock无需真实 Agent 即可测试多 Agent 工作流。
A2A (Agent2Agent) 是 Agent 之间发现和通信的方式。它处理 Agent 卡、消息路由和 Agent 之间的流式响应。
测试多 Agent 系统在所有 Agent 都是真实的时候很困难。一个 Agent 挂了,你的整个测试套件就崩了。
A2AMock 提供一个本地 A2A 服务器,支持完整的 Agent 卡发现、消息路由、任务管理和 SSE 流式传输。注册你的 Agent,定义它们的响应方式,端到端地测试你的多 Agent 工作流,而不需要任何东西真正运行。
import { A2AMock } from "@copilotkit/aimock/a2a";
const a2a = new A2AMock();
a2a.registerAgent({
name: "translator",
description: "Translates text between languages",
skills: [{ id: "translate", name: "Translate" }],
});
a2a.onMessage("translator", "translate", [{ text: "Translated text" }]);
const url = await a2a.start();
// Agent card at: ${url}/.well-known/agent-card.json
// JSON-RPC at: ${url}/
以下是带有增量更新的长时间运行任务的模式:
a2a.onStreamingTask("agent", "long-task", [
{ type: "status", state: "TASK_STATE_WORKING" },
{ type: "artifact", parts: [{ text: "partial result" }], name: "output" },
{ type: "artifact", parts: [{ text: "final result" }], lastChunk: true, name: "output" },
], 50); // 50ms delay between events
关于任务管理、Agent 卡和 JSON-RPC 方法,请参阅完整文档这里。
6、VectorMock
VectorMock提供RAG 管道的确定性检索。
如果你的应用使用了检索增强生成,你的测试就依赖于当前向量数据库中的内容。你的开发索引是混乱的,你的预发布索引与生产环境不匹配,而且每次有人插入新向量时结果都会变化。
VectorMock 是一个 Mock 向量数据库服务器,支持 Pinecone、Qdrant 和 ChromaDB API 格式,包括集合管理、插入、查询和删除操作。
import { VectorMock } from "@copilotkit/aimock/vector";
const vector = new VectorMock();
vector.addCollection("docs", { dimension: 1536 });
vector.onQuery("docs", [
{ id: "doc-1", score: 0.95, metadata: { title: "Getting Started" } },
{ id: "doc-2", score: 0.87, metadata: { title: "API Reference" } },
]);
const url = await vector.start();
// point your vector DB client at `url`
如果你需要根据查询改变结果,可以使用动态处理程序:
vector.onQuery("docs", (query) => {
const topK = query.topK ?? 10;
return Array.from({ length: topK }, (_, i) => ({
id: `result-${i}`,
score: 1 - i * 0.1,
}));
});
以下是兼容 Pinecone、Qdrant 和 ChromaDB API 的端点。
一致的检索结果。完整文档在这里。
7、服务:搜索、重排和内容审核
这些是大多数人忘记 Mock 的 API,也是悄悄让你的测试套件变得非确定性的那些。
AIMock 中的服务提供了 Web 搜索、重排和内容审核的内置 Mock。在你的 LLMock 实例上注册固件模式,请求会根据查询/输入文本进行匹配。不需要单独的服务器。
- Tavily 搜索 - 通过查询模式 Mock Web 搜索结果,端点为
POST /search - Cohere 重排 - Mock 重排后的文档列表,端点为
POST /v2/rerank - OpenAI 内容审核 - Mock 内容审核决策,端点为
POST /v1/moderations。未匹配的请求默认返回未标记。
import { LLMock } from "@copilotkit/aimock";
const mock = new LLMock();
// String pattern — case-insensitive substring match
mock.onSearch("weather", [
{ title: "Weather Report", url: "https://example.com/weather", content: "Sunny today" },
]);
// RegExp pattern
mock.onSearch(/stock\s+price/i, [
{ title: "ACME Stock", url: "https://example.com/stocks", content: "$42.00", score: 0.95 },
]);
// Catch-all — empty results for unmatched queries
mock.onSearch(/.*/, []);
mock.onRerank("machine learning", [
{ index: 0, relevance_score: 0.99 },
{ index: 2, relevance_score: 0.85 },
]);
mock.onModerate("violent", {
flagged: true,
categories: { violence: true, hate: false },
category_scores: { violence: 0.95, hate: 0.01 },
});
如果你只需要兜底响应来阻止真实请求,可以在配置中启用全部三个:
{
"services": {
"search": true,
"rerank": true,
"moderate": true
}
}
字符串模式使用不区分大小写的子串匹配。正则表达式模式进行完整的正则匹配。第一个匹配生效。所有服务请求都记录在日志中,这样你可以检查确切的调用情况。完整文档在这里。
8、漂移检测:在用户之前发现提供商的变更
这是其他 Mock 工具做不到的事情之一。
问题在于:Mock 是你编写固件时 API 行为方式的快照。OpenAI 添加了一个字段。Claude 改变了一个默认值。Gemini 调整了它的流式格式。你的 Mock 仍然通过。CI 是绿色的。然后你的应用在生产环境中崩溃了。
漂移检测每天在 CI 中运行三方比较:
- SDK 类型 - TypeScript 类型定义说明的形状应该是什么
- 真实 API 响应 - 对 OpenAI、Anthropic、Gemini 的实际在线请求
- AIMock 输出 - 相同请求下 Mock 返回的内容
如果这三者中有任何一个不一致,你在 24 小时内就能知道。而不是等到用户提交 Bug 才发现。
以下是检测到漂移时的输出示例:
$ pnpm test:drift
[critical] AIMOCK DRIFT — field in SDK + real API but missing from mock
Path: choices[].message.refusal
SDK: null Real: null Mock: <absent>
[critical] TYPE MISMATCH — real API and mock disagree on type
Path: content[].input
SDK: object Real: object Mock: string
[warning] PROVIDER ADDED FIELD — in real API but not in SDK or mock
Path: choices[].message.annotations
SDK: <absent> Real: array Mock: <absent>
✓ 2 critical (test fails) · 1 warning (logged) · detected before any user reported it
严重级别:
- 严重(Critical):Mock 与真实 API 不匹配,测试立即失败
- 警告(Warning):提供商添加了一个新字段,但 SDK 和 Mock 都还不了解它,记录为早期预警
- 正常(Ok):三个来源一致,无需操作
以下是三方比较在底层的工作方式:
import { extractShape, triangulate, formatDriftReport, shouldFail } from "./schema";
// 1. Get the SDK shape (what TypeScript says)
const sdkShape = openaiChatCompletionShape();
// 2. Call the real API and the mock in parallel
const [realRes, mockRes] = await Promise.all([
openaiChatNonStreaming(config, [{ role: "user", content: "Say hello" }]),
httpPost(`${instance.url}/v1/chat/completions`, { /* ... */ }),
]);
// 3. Extract response shapes
const realShape = extractShape(realRes.body);
const mockShape = extractShape(JSON.parse(mockRes.body));
// 4. Three-way comparison
const diffs = triangulate(sdkShape, realShape, mockShape);
const report = formatDriftReport("OpenAI Chat (non-streaming text)", diffs);
// 5. Critical diffs fail the test
if (shouldFail(diffs)) {
expect.soft([], report).toEqual(
diffs.filter(d => d.severity === "critical")
);
}
自己运行漂移检测:
# Run drift checks against live endpoints
pnpm vitest --config vitest.config.drift.ts
MSW、VidaiMock、Mokksy——它们都做不到这一点。你的 Mock 不应该悄无声息地变得过时。完整文档在这里。
9、录制与回放:告别手写固件
手写固件在简单场景下还可以。但当你需要处理带工具调用的多轮 Agent 对话、流式响应和分支逻辑时,很快就变得痛苦不堪。MSW、VidaiMock、mock-llm、piyook——没有一个 LLM Mock 工具解决了这个问题。
录制与回放的工作方式就像录像机。以下是具体流程:
- 客户端向 AIMock 发送请求
- AIMock 像往常一样尝试固件匹配
- 未命中时:请求被转发到配置的上游提供商
- 上游响应立即回传给客户端
- 如果是流式响应则先合并,然后作为固件保存到磁盘和内存
- 后续相同的请求将匹配新录制的固件
使用 CLI 快速开始:
$ npx aimock --fixtures ./fixtures \
--record \
--provider-openai https://api.openai.com \
--provider-anthropic https://api.anthropic.com
录制的固件会自动保存,格式如下:
{
"fixtures": [
{
"match": { "userMessage": "What is the weather?" },
"response": { "content": "I don't have real-time weather data..." }
}
]
}
AIMock 自动处理六种格式的流式合并:OpenAI SSE、Anthropic SSE、Gemini SSE、Cohere SSE、Ollama NDJSON 和 Bedrock EventStream。认证头部会转发给上游提供商,但绝不会保存在固件中。
它还支持编程式 API,通过 enableRecording() 和 requestTransform 来规范化时间戳等动态数据。
在 CI 中,添加 --strict 参数,这样任何未匹配的请求都会返回 503 并立即失败,而不是悄悄地漏过去。不加这个参数的话,缺失的固件会返回 404,你的测试套件可能永远不会告诉你有真实 API 调用被尝试了。
完整文档在这里。
10、混沌测试:验证你的应用能处理故障
你的应用最终会从 OpenAI 收到一个 500 错误。从工具收到格式错误的 JSON 响应。从向量数据库遭遇中途断开连接。问题是你是在测试中发现还是在生产环境中发现。
混沌测试让你以可配置的概率在三个层级注入故障:服务器级别、每个固件级别或每个单独请求级别。
三种故障模式:
- 丢弃(drop) - 返回 HTTP 500,附带
{"error":{"message":"Chaos: request dropped","code":"chaos_drop"}} - 格式错误(malformed) - 返回 HTTP 200,附带无效 JSON
- 断开连接(disconnect) - 立即销毁 TCP 连接,不返回任何响应
服务器级别的混沌应用于所有请求:
import { LLMock } from "@copilotkit/aimock";
const mock = new LLMock();
mock.setChaos({
dropRate: 0.1, // 10% of requests return 500
malformedRate: 0.05, // 5% return broken JSON
disconnectRate: 0.02, // 2% drop the connection
});
// remove all chaos later
mock.clearChaos();
固件级别的混沌仅针对特定响应:
{
"fixtures": [
{
"match": { "userMessage": "unstable" },
"response": { "content": "This might fail!" },
"chaos": {
"dropRate": 0.3,
"malformedRate": 0.2,
"disconnectRate": 0.1
}
}
]
}
按请求的头部覆盖一切——适用于在单个测试中强制特定故障:
// Force 100% disconnect on this specific request
await fetch(`${mock.url}/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-aimock-chaos-disconnect": "1.0",
},
body: JSON.stringify({ model: "gpt-4", messages: [{ role: "user", content: "hello" }] }),
});
所有混沌事件都通过日志中的 chaosAction 字段进行跟踪,并在 Prometheus 指标中计数。完整文档在这里。
11、还有哪些功能
除了核心模块之外,AIMock 还提供了更多值得了解的功能。
嵌入 - POST /v1/embeddings 完全支持。通过固件返回显式向量,或者让 AIMock 从输入文本的哈希中确定性地生成它们。相同的输入总是产生相同的向量,默认维度为 1536。
WebSocket API - OpenAI Realtime、OpenAI Responses over WebSocket 和 Gemini Live 都使用原始 RFC 6455 帧格式支持。如果你的应用使用语音或实时流式 Agent,这些都已覆盖。
顺序响应 - 对于相同的提示,每次连续调用返回不同的响应。适用于测试重试逻辑和同一消息应有不同行为的多轮工作流。
[
{ "match": { "userMessage": "retry", "sequenceIndex": 0 }, "response": { "content": "First attempt" } },
{ "match": { "userMessage": "retry", "sequenceIndex": 1 }, "response": { "content": "Second attempt" } },
{ "match": { "userMessage": "retry" }, "response": { "content": "Fallback" } }
]
流式物理特性 - 配置 ttft(首个 Token 时间)、tps(每秒 Token 数)和 jitter(抖动)来模拟真实的时间特征。预置的配置文件覆盖了快速模型、推理模型和过载系统。
Prometheus 指标 - 请求计数、延迟直方图和当前固件数量在 /metrics 端点提供。使用 --metrics 标志启用。
Docker 和 Helm - 官方 Docker 镜像可在 ghcr.io/copilotkit/aimock(GitHub Container Registry)获取,用于 CI/CD。它作为纯 HTTP 服务器运行,因此任何语言都可以使用它。测试运行端不需要 Node.js。
12、AG-UI 在生产环境中使用 AIMock
AG-UI 是将 AI Agent 连接到前端应用的开放协议——已被 LangGraph、CrewAI、Mastra、Google ADK、AWS Bedrock AgentCore 等采用。
它在端到端测试套件中使用 AIMock,通过固件驱动的响应验证跨 LLM 提供商的 AI Agent 行为。AG-UI 是一个在生产环境中使用的协议。如果你想看看真实大规模的 AIMock 设置是什么样的,那是一个很好的起点。
13、迁移到 AIMock
文档包含了 MSW、VidaiMock、mock-llm、Python Mock 和 Mokksy 的逐步迁移指南。你会找到并排比较以及你能获得和保留什么的详细说明。
如果你正在使用 MSW,不必替换所有内容:你可以保留 MSW 用于一般的 REST 和 GraphQL Mock,只在 AI 端点上使用 AIMock。
如果你已经在使用 @copilotkit/llmock,升级就是一个查找和替换:
pnpm remove @copilotkit/llmock
pnpm add @copilotkit/aimock
LLMock 类、所有固件格式和编程式 API 都没有变化。你现有的测试将原样工作。
14、开始使用
npm install @copilotkit/aimock
- 文档: aimock.copilotkit.dev
- GitHub: github.com/CopilotKit/aimock
- npm: @copilotkit/aimock
- CLI:AIMock CLI
你的测试套件应该和你的技术栈一样完整。这就是 AIMock 的意义所在。
原文链接: AIMock: One Mock Server For Your Entire AI Stack
汇智网翻译整理,转载请标明出处