ARTICLE DETAIL

资讯详情

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

Mastra 记忆(Memory)冒烟测试实战:从 Studio UI 到 API 的会话持久性验证指南

Mastra 记忆(Memory)冒烟测试实战:从 Studio UI 到 API 的会话持久性验证指南 Mastra 记忆Memory冒烟测试实战从 Studio UI 到 API 的会话持久性验证指南【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra导读本文以 Mastra 仓库自带的冒烟测试规范memory.md为主线系统讲解如何验证 Mastra Agent 的对话记忆是否真正记得住既包含在 Studio 界面中逐步验证上下文保留、导航持久化、跨会话持久化的操作流程也包含在--skip-browser场景下通过 curl 调用/agents/:agentId/generate与/memory/threads接口完成的两调用持久性断言。读完本文你将掌握一套可复制、可运行的记忆功能验收清单并能理解请求体中memory: { thread, resource }嵌套结构、resourceId大小写敏感等容易踩坑的底层原因。一、记忆冒烟测试要解决什么问题Mastra 的mastra/memory包为 Agent 提供跨消息、跨线程的持久化会话历史。在 packages/memory/README.md 中接入方式是把一个Memory实例挂到 Agent 上并在生成回复时传入稳定的线程与资源 IDimport { Agent } from mastra/core/agent; import { Memory } from mastra/memory; const agent new Agent({ id: support-agent, name: Support agent, instructions: Answer support questions using the conversation history., model: openai/gpt-5.6-sol, memory: new Memory(), }); const response await agent.generate(What did we discuss last time?, { memory: { thread: conversation-123, resource: user-456 }, });记忆冒烟测试对应smoke test --test memory的核心目标是验证三件事上下文保留Agent 能否在后续提问中引用先前消息例如用 it 指代上文提到的东京天气会话持久化在页面导航、刷新、甚至跨会话后历史消息是否仍然可见线程隔离新线程是否从零开始旧线程是否仍可访问。该测试在整个冒烟测试矩阵中属于必测项定义于 SKILL.md 的 Mandatory Test Checklist第 7 项--test memory或全量运行前提是先完成 Setup见 setup.md并有一个配置了记忆的 Agent 实例。二、Studio UI 五步验证流程当不带--skip-browser运行时记忆测试需要在 Studio 界面默认http://localhost:4111中依次完成以下五个检查点。1. 开启全新会话导航到/agents路由选择一个已配置记忆的 Agent例如 Weather Agent发送Whats the weather in Tokyo?等待回复并记录结果。2. 验证上下文保留发送跟进消息What about comparing it to London?观察 Agent 是否在回复中引用了 Tokyo记录 Agent 是否理解 it 指代的是天气这一主题。这一步是记忆功能最直接的信号如果第二条消息的回复里完全没有 Tokyo 的影子说明请求携带的线程上下文没有生效。3. 验证导航持久性导航离开例如前往/tools再导航回/agents并选择同一个 Agent观察会话历史是否仍然可见记录界面显示了哪些历史消息。前端路由切换不应破坏内存中已加载的会话状态此步验证的是 Studio UI 的会话管理而不是存储层。4. 验证跨会话持久性视环境而定记录当前 thread/conversation刷新页面F5重新进入同一个 Agent记录历史是否保留。这是区分内存存储与持久化存储的关键一步刷新后历史丢失基本可以判定使用的是默认的内存存储详见下文第四节。5. 验证新线程在 UI 支持的前提下开启一个全新会话new thread确认新线程没有任何历史记录旧线程是否仍然可访问。观察记录表检查项需要记录的内容Context retention上下文保留Agent 是否引用了之前的消息Navigation导航导航离开后历史是否仍可见Page refresh页面刷新刷新后历史是否保留New thread新线程开启全新会话时的行为对应到浏览器自动化任务可以抽象为下面的操作序列同样适用于 UI 自动化工具Navigate to: /agents Click: Select agent Type: Whats the weather in Tokyo? Send: Message Wait: For response Type: What about comparing it to London? Send: Message Verify: Response references Tokyo Navigate to: /tools Navigate to: /agents Click: Same agent Verify: Previous messages visible Refresh: Page (F5) Navigate to: /agents Click: Same agent Verify: History still visible (if persistent storage)三、存储配置决定刷新后是否记得的根本因素UI 测试第 4 步的结果完全取决于Memory实例背后挂载的存储实现。冒烟测试规范给出了四类配置的对照类型持久性配置方式In-memory仅会话内有效默认LibSQL持久化mastra/libsqlstoragePostgreSQL持久化mastra/pgstorageTurso持久化mastra/tursostorage在仓库中这三个持久化存储包分别位于 stores/libsql/package.jsonmastra/libsql、stores/pg/package.jsonmastra/pg和 stores/turso/package.jsonmastra/turso。冒烟测试命令行参数--db的取值正是libsql、pg、turso三者见 SKILL.md 的参数表默认是libsql。从 packages/memory/src/index.ts 的Memory类实现可以印证两点线程归属校验validateThreadIsOwnedByResourceindex.ts会检查线程是否属于当前请求的resourceId如果线程存在但属于其他资源会直接抛出Thread with id ... is for resource ...错误——这解释了为何请求必须同时携带匹配的thread与resource查询方向语义recall方法在未显式指定orderBy且限制了消息条数时会先按createdAt DESC取最新消息再反转恢复时间正序index.ts保证喂给 LLM 的历史始终是最近 N 条且按时间正序。工程提示集成测试中记忆相关用例分别覆盖了 LibSQL、PG、Upstash 等存储见 packages/memory/integration-tests/src/with-libsql-storage.test.ts 与 with-pg-storage.test.ts冒烟测试选型时可参考这些真实用例的存储接入方式。四、Curl / API 验证--skip-browser场景当显式传入--skip-browser时记忆测试退化为纯 API 验证。这一部分包含整份规范中信息密度最高、最容易被误用的细节务必逐条核对。4.1 请求体形状memory必须是嵌套对象当前/agents/:agentId/generate路由要求 thread/resource 位于memory对象内{ messages: [{ role: user, content: ... }], memory: { thread: thread-id, resource: resource-id } }顶层threadId/resourceId只被已废弃的/generate-legacy路由读取。把它们发给/generate会被静默丢弃表现就是Agent 好像失忆了——这不是记忆功能坏了而是请求格式不对。这一行为在服务端源码中有明确对应新路由POST /agents/:agentId/generatehandlers/agents.ts从 body 中解构出memory选项做线程归属/权限校验后原样传给agent.generatehandlers/agents.ts旧路由POST /agents/:agentId/generate-legacyhandlers/agents.ts使用agentExecutionLegacyBodySchema其中声明了顶层resourceId、threadId以及兼容小写的resourceid字段schemas/agents.ts随后调用agent.generateLegacy(messages, { resourceId, threadId, ... })handlers/agents.ts。两条路由的 schema 边界决定了参数去向新路由只认memory对象旧路由只认顶层字段。4.2 两调用持久性检查规范给出了一个可直接运行的验证脚本——用同一个threadresource先播种上下文、再验证回忆TIDsmoke-memory-$(date %s) RIDsmoke-user # Call 1: seed context curl -s -X POST http://localhost:4111/api/agents/agentKey/generate \ -H Content-Type: application/json \ -d {\messages\:[{\role\:\user\,\content\:\Remember: my name is Abhi.\}],\memory\:{\thread\:\$TID\,\resource\:\$RID\}} # Call 2: same thread, verify recall curl -s -X POST http://localhost:4111/api/agents/agentKey/generate \ -H Content-Type: application/json \ -d {\messages\:[{\role\:\user\,\content\:\What is my name?\}],\memory\:{\thread\:\$TID\,\resource\:\$RID\}} # Assert: thread exists in storage curl -s http://localhost:4111/api/memory/threads?resourceId$RID | \ jq {total, ids: (.threads | map(.id))}注意 URL 前缀服务端路由实际注册的路径是/agents/:agentId/generate与/memory/threads见下文 4.3/api前缀取决于开发服务器是否启用了 API 挂载前缀本地 dev server 通常为http://localhost:4111/api/...以实际环境为准。通过标准Pass criteria第 2 次调用的回复中引用了 AbhiGET /api/memory/threads?resourceIdrid返回的total 1且存在一个id与$TID匹配的线程更严格的做法在另一个resourceId下再播种第二个线程并确认过滤条件把它排除在外。4.3 响应形状/memory/threads返回分页对象而非裸数组GET /api/memory/threads的响应是{ threads: [...], total: 0, page: 0, perPage: 100, hasMore: false }不是裸数组。每个线程条目包含{ id, resourceId, title, metadata, createdAt, updatedAt }。源码佐证位于 handlers/memory.ts 的LIST_THREADS_ROUTE其响应 schemalistThreadsResponseSchema由paginationInfoSchematotal/page/perPage/hasMore加上threads数组构成schemas/memory.ts线程条目的字段定义在threadSchemaschemas/memory.ts。分页参数方面createPagePaginationSchema(100)表明page默认 0、perPage默认 100且两者都必须是非负整数schemas/common.ts。4.4 查询参数resourceId大小写敏感resourceId注意大写I是大小写敏感的。如果误写成小写resourceid该参数会被静默忽略接口会返回所有线程——这会让一个本来失败的测试看起来通过了属于典型的假阳性陷阱。此外agentId为可选参数resourceId省略时同样返回所有线程schemas/memory.ts 明确注释了这一点服务端在处理时会调用getEffectiveResourceId(requestContext, resourceId)让请求上下文中的资源键优先于客户端传入值handlers/memory.ts。4.5 常见 API 排障对照现象原因修复第 2 次调用忘记上下文发送了顶层threadId/resourceId而非memory: { thread, resource }把参数挪进memory对象第 2 次调用忘记上下文排除上述情况agentKey与Mastra({ agents })配置中的 key 不一致使用配置中的 agent key而不是 agent 的id字段/memory/threads返回了其他资源的线程把resourceId打成了resourceid恢复大小写未知参数会被丢弃且不触发任何过滤五、常见问题速查表以下问题矩阵同时适用于 UI 与 API 两种验证路径是记忆冒烟测试最核心的判读依据问题原因修复刷新后无历史使用的是内存存储默认配置配置持久化存储mastra/libsql/mastra/pg/mastra/tursoAgent 忘记上下文未配置记忆在 agent 配置中加上memory找不到线程Thread not found线程 ID 无效开启新会话六、测试执行与结果记录记忆测试通过smoke test --env local --existing-project ~/my-app --test memory这类命令单独触发执行时遵循 SKILL.md 的规则无论--test指定了什么Setup第 1 步始终先运行随后只执行被指定的测试项。测试完成后把结果追加到$SMOKE_DIR/smoke-report.md建议按环境local/staging/production、项目、逐项 PASS/FAIL 及证据响应摘录、线程 ID、截图或document.body.innerText组织。尤其要区分本地 Studio 冒烟是只做了 API/curl 校验还是同时完成了浏览器界面校验——两者证明的层面不同接口层 vs 界面层。结语记忆冒烟测试的关键不在于发一条消息看回不回而在于用一套固定的、可重复的序列去逼问三个问题上下文有没有被引用、刷新后历史还在不在、新线程是不是干净的。配合本节给出的 curl 断言你可以在完全没有浏览器的情况下仅凭两次generate调用加一次threads查询就确定 Agent 的记忆链路请求携带 → 存储落盘 → 上下文召回是否完整。最容易翻车的三个点请牢记memory对象必须嵌套、resourceId必须大写I、agentKey必须匹配Mastra({ agents })配置而非 agent 的id。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表