Microsoft Fabric の Fabric Data Agent を REST API から本番利用したいという相談が増えています。しかし 2024-05-01-preview のエンドポイントを Service Principal で呼び出すと、Assistant/Thread ID が毎回同じになる、データソースに到達できないといった落とし穴があります。本記事では、この挙動の正体と現時点での本番適用可否、代替アーキテクチャを整理します。
Fabric Data Agent REST API(2024-05-01-preview)で実際に起きること
検証環境と事象の整理
まずは、実際に多くの人がハマっているパターンを整理します。
- API バージョン:
2024-05-01-preview - ベース URL:
https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/aiskills/{agentId}/aiassistant/openai/ - 認証方式:Service Principal(クライアント資格情報フロー)
- 前提:Fabric Data Agent はワークスペース上で作成・公開済みで、Fabric UI からは正常に動作している
この環境で、OpenAI Assistants API と同じ感覚で以下の REST を呼び出すと、次のような挙動になります。
| 呼び出し | 期待した挙動 | 実際の挙動(2024-05-01-preview) |
|---|---|---|
POST /assistants | 呼び出しのたびに新しい assistant_id が採番される | 毎回同じ assistant_id が返る |
POST /threads | ユーザーや会話ごとに新しい thread_id が採番される | 毎回同じ thread_id が返る |
POST /threads/{threadId}/runs(公開済み Data Agent の ID を指定) | Data Agent 経由で Lakehouse/Warehouse 等にアクセスし、SQL 実行結果を返す | 「技術的な問題でデータを取得できない」といった汎用エラー応答になり、実データが返らない |
アプリケーション側の観点では、次の問題が顕在化します。
- ユーザーごとに会話を分離できない(常に同じ Thread を共有しているような状態になる)
- 会話履歴が混在し、どのユーザーの質問に対する応答か分からなくなる
- 実データに到達できないため、「Data Agent を外部 Web アプリから叩いて業務レポートを返す」ようなシナリオが成立しない
これはバグなのか? Microsoft の公式見解
Microsoft Q&A で同様の質問が行われ、Microsoft スタッフから次のような回答が出ています(要約)。
- Assistant/Thread ID が毎回同じになるのは 2024-05-01-preview の仕様(不具合ではない)。
- 公開済みの Data Agent に対する REST ラッパーは、Assistant/Thread を「永続エンティティ」として扱う設計になっている。
- Service Principal(クライアント資格情報フロー)による外部 REST 連携は現時点でサポート外であり、そのためデータソースに到達できず汎用エラーになる。
- このため、現行の REST API パターンは、本番でのマルチユーザー・スケール利用には適していない。
加えて、Fabric データ エージェント自体が現在も「プレビュー機能」であることは、公式ドキュメントにも明記されています。
なぜ Assistant/Thread ID が“使い回し”になるのか
Azure OpenAI Assistants API との設計思想の違い
多くの開発者が混乱するポイントは、「Azure OpenAI の Assistants API では毎回新しい Assistant/Thread ができるのに、Fabric Data Agent の REST ではそうならない」という点です。
Q&A の回答内容から読み解ける仕様を、ざっくり比較すると次のようになります。
| 項目 | Azure OpenAI Assistants API | Fabric Data Agent REST API (2024-05-01-preview) |
|---|---|---|
POST /assistants | 毎回新しい Assistant を作成。設定やツールを含めて「インスタンス」が増える。 | 公開済み Data Agent をラップした「永続 Assistant」1 つを返すイメージ。 |
POST /threads | 毎回新しい Thread を作成。会話セッションの単位として分離される。 | Data Agent 側で管理される Thread を指す ID が返り、複数回呼んでも同じ ID が返る。 |
| 用途 | アプリ側が Assistant/Thread を細かく管理して、多数のセッションをさばく。 | Fabric の Data Agent アーティファクトを OpenAI 互換インターフェースで公開するためのラッパー。 |
| セッション分離 | Assistant ID・Thread ID ごとに独立。 | API だけで完全分離する前提にはなっていない。 |
Fabric のチュートリアルに出てくる Notebook 用のサンプルコードでも、api-version="2024-05-01-preview" を指定した FabricOpenAI クラスを使い、assistants/threads/runs を呼び出す形になっていますが、これはあくまで「Notebook から Data Agent を操作するためのラッパー」であり、マルチテナント Web アプリのためのフル機能 Assistants API ではないと考えるのが安全です。
結果として起こること
- アプリから
CreateAssistantAsync/CreateThreadAsyncを何度呼んでも、同じ ID が返る。 - 同じ Thread に対して複数ユーザーのメッセージを書き込むことになり、会話が混在する。
- Thread を削除・再作成しても、裏側の永続エンティティの扱いが分からず、意図しない副作用を生みやすい。
つまり、「Assistant/Thread を毎回新規作成する」という OpenAI Assistants 流の設計を、そのまま Fabric Data Agent REST に当てはめると破綻しやすい、ということです。
Service Principal 認証がうまく動かない理由
公式ドキュメントの立場:Data Agent は「ユーザー ID のみサポート」
Azure AI Foundry と Fabric Data Agent を連携する公式ドキュメントでは、次のように明記されています。
- Fabric Data Agent はユーザー ID ベースの認証のみサポート。
- Service Principal(SPN)認証はサポートされていない。
- Azure AI Foundry との連携では、On-Behalf-Of(OBO)/ Identity Passthrough で「エンドユーザーの権限」で Data Agent にアクセスする。
コミュニティでも、Service Principal で Azure AI Foundry エージェントから Fabric Data Agent を叩こうとして失敗した事例が複数報告されており、「Data Agent はまだ Service Principal をサポートしておらず、今後対応予定」といったコメントが付けられています。
「UI では動くのに REST API だと失敗する」構造
Fabric Data Agent UI や Copilot in Power BI では正しくデータにアクセスできるのに、外部アプリから REST API(SP 認証)で叩くと汎用エラーになってしまう――このギャップは、実行コンテキストの違いから説明できます。
| 経路 | 認証コンテキスト | Data Agent から見た「誰の権限か」 | 結果 |
|---|---|---|---|
| Fabric Data Agent UI Copilot in Power BI | サインイン中ユーザーの AAD トークン | 「実際の人間のユーザー」。RLS/CLS などもユーザー単位で評価。 | Data Agent がデータソースにアクセスでき、正しい回答を返す。 |
| Azure AI Foundry Agent +Fabric データエージェントツール | ユーザーまたは OBO トークン | エージェント → Data Agent へ OBO でユーザー権限を引き継ぐ。 | 同様に、ユーザー権限でデータにアクセスできる。 |
| 外部 Web アプリ → Data Agent REST 直叩き(SP) | Service Principal(アプリ ID) | 「人ではないアプリケーション ID」。ユーザー固有の権限情報がない。 | Data Agent が期待どおりに認可できず、汎用エラーで失敗する。 |
つまり、Data Agent は本来「ユーザーごとの権限を尊重するチャット体験」を前提に設計されており、データアクセスの責任主体はエンドユーザーです。そのため、匿名のアプリケーション(Service Principal)のみで Data Agent を叩くシナリオは、そもそも想定されていないと言えます。
現時点での本番運用可否をどう判断するか
2024-05-01-preview REST+Service Principal の評価
ここまでを踏まえ、「2024-05-01-preview の REST API を Service Principal で叩いて本番運用できるか?」という問いに対しては、かなり厳しめに見る必要があります。
| 観点 | 2024-05-01-preview REST+SP | 典型的な本番要件 |
|---|---|---|
| セッション分離(Assistant/Thread) | 同じ ID が返る設計のため、完全分離は困難。 | ユーザー/会話ごとに論理的・物理的な分離が欲しい。 |
| マルチユーザー同時利用 | Thread 共有により会話が混在するリスク。 | 多数のユーザーが同時に利用しても会話が混ざらないこと。 |
| 認証方式 | Service Principal はサポート外。ユーザー委任は Fabric 内や Foundry 経由でのみ。 | サーバーサイドでは SP や Managed Identity を使いたいケースが多い。 |
| ログ/監査 | 汎用エラーメッセージのみで、失敗理由の切り分けが難しい。 | 失敗理由・実行ログを監査・トラブルシュートしたい。 |
| 公式ステータス | 機能全体が「プレビュー」と明記。SLA なし。 | GA(一般提供)/SLA 付きエンドポイントを優先したい。 |
このテーブルだけ見ると、ミッションクリティカルな本番用途には明らかに不向きであることが分かります。特に、セッション分離・マルチユーザー同時利用・認証方式の 3 点がネックです。
逆に、次のような条件なら「限定的 PoC・社内検証」としては許容できるケースもあります。
- 利用者がごく少数で、ID 共有による会話混在リスクを把握した上で使う。
- 扱うデータが機密ではない(サンプルデータや疑似データ)。
- 障害時には Fabric UI や Notebook からの実行に切り替えればよい、という割り切りができる。
チェックリストでざっくり判定する
本番適用可否を判断するためのシンプルなチェックリストを用意してみます。
- Service Principal や Managed Identity が 必須 である
- ユーザーごと/会話ごとに 厳密なセッション分離 が必要である
- ユーザーごとの権限(RLS/CLS 等)を 厳密に尊重 する必要がある
- 会話ログの保存期間・削除・監査など、コンプライアンス要求が厳しい
上記のうち 1 つでも「はい」が付くなら、2024-05-01-preview の REST+SP パターンは本番採用を見送るのが無難です。
当面の推奨アーキテクチャ
では、今すぐ Fabric Data Agent を業務で活用したい場合、どの経路を選べばよいのでしょうか。現時点(2025 年末)の情報を踏まえると、次の 3 パターンが現実解です。
パターン 1:Fabric UI/Copilot in Power BI で完結させる
もっともシンプルなのは、Fabric の UI や Copilot in Power BI 上でデータエージェントをそのまま使うパターンです。
- ユーザーは Power BI や Fabric ポータルにサインインしてチャットするだけ。
- 認証・権限チェック・RLS/CLS 適用はすべて Fabric 側が面倒を見てくれる。
- 「レポートの確認」「データ探索」「アドホックな Q&A」など、人が直接触る業務には十分。
このパターンの欠点は、外部 Web アプリや業務システムと深く統合しづらいことです。ただし、最初の本番活用としては一番安全で実装コストも低いため、「まずはここから」がおすすめです。
パターン 2:Fabric Notebook 経由でプログラム実行する
次のステップとして、Fabric Notebook から Data Agent を呼び出し、Notebook をアプリケーションからトリガーするというパターンがあります。Notebook のサンプルコードでは、以下のように FabricOpenAI クラスを定義し、api-version=2024-05-01-preview で Data Agent API を叩いています。
from openai import OpenAI
from synapse.ml.mlflow import get_mlflow_env_config
cfg = get_mlflow_env_config()
class FabricOpenAI(OpenAI):
def __init__(self, base_url: str):
super().__init__(
api_key="", # Fabric 側で AAD 認証するため未使用
base_url=base_url,
default_query={"api-version": "2024-05-01-preview"},
)
def _prepare_options(self, options):
headers = dict(options.headers or {})
# Notebook の実行ユーザーのトークンを利用
headers["Authorization"] = f"Bearer {cfg.driver_aad_token}"
headers.setdefault("Accept", "application/json")
options.headers = headers
return super()._prepare_options(options)
このパターンのポイントは次のとおりです。
- Notebook は常に「Fabric にサインインしているユーザーの権限」で動作する。
- 外部システムからは、Notebook 実行ジョブをトリガーするだけに留めることもできる。
- Notebook 内で Data Agent を呼び出せば、RLS/CLS もユーザー単位で適用される。
Web アプリから直接 Data Agent を叩くのではなく、「アプリ → Notebook(ジョブ)」という間接的な経路にすることで、プレビュー API への依存を Notebook 内に閉じ込める、という設計が可能です。
パターン 3:Azure AI Foundry Agent + Fabric データエージェントツール
より本格的な対話エージェントを作る場合は、Azure AI Foundry のエージェント機能に Fabric Data Agent をツールとして接続するパターンが有力です。
- Azure AI Foundry 側で「メインのエージェント」を作成(モデルは GPT-4o 等)。
- エージェントに
fabric_dataagentツールを追加し、公開済み Fabric Data Agent への接続情報を登録。 - エージェントはユーザーからの質問を受け取り、必要に応じて Fabric Data Agent をツール呼び出しする。
- REST API からは Foundry の Agents API(
api-version=2025-05-15-previewなど)を利用。
この構成のメリットは次の通りです。
- エージェント側で Thread/Run のライフサイクルをしっかり管理できる。
- 複数チャネル(Web アプリ、Teams、Copilot Studio など)に展開しやすい。
- Fabric Data Agent 側は「データアクセスのためのツール」としてシンプルに保てる。
注意点として、先述の通り Fabric Data Agent はユーザー ID のみサポートであり、Foundry 側も OBO でそれを利用するため、最終的にはユーザー委任トークン(Auth Code + PKCE, OBO 等)を取得する仕組みが必要になります。
どうしても REST API(Assistants 互換)を直接使いたい場合の暫定策
とはいえ、「既に C# から 2024-05-01-preview の REST を叩く実装を書いてしまった」「PoC でどうしてもこの API を使っておきたい」といった状況もあると思います。その場合に、被害を最小化するための暫定対処をまとめます。
1. Service Principal ではなく、ユーザー委任トークンを使う
まず大前提として、Service Principal 単体での Data Agent 呼び出しは避けるべきです。代わりに:
- フロントエンド(SPA など)でユーザーサインイン → アクセストークン取得
- バックエンド API で On-Behalf-Of フローによりトークンを交換
- そのユーザートークンを
Authorization: Bearerとして Data Agent REST に渡す
こうすることで、少なくとも Data Agent 側からは「実ユーザーの権限」でアクセスしているように見えます。完全にサポートされたパターンというわけではありませんが、SP 直叩きよりはずっと実態に近い形になります。
2. Assistant/Thread を「毎回作る」のではなく「参照する」ものとして扱う
次に重要なのが、Assistant/Thread の扱い方です。
- 管理者が一度だけ
POST /assistants,POST /threadsを実行し、返ってきた ID を設定値として保存する。 - アプリ本番コードでは
CreateAssistantAsync/CreateThreadAsyncを原則呼ばず、設定から ID を読むだけにする。 - 以降は「固定の Assistant/Thread に対してメッセージを追加・Run を作成する」イメージで扱う。
もちろん、このやり方でも 物理的なセッション分離はできません。ただ、「毎回 Assistant/Thread を作成したのに ID が同じで混乱する」という状況は避けられます。
3. 会話混在リスクを前提にした設計にする
Thread を共有する以上、ユーザー A の質問とユーザー B の質問が、同じ会話コンテキストに載る可能性があります。これをゼロにすることはできないため、設計上の工夫でダメージを減らします。
- プロンプトに「社内 ID」「テナント ID」などを明示的に含める(例:
[UserId: 1234] 以下の質問にだけ答えてください…) - 高機密データ(個人情報・財務データなど)はこの経路では扱わない。
- 重要業務では Foundry 経由や Notebook 経由の安全な経路に逃がす。
あくまで「暫定的な PoC 用途」と割り切り、本番システムの中枢に置かないことが重要です。
4. エラーハンドリングとフォールバック経路を用意する
Data Agent REST からは、しばしば「技術的な問題でデータを取得できない」といった抽象的なエラーメッセージしか返りません。 そのため、以下のような工夫が必要です。
- タイムアウトと再試行回数を明示的に設定する。
- 一定回数失敗したら
- Fabric UI への遷移リンクを提示する
- Notebook ジョブを起動して結果を別経路で返す
- 「今は Data Agent から回答できない」ことを明示したメッセージを返す
- ログには「Data Agent の HTTP ステータス」「レスポンス本文」「ユーザー ID」などを記録しておく。
C# 実装のアンチパターンと改善例
アンチパターン:毎回 Assistant/Thread を新規作成する
よくあるパターンとして、以下のような実装があります。
public async Task<(string Answer, string ThreadId)> AskAsync(
string bearerToken,
string question,
string? threadId = null)
{
using var http = _httpClientFactory.CreateClient("FabricAgent");
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", bearerToken);
// ❌ 毎回 Assistant を作る
var assistant = await CreateAssistantAsync(http);
// ❌ ThreadId が null のときに毎回 Thread を作る
if (string.IsNullOrEmpty(threadId))
{
var newThread = await CreateThreadAsync(http);
threadId = newThread.Id;
}
await CreateMessageAsync(threadId!, "user", question, http);
var run = await CreateRunAsync(threadId!, assistant.Id, http);
// Run 完了待ち+回答取得…
}
2024-05-01-preview では、CreateAssistantAsync/CreateThreadAsync を何度呼んでも同じ ID が返るため、このような実装は意味のないオーバーヘッドになりがちです。さらに、「なぜ毎回同じ ID なのか?」という疑問を生み、トラブルシュートを難しくします。
改善例:Assistant/Thread ID を構成情報として固定する
プレビュー仕様を前提にする限り、以下のように「Assistant/Thread ID は設定値として固定し、毎回参照するだけ」にした方が分かりやすくなります。
public sealed class FabricDataAgentClient
{
private readonly HttpClient _http;
private readonly string _assistantId;
private readonly string _threadId;
public FabricDataAgentClient(HttpClient http, IConfiguration config)
{
_http = http;
_assistantId = config["Fabric:AssistantId"]!;
_threadId = config["Fabric:ThreadId"]!;
}
public async Task<string> AskAsync(string userAccessToken, string question)
{
_http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", userAccessToken);
await CreateMessageAsync(_threadId, "user", question);
var run = await CreateRunAsync(_threadId, _assistantId);
var answer = await WaitAndReadAnswerAsync(_threadId, run.Id);
return answer;
}
// CreateMessageAsync / CreateRunAsync / WaitAndReadAnswerAsync の中身は割愛
}
もちろん、Thread を共有している以上、完全なセッション分離にはならない点は変わりません。それでも、「Assistant/Thread ID の扱い方」を明示し、プレビュー仕様と折り合いをつけるという意味で、実装上の混乱を減らすことができます。
将来の API 進化に備える設計の考え方
Microsoft 側でも、Data Agent と Azure AI Foundry/Copilot Studio との統合、CI/CD・ALM・Git 連携など、周辺機能の拡充が進んでおり、REST API 周りも今後変化する可能性があります。
プレビュー期の API に強く依存しすぎないために、次のような設計を意識するとよいでしょう。
- Data Agent 呼び出し部分をアプリ内で明確に分離し、将来別の経路(Foundry 経由など)に差し替えられるようにする。
- アプリ全体のドメインから見れば「自然言語でデータを聞くためのサービス」という抽象インターフェースだけを定義し、実装として Fabric/Foundry/他の LLM を差し替え可能にする。
- 会話履歴やセッション管理は、可能な限りアプリ側で完結させ、Data Agent 側には「質問+補足コンテキスト」を送るだけにする。
- 本番で利用するパスは、なるべく公式ドキュメントで推奨されている経路(Fabric UI・Notebook・Azure AI Foundry・Copilot Studio 等)に寄せる。
こうしておけば、今後 Data Agent の REST API が GA し、Service Principal 対応やセッション分離機能が追加されたときにも、呼び出し実装のみを差し替えて安全に移行しやすくなります。
まとめ:今どきの Fabric Data Agent REST API との付き合い方
最後に、本記事の要点を整理します。
- Assistant/Thread ID が毎回同じになるのは、2024-05-01-preview の仕様であり、不具合ではない。
- この仕様により、REST API だけでユーザーごとのセッションを厳密に分離することは困難。
- Service Principal での外部 REST 連携は Data Agent 側でサポートされておらず、データソースにアクセスできず汎用エラーになりやすい。
- 現状の「REST+SP」パターンは、本番用途には基本的に非推奨。使うとしても限定的な PoC にとどめるのが安全。
- 本番用途では、Fabric UI/Notebook/Azure AI Foundry/Copilot Studio といった「ユーザー委任の対話認証」を前提とした経路を優先する。
- 将来の GA や Service Principal 対応に備え、Data Agent 依存部分を疎結合に設計しておくと移行がスムーズ。
「Fabric Data Agent を REST から本番利用したい」というニーズ自体は非常に強く、今後も機能改善が期待されます。一方で、2024-05-01-preview 時点ではまだ“実験的”要素が濃いのも事実です。要件が厳しい本番環境では、今回紹介したチェックポイントと代替アーキテクチャを踏まえ、慎重に採用可否を判断していきましょう。

コメント