せっかく作ってもらったのに Zenn だと og:image 設定できなくて途方に暮れたアイキャッチ画像
LayerX AI Agent ブログリレー 14日目の記事です。
バクラク事業部 スタッフエンジニアの @izumin5210 です。
Platform Engineering 部 Enabling チームでいろいろなことをしています。
この記事では AI Agent をプロダクトに組み込んでいくにあたり、複雑な Agent を production で動かすのに避けては通れないであろう Durable Execution を実現してくれる Workflow Engine である Temporal を紹介します。
また、この AI Agent の盛り上がりは AI・機械学習・データの専門家の独壇場のように見えてしまっているかもしれませんが、実際のプロダクト組み込みを見据えるとソフトウェアエンジニアリングによって解決すべきチャレンジも山ほどあるよ! と言うのを伝えるのも目的です。
AI Agent とは
目標に向けて環境と相互作用しながらタスクをこなすインテリジェントシステム
– 現場で活用するためのAIエージェント実践入門 より引用
です。 もうちょっと具体的に馴染みある単語を使うと「コンテキストをもとに思考し、与えられた Tool を自律的に利用して情報を得る・あるいは環境に働きかける」ようなソフトウェアを本記事では想定しています。
AI Agent と Durable Execution
実装した AI Agent をプロダクト上でどうやって動作させるか、というのは単純な話ではありません。
AI Agent の実行はそもそもロングタスクになりやすいためです。
AI Agent が入力(テキストや画像など)を受け取り、そこから自律的な判断・ツール実行をしつつ結果を出力する一連のプロセスを Agent Loop と呼ぶことがあります。
- 入力 + コンテキストを LLM に渡して処理してもらう
- ツール実行が必要だと判断したらその旨が返ってくる
- 上記を受け取りツールを実行し、その結果を LLM に戻して処理継続
- 2に戻る、あるいは最終的な出力を生成
この Agent Loop を確実に完了させることは、実はそれなりに難易度が高いです。
- この Agent Loop を回している間にクライアントとの接続が切れたらどうなるか
- ハードウェアトラブルでインスタンスが落ちるかも
- ネットワーク不調などにより一時的なエラーが発生したら?
Agent Loop 全体がすぐに終われば問題は大きくないかもしれません。しかし、数分~数十分かかるようなタスクでは前述に挙げたような要因でタスクが中断されることはそれなりにありえるでしょう。
また、 Agent が呼び出すツールが副作用を伴う場合、例えば Agent に何らかのデータを作成させるような用途では、単純にリトライをしてしまうとデータが多重に作成されてしまいバグの原因となりえます。 冪等に作ればいい、と言ってしまえばそれはそうですが、AI Agent では処理開始時点でどのような処理をするかは自明ではなく、また同じ入力であってもツール呼び出しのパラメタが一致する保証もないため、全体として冪等な振る舞いをさせる難易度はかなり高いと言えます。
AI Agent のように実行が長時間にわたるような用途においては、処理が中断・再開可能であることの重要性が高いでしょう。
Agent Loop を1つのワークフローと捉えたときに、「LLM に処理してもらう」「ツール実行」などの個々のタスクの実行を記録し、中断・再開時には完了済みのタスクはそのままに残りのタスクのみを処理することで、ワークフローを確実に完遂するような Durable な(耐久性のある)実行エンジンが必要です。
Temporal Workflow
前述したような Durable な Workflow Engine として利用できるのが Temporal Workflow です。
Temporal Workflow はノーコードや設定ファイルではなく、一見普通の(?)手続き的なコードで複数のタスクからなる処理(Workflow)を記述することができます。
(SDK は Go, Java, PHP, Python, TypeScript, .Net, Ruby で提供されていますが、ここでは TypeScript を例にします)
export async function getUserNameById(id: string): Promisestring> {
const user = await repository.getUserById(id)
return user.name
}
export async function greet(name: string): Promisestring> {
return `👋 Hello, ${name}!`;
}
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from './activities';
const { getUserNameById, greet } = proxyActivitiestypeof activities>();
export async function sayHelloWorkflow({ userId }: { userId: string }): Promisestring> {
const name = await getUserNameById(userId)
return await greet(name);
}
上記のコードの Workflow 関数内で Activity を呼び出すとタスクキューにエンキューされ、ワーカープロセスで処理されます。 この Activity の実行結果や状態を管理し、かつ Workflow 自身が副作用や外部依存を持たないことで Workflow 全体として Durable な実行が可能になっています。
Workflow Engine Design Principles with Temporal | Temporal より引用
前節の Agent Loop における「LLM の処理」「ツール実行」などが Temporal Workflow における Activity として実行されることで、 Agent Loop 全体を Durable にすることができる… というのが AI Agent を Temporal Workflow で動かすモチベーションです。
+---------------------+ | Temporal Server | (Stores workflow state, +---------------------+ schedules activities, ^ persists progress) | Save state, | Schedule Tasks, progress, | load state on resume timeouts | | +------------------------------------------------------+ | Worker | | +----------------------------------------------+ | | | Workflow Code | | | | (Agent Orchestration Loop) | | | +----------------------------------------------+ | | | | | | | v v v | | +-----------+ +-----------+ +-------------+ | | | Activity | | Activity | | Activity | | | | (Tool 1) | | (Tool 2) | | (Model API) | | | +-----------+ +-----------+ +-------------+ | | | | | | +------------------------------------------------------+ | | | v v v [External APIs, services, databases, etc.]
sdk-python/temporalio/contrib/openai_agents at 1.18.0 · temporalio/sdk-python より引用
また、Workflow Engine としてもさまざまな便利な機能が用意されており、複雑な処理が必要な Workflow にも対応することができます。
Durable Execution によってもたらされる設計・実装上のメリット
実行を Durable にしてくれる、というだけで AI Agent を実装する上でかなりありがたいのですが、Durable であること、あるいは Durable Workflow があることで他にも設計・実装上のメリットがいくつかあります。
バックグラウンドタスク・あるいはユーザ操作起点でないタスクも乗せることができる
これは割と単純な話ですが、Temporal Workflow は本質的には AI などは関係なく単なる Durable な Workflow Engine です。 なので当然 Agent Loop 以外の処理も乗せることができます。また、AI Agent と言われるとチャットを想像するかもしれませんが、その interface はチャットに限りません。
例えばアップロードされたファイルに前処理をした上で Agent に渡すであるとか、あるいはメール受信や定期実行のジョブといったユーザによる能動的な操作ではないイベントをトリガとする処理も Workflow 内に自然に組み込むことが可能です。
外部からの操作を message passing で受け付けることができる
よくあるチャットアプリケーションのような例で、Agent のメッセージに対してユーザから返信を受けたときに、コンテキストを引き継ぎつつ Agent Loop を再開したいというのはよくあるケースです。
この「ユーザから返信を受け付ける」など外部からの操作を起動済みの Workflow に取り込みたい場合に Temporal Workflow が提供する Signal などの message passing の仕組みが利用できます。
import { defineSignal, setHandler } from "@temporalio/workflow";
const messageSignal = defineSignal[{ text: string }]>("message");
async function agentWorkflow() {
setHandler(messageSignal, ({ text }) => {
});
}
const wfHandle = temporalClient.workflow.getHandle(workflowId);
await wfHandle.signal(messageSignal, { text: message });
この「外から Workflow に干渉できる機構」は思いのほか便利で、後述するようにツール実行確認のような HITL(Human-in-the-loop) を柔軟に実装することができます。
一時的な状態を “ローカル変数” っぽく扱える
Workflow を Durable にするために、Workflow の実装には副作用や環境依存がなく、かつ Activity の実行結果が保存されています。ということは、Workflow 実行時の任意のステップでの変数の状態を再現することが可能なはずです。 Workflow 実行中の中間状態の DB などへの永続化は明示的には必要ありません。 Workflow 実装者のメンタルモデルとしてはローカル変数と同じカジュアルさで、いい意味で雑に状態を管理することができます。
例えば Claude Code では Agent が処理中であってもメッセージを受け付けてキューイングしてくれますが、それと似たようなことが単純な機構で実現できます。
import { defineSignal, setHandler } from "@temporalio/workflow";
const messageSignal = defineSignal[Message]>("message");
async function agentWorkflow() {
const queuedMessages: Message[] = [];
setHandler(messageSignal, (message) => {
queuedMessages.push(message);
});
}
ユーザの操作の待ち受けなどがあるタスクも扱える
Temporal Workflow には特定の条件を満たすまで待機する condition()
や排他実行のための Mutex
などの仕組みも標準で用意されています。
これと Signal を組み合わせることで「特定の外部操作を受け付けるまで待機」という実装が可能です。 ツール実行確認などいわゆる HITL(Human-in-the-loop) などの実装に利用できます。
const approvalSignal = defineSignal[{ userId: string }]>("approval");
async function agentWorkflow() {
const approvedUserIdSet = new Setstring>();
setHandler(approvalSignal, ({ userId }) => {
approvedUserIdSet.add(userId);
});
await condition(() => approvedUserIdSet.size > 5);
}
HITL はユーザの操作がいつ来るかわからないため長時間の実行になりますが、そこは Temporal なのでしっかり Durable に扱ってくれます。
Workflow のバージョニングに対応
数時間~数日のレベルの long-running なタスクだとデプロイなどによって state の形や Agent そのものの実装が変わってしうと実行結果がおかしくなるリスクがあります。
しかし Temporal Workflow はそもそもが長時間のタスク実行を想定している基盤なので、バージョニングのための機構を備えています。
実際にある程度複雑な AI Agent プロダクト機能をリリースするにあたっては、このあたりをどれくらいケアしていくかは慎重に設計していく必要があります。 が、少なくとも基盤側にこのようなサポートが入っていると進めやすくなるでしょう。
まとめ – AI Agent 時代のソフトウェアエンジニアリング
プロダクトに AI Agent 機能を組み込むにあたっていずれは避けて通れなくなる、 Durable Exectuion に対応した Workflow Engine の Temporal Workflow について紹介し、これによって AI Agent 開発にどのようなメリットがあるかを解説しました。バクラクではこの Temporal をフル活用して AI Agent を作る、あるいは AI Agent を動かすための基盤を整備していきます。
冒頭にも書きましたが、 AI Agent の盛り上がりはどうしても AI や機械学習の専門家の仕事なんでしょ? 僕たち普通のソフトウェアエンジニアの面白い仕事はなくなる? とか思っていませんか? わかりやすく話題なるのも Context Engineering やメモリ戦略, RAG による知識の拡張など AI っぽい新しい技術が多く見えているかもしれません。
しかし、実際にプロダクトに AI Agent を組み込もうとすると、 Durable Execution のための実行基盤や認証認可, 評価や Observability など、従来のソフトウェアエンジニアリングにおいても非常にたくさんのチャレンジが存在します。 そして、その中にはまだうまく解決しきれてないものもいくつもあります。 今ここに Bet することで未来の技術の当たり前を発明できるかもしれない、そういうタイミングではないかと自分は考えています。
LayerX では AI Agent 時代のソフトウェアエンジニアリング・ Platform Engineering を共に作り上げていく仲間を募集しています!
Views: 0