> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-TW/llms.txt # 儲存空間 儲存空間 API 已標準化,所有方法都使用一致的分頁與命名模式。 ## 資料庫遷移 請透過一般遷移流程執行這些 SQL 遷移(例如 Prisma Migrate、Drizzle Kit,或 DBA 審查流程)。 ### Scorer 資料表欄位重新命名 `mastra_scorers` 中的 `runtimeContext` 欄位已重新命名為 `requestContext`。 > **誰需要進行此遷移:** 只有在使用 `@mastra/pg` 或 `@mastra/libsql` 搭配 Evals/評分,而且 `runtimeContext` 欄位已有現有資料時才需要。 > **未遷移時會造成什麼問題:** 現有分數記錄的請求內容資料將無法存取。 部署 v1(初始化時會新增 `requestContext` 欄位)後,請複製資料並刪除舊欄位: ```sql UPDATE mastra_scorers SET "requestContext" = "runtimeContext" WHERE "runtimeContext" IS NOT NULL; ALTER TABLE mastra_scorers DROP COLUMN "runtimeContext"; ``` ### 重複 span 遷移 如果從舊版 Mastra 升級,`mastra_spans` 資料表中可能有重複的 `(traceId, spanId)` 項目。V1 會為這些欄位新增唯一條件約束,以確保資料完整性,但若存在重複項目,就無法新增此條件約束。 > **誰需要進行此遷移:** 只有在保有 Mastra v1 之前版本的現有 span 資料,而且遇到重複鍵違規或條件約束建立失敗錯誤時才需要。 > **未遷移時會造成什麼問題:** 儲存空間初始化可能會在嘗試新增唯一條件約束時失敗,或可能看到「duplicate key value violates unique constraint」等錯誤。 #### 選項 1:使用 CLI(建議) 執行遷移命令,自動刪除重複 span 並新增條件約束: ```bash npx mastra migrate ``` CLI 會封裝專案並連線至已設定的儲存空間,再執行遷移。移除重複項目時,會保留最完整的記錄(依 `endTime` 與屬性判斷)。 #### 選項 2:手動執行 SQL(PostgreSQL) 如果偏好手動執行遷移: ```sql -- Remove duplicates, keeping the most complete record DELETE FROM mastra_spans a USING mastra_spans b WHERE a.ctid < b.ctid AND a."traceId" = b."traceId" AND a."spanId" = b."spanId"; -- Add the unique constraint ALTER TABLE mastra_spans ADD CONSTRAINT mastra_spans_trace_span_unique UNIQUE ("traceId", "spanId"); ``` #### 選項 3:手動遷移(其他資料庫) 對於 ClickHouse、LibSQL、MongoDB 或 MSSQL,請使用程式化 API: ```typescript const storage = mastra.getStorage() const observabilityStore = await storage.getStore('observability') // Check if migration is needed const status = await observabilityStore?.checkSpansMigrationStatus() console.log(status) // Run the migration const result = await observabilityStore?.migrateSpans() console.log(result) ``` ### JSON 欄位(TEXT → JSONB) **僅限 PostgreSQL。**`mastra_threads` 的 `metadata` 欄位與 `mastra_workflow_snapshot` 的 `snapshot` 欄位已從 TEXT 變更為 JSONB。 > **建議:** 遷移至 JSONB 後,即可使用 PostgreSQL 原生 JSON 運算子與 GIN 索引,提升 JSON 欄位的查詢效能。 ```sql ALTER TABLE mastra_threads ALTER COLUMN metadata TYPE jsonb USING metadata::jsonb; ALTER TABLE mastra_workflow_snapshot ALTER COLUMN snapshot TYPE jsonb USING snapshot::jsonb; ``` ## 已新增 ### 在 `MastraCompositeStore` 中組合儲存空間 `MastraCompositeStore` 現在可以組合來自不同轉接器的儲存空間領域。不同用途需要使用不同資料庫時可使用它,例如 Memory 與 Workflow 使用 PostgreSQL,但可觀測性使用專用資料庫。 ```typescript import { MastraCompositeStore } from '@mastra/core/storage' import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg' import { MemoryLibSQL } from '@mastra/libsql' import { Mastra } from '@mastra/core' // Compose domains from different stores const mastra = new Mastra({ storage: new MastraCompositeStore({ id: 'composite', domains: { memory: new MemoryLibSQL({ url: 'file:./local.db' }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }), }, }), }) ``` 詳情請參閱[儲存空間組合參考文件](https://mastra.zisheng.pro/zh-TW/reference/storage/composite)。 ## 已變更 ### `MastraStorage` 重新命名為 `MastraCompositeStore` `MastraStorage` 類別已重新命名為 `MastraCompositeStore`,更準確反映它作為複合儲存空間實作的角色:將不同領域路由至不同的底層儲存空間。這可避免與一般「Mastra Storage」概念(Mastra 執行個體上的 `storage` 屬性)混淆。 為維持回溯相容性,舊的 `MastraStorage` 名稱仍可作為已棄用的別名使用,但將在未來版本移除。 遷移時,請更新匯入與執行個體化方式: ```diff - import { MastraStorage } from "@mastra/core/storage"; + import { MastraCompositeStore } from "@mastra/core/storage"; import { MemoryLibSQL } from "@mastra/libsql"; import { WorkflowsPG } from "@mastra/pg"; export const mastra = new Mastra({ - storage: new MastraStorage({ + storage: new MastraCompositeStore({ id: "composite", domains: { memory: new MemoryLibSQL({ url: "file:./memory.db" }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), }, }), }); ``` > **備註:** 如果直接使用單一儲存空間實作(例如 `PostgresStore` 或 `LibSQLStore`),請保留現有設定。這只會影響明確使用 `MastraStorage` 建立複合儲存空間的程式碼。 ### 儲存空間執行個體必須使用 `id` 屬性 儲存空間執行個體現在必須使用 `id` 屬性。此唯一識別碼用來在 Mastra 中追蹤及管理儲存空間執行個體。應為應用程式中的每個儲存空間執行個體設定具描述性且不重複的 `id` 字串。 遷移時,請在儲存空間建構函式中新增 `id` 欄位。 ```diff const storage = new PostgresStore({ + id: 'main-postgres-store', connectionString: process.env.POSTGRES_CONNECTION_STRING, schemaName: 'public', }); const upstashStore = new UpstashStore({ + id: 'upstash-cache-store', url: process.env.UPSTASH_REDIS_REST_URL, token: process.env.UPSTASH_REDIS_REST_TOKEN, }); ``` ### 分頁從 `offset/limit` 改為 `page/perPage` 所有分頁 API 現在都使用 `page` 與 `perPage`,而非 `offset` 與 `limit`,以符合以頁面為基礎的網頁分頁方式。 遷移時,請將所有分頁參數從 `offset/limit` 更新為 `page/perPage`。請注意,`page` 從 0 開始。 ```diff memoryStore.listMessages({ threadId: 'thread-123', - offset: 0, - limit: 20, + page: 0, + perPage: 20, }); ``` ### `getMessagesPaginated` 改為 `listMessages` `getMessagesPaginated()` 方法已由 `listMessages()` 取代。新方法支援 `perPage: false`,可在不分頁的情況下擷取所有記錄。這項變更符合 `list*` 命名慣例,並增加擷取所有記錄的彈性。 遷移時,請重新命名方法並更新分頁參數。現在可以使用 `perPage: false` 擷取所有記錄。 ```diff + const memoryStore = await storage.getStore('memory'); + // Paginated - const result = await storage.getMessagesPaginated({ + const result = await memoryStore?.listMessages({ threadId: 'thread-123', - offset: 0, - limit: 20, + page: 0, + perPage: 20, }); // Fetch all records (no pagination limit) + const allMessages = await memoryStore?.listMessages({ + threadId: 'thread-123', + page: 0, + perPage: false, + }); ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/storage-get-messages-paginated . > ``` ### 透過 `getStore()` 存取特定領域的儲存空間 儲存空間作業現在會透過特定領域的儲存空間存取,而非直接在儲存空間執行個體上存取。 領域包括: - **`memory`** - 討論串、訊息與資源 - **`workflows`** - Workflow 快照 - **`scores`** - 評估分數 - **`observability`** - Trace 與 span - **`agents`** - 已儲存的 Agent 資料 遷移時,請使用領域名稱呼叫 `getStore()`,再呼叫所傳回儲存空間上的方法。 ```diff const storage = mastra.getStorage(); // Memory operations (threads, messages, resources) - const thread = await storage.getThread({ threadId: '123' }); - await storage.saveThread({ thread }); + const memoryStore = await storage.getStore('memory'); + const thread = await memoryStore?.getThreadById({ threadId: '123' }); + await memoryStore?.saveThread({ thread }); // Workflow operations (snapshots) - const snapshot = await storage.loadWorkflowSnapshot({ runId, workflowName }); - await storage.persistWorkflowSnapshot({ runId, workflowName, snapshot }); + const workflowStore = await storage.getStore('workflows'); + const snapshot = await workflowStore?.loadWorkflowSnapshot({ runId, workflowName }); + await workflowStore?.persistWorkflowSnapshot({ runId, workflowName, snapshot }); // Observability operations (traces, spans) - const traces = await storage.listTraces({ page: 0, perPage: 20 }); + const observabilityStore = await storage.getStore('observability'); + const traces = await observabilityStore?.listTraces({ page: 0, perPage: 20 }); // Score operations (evaluations) - const scores = await storage.listScoresByScorerId({ scorerId: 'helpfulness' }); + const scoresStore = await storage.getStore('scores'); + const scores = await scoresStore?.listScoresByScorerId({ scorerId: 'helpfulness' }); ``` ### `getThreadsByResourceId` 改為 `listThreads` `getThreadsByResourceId()` 方法已由 `listThreads()` 取代。新方法新增分頁支援,並可依 `resourceId`、`metadata` 或兩者篩選。 > **Important:** 舊的 `getThreadsByResourceId()` 會傳回所有相符討論串,且不分頁。新的 `listThreads()` 必須提供分頁參數。若要保留擷取所有討論串的舊有行為,請使用 `perPage: false`。 遷移時,請使用 Memory 儲存空間與新的 `listThreads()` 方法,並提供分頁與選填的篩選物件。 ```diff - const threads = await storage.getThreadsByResourceId({ - resourceId: 'res-123', - }); + const memoryStore = await storage.getStore('memory'); + + // Paginated (recommended for large datasets) + const result = await memoryStore?.listThreads({ + filter: { resourceId: 'res-123' }, + page: 0, + perPage: 20, + }); + const threads = result?.threads; + + // Or fetch all threads like before (use perPage: false) + const allResult = await memoryStore?.listThreads({ + filter: { resourceId: 'res-123' }, + perPage: false, + }); + const allThreads = allResult?.threads; ``` 新方法也支援: - 列出所有討論串(省略篩選條件) - 僅依中繼資料篩選 - 結合 resourceId 與中繼資料篩選條件 ```typescript // List all threads await memoryStore?.listThreads({ page: 0, perPage: 20 }) // Filter by metadata only await memoryStore?.listThreads({ filter: { metadata: { status: 'active' } }, page: 0, perPage: 20, }) // Combined filter await memoryStore?.listThreads({ filter: { resourceId: 'user-123', metadata: { category: 'support' }, }, page: 0, perPage: 20, }) ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/storage-list-threads-by-resource-to-list-threads . > ``` ### `getWorkflowRuns` 改為 `listWorkflowRuns` `getWorkflowRuns()` 方法已重新命名為 `listWorkflowRuns()`。這項變更符合 `list*` 方法傳回集合的慣例。 遷移時,請使用 Workflow 儲存空間、重新命名方法呼叫,並更新分頁參數。 ```diff - const runs = await storage.getWorkflowRuns({ + const workflowStore = await storage.getStore('workflows'); + const runs = await workflowStore?.listWorkflowRuns({ fromDate, toDate, + page: 0, + perPage: 20, }); ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/storage-list-workflow-runs . > ``` ### `getMessagesById` 改為 `listMessagesById` `getMessagesById()` 方法已重新命名為 `listMessagesById()`。這項變更符合 `list*` 方法傳回集合的慣例。 遷移時,請使用 Memory 儲存空間並重新命名方法呼叫。 ```diff + const memoryStore = await storage.getStore('memory'); - const result = await storage.getMessagesById({ + const result = await memoryStore?.listMessagesById({ messageIds: ['msg-1', 'msg-2'], }); ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/storage-list-messages-by-id . > ``` ### 儲存空間 `getMessages` 與 `saveMessages` 簽章 `getMessages()` 與 `saveMessages()` 方法的簽章與傳回型別已變更。格式多載已移除,方法現在一律使用 `MastraDBMessage`。這項變更移除格式變體,簡化 API。 遷移時,請使用 Memory 儲存空間、移除格式參數,並更新程式碼以使用一致的傳回型別。 ```diff + const memoryStore = await storage.getStore('memory'); + // Always returns { messages: MastraDBMessage[] } - const v1Messages = await storage.getMessages({ threadId, format: 'v1' }); - const v2Messages = await storage.getMessages({ threadId, format: 'v2' }); + const result = await memoryStore?.getMessages({ threadId }); + const messages = result?.messages; // MastraDBMessage[] // SaveMessages always uses MastraDBMessage - await storage.saveMessages({ messages: v1Messages, format: 'v1' }); - await storage.saveMessages({ messages: v2Messages, format: 'v2' }); + const saveResult = await memoryStore?.saveMessages({ messages: mastraDBMessages }); + const saved = saveResult?.messages; // MastraDBMessage[] ``` ### 向量儲存 API 從位置引數改為具名引數 所有向量儲存方法現在都使用引數物件,而非位置引數。這讓每個值的用途在呼叫位置清楚可見,也能讓方法簽章在不依賴引數順序的情況下演進。 遷移時,請更新所有向量儲存方法呼叫,改用引數物件。 ```diff - await vectorDB.createIndex(indexName, 3, 'cosine'); + await vectorDB.createIndex({ + indexName: indexName, + dimension: 3, + metric: 'cosine', + }); - await vectorDB.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]); + await vectorDB.upsert({ + indexName: indexName, + vectors: [[1, 2, 3]], + metadata: [{ test: 'data' }], + }); - await vectorDB.query(indexName, [1, 2, 3], 5); + await vectorDB.query({ + indexName: indexName, + queryVector: [1, 2, 3], + topK: 5, + }); ``` ### 向量儲存方法重新命名 `updateIndexById` 與 `deleteIndexById` 方法已分別重新命名為 `updateVector` 與 `deleteVector`。新名稱指出這些方法會對向量執行作業。 遷移時,請重新命名方法並傳入引數物件。 ```diff - await vectorDB.updateIndexById(indexName, id, update); - await vectorDB.deleteIndexById(indexName, id); + await vectorDB.updateVector({ indexName, id, update }); + await vectorDB.deleteVector({ indexName, id }); ``` ### PGVector 建構函式從連線字串改為物件 PGVector 建構函式現在必須使用物件參數,而非連線字串。這項變更讓所有儲存空間轉接器的 API 更一致。 遷移時,請將連線字串作為物件屬性傳入。 ```diff - const pgVector = new PgVector(process.env.POSTGRES_CONNECTION_STRING!); + const pgVector = new PgVector({ + connectionString: process.env.POSTGRES_CONNECTION_STRING, + }); ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/vector-pg-constructor . > ``` ### PGVector 的 `defineIndex` 改為 `buildIndex` `defineIndex()` 方法已移除,請改用 `buildIndex()`;新名稱指出此方法會建置索引。 遷移時,請重新命名方法並傳入引數物件。 ```diff - await vectorDB.defineIndex(indexName, 'cosine', { type: 'flat' }); + await vectorDB.buildIndex({ + indexName: indexName, + metric: 'cosine', + indexConfig: { type: 'flat' }, + }); ``` ### `PostgresStore`:`schema` 改為 `schemaName` PostgresStore 建構函式中的 `schema` 參數已重新命名為 `schemaName`。新名稱指出此值是資料庫結構描述的名稱。 遷移時,請重新命名參數。 ```diff const pgStore = new PostgresStore({ connectionString: process.env.POSTGRES_CONNECTION_STRING, - schema: customSchema, + schemaName: customSchema, }); ``` > **Codemod:** 你可以使用 Mastra 的 codemod CLI 自動更新程式碼: > > ```bash > npx @mastra/codemod@latest v1/storage-postgres-schema-name . > ``` ### 分數儲存方法改為 `listScoresBy*` 模式 分數儲存 API 已重新命名,以遵循 `listScoresBy*` 模式。這項變更與更廣泛的 API 命名慣例保持一致。 遷移時,請將方法名稱從 `getScores` 更新為 `listScoresByScorerId` 及相關變體。 ```diff - const scores = await storage.getScores({ scorerName: 'helpfulness-scorer' }); + const scores = await storage.listScoresByScorerId({ + scorerId: 'helpfulness-scorer', + }); + // Also available: listScoresByRunId, listScoresByEntityId, listScoresBySpan ``` ## 已移除 ### 非分頁儲存空間函式 非分頁儲存空間函式已移除,請改用分頁版本。所有 list 作業現在都使用分頁,但可以使用 `perPage: false` 擷取所有記錄。這項變更讓 API 保持一致,並避免意外載入大型資料集。 遷移時,請透過領域儲存空間使用分頁方法。若要擷取所有記錄,請使用 `perPage: false`。 ```diff - // Non-paginated direct access - const messages = await storage.getMessages({ threadId }); + // Use paginated methods via domain stores + const memoryStore = await storage.getStore('memory'); + const result = await memoryStore?.listMessages({ threadId, page: 0, perPage: 20 }); + // Or fetch all + const allMessages = await memoryStore?.listMessages({ + threadId, + page: 0, + perPage: false, + }); ``` ### `getTraces` 與 `getTracesPaginated` 儲存空間已移除 `getTraces()` 與 `getTracesPaginated()` 方法。請透過可觀測性套件存取 Trace,而非核心儲存空間。 遷移時,請改用可觀測性儲存方法。 ```diff - const traces = await storage.getTraces({ traceId: 'trace-123' }); - const paginated = await storage.getTracesPaginated({ page: 0, perPage: 20 }); + // Use observability API for traces + import { initObservability } from '@mastra/observability'; + const observability = initObservability({ config: { ... } }); + // Access traces through observability API ``` ### Evals 測試公用程式 `@internal/test-utils` 已移除 Evals 領域測試公用程式。這項變更反映舊版 Evals 功能已移除。 遷移時,請直接使用儲存空間 API 進行測試,而非專用的 Evals 測試公用程式。 ```diff - import { createEvalsTests } from '@internal/test-utils/domains/evals'; - createEvalsTests({ storage }); + // Use storage APIs directly for testing ``` ### MSSQL 儲存空間中的 TABLE\_EVALS MSSQL 儲存空間實作已移除 `TABLE_EVALS` 資料表。這項變更反映舊版 Evals 功能已移除。 如果原本使用 MSSQL 儲存空間搭配 Evals,請遷移至其他儲存空間轉接器,或移除 Evals 功能。