ARTICLE DETAIL

资讯详情

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

OGX 测试录制系统深度解析:基于 API 录制回放的确定性集成测试方案

OGX 测试录制系统深度解析:基于 API 录制回放的确定性集成测试方案 OGX 测试录制系统深度解析基于 API 录制回放的确定性集成测试方案【免费下载链接】ogxOpen GenAI Stack项目地址: https://gitcode.com/GitHub_Trending/ll/ogx本文以 OGXOpen GenAI Stack仓库中的测试录制系统为核心系统讲解其目录结构、JSON 录音格式、字段规范化规则、四种运行模式以及背后的源码实现原理。读完本文你将掌握如何使用OGX_TEST_INFERENCE_MODE环境变量在 replay、record-if-missing、record、live 四种模式间切换理解请求哈希匹配与测试隔离机制并能独立完成录音的新增、回放与批量重规范化操作。一、为什么需要录制回放OGX 的集成测试位于 tests/integration/ 目录覆盖 inference、agents、responses、vector_io、tool_runtime 等众多 API 面。这些测试天然依赖 OpenAI、Ollama、Google Vertex AI、Tavily 等外部服务——如果每次跑测试都实时调用真实 API会带来三个问题成本与稳定性真实 API 调用产生费用且受网络抖动、服务限流影响测试结果不稳定确定性缺失LLM 生成内容带有随机性同样的请求两次运行可能返回不同文本导致断言不可靠开发体验差开发者本地没有 API Key 或无法访问外部服务时测试无法运行。录制回放record/replay系统的目标正是解决这些问题预先将一次真实的 API 调用请求与响应配对以 JSON 文件落盘测试运行时不再访问真实服务而是按请求哈希查找并重放录音从而在完全离线、确定性、零成本的前提下验证全部行为。该系统的入口说明位于 tests/integration/recordings/README.md核心实现集中在 src/ogx/testing/api_recorder.py约 1759 行配套的批量规范化脚本为 scripts/normalize_recordings.py。二、目录结构录音放在哪里README 指出recordings/目录存放推理操作的请求/响应 JSON 对。实际上录音并非集中在单一目录而是按测试文件所在目录就近存放tests/integration/ ├── agents/recordings/ # agents 套件的录音865 个 JSON 文件 ├── responses/recordings/ # responses 套件 ├── inference/recordings/ # inference 套件 ├── messages/recordings/ ├── interactions/recordings/ ├── tool_runtime/recordings/ └── common/recordings/ # 会话级session 级公共录音的默认目录这一布局由 src/ogx/testing/api_recorder.py 中的ResponseStorage._get_test_dir()决定对测试tests/integration/inference/test_foo.py::test_bar录音目录解析为tests/integration/inference/recordings/。目录不预先创建而是在首次写入时通过_ensure_directory()自动mkdir(parentsTrue, exist_okTrue)。存储类ResponseStorage的默认基目录由常量DEFAULT_STORAGE_DIR REPO_ROOT / tests/integration/common指定api_recorder.py即 session 级共享录音落在tests/integration/common/recordings。三、录音格式每个 JSON 文件长什么样README 定义了录音文件的核心结构request规范化后的请求参数method、endpoint、body与response响应体由 Pydantic 模型序列化而来。实际写盘时见ResponseStorage.store_recording()api_recorder.py文件顶层还包含test_id与id_normalization_mapping字段。以 tests/integration/agents/recordings/007a9180a7aa38e17c1135ebf1f75e0d5ce1ea58e2261deba8c41e51196078ec.json 为例一个真实的 OpenAI 风格录音{ test_id: tests/integration/agents/test_openai_responses.py::test_output_guardrails_unsafe_content[openai_client-txtollama/llama3.2:3b-instruct-fp16-False], request: { method: POST, url: http://0.0.0.0:11434/v1/v1/chat/completions, headers: {}, body: { model: llama-guard3:1b, messages: [ { role: user, content: Task: Check if there is unsafe content ... } ], stream: false, temperature: 0.0 }, endpoint: /v1/chat/completions, model: llama-guard3:1b }, response: { body: { __type__: openai.types.chat.chat_completion.ChatCompletion, __data__: { id: rec-007a9180a7aa, choices: [ { finish_reason: stop, message: { content: safe, role: assistant } } ], created: 0, model: llama-guard3:1b, object: chat.completion, system_fingerprint: fp_ollama, usage: { completion_tokens: 2, prompt_tokens: 414, total_tokens: 416 } } }, is_streaming: false }, id_normalization_mapping: {} }几个值得注意的细节Pydantic 类型保真响应体不是普通字典而是带__type__完整模块路径 类名和__data__的包装结构_serialize_responseapi_recorder.py。回放时通过_deserialize_response找到原类并用model_validate/model_construct重建对象api_recorder.py保证测试拿到的仍是强类型 Pydantic 对象流式响应当is_streaming: true时body变为 chunk 列表每个 chunk 同样按__type__/__data__包装回放时通过生成器逐个 yield异常也可以被录制录制模式下若真实调用抛异常系统会通过serialize_exception把异常序列化存入录音is_exception: true回放时用deserialize_exception原样还原异常从而验证错误路径模型列表特殊命名对/v1/models、/api/tags等模型列表端点文件名带有模型标识摘要如models-{hash}-{digest}.json用于区分不同服务端返回的不同模型集合_model_identifiers_digestapi_recorder.py。回放时_combine_model_list_responses还会把多份录音按模型 ID 做并集合并api_recorder.py。四、规范化机制让 git diff 干净如初LLM 服务的响应中存在大量每次运行都不同、但对测试行为无影响的字段如请求 ID、时间戳、耗时。若原样写入录音每次重录都会产生巨大的 git diff淹没真正有意义的变更。为此系统在写盘时自动执行规范化_normalize_responseapi_recorder.py。OpenAI 风格响应字段规范化结果说明idrec-{request_hash[:12]}基于请求的确定性哈希取前 12 位仅对object ! model的完成类响应生效模型对象保留真实 IDcreated0epoch时间戳归零模型对象的 created 可能保留Ollama 风格响应字段规范化结果created_at1970-01-01T00:00:00.000000Ztotal_duration0load_duration0prompt_eval_duration0eval_duration0Ollama 的 duration 字段依赖系统负载是 diff 噪声的主要来源因此全部归零。注意只有当字段值非None时才覆盖避免破坏结构语义。更深层的哈希级规范化除了响应字段请求哈希计算阶段也做了多重建模以保证同一次请求在录制与回放时哈希完全一致_normalize_body_for_hash与normalize_inference_requestapi_recorder.py 与 api_recorder.py浮点数统一round(value, 5)字符串内嵌的长小数4 位以上统一四舍五入到 5 位避免不同服务端浮点精度差异导致哈希漂移对 file_search 相关的向量检索分数、attributes 字典、document_id、citation 标记等运行期不稳定字段替换为占位符__NORMALIZED___normalize_file_search_metadataapi_recorder.py对 Bedrock 的 OpenAI 兼容端点排除stream_options字段仅剥离extra_body/extra_query中的project_id真实凭证与 dummy 凭证不同但保留其他合法参数如 vLLM 的guided_choice。这些规范化共同保证了重录测试 → 只产生最小 diff让代码评审可以聚焦真实的行为变化。五、四种运行模式与实战命令README 给出了通过环境变量OGX_TEST_INFERENCE_MODE控制的四种模式对应源码中的枚举APIRecordingModeapi_recorder.pylive、record、replay、record-if-missing。模式读取逻辑在get_api_recording_mode()api_recorder.py未设置时默认replay这与 tests/integration/conftest.py 中的 session 级兜底保持一致。1. Replay 模式默认OGX_TEST_INFERENCE_MODEreplay pytest tests/integration/只允许使用已有录音找不到录音即失败。此时api_recorder抛出RuntimeError错误信息会附带 model、method、url 与提示命令Recording not found for request hash: ... Model: llama-guard3:1b | Request: POST http://0.0.0.0:11434/v1/v1/chat/completions Run ./scripts/integration-tests.sh --inference-mode record-if-missing with required API keys to generate.这保证了 CI 与本地环境在无任何外部依赖时依然 100% 确定性地运行。2. Record-if-missing 模式新增测试推荐OGX_TEST_INFERENCE_MODErecord-if-missing pytest tests/integration/存在录音则回放不存在则现场调用真实 API 并落盘。这是迭代开发新测试的首选模式先在record-if-missing下跑一遍生成全部录音之后切回replay获得离线确定性。3. Record 模式强制重录OGX_TEST_INFERENCE_MODErecord pytest tests/integration/强制录制所有 API 交互并覆盖已有录音。使用需谨慎它可能因 LLM 输出变化、模型版本升级而改写录音内容因此重录后应检查 git diff确认只有预期内的变化。4. Live 模式OGX_TEST_INFERENCE_MODElive pytest tests/integration/完全跳过录音与回放所有请求直连真实 API。setup_api_recording()在该模式下直接返回None不安装任何 monkey patchapi_recorder.py。此模式适合调试真实服务问题但会失去确定性。六、关键环境变量一览结合 api_recorder.py 与 tests/integration/conftest.py录制系统实际受以下环境变量控制环境变量默认值作用OGX_TEST_INFERENCE_MODEreplay四种运行模式live / record / replay / record-if-missingOGX_TEST_RECORDING_DIRtests/integration/common录音存储基目录session 级OGX_TEST_DEBUG空设为1/true/yes开启录制调试日志打印哈希计算、路径解析细节OGX_TEST_STACK_CONFIG_TYPElibrary_clientserver表示服务端模式走 HTTP 头注入 test_id 路径TEST_API_BASE_URL—判定本地 OGX 测试服务模型列表 URL 时使用七、请求哈希与匹配原理录音文件以请求哈希命名{request_hash}.json完整 SHA256见store_recording回放时通过find_recording(request_hash)精确查找api_recorder.py。哈希由normalize_inference_request计算method endpoint(path) 规范化 body test_id组成的 JSON 经sort_keysTrue排序后取 SHA256。这里有两个关键设计test_id 隔离哈希包含当前测试的 nodeid因此不同测试即使发出完全相同的请求哈希也不同录音互不串扰。test_id 通过 src/ogx/core/testing_context.py 中的ContextVar维护tests/integration/conftest.py 的_track_test_contextfixture 在每个测试运行前set_test_context(request.node.nodeid)模型列表端点例外/v1/models、/api/tags等基础设施级请求把 test_id 置空_is_shared_model_list_requestapi_recorder.py因为模型发现发生在 session 初始化阶段必须跨测试共享同一份录音。查找还有 fallback 机制先查测试专属目录tests/integration/suite/recordings/找不到再回退到基目录tests/integration/common/recordings/兼容session 级录制、测试级回放的场景。在server 模式下test_id 无法通过进程内 ContextVar 传递系统通过patch_httpx_for_test_id()api_recorder.pypatch OpenAI/OgxClient 的_prepare_request把__test_id注入X-OGX-Provider-Data请求头服务端再从 header 还原上下文_get_test_context_with_fallbackapi_recorder.py。此外为了保证测试内生成的资源 IDfile-、vs_、call_ 等在录制与回放间一致系统还通过set_id_override覆盖 OGX 的 ID 生成器按测试上下文为每种 ID 分配确定性连续块_allocate_test_scoped_idapi_recorder.py。八、覆盖范围哪些客户端被 patchpatch_inference_clients()api_recorder.py一次性安装全部 monkey patch覆盖OpenAI 客户端chat/completions、completions、embeddings、models.list、responses.createOllama AsyncClientgenerate、chat、embed、ps、pull、listGoogle genai可选generate_content、generate_content_stream、embed_content仅当 google-genai 已安装工具运行时Tavily 搜索invoke_tool工具请求按 provider tool_name kwargs 哈希匹配aiohttpNVIDIA/vLLM 的 rerank 端点/rerank在 HTTP 层拦截确保客户端侧后处理如max_num_results在回放时仍真实执行httpx/v1/messages、/interactions的 Messages API 直通路径支持非流式与 SSE 流式aiter_lines逐行录制/重放。值得注意的是文件处理器如 PyPDF刻意不做录制回放_patched_file_processor_methodapi_recorder.py它们是本地确定性操作且file_id每次运行随机生成会导致哈希查找失败因此始终执行真实实现。unpatch_inference_clients()在上下文退出时恢复所有原始方法。九、重新规范化已有录音当你更新了规范化逻辑、或想清理历史录音中的噪声字段时运行python scripts/normalize_recordings.py该脚本递归扫描tests/下所有名为recordings的目录对每个 JSON 重新应用 OpenAI/Ollama 字段规范化scripts/normalize_recordings.py。脚本刻意不修改请求体——因为那会改变请求哈希导致录音查找失配见脚本第 64-65 行注释。执行前建议先预览python scripts/normalize_recordings.py --dry-run--dry-run只输出Would normalize清单与Summary: N/M files modified统计不落盘任何文件。十、与集成测试运行脚本的配合录制模式通常不直接通过 pytest 环境变量驱动而是经 scripts/integration-tests.sh 统一封装它内部把--inference-mode映射为OGX_TEST_INFERENCE_MODE并自动启动/停止 OGX server 或 docker 容器。常用组合# 生成/更新录音需要配置好 API Key 与 --setup 对应的模型 ./scripts/integration-tests.sh --stack-config server:ci-tests --suite base --setup ollama --inference-mode record # 迭代开发缺失才录制 ./scripts/integration-tests.sh --stack-config server:ci-tests --suite base --setup ollama --inference-mode record-if-missing # 日常确定性回归默认 replay ./scripts/integration-tests.sh --stack-config server:ci-tests --suite base --setup ollama运行失败时replay模式抛出的错误信息会直接建议使用--inference-mode record-if-missing配合对应 API Key 重新生成形成完整的开发闭环。结语OGX 的测试录制系统是一套兼顾离线确定性与真实行为保真的工程化方案请求哈希保证精确匹配test_id 注入保证测试隔离字段规范化保证 diff 可读Pydantic 类型包装保证回放对象强类型四模式切换覆盖从日常回归、新增测试到强制重录的全场景。对开发者而言理解这套机制意味着新增测试时用record-if-missing一跑即得录音提交前切回replay即可获得稳定、离线、零成本的集成测试体验。【免费下载链接】ogxOpen GenAI Stack项目地址: https://gitcode.com/GitHub_Trending/ll/ogx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表