GWに Mastra を触ってみようと思っていたところ、Workflows の書き方が新しくなるという公式ブログを見かけたので、実際のコードも交えて変更点を確認してみました。
Mastra とは
Mastra は、AI機能を素早く、堅牢に、そして拡張可能に開発するための TypeScript 製フレームワークです。
個人的には、LLM アプリケーションといえば Python が主流という印象がある中で、TypeScript で書けるというだけで嬉しいです。
詳しくは以下のリンクも参考になります。
新しい Workflow: vNext Workflows
以下は、基本的に公式ブログからの要点の抜粋です。
背景
従来の Mastra Workflows は、シンプルで直感的な .then()
や .step()
構文による構築が高く評価されていましたが、次の2点が課題とされていました。
- 複雑な制御フローの表現が困難
- 既存のワークフローエンジンとの統合が難しい
これらの課題を解決すべく登場したのが vNext Workflows
です。
変更点
1. 制御フローの強化
複雑な分岐や繰り返し処理を直感的に記述できるように、以下の改善が加えられました。
-
.branch()
による分岐記述 -
.parallel()
の導入- 複数ステップを条件無しで並列実行可能に
-
.then()
の1本化-
.step()
は廃止され、.then()
に統一
-
- ループ構文の追加
-
while
やuntil
により、条件が成立するまでワークフローを繰り返せるように
-
-
after()
の廃止
2. 型安全性の向上
vNext では、すべてのステップに対して入力スキーマと出力スキーマの定義が必要になります。
- ステップの入力は前ステップの出力と自動的に接続
-
.branch()
や.parallel()
の結果は{[stepId]:output}
形式のユニオン型で返却 - ステップの再開時に渡される値は
resumeSchema
として定義可能になった
これらの変更により、入出力の型が合わないときに型エラーの検出が可能になりました。
3. マルチエンジン対応(今後対応予定)
将来的に以下のような外部のワークフローエンジンでMastraの Workflow を扱えるようになります。
導入方法
ブログによると、2025年 5月 6日のリリースから vNext に切り替わる予定です。
従来のワークフローを利用している場合は、@mastra/core/workflows/legacy から import することで引き続き使用可能です。
ただし、執筆時点ではまだ vNext
に切り替わっていませんでした。
今の段階で触りたい場合、 @mastra/core/workflows/vNext
から import してくることで試すことができます。
触ってみた
実際に vNext を試してみて、特に印象に残った点は以下の2つです。
型エラーがわかりやすくなった
以下のようなステップを組んだ際、入出力の型が一致していない場合に、エディタ上で即座に型エラーとして指摘されました。
import { createWorkflow, createStep } from '@mastra/core/workflows/vNext';
import { z } from 'zod';
export const firstStepInputSchema = z.object({
inputValue: z.number(),
})
export const firstStepOutputSchema = z.object({
doubledValue: z.number(),
})
const firstStep = createStep({
id: 'first-step',
inputSchema: firstStepInputSchema,
outputSchema: firstStepOutputSchema,
execute: async ({ inputData }) => {
return { doubledValue: inputData.inputValue * 2 };
},
})
export const plusOneStepInputSchema = z.object({
doubledValue: z.number(),
})
export const plusOneStepOutputSchema = z.object({
plusOneValue: z.number(),
})
const plusOneStep = createStep({
id: 'plus-one-step',
inputSchema: plusOneStepInputSchema,
outputSchema: plusOneStepOutputSchema,
execute: async ({ inputData }) => {
return { plusOneValue: inputData.doubledValue + 1 };
},
})
const mathWorkflow = createWorkflow({
id: 'math-workflow',
inputSchema: z.object({
inputValue: z.number(),
}),
outputSchema: z.number(),
steps: [firstStep, plusOneStep],
})
.then(firstStep)
.then(plusOneStep)
.commit();
たとえば、plusOneStepInputSchema を以下のように変更した場合、画像のようにエラーがでます。
export const plusOneStepInputSchema = z.object({
inputValue: z.number(),
})
即座にエラーがでるので、実装時も安心しながら開発ができます。
複雑なフローが書きやすくなった
次のような並列処理・分岐がある例を考えてみます。
このワークフローは、以下のコードで実現されています。(Stepなど、コード全体は付録に記載します)
const mathWorkflow = createWorkflow({
id: 'math-workflow',
inputSchema: z.object({
inputValue: z.number(),
}),
outputSchema: z.number(),
steps: [firstStep, plusOneStep, plusTwoStep, plusThreeStep, aggregateStep, resultStep],
})
.then(firstStep)
.parallel([plusOneStep, plusTwoStep, plusThreeStep])
.then(aggregateStep)
.branch([
[async ({inputData}) => inputData.aggregatedValue >= 100, highValueStep],
[async ({inputData}) => inputData.aggregatedValue 100, lowValueStep],
])
.then(resultStep)
.commit();
legacy
で書く場合、次のようになりました。
(違う例で試していたので、Stepの名前等はことなります。フローの組み方もなにか間違えている気もするので、より良い書き方があれば教えて下さいmm)
const weatherWorkflowTwo = new Workflow({
name: 'weather-workflow-two',
triggerSchema,
})
.step(fetchWeather)
.after(fetchWeather)
.step(clothingSuggestion)
.step(ambrellaSuggestion)
.step(washingSuggestion)
.after([clothingSuggestion, ambrellaSuggestion, washingSuggestion])
.step(highTempStep, {
when: async ({context}) => {
const forecast = context?.getStepResult(fetchWeather);
return forecast?.some(item => item.maxTemp > 30);
}
})
.then(mergeSteps)
.step(lowTempStep, {
when: async ({context}) => {
const forecast = context?.getStepResult(fetchWeather);
return forecast?.some(item => item.maxTemp 10);
}
})
.then(mergeSteps)
.commit();
おわりに
GWを機に Mastra を触ってみましたが、TypeScript の恩恵で型安全かつ開発体験の良い LLM アプリケーションが構築できそうだと感じました。
今後も引き続き試してみたいと思います。
付録
vNext を使ったコード全体はこちらです。https://gist.github.com/onsd/38ce6f833b93404a34e656716ed0b176
Views: 0