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/aimockAIMock 是开源的完全免费。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 应用通信的一切。以下是它包含的内容LLMock11 个提供商完整的流式传输、工具调用、推理模型MCPMock本地 MCP 服务器支持完整的 JSON-RPC 2.0、会话管理、工具、资源、提示A2AMockAgent 卡发现、消息路由、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、LLMockLLMock有完整的流式传输、工具调用、跨所有主流提供商的推理能力.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、MCPMockMCPMock用于测试工具集成的 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、A2AMockA2AMock无需真实 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、VectorMockVectorMock提供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 /searchCohere 重排- Mock 重排后的文档列表端点为POST /v2/rerankOpenAI 内容审核- 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\sprice/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严重级别严重CriticalMock 与真实 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.tsMSW、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 dont 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/aimockGitHub Container Registry获取用于 CI/CD。它作为纯 HTTP 服务器运行因此任何语言都可以使用它。测试运行端不需要 Node.js。12、AG-UI 在生产环境中使用 AIMockAG-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/aimockLLMock类、所有固件格式和编程式 API 都没有变化。你现有的测试将原样工作。14、开始使用npm install copilotkit/aimock文档aimock.copilotkit.devGitHubgithub.com/CopilotKit/aimocknpmcopilotkit/aimockCLIAIMock CLI你的测试套件应该和你的技术栈一样完整。这就是 AIMock 的意义所在。原文链接AIMockAI技术栈Mock套件 - 汇智网