ARTICLE DETAIL

资讯详情

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

Mastra 实验 Worker 冒烟测试指南:`mastra experiment build` 与 protocol-v1 NDJSON 协议验证

Mastra 实验 Worker 冒烟测试指南:`mastra experiment build` 与 protocol-v1 NDJSON 协议验证 Mastra 实验 Worker 冒烟测试指南mastra experiment build与 protocol-v1 NDJSON 协议验证【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra导读本文围绕 Mastra 仓库中mastra experiment build实验 Worker 的冒烟测试方案展开讲解如何构建一个独立standalone的 companion worker并通过 protocol-v1 NDJSON 格式向其发起一次完整的实验请求、验证其事件生命周期。读完本文你将掌握实验 Worker 的构建命令与产物结构、experiment-worker-manifest.json清单中协议身份launch/protocol/build的解读方法、数据集 SHA-256 认证与规范化canonicalization的计算方式以及一份可直接复制的端到端验证脚本与失败分类清单。一、实验 Worker 是什么一次构建、异地启动、协议驱动在 Mastra 中mastra experiment build会基于用户项目的src/mastra入口打包出一个独立的实验 Worker 产物。它不依赖原项目继续运行——构建完成后产物可以被拷贝到任意目录、从清单描述的workingDirectory以node index.mjs启动然后通过标准输入stdin读取 NDJSON 格式的协议帧并输出协议事件。这一设计在 e2e-tests/experiment-worker/README.md 中被称为 validates the published, installedmastra experiment buildcontract from isolated customer projects——即从隔离的客户项目中验证已发布、已安装的 CLI 契约。冒烟测试的核心目标是验证两件事mastra experiment build能产出可独立运行的 Worker 产物该 Worker 能完整处理一个 protocol-v1 NDJSON 实验请求正确认证、完整生命周期、干净退出。对应实现上构建命令的核心逻辑位于 packages/cli/src/commands/experiment/build.ts它解析--dirMastra 源码目录默认src/mastra、--root、--output-dir默认.mastra/experiment-worker等参数通过ExperimentBundler执行prepare→bundle→writeArtifactManifest三步最终在输出目录落盘 Worker 入口与清单文件。二、步骤 1构建 Worker 产物在项目根目录执行pm指代你使用的包管理器如pnpm/npm/yarnpm exec mastra experiment build --output-dir .mastra/experiment-worker构建通过后需要确认以下产物与特征构建命令以退出码0结束experiment-worker-manifest.json文件存在清单文件摘要校验完整且不会出现EISDIR错误即清单哈希计算时不能把 pnpm 目录符号链接当作普通文件去读原生依赖native dependencies要么被打包进产物要么被显式 externalize 且可解析生成的 pnpm build 审批approvals中包含布尔值而不是set this to true or false之类的占位符。从源码看清单的写入发生在 build.ts 的bundler.writeArtifactManifest(outputDirectory, pkgJson.version)其中pkgJson.version即 CLI 自身版本会被记录进清单的build.cliVersion字段。原生依赖场景以 DuckDB 为例如果主项目包含 DuckDB 等原生存储依赖文档建议另外构建一个最小的隔离 Mastra 入口只含一个 workflow 和一个确定性 scorer以区分失败是项目特有还是与原生依赖无关。这一点在 E2E 测试中有完整对应native-duckdb.test.ts使用独立的 fixture 安装根DuckDB never shares the runtime fixtures dependency layout构建后断言产物package.json的dependencies中包含duckdb/node-api从而验证原生依赖被显式 externalize 而非静默缺失。对应实现见 e2e-tests/experiment-worker/tests/native-duckdb.test.ts。三、步骤 2从清单读取协议身份而不是假设默认值experiment-worker-manifest.json是 Worker 如何启动、说何种协议的唯一事实来源source of truth。因此文档明确要求读取清单而非假设默认值。MANIFEST.mastra/experiment-worker/experiment-worker-manifest.json jq {launch, protocol, buildId: .build.buildId} $MANIFEST撰写本文时清单上报的默认值如下但后续所有步骤都应从清单动态读取并核对任何差异都需要记录并上报字段默认值launch.executablenodelaunch.arguments[index.mjs]launch.workingDirectory.protocol.framingndjsonprotocol.versions[1]protocol.datasetCanonicalizationVersion1清单的完整结构在 E2E 测试的 inspect-manifest.ts 中被类型化为ExperimentWorkerManifest接口包含六大部分artifactVersion与kind产物格式版本当前为1与种类mastra-experiment-workerbuildbuildId、cliVersion、createdAtprotocol支持的版本列表、framingndjson、数据集规范化版本launch可执行文件、参数、工作目录dependencies源项目的 manifest 与 lockfile 引用artifact与filesSHA-256 内容摘要、排除清单experiment-worker-manifest.json与node_modules必须被排除、每个文件的路径与摘要。该测试还校验了文件列表按路径排序、路径不能逃逸产物根目录、以及contentDigest必须与 files 条目推导出的摘要一致。另一个关键约定packet.artifacts.buildId永远来自.build.buildId。用一个刻意错误的 build ID 发起请求Worker 应在加载实验之前就以协议退出码70失败。四、步骤 3运行一个正确认证的请求核心验证这一步是冒烟测试的核心在项目根目录下用下面这段脚本生成一个数据集条目 → 递归排序对象键保留数组顺序完成规范化 → 计算 SHA-256 认证摘要 → 依据清单的协议版本与规范化版本写出完整的 NDJSON 请求 → 再从launch.workingDirectory按launch.executablelaunch.arguments启动 Worker。node --input-typemodule .mastra/experiment-request.ndjson EOF import { createHash } from node:crypto; import { readFileSync } from node:fs; const canonicalize value value null || typeof value ! object ? JSON.stringify(value) : Array.isArray(value) ? [${value.map(canonicalize).join(,)}] : {${Object.keys(value) .sort() .map(key ${JSON.stringify(key)}:${canonicalize(value[key])}) .join(,)}}; const manifest JSON.parse( readFileSync(.mastra/experiment-worker/experiment-worker-manifest.json, utf8), ); const assert (actual, expected, what) { if (JSON.stringify(actual) ! JSON.stringify(expected)) { throw new Error( manifest ${what} is ${JSON.stringify(actual)}, expected ${JSON.stringify(expected)} — update the launch command and request below to match the manifest, and report the change, ); } }; // This request is written as one NDJSON line, so a different framing invalidates it. assert(manifest.protocol.framing, ndjson, protocol.framing); const protocolVersion manifest.protocol.versions.at(-1); const canonicalizationVersion manifest.protocol.datasetCanonicalizationVersion; const items [{ id: item-1, input: { value: 21 }, toolMocks: [] }]; const digest createHash(sha256).update(canonicalize(items)).digest(hex); const experimentId smoke-experiment-1; console.log(JSON.stringify({ type: run, protocolVersion, supportedProtocolVersions: manifest.protocol.versions, experimentId, jobId: smoke-job-1, attempt: 1, idempotencyKey: smoke-attempt-1, deadlineAt: new Date(Date.now() 30_000).toISOString(), datasetAttestation: { itemCount: items.length, digest, canonicalizationVersion }, packet: { protocolVersion, experimentId, tenant: {}, environment: {}, artifacts: { buildId: manifest.build.buildId }, target: { type: workflow, id: YOUR_WORKFLOW_REGISTRY_KEY }, dataset: { itemCount: items.length, digest, canonicalizationVersion, items }, scorers: [], limits: { concurrency: 1, timeoutMs: 5000 }, policies: { allowedToolIds: [], allowedNetworkHosts: [] }, secretReferences: [], }, })); EOF # Build the invocation from manifest.launch rather than assuming it. # workingDirectory and arguments are relative to the worker output directory. WORKER_DIR.mastra/experiment-worker MANIFEST$WORKER_DIR/experiment-worker-manifest.json LAUNCH_CWD$(cd $WORKER_DIR/$(jq -r .launch.workingDirectory $MANIFEST) pwd) LAUNCH_EXE$(jq -r .launch.executable $MANIFEST) LAUNCH_ARGS() while IFS read -r arg; do LAUNCH_ARGS($arg); done (jq -r .launch.arguments[] $MANIFEST) (cd $LAUNCH_CWD $LAUNCH_EXE ${LAUNCH_ARGS[]}) \ .mastra/experiment-request.ndjson \ .mastra/experiment-stdout.ndjson \ 2 .mastra/experiment-stderr.log status$? printf worker exit code: %s\n $status cat .mastra/experiment-stdout.ndjson cat .mastra/experiment-stderr.log 2其中YOUR_WORKFLOW_REGISTRY_KEY需要替换为项目里已注册的 workflow key在 run-protocol.ts 中目标类型同样支持agent与workflow两种target.type。注意datasetAttestation与packet.dataset中的 item count、digest、canonicalizationVersion 三者必须一致否则认证会失败。关键概念数据集规范化与认证规范化canonicalization递归地对对象键排序、保留数组顺序得到确定性的字符串后再计算 SHA-256。这段逻辑并非冒烟脚本独有——E2E 测试的 run-protocol.ts 中canonicalize的实现与冒烟脚本完全一致而createRunRequest也以相同方式生成datasetAttestation与packet.dataset并额外通过manifest.build.buildId填充packet.artifacts.buildId。期望的成功生命周期accepted → run-started → item-completed → terminal通过标准Worker 退出码为0协议事件序号连续从 0 递增无跳号终态为terminal且结果completeditem 输出与目标workflow/agent的预期输出一致诊断信息走 stderr协议事件走 stdout两者严格分离。事件序号的连续性校验在 run-protocol.ts 中有直接体现parseProtocolOutput要求每个事件的sequence与数组下标一一对应否则抛出Non-contiguous protocol sequence。而完整生命周期的断言则在 minimal-agent.test.ts 中期望事件序列精确等于[accepted, run-started, item-completed, terminal]。顺带一提E2E 测试还验证了 Worker 的可移植性portability产物被拷贝到独立目录、删除原项目后再运行见copyArtifact与deleteRoots: [projectRoot]的用法同时通过minimalWorkerEnvironment仅保留PATH、HOME、TMPDIR等白名单环境变量在近乎干净的环境中启动 Worker模拟真实生产启动条件。五、步骤 4失败分类——产品缺陷 vs 环境噪音冒烟测试中出现的下列问题应被当作**产品缺陷product issues**处理而不是冒烟环境噪音持久化前未创建实验记录调用方传入的experimentId被直接用于持久化而未先创建实验记录导致Experiment not found或存储层 update-not-found 失败清单哈希遇到EISDIR清单哈希计算把 pnpm 目录符号链接当作文件读取而抛出EISDIR原生依赖无法打包也无法 externalize原生依赖既无法被 bundle 进产物也无法干净地 externalize 到 Worker 中可解析构建策略中存在未解决的审批占位符生成的 build policy 中包含未解析的审批占位符如set this to true or false。文档特别强调临时性的 no-persistence patch 只能用来证明剩余协议路径是通的它不能使已发布的 Worker 通过验收——换句话说绕过持久化问题并不等于修复了产品缺陷。六、报告要素完成冒烟测试后报告应至少记录以下证据构建命令与产物路径构建 IDbuild.buildId请求目标target type 与 idWorker 退出码事件序列完整生命周期回放终端结果terminal statusstderr 诊断信息使用过的任何 workaround。这与 E2E 套件的证据模型一致每个测试用例通过recordAssertionEvidence记录结构化断言证据见 assertion-evidence.ts并把协议转录.protocol.ndjson与构建日志.build.log落盘到报告目录便于事后追溯。七、与 E2E 套件的衔接从冒烟到全量验证本文所述的冒烟流程在仓库中对应一套完整的 E2E 测试套件e2e-tests/experiment-worker/其本地命令为pnpm install --frozen-lockfile pnpm test:experiment # 确定性、无凭据的实验门禁用于 PR pnpm test:full # 全量包管理器、workspace、browser、LSP、原生依赖、Docker/Postgres、可移植性、负边界 pnpm test:scenario -- minimal-agent # 按场景选择 pnpm test:workflow-routing # workflow 路由其中test:experiment是 PR 上使用的确定性门禁test:full:strict额外覆盖包管理器、workspace、浏览器、LSP、原生依赖、Docker/Postgres、可移植性与负边界测试。套件默认会构建 CLI、把 snapshot 包发布到临时的 Verdaccio registry 并在结束后清理CI 模式下则由MASTRA_E2E_REGISTRY_STORAGE、MASTRA_E2E_REGISTRY_CONFIG、MASTRA_E2E_REGISTRY_ARTIFACT_DIGEST等环境变量注入不可变的发布产物并要求安装前校验发布者的规范 registry digest防止拷贝或下载的存储被篡改。从源码结构看可以推断整套验证围绕清单manifest契约展开构建产物 → 检查清单 → 拷贝产物到独立目录 → 以最小环境启动 → 断言协议事件与退出码 → 记录证据并清理资源OwnedResources只清理 harness 创建的临时路径、进程组、端口、registry、数据库与容器。这套流水线把本文的四个冒烟步骤固化成了可重复、可审计的自动化门禁。结语mastra experiment build冒烟测试的核心方法论可以概括为三条原则一切以 manifest 为准不假设 launch 与 protocol 默认值、认证先行数据集规范化 SHA-256 摘要必须与清单版本一致、诊断与协议分离stdout 只承载协议事件。按照本文的四个步骤——构建、检查协议身份、运行正确认证的请求、分类失败——你可以在任意 Mastra 项目中快速验证实验 Worker 产物的完整性与协议兼容性并把结果整理成可追溯的报告。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表