こんにちは、俊介です! 普段は Azure を中心に、企業向けの AI ソリューションを設計・構築しています。
Microsoft Foundry の「ルーチン」が GA しました。
作ったエージェントを、決まった時刻に自動で呼び出してくれる仕組みです。
毎朝9時にレポートを書かせる、みたいなことがポータルの設定だけでできます。
プレビュー版を検証していたのですが、GA したので運用に近い形で回してみました。
毎朝9時に Azure AI 関連のアップデートを検索させて、ブログのネタになりそうなものを3件、表にして返させる。
どんなユーザー向け?
- Foundry でエージェントを作っていて、定期実行させたいと思っている人
- 定時実行を Logic Apps や Azure Functions のタイマートリガーで組もうとしていて、Foundry 側で完結するなら乗り換えたい人
逆に、すでに本番運用の定時実行基盤があって、実行ログの追跡まで作り込んである人には、いま急いで乗り換える理由は無いと思います。
ルーチンとは
ルーチンは、Foundry のエージェントを外から自動で起動するための仕組みです。
トリガーが1つ、アクションが1つ、これだけで1つのルーチンになります。
1つのルーチンに複数のトリガーを持たせたり、複数のエージェントを順番に呼んだりはできません。
分岐も条件もありません。
トリガーは3種類
| 種別 | 内容 | 今回の扱い |
|---|---|---|
| スケジュール | cron 式で繰り返し実行する。最短間隔は5分で、それより短い cron は拒否される | 今回はこれを使う |
| タイマー | 将来の特定日時に1回だけ実行する | 試していない |
| イベント | 外部のイベントで実行する。GitHub の issue トリガーと、Teams の新着チャンネルメッセージで発火する custom トリガーの2種類 | 試していない |
イベントトリガーには、他の2つには無い注意点があります。
イベントトリガーは外部システムとの接続(コネクタ接続)に依存していて、その接続の持ち主が対象リソースへのアクセスを失うと発火しなくなります。
担当が変わったタイミングで静かに止まる、という事故が起きやすい構造です。
アクションは1種類しかない
アクションは「Foundry のエージェントを1つ呼ぶ」だけです。
メールを送るとか、Storage にファイルを置くとか、そういうアクションは用意されていません。
何かをしたいなら、それをするエージェントを先に作っておく必要があります。
動かすための前提
| 前提 | 内容 |
|---|---|
| プロジェクト | エージェントが1つ以上デプロイされている Foundry プロジェクト |
| ロール | プロジェクトスコープで Foundry User 以上 |
| エージェントの種類 | プロンプトエージェントかホステッドエージェント。ワークフローエージェントは呼べない |
| 実行するID | 既定はエージェント自身のID。ルーチンを作った人のIDに変えられるのは作成時だけで、あとから変更できない |
| Application Insights | 実行の中身を見るなら接続が必要 |
ロール名についても一点補足します。
Foundry User は以前 Azure AI User という名前でした。
リネーム後も画面によっては旧名が残っているので、ドキュメントの表記と画面の表記が合わなくても、同じものを指していると思って大丈夫です。
そもそも、なぜ Foundry 側でやるのか
定期実行だけなら Logic Apps でも Azure Functions のタイマートリガーでも組めます。
実際そちらのほうが、スケジュールの自由度も、失敗時のリトライの作り込みも上です。
今回Foundry 側にルーチンがあるのかというと、エージェントと同じ場所で管理できることと、実行履歴がエージェントの実行履歴として残ることの2点だと思っています。
呼び出し側のリソースを別サービスに持たせると、誰がこのエージェントを叩いているのかが追いにくくなります。
とはいえ、この判断は制約を知ったうえでするものです。
どこまでできてどこからできないのかを、ここから実際に触って確かめていきます。
作る
エージェントを作る
ルーチンは既存のエージェントを呼ぶ仕組みなので、先に呼ばれる側が要ります。
名前は routine-test-agent にしました。
手順(システムプロンプト)はこれをそのまま貼れば同じものが作れます。
あなたは Azure と Microsoft Foundry のアップデート調査担当のアシスタントです。
呼び出されたら、Web 検索を使って直近1週間に公開された
Azure AI 関連のアップデート情報を調べ、
ブログのネタになりそうなものを3件選んでください。
出力は次の形式のマークダウンの表だけを返してください。前置きと後書きは書かないでください。
| アップデート名 | 注目度 | ひとことコメント | 参考URL |
注目度は 高 / 中 / 低 の3段階で付けてください。
ひとことコメントは60文字以内で、なぜ注目すべきかを書いてください。
情報が見つからない場合は、見つからなかったと1行だけ書いてください。
ツール欄には Web 検索が最初から入っています。
自分で追加したものではなく、新規作成した時点で入っています。
今回は使うのでそのままにしますが、外部ツールを使わせたくない場合はここを空にしてください。
手順に「外部ツールは使いません」と書いても、ツールの構成は別物です。
ルーチンを作る
ドキュメントどおりに進むと迷います。
左のナビゲーションから Routines を選ぶと書かれているのですが、そこには存在しません。
左ナビの「作成」グループにあるのは エージェント / モデル / サービス / ツール などで、ルーチンはありません。
正解は「エージェント」ページを開いた先のタブでした。
ページ上部に エージェント / ルーチン / ワークフロー と3つ並んでいます。
8月に見たときは「ルーチン」にも「ワークフロー」にもプレビューのバッジが付いていました。
いまはルーチンのバッジだけが外れて、ワークフローには残っています。
タブを開いて「新しいルーチン」を選ぶと、作成ダイアログが出ます。
今回入れた値です。
| 項目 | 値 |
|---|---|
| 名前 | daily-learn-report2 |
| エージェント | routine-test-agent |
| 会話 | 空のまま(任意項目) |
| プロンプト | 今週のアップデートを3件調べて表で返してください。 |
| トリガーの種類 | 定期的なスケジュール |
| 頻度 | 毎日 |
| 時刻 | 9:00 AM |
必須マークが付いているのは 名前・エージェント・プロンプト・トリガー の4つです。
「会話」だけ任意で、会話IDを指定すると実行を既存のスレッドに紐づけられます。
トリガーの種類を「定期的なスケジュール」にすると、頻度と時刻を選ぶ欄が出てきます。
頻度は 毎時 / 毎日 / 毎週 / 平日 / 週末 / カスタム の6つです。
時刻は30分刻みのドロップダウンで、自由入力はできません。
ただし「カスタム」を選ぶと、時刻のドロップダウンが消えて「CRON を入力」というテキストボックスに変わります。
cron 式をポータルから直接書けます。
Microsoft Learn のドキュメントを読んだだけだと毎日か毎週しか選べないように見えるので、ここは実物のほうができます。
なお、時刻にタイムゾーンの表示はありません。
9:00 AM がどこの9時なのかは、この画面からは分かりません。
ここはあとで確かめます。
入力が終わったら「作成して開始」を押します。
ボタンの名前のとおり、作成した時点で有効になります。
実行と確認
テスト実行してみる
作成すると、そのルーチンの詳細ページに移ります。
状態は「アクティブ」で、右上に「一時停止」と「テスト実行」が並んでいます。
スケジュールは翌朝9時に入れてありますが、待つ必要はありません。
「テスト実行」を押せばその場で実行できます。
実行履歴の見かた
押すと、ページの下に実行履歴の表が現れます。
| 列 | 内容 |
|---|---|
| 応答 | その実行の応答ID。リンクになっていて、押すとトレースが開く |
| トリガー済み | 発火した時刻 |
| 開始までの時間 | 発火してから呼び出しが始まるまでの時間 |
| 状態 | Dispatching / Completed など |
「開始までの時間」は、実行にかかった時間ではありません。
自分は最初そう読んでいました。
表の左上には状態で絞り込むドロップダウン、右上には日付範囲のボタンがあります。
既定は過去7日なので、それより前の実行を見たいときはここを変えます。
押した直後に見ると、応答IDが -- で状態が Dispatching の行が一瞬だけ見えます。
キューに入って、これから呼び出しに行く状態です。
数秒で Completed に変わります。
応答IDのリンクから、その実行の中身を見に行けます。
ただ、ここから長くなるので後回しにします。
翌朝、定刻に発火した
ここまでは全部テスト実行です。
ボタンを押して動かしているので、スケジュールそのものはまだ見届けていません。
翌朝の9時を待ちました。
載っていました。
本当に動いていたのかを確かめる
定刻に発火するところまでは見届けました。
実行履歴には Completed が並んでいます。
ただ、その前に確かめておきたいことがありました。
この画面の Completed が、何を指しているのかです。
Completed は、呼び出しが届いたという意味だった
ドキュメントに書かれていました。
A successful run means the downstream API accepted the dispatch request. It doesn't guarantee that asynchronous work started by the agent has completed.
成功というのは呼び出しのAPIがディスパッチ要求を受理したという意味であって、エージェントが始めた仕事が終わったことは保証しない、と書かれています。
ルーチンの画面が見ているのは、エージェントの仕事ではなく、呼び出しが届いたかどうかでした。
| 画面の列 | 何を表しているか |
|---|---|
| 状態(Completed) | 呼び出しが受理された |
| 開始までの時間(3s) | 呼び出しが届くまで |
| 応答ID | 呼び出しの結果として発行されたID |
さきほど「開始までの時間は実行にかかった時間ではない」と書きましたが、理由はこれです。
同じ実行をトレースで見ると14.4秒かかっています。画面の3秒とは、別のものを測っています。
エージェントが何をして何を返したのかは、この画面のどこにも出てきません。
トレースは、開く日と開かない日があった
中身を見るには、応答IDを押してトレースを開きます。
ただ、日によって開きませんでした。
Application Insights は繋がっているのに、そこに記録が届いていない日がありました。条件はつかめませんでした。
送られていないものは表示できない、というだけなので、画面は正直でした。
確かめるには、Application Insights を直接見に行くしかありませんでした。
Application Insights を直接引く
Azure ポータルで、プロジェクトに繋がっている Application Insights を開いて、左メニューの「監視」から「ログ」を選びます。
ここに貼ったのがこれです。
genAIContent
| where timestamp > ago(14d)
| where agentName == 'routine-test-agent'
| extend msgs = parse_json(outputMessages)
| mv-expand msg = msgs
| mv-expand part = msg.parts
| extend t = tostring(part.type)
| summarize ts = min(timestamp),
['ツール呼び出し回数'] = countif(t == 'tool_call_response'),
['エージェントの出力'] = maxif(tostring(part.content), t == 'text')
by operation_Id, ['エージェント'] = agentId
| extend ['実行日時 (JST)'] = format_datetime(datetime_utc_to_local(ts, 'Asia/Tokyo'), 'yyyy/MM/dd HH:mm:ss')
| project ['実行日時 (JST)'], ['エージェント'], ['ツール呼び出し回数'], ['エージェントの出力']
| order by ['実行日時 (JST)'] descやっていることは単純で、エージェントの実行を1行にまとめて、ツールを何回呼んだかと、最後に返した文章を並べているだけです。
2行目の ago(14d) が期間の指定です。もっと長く回しているなら、ここを伸ばしてください。
この行は省かないほうがいいです。省くとポータル側の既定である過去24時間で走ってしまい、古い実行が出てきません。自分はこれで一度、記録が消えたと勘違いしました。
記録が残っていた実行を全部引くと、ツール呼び出しはどれも15回から116回の間に収まっていました。
ゼロの行はひとつもありません。
動いてはいた
行を開くと、そのとき返した表がそのまま入っています。
9月17日の朝9時の実行は、検索を51回叩いて、こう返していました。
| アップデート名 | 注目度 | ひとことコメント |
|---|---|---|
| Introducing Foundry Dev Pack: One Command to Start Building | 中 | Foundry開発環境を一括構築、導入体験の改善が大きい |
| Azure AI Speech LLM 2607 | 高 | 多言語音声認識と低遅延化で音声エージェント実装に効く |
| Your Agent Found the Right Schema. Then Ignored It. | 中 | RAG/ツール選択評価の落とし穴と微調整効果が具体的 |
参考URLも指示どおり付いています。
注目度も3段階で付いています。
指示した形式を守って、ちゃんと仕事をしていました。
毎朝きちんと数十回検索して、毎回ちがう3件を選んで返しています。
画面が Completed としか言わない裏側で、エージェントは働いていました。
ただし、何を読んだのかは分からない
ひとつだけ、どうしても分からないものが残りました。
ツールの応答です。
"type": "tool_call_response",
"response": "[Search content redacted]"何を検索して、何が返ってきたのかは追えません。
記録が残っていた実行を全部見ましたが、ツールの応答は1件の例外もなく、これで伏せられていました。
出力の表は読めます。でも、その表の根拠になった検索結果は読めません。
エージェントが働いていたことは確かめられた。でも、何を根拠に書いたのかは確かめられない。
自分で使うぶんには気になりませんが、無人で回したものをあとから検証したいときに、ここが効いてきます。
MS Learn MCP でも試した
ここまで Web 検索を使ってきましたが、同じことを MCP でもやってみました。
Microsoft Learn の MCP サーバーです。
返ってくるのが公式ドキュメントそのものなので、根拠がはっきりします。自分が普段調べるときのやり方にも近い。
MCP サーバーを足す
追加はポータルから完結します。
| 項目 | 値 |
|---|---|
| エンドポイント | https://learn.microsoft.com/api/mcp |
| 認証 | なし |
公開サーバーなので認証情報は要りません。
ひとつだけクセがあります。
追加した直後の承認設定は「ツールを自動承認しない」になっています。
この状態だと、ツールを呼ぶたびに承認を求められます。自分で叩くぶんには押せばいいのですが、ルーチンは無人で動くので、承認する人がいません。
「すべてのツールを常に自動承認する」に変えてから、ルーチンにしました。
エージェントの手順とルーチンの設定は、これまでと同じです。ツールだけ差し替えました。
ちゃんと動きました
翌朝9時の実行を開きました。
まとめ
まわしてみた所感です。
| 観点 | 評価 | コメント |
|---|---|---|
| 作るまでの手軽さ | ◎ | ポータルだけで完結。必須項目は4つ。cron もその場で書ける |
| 毎朝きちんと動くか | ◎ | 毎朝きちんと発火して、空振りはゼロ。検索も数十回叩いている |
| 結果を確かめられるか | △ | トレースが開けない日があった。App Insights を直接引けば確認できる |
| 失敗に気づけるか | △ | Completed は呼び出しが受理された意味。エージェント側が転んでも成功に見える |
| 本番投入 | △ | 動くことは確認できた。ただし動いたことを確認する手段が安定しない。無人で回す以上、確かめられないものは動いていないのと同じだと思う |
作るところはよくできています。
エージェントを1つ用意して、名前とプロンプトとスケジュールを入れれば終わりです。
コードは書きませんし、別のサービスを挟む必要もありません。
平日だけ、週末だけ、といったプリセットが最初から用意されているのも実用的でした。
引っかかるのは、そのあとです。
毎朝ちゃんと動いていたことは、この記事を書く過程で確認できました。
ただ、それを確認するのに Application Insights を直接叩く必要がありました。
これはGAしたばかりだったのがよくわかっていませんが…
ただ、いま乗せるなら、出力を自分で受け取って残す仕掛けを併せて用意しておいたほうがいいと思います。
エージェントに結果をどこかへ書かせるところまでやらせる、という作りにしておけば、ルーチン側の記録に頼らずに済みます。
なお、一時停止と削除は問題なくできました。
一時停止は設定を残したままスケジュールだけ止められます。
削除すると実行履歴も一緒に消えるので、結果を見返したい場合は消す前に控えてください。
おわりに
ここまで読んでくださってありがとうございました!
今回は「毎朝ネタを3件出させる」という、結果を自分の目で読む用途だったので、記録が無くても実害はありませんでした。
これが「出てきたものをそのまま次の処理に流す」だったら、怖くて乗せられなかったと思います。
同じルーチンでも、何をさせるかで評価が変わるところが面白かったです。
Microsoft Learn の MCP のほうは、承認設定まわりだけでも書くことがけっこうあります。
いつか単体で並べてみたいなと思っています。
また書きます!!