Azure AI Foundryで管理できるAIエージェントをSDKで管理する
この記事ではAzure AI FoundryのSDKを利用して様々ツールを構築する実装方法について解説します。
Azure AI Foundryは、開発者がAIをアプリケーションに組み込むためのオープンで柔軟、かつ安全なプラットフォームです。1,900以上のオープンソースモデルと独自モデルを選択することができます。これらにプロンプトやツールを導入することでAIエージェントを構築し外部から利用することができます。
Azure AI Foundryでエージェントを開発する場合はほとんどの場合ポータル上で解決します。一方、一時的に使うようなエージェントや外部アプリからアクセスが必要な場合はSDKを利用することで効率的に利用できます。
Azure AI Foundryポータル上での操作や動作確認は以前に作成した以下の記事が参考になると思います。
1. Azure AI Foundryを利用する為に使える言語
今回扱うSDKについてはC#のものですが、他の言語や手段でAzure AI Foundryを利用することが可能です。のちのちUnityでも活用したいと考えているのでC#で今回は調査しています。
- Azure AI Foundryポータル
- C#
- Python
- TypeScript
- Rest API
2. Azure AI FoundryのAIエージェントが使えるツール
Azure AI Foundryで構築できるAIエージェントはツールとして様々なサービスや連携が可能です。
2.1 Knowledge
AIエージェントにデータソースを接続することで知識を拡張することができます。ファイル形式のドキュメントや、Sharepoint等の既にある業務で蓄積した情報を活用することができます。また、Webの情報を取得する為のGrounding With Bing Search等も利用できます。今回の実装では以下の2つの実装について触れていく予定です。
- Files
- Grounding With Bing Search
2.2 Actions
AIエージェントが応答として他システムやサービスなどタスクを実行することも可能です。python等のCode Interpreterをはじめとして、カスタム関数の呼び出し、Azure関連ではAzure Functionsなどが利用可能です。
今回の実装では以下の2つの実装について触れていく予定です。
- Code Interpreter
- カスタム関数
2.3 Agents
Azure AI FoundryのAIエージェントは定義済みの他のAIエージェントを組合わせてマルチエージェント構成にすることも可能です。
3. まずはSDKを使ってみる
これまで簡単にAzure AI Foundry上で扱えるAIエージェントの機能の一部を紹介しました。次に実際にC#のSDKを使った実装を見ていきたいと思います。最初に公式サイトで紹介されているクイックスタートを使ってAIエージェントの生成から入力、削除の仕方についてみていきましょう。
このクイックスタートは以下のサイトで紹介されているものですが、Code Intepreterを利用しy=4X+9のグラフをAIエージェントに作成させる例となっています。
3.1 クイック スタート: 新しいエージェントを作成する
基本的な実行手順は上記サイトにあるのですが一部必要なライブラリなどが不足しているのでそういったところを補完しながら解説してきたいと思います。
今回使用しているライブラリ等の環境は以下の通りです。
- ツール
- Visual Studio Code
- .NET 9
- Azure CLI
- Package
- Azure.AI.Agents.Persistent
- Azure.Identity
- Microsoft.Extensions.Configuration
- Microsoft.Extensions.Configuration.FileExtensions
- Microsoft.Extensions.Configuration.Json
幾つかパッケージをインポートするのですが、Azure AI Foundryに関するものはAzure.AI.Agents.Persistentになります。
Azure.Identityは、Azure AI Foundryへの接続時の認証に利用します。今回はAPIKeyではなく、Azureアカウントにログインした状態で利用します。ログインにはAzure CLIを利用します。
それ以外にAzure AI Foundryのプロジェクトに接続する為のエンドポイント等設定に関するものをappsettings.jsonに保存して参照するためにMicrosoft.Extensions.Configuration系のライブラリも利用します。
3.1.1 Azure AI Foundryのリソースを作成する。
最初にAzure AI FoundryとAzure AI Foundry Projectを作成します。作成手順については以前のワークショップに手順があるので参考にして下さい。
特に難しい設定はないのですが、リージョンについては使用したいLLMが提供されているリージョンを選択するといいと思います。
https://learn.microsoft.com/ja-jp/azure/ai-foundry/agents/concepts/model-region-supportquickstart?wt.mc_id=WDIT-MVP-5003104
リソースの作成が完了したらAzure AI Foundryポータルにアクセスし作成したプロジェクトのエンドポイントを控えておくのを忘れないでください。
3.1.2 実装
次に実際にクイックスタートのソースコードをビルドして実行しましょう。
最初に任意のディレクトリに移動し、ターミナルを開きます。
最初に.NET コンソールアプリのプロジェクトを用意します。以下のコマンドを実行してください。
PS C:\hoge> dotnet new console -o [任意のプロジェクト名]
次に作成したプロジェクトのディレクトリ配下に移動し、必要なパッケージを追加します。
PS C:\hoge> cd [任意のプロジェクト名]
PS C:\hoge\[任意のプロジェクト名] > dotnet add package Azure.AI.Agents.Persistent
PS C:\hoge\[任意のプロジェクト名] > dotnet add package Azure.Identity
PS C:\hoge\[任意のプロジェクト名] > dotnet add package Microsoft.Extensions.Configuration
PS C:\hoge\[任意のプロジェクト名] > dotnet add package Microsoft.Extensions.Configuration.FileExtensions
PS C:\hoge\[任意のプロジェクト名] > dotnet add package Microsoft.Extensions.Configuration.Json
次にクイックスタートのサンプルコードをProgram.csに上書きコピーし、保存します。保存が完了したら以下のコマンドを入力しビルドを実行してください。パッケージなどが正しく追加されていれば正常にビルドが完了するはずです。
PS C:\hoge\[任意のプロジェクト名] > dotnet build
最後に、エンドポイント等を保存した設定ファイルをexeと同階層に配置します。
{
"ProjectEndpoint": "[ポータルのエンドポイント]",
"ModelDeploymentName":"LLMモデル名"
}
以上で準備完了です。以下のコマンドを実行するとAzure AI Foundryに接続しエージェントが生成され、処理が実行されます。最初にAzure CLIのコマンドでAzureにログインすることを忘れないでください。az loginを実行するとブラウザでAzureにログインするアカウントが表示されます。通常通りログインし、コンソールでログイン状態が維持されるようにして下さい。
クイックスタートでは、事前にきめられたメッセージを送付し処理が行われたあと、作成したエージェント等は削除される仕様です。もし、どのような情報が作成されているか気になる方は最後の2行が削除処理なので、その直前にキー入力待ち等を入れて処理を止めつつAzure AI Foundryポータルで確認するといいと思います。
PS C:\hoge\[任意のプロジェクト名] > az login
PS C:\hoge\[任意のプロジェクト名] > .\bin\Debug\net9.0\[任意のプロジェクト名].exe
3.1.3 コードの解説
クイックスタートの実装内を抜粋しながら内容を見ていきたいと思います。
Azure AI Foundryとの接続
今回はAIエージェントだけを生成するための手順なので以下の実装でAIエージェントを操作する為のクライアントオブジェクトを取得しています。AIエージェント以外のAzure AI Foundry全体に対する操作などが必要な場合は別途、Azure.AI.Projectsパッケージを導入しAzure AI Foundryのプロジェクトにアクセスします。
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
エージェントの作成
次にAzure AI Foundry上にAIエージェントを作成します。今回のサンプルではPythonのCode Intepreterを使ってグラフを作成していました。この機能はAIエージェントのツールとして有効化しておく必要があるためtoolsプロパティにCodeInterpreterToolDefinitionを渡しています。この設定を行うことでAIエージェントはCode Intepreterが使えるようになり必要に応じてpythonコードを実行して処理を行うことが可能になります。
instructionsはいわゆるプロンプトに相当するものです。このAIエージェントの役割等を記述します。
PersistentAgent agent = client.Administration.CreateAgent(
model: modelDeploymentName,
name: "My Test Agent",
instructions: "You politely help with math questions. Use the code interpreter tool when asked to visualize numbers.",
tools: [new CodeInterpreterToolDefinition()]
);
スレッドの作成
次にThreadを作成します。Azure AI Foundryでは会話のひとまとまりをThreadという単位で管理しています。Theadを作成すると、Azure AI Foundry上でThreadが作成されそのやりとりをポータル上からも確認することができます。このクイックスタートではその都度削除していますが、検証時にはエージェントの応答内容の確認が可能になりデバックもやり易くなります。
PersistentAgentThread thread = client.Threads.CreateThread();
メッセージの作成と送信
実際にエージェントに何かリクエストする場合はメッセージを作成します。メッセージはThreadに紐づくため、Thread IDと合わせて送ります。今回のクイックスタートではグラフの描画を依頼しています。
client.Messages.CreateMessage(
thread.Id,
MessageRole.User,
"Hi, Agent! Draw a graph for a line with a slope of 4 and y-intercept of 9.");
以上でリクエストの準備が完了しました。実際にリクエストするに処理を実行します。実行はclient.Runsプロパティを利用して行います。今回は単発の処理なのでCreateRunで実行しています。リアルタイムでやり取りを継続するような場合はストリーミングで処理する方法もあります。
ThreadRun run = client.Runs.CreateRun(
thread.Id,
agent.Id,
additionalInstructions: "Please address the user as Jane Doe. The user has a premium account.");
応答に対する処理
後はレスポンスに関する処理です。リクエストを実行したあと、エージェントが処理を完了するまでは実行状態を監視します。先ほどのCreateRunにより実行した内容の状態を監視し完了後にメッセージの処理を行います。
do
{
Thread.Sleep(TimeSpan.FromMilliseconds(500));
run = client.Runs.GetRun(thread.Id, run.Id);
}
while (run.Status == RunStatus.Queued
|| run.Status == RunStatus.InProgress
|| run.Status == RunStatus.RequiresAction);
処理が完了したら最後にメッセージを取得し、メッセージの種類に合わせて処理を行います。今回の場合はcode interpreterによってpng画像が返ってくるので、そのための処理が入っています。メッセージのコンテキストがMessageImageFileContentの場合はファイルの情報が作成されているので、情報を取得しローカルに保存されます。何らかのエラーや画像が作成できなかった場合は文字メッセージが返ってくることがあります。このためMessageTextContentが返ってきた場合はその結果をコンソールに出力しています。
非常にシンプルな作りですが、簡単にcode interpreterを使ったエージェントを構築することが可能なことが分かります。基本的な実装方法はこの方法になりますが、最初に解説したファイル検索やBingを使った検索といったほかのツールを利用する場合はそれに合わせた実装を加える必要があります。それらについては以降の節で紹介していきたいと思います。
4. Azure AI Foundryに利用できるSDKの注意点
Azure Ai Foundry上のリソースにアクセスするために使えるパッケージは以下の2つです。
-
Azure.AI.Agents.Persistent(Nuget)
OpenAI、Microsoft、および他のLLMプロバイダーからのモデル、ツール、および機能を活用して、Azure AI Agents Serviceを使用してAgentを開発 -
Azure.AI.Projects(Nuget)
Azure AI Foundry内のリソース管理を容易にするライブラリ- 主な機能
- エージェントの作成/実行(GetPersistentAgentsClient)
- AzureOpenAI クライアントの取得(GetAzureOpenAIChatClient)
- Azure AI Foundry プロジェクトにデプロイされた AI モデルを取得
- Azure AI Foundry プロジェクトで接続されている Azure リソースを取得
- ドキュメントをアップロードし、それらを参照するデータセットを作成(Datasets)
- 検索インデックスを作成/列挙(Indexes)
- チャットの完了、テキストまたは画像の埋め込み用のAzure AI Inferenceクライアントを取得(Inference)
- 主な機能
上記のとおり、Azure AI Foundryに関する処理を行う為にはAzure.AI.Projectsを使うのですがPrerelease版であることと、最新バージョンで破壊的アップデートが入っているため注意が必要です。
AIエージェントのみ使う場合
Azure AI FoundryのプロジェクトのAgents機能のみ使う場合は以下の2パターンになります。
- Azure.AI.Agents.Persistentを利用
Azure AI FoundryプロジェクトのAIエージェントのみ利用するのであればこのパッケージのみで実現できます。 - Azure.AI.Projectsを利用
v1.0.0-beta.8までは、Azure.AI.Projects内にAzure.AI.Agents.Persistent相当の機能が含まれているためこちらでも利用可能です(ただし、利用できるクラス名などが若干異なります。例:PersistentAgent→Agent)。v1.0.0-beta.9からはAzure.AI.Agents.Persistentと併用が必要です。
Azure AI Foundryプロジェクトの機能と併用する場合
Agents機能以外のAzure AI Foundryプロジェクトの機能も併用する場合はバージョンに注意が必要です。このためサンプルによってはビルドエラーになることもあります。実装方法を調べる時はNugetからGithubリポジトリにアクセスると該当バージョンにあったサンプルを入手できるので参考にして下さい。
パッケージの組合せ
どの機能を利用するか | 使用するパッケージとバージョン | 追加で必要なパッケージ |
---|---|---|
AIエージェントのみ |
|
- |
AIエージェントのみ |
|
|
Azure AI Foundryプロジェクトの情報も利用 |
|
|
Azure AI Foundryプロジェクトの情報も利用 | Azure.Identity |
5. ツールを利用したAIエージェントを作成し実行する
最初に紹介したコードではCode Intepreterを利用しAIエージェントがPythonコードを生成しグラフを作成しました。Azure AI Foundryではこれ以外にも様々なツールを利用することができます。ここでは以下のツールを実装したエージェントを作成しその動作を確認してみたいと思います。
- Function Calling
- Files Search
- Grounding with Bing Search
サンプルのコードは以下の場所にあります。
https://github.com/TakahiroMiyaura/AzureAIFoundrySamples.git
このサンプルをうごかすためにはAzure AI FoundryをAzureポータルに準備しておく必要があります。
また、Azure AI Foundryプロジェクトのエンドポイントを控えておくことも忘れずに!
準備と実行
ビルドして実行するためには次の手順を踏んでください。
記事作成時点(2025/07/12時点)では各パッケージバージョンは以下の通りです
パッケージ名 | バージョン |
---|---|
Azure.AI.Agents.Persistent | 1.0.0 |
Azure.AI.Projects | 1.0.0-beta.10 |
Azure.Identity | 1.14.2 |
Microsoft.Extensions.Configuration | 9.0.7 |
Microsoft.Extensions.Configuration.UserSecrets | 9.0.7 |
サンプルのプロジェクトをクローンします。
PS C:\hoge> git clone https://github.com/TakahiroMiyaura/AzureAIFoundrySamples.git
PS C:\hoge> cd AzureAIFoundrySamples
次ににAzure AI Foundryのエンドポイントを登録します。
PS C:\hoge\AzureAIFoundrySamples>dotnet user-secrets init
PS C:\hoge\AzureAIFoundrySamples>dotnet user-secrets set "projectEndpoint" "[Azure AI Foundryのエンドポイント]"
次に必要なパッケージを追加します。
PS C:\hoge\AzureAIFoundrySamples > dotnet add package Azure.AI.Projects --prerelease
PS C:\hoge\AzureAIFoundrySamples > dotnet add package Azure.AI.Agents.Persistent
PS C:\hoge\AzureAIFoundrySamples > dotnet add package Azure.Identity
PS C:\hoge\AzureAIFoundrySamples > dotnet add package Microsoft.Extensions.Configuration
PS C:\hoge\AzureAIFoundrySamples > dotnet add package Microsoft.Extensions.Configuration.UserSecrets
最後にビルドしてコンソールアプリを作成します。
PS C:\hoge\AzureAIFoundrySamples > dotnet build
実行
ビルド完了後実行してみましょう。
PS C:\hoge\AzureAIFoundrySamples > .\bin\Debug\net9.0\AzureAIFoundrySamples.exe
1.Basic chat
2.Code Interpreter chat
3.Function Calling chat
4.File Search chat
5.Bing Search chat
Enter your query (type 'exit' to quit):
上記のようにメニューが表示されます。サンプルは機能を試すために幾つかのクラスに分けて作っています。
No. | メニュー名 | 説明 |
---|---|---|
1 | Basic chat | 基本的なチャット |
2 | Code Interpreter chat | コードインタープリタを実装したチャット |
3 | Function Calling chat | 関数呼び出しを実装したチャット |
4 | File Search chat | ファイル検索を実装したチャット |
5 | Bing Search chat | Bing検索を実装したチャット |
1についてはpromptを与えただけのエージェントになるため紹介は割愛します。
エージェントを生成後Azure AI Founryポータルにアクセスすることで生成されたエージェントやスレッドの情報を見ることができます。
Code Interpreter chat
コードインタプリタを利用できるようにしたチャットです。promptに年齢、性別、月に訪れる映画館の回数をデータにもたせています。本来データはファイルにしてファイル検索として使うのですが、簡易的にこのような実装としています。
コードインタプリタを利用可能なため、グラフを画像で取得することが可能です。
メニューで2を選択するとエージェントが用意されます。
2
Creating agent...
Agent created with ID: asst_gEQixNfZ2E69nQu4H81xZUfd
Creating thread...
Thread created with ID: thread_mXAJMXJiKIqZ9OlvUWK1PAYr
Enter your query (type 'exit' to quit):
helpでできることを表示してみます。
help
I can assist you with analyzing the survey data about how many times people go to the movies each month. Here are some things I can do:
1. **Summarize the data**: Provide insights like average movie visits, gender-based trends, or age group analysis.
2. **Visualize the data**: Create charts or graphs to show trends or comparisons.
3. **Answer specific questions**: For example, "What is the average number of movie visits for males?" or "Which age group visits the movies most often?"
4. **Export results**: Save visualizations or analysis results for you to download.
何かグラフを表示てできるような問合せを行います。
Please show the ratio of each age group and gender in this questionnaire in a pie chart.
To create a pie chart showing the ratio of each age group and gender in the questionnaire, I will first categorize the data into age groups and genders, calculate their proportions, and then visualize the results. Let me proceed.It seems there was an issue combining the age group labels with gender labels due to the categorical data type. I will resolve this and retry creating the pie chart.The pie chart showing the ratio of each age group and gender in the questionnaire has been successfully created. You can download it using the link below:
[Download Pie Chart](sandbox:/mnt/data/age_gender_ratio_pie_chart.png)Getting file with ID: assistant-QLgRJygGJNSHLeThuktUyM
File save to D:\.....\AzureAIFoundrySamples\shared\files\assistant-QLgRJygGJNSHLeThuktUyM.png
Function Calling chat
Function Callingを利用できるようにしたチャットです。例えば、3日後の日付を問い合わせるとクライアントアプリ側の関数が呼び出され現在日時に日付けの演算処理を行ないます。処理結果はエージェントに送られユーザに結果を返します。
メニューで3を選択するとエージェントが用意されます。
3
Creating agent...
Agent created with ID: asst_iE8FBdfDY74jOy2zhnw8aasQ
Creating thread...
Thread created with ID: thread_cDoaW71LlpkTuIIiksqi5ro4
Enter your query (type 'exit' to quit):
日付け計算するようにメッセージを送ります。
day after 3
日付け計算のためにFunction Callingが実行されます。
Call Function:
Call Id: call_iZ7WaNoswe6tiZUeProTZFlw
Function Name: CalculateDateTime
Args: {"info":3}
The date 3 days from now will be July 15, 2025.
File Search chat
ファイル検索を利用できるようにしたチャットです。ファイルに書かれいている情報を知識として利用することができます。ファイルはHomeAppliances.mdになります。架空の家電の情報が記載されています。
最初に4を選択するとエージェントが準備されます。この時HomeAppliances.mdをアップロードしVector検索が可能な情報に変換されてエージェントに組み込まれます。
4
Creating agent...
Uploading file: D:\....\AzureAIFoundrySamples\bin\Debug\net9.0\..\..\..\shared\files\HomeAppliances.md
File uploaded: assistant-W74iEADn7nyWqzusUvgVQU
Vector store created: vs_jvpgT1zQ8EKnFRSser14JZ8P
Agent created with ID: asst_iaf9flyEZFpbJYaSqwd77B6R
Creating thread...
Thread created with ID: thread_DX9BtNb8Ycd4GAEBSL4cmx8z
Enter your query (type 'exit' to quit):
ファイルの内容について聞くことで製品カタログに関する情報を返してくれます。
What is the content of the file?
The uploaded file contains a catalog of home appliances categorized by their usage areas, such as living rooms, studies, kitchens, and dining areas. Each appliance entry includes details like model number, product category, name, description, and price. Here's a summary of the content:
### Living Room and Study Appliances:
1. **ExVision 4K Smart TV** (Model: TV-EX1000) - 55-inch 4K smart TV with voice control and app support. Price: \98,000.
2. **LightPro Cyclone Vacuum Cleaner** (Model: VC-LightPro) - Lightweight, powerful suction, and quiet design. Price: \24,800.
3. **PureAir Air Purifier** (Model: AP-PureAir) - Covers 20 tatami mats, pollen and virus filter. Price: \29,800.
4. **MoistPlus Ultrasonic Humidifier** (Model: HM-MoistPlus) - Automatic humidity adjustment. Price: \13,500.
次にTVに関する情報を問い合わせると製品情報が返ってきます。
tell me about tv
The **ExVision 4K Smart TV** (Model: TV-EX1000) is a 55-inch 4K smart television. It supports voice control and offers compatibility with a variety of apps. The price of this TV is \98,000【6:0†source】. Let me know if you need further details!
次にファイル検索が与えられた情報だけでしか知識を活用できないことを確認します。製品リストはメーカーの情報を含んでいないため、比較対象のために競合製品を列挙してもらうように依頼します。
What TV products do your competitors offer?
The uploaded file does not mention any specific competitor TV products. It only lists the **ExVision 4K Smart TV** as part of the catalog. If you have additional files or need help finding competitor products, feel free to provide more details!
上記のように、ファイル内にある情報を元に返答していることが分かります。
Bing Search chat
Grounding With Bing Searchを利用したチャットになります。このチャットではFile Searchでも使っているファイル情報も渡しています。Bing検索が利用できるためファイルにない情報はBing検索で取得することが可能になります。
メニューで5を選択するとファイルの情報と、Bing検索をツールとして追加したエージェントが生成されます。
5
Creating agent...
Uploading file: D:\WorkSpaces\Git\AzureAIFoundrySamples\bin\Debug\net9.0\..\..\..\shared\files\HomeAppliances.md
File uploaded: assistant-LGbLq5bBthzg6YD8n9wmRX
Vector store created: vs_DW8WZaa85Ar0uEl3BlvJC7b5
Agent created with ID: asst_IrebvIJfvfLv4uFxr6xkKqYB
Creating thread...
Thread created with ID: thread_5RJ0DsxeVjTeRXbZArvIN1Mi
Enter your query (type 'exit' to quit):
Bing検索していることを確認します。File Searchと同じようにやりとりを行ないます。最後の競合製品に関する質問の際にBing検索が実施され結果が返ってきます。Bing検索を利用しているので実在の製品が応答に含まれていることを確認できます。
What TV products do your competitors offer?
Competitors in the TV market offer a wide range of products with advanced features. Some notable brands and their offerings include:
1. **Samsung**:
- Known for QLED and Neo QLED TVs with exceptional picture quality, vibrant colors, and AI upscaling.
- Popular models: Samsung S90C OLED, QN90C QLED【15:1†source】.
...
These brands compete by integrating features like smart engines (e.g., Tizen, webOS, Android), advanced display technologies (OLED, QLED), and connectivity options to enhance user experience【15:3†source】【15:4†source】. Let me know if you'd like more details about any specific brand or model!
5.1 実装内容の解説
次にこのサンプルの実装内容のうち、特にエージェントわたすtoolについての実装を中心に解説したいと思います。
5.1.1 エージェントの基底設計と初期化(AbstractAgentBase.cs)
AbstractAgentBase.csはエージェントの基本的な処理を行なう基底クラスとして作成しています。以下のようなどのタイプでも行なう同じような処理をまとめています。
- Azure AI Foundryプロジェクトのアクセス
- エージェントの作成
- スレッド/メッセージ作成
- リクエスト
- レスポンスの処理
コンストラクタで初期化の処理を実行しています。ユーザーシークレットからAzure AIプロジェクトのエンドポイントを取得し、AIProjectClient
とPersistentAgentsClient
を初期化します。れにより、Azure AIサービスとの連携が可能になります。
protected AbstractAgentBase()
{
IConfigurationRoot config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.Build();
if (config == null) throw new NullReferenceException("Config is null");
string projectEndpoint = config[Key] ?? throw new KeyNotFoundException($"key:{Key} not found");
ProjectClient = new(new Uri(projectEndpoint), new DefaultAzureCredential());
AgentClient = ProjectClient.GetPersistentAgentsClient();
}
5.1.2 エージェント生成
コンストラクタで初期化したAzure AI Foundryプロジェクトに対してエージェントを生成します。
指定したモデル・指示文・ツール・パラメータでエージェント(AIサービス上の実体)を生成します。角チャットは子クラスでtoolやリソースをカスタマイズしています。
Agent = await CreateAgentAsync(
name: "SampleAgent",
instructions: instructions,
tools: tools,
toolResources: toolResources,
temperature: Temperature,
topP: TopP
);
await Console.Out.WriteLineAsync($"Agent created with ID: {Agent.Id}");
既存エージェントの取得について
Azure AI Foundryでは、既存のエージェントをID指定で取得し再利用することも可能です。
たとえば、以下のようにGetAgentAsync
を利用します。既存のエージェントIDを指定して取得することで、リソースの再利用や複数セッションでの共有が可能です。agentIdの取得はこのクラスのProjectClientからAzure AI Foundryを経由して取得することが可能です。
// 参考: 既存エージェントの取得例
Agent = await AgentClient.Administration.GetAgentAsync(agentId);
5.1.3 スレッド生成
会話の文脈(スレッド)を新規作成します。スレッドIDは、以降のユーザーとのやりとりの単位となります。
Thread = (await AgentClient.Threads.CreateThreadAsync()).Value;
await Console.Out.WriteLineAsync($"Thread created with ID: {Thread.Id}");
作成したスレッドは削除しない限りAzure AI Foundryポータルからも確認が可能です。
実際のエージェントの動き等も確認ができるためデバッグ検証も容易です。
5.1.4 対話ループ
エージェントとスレッドの作成が完了すればあとはユーザの入力におうじてエージェントに処理を依頼します。
5.1.4.1 ユーザー入力の取得
ユーザ入力についてはコンソールから受け付けるようにしています。入力が空の場合は再入力を促し、"exit"と入力された場合はループを終了します。
await Console.Out.WriteLineAsync();
await Console.Out.WriteLineAsync("Enter your query (type 'exit' to quit):");
string? prompt = await Console.In.ReadLineAsync();
if (string.IsNullOrEmpty(prompt)) continue;
if (prompt.Equals("exit", StringComparison.InvariantCultureIgnoreCase)) break;
5.1.4.2 メッセージの作成と送信
ユーザの入力を受け取るとエージェントにリクエストする準備をします。CreateMessageAsyncメソッドを実行すると対象のスレッドにメッセージを作成するように登録します。次のRunを実行することでエージェントが処理を開始します。
- threadId : メッセージを送るスレッド
- role : このメッセージを送るロール
- content : エージェントに送るメッセージ
_ = await AgentClient.Messages.CreateMessageAsync(
threadId: Thread.Id,
role: MessageRole.User,
content: prompt
);
5.1.4.3 応答のストリーミング受信と処理
先ほど登録したメッセージを実行します。今回は応答をストリーミングで受け取るCreateRunStreamingAsyncメソッドを利用します。このメソッドを呼び出すことでリクエストの処理が開始され、エージェントが推論しレスポンスを返します。エージェントはレスポンスを複数返す場合があるためストリーミングで応答が終わるまでループでメッセージを処理します。受信した応答はHandleStreamingUpdateAsync
で逐次処理され、ユーザーにリアルタイムで表示されます。
- threadId: スレッドId
- agentId: エージェントId
- maxCompletionTokens: 処理を完了するトークンの最大値
- maxPromptTokens: promptの最大トークン数
temperture,topPはエージェントの推論の傾向を調整する為のパラメータです。
AsyncCollectionResult<StreamingUpdate> streamingUpdate = AgentClient.Runs.CreateRunStreamingAsync(
threadId: Thread.Id,
agentId: Agent.Id,
maxCompletionTokens: MaxCompletionTokens,
maxPromptTokens: MaxPromptTokens,
temperature: Temperature,
topP: TopP
);
await foreach (StreamingUpdate update in streamingUpdate)
{
await HandleStreamingUpdateAsync(update);
}
5.1.5 ストリーミング応答・アクションハンドリング
エージェントからの応答を処理します。応答には幾つかタイプがあるのでそれに合わせて処理を実装します。今回のサンプルはCode Intepreter、Function Callingを利用しているため、エージェントからの応答はテキストメッセージ以外にも関数呼出しのためのアクションの要求、やCode Intepreterの処理結果としてのファイルダウンロード処理などを実装します。
Function Callingについては応答の状態が、[StreamingUpdateReason.RunRequiresAction]となります。このためこの種類の状態であれば、該当するクライアントの処理を実行します。
Code Intepreterのファイルダウンロードについては応答完了時に含まれるコンテンツからファイル情報を取得してローカルに保存します。
protected async Task HandleStreamingUpdateAsync(StreamingUpdate update)
{
switch (update.UpdateKind)
{
case StreamingUpdateReason.RunRequiresAction:
RequiredActionUpdate requiredActionUpdate = (RequiredActionUpdate)update;
await HandleActionAsync(requiredActionUpdate);
break;
case StreamingUpdateReason.MessageUpdated:
MessageContentUpdate messageContentUpdate = (MessageContentUpdate)update;
await Console.Out.WriteAsync(messageContentUpdate.Text);
break;
case StreamingUpdateReason.MessageCompleted:
MessageStatusUpdate messageStatusUpdate = (MessageStatusUpdate)update;
PersistentThreadMessage tm = messageStatusUpdate.Value;
var contentItems = tm.ContentItems;
foreach (MessageContent contentItem in contentItems)
{
if (contentItem is MessageImageFileContent imageContent)
{
await DownloadImageFileContentAsync(imageContent);
}
}
break;
case StreamingUpdateReason.RunCompleted:
await Console.Out.WriteLineAsync();
break;
case StreamingUpdateReason.RunFailed:
RunUpdate runFailedUpdate = (RunUpdate)update;
await Console.Out.WriteLineAsync($"Error: {runFailedUpdate.Value.LastError.Message} (code: {runFailedUpdate.Value.LastError.Code})");
break;
}
}
未使用のStreamingUpdateReasonについて
公式ドキュメント(Azure AI Agent Service documentation)によると、StreamingUpdateReason(または同等のイベント種別)には、Run/RunStep/Message/Thread/Terminal(Error, Done)など多くの種類が定義されています。
本実装では、以下の値はハンドリングされていません:
種類 | 概要 |
---|---|
RunCreated | Run(実行)が新規作成されたことを示すイベント |
RunQueued | Runがキューに入ったことを示すイベント |
RunInProgress | Runが実行中であることを示すイベント |
RunCancelling | Runのキャンセル処理が進行中であることを示すイベント |
RunCancelled | Runがキャンセルされたことを示すイベント |
RunExpired | Runが有効期限切れで終了したことを示すイベント |
RunStepCreated | Run内のステップが新規作成されたことを示すイベント |
RunStepInProgress | ステップが実行中であることを示すイベント |
RunStepDelta | ステップの進捗や部分的な結果がストリーミングで送信されるイベント |
RunStepCompleted | ステップが正常に完了したことを示すイベント |
RunStepFailed | ステップが失敗したことを示すイベント |
RunStepCancelled | ステップがキャンセルされたことを示すイベント |
RunStepExpired | ステップが有効期限切れで終了したことを示すイベント |
MessageCreated | メッセージが新規作成されたことを示すイベント |
MessageInProgress | メッセージが生成中であることを示すイベント |
MessageDelta | メッセージの部分的な内容がストリーミングで送信されるイベント |
MessageIncomplete | メッセージが不完全な状態であることを示すイベント |
ThreadCreated | スレッド(会話単位)が新規作成されたことを示すイベント |
Error | エラーが発生したことを示すイベント |
Done | ストリームの正常終了を示すイベント |
これらのイベントは、エージェントの状態遷移や詳細な進捗管理、エラーや完了通知など、より細かな制御やUI更新、ログ取得などに活用できます。必要に応じてこれらのイベントもハンドリングすることで、より堅牢でユーザーフレンドリーなエージェント実装が可能となります。
参考情報
5.1.6 リソースのクリーンアップ(スレッド・エージェントの削除)
今回のサンプルはエージェントを毎回生成しています。このため後始末として使用したスレッドやエージェントの削除を行なっています。スレッド(会話単位)が存在する場合、DeleteThreadAsync
でAzure上からスレッドを削除します。エージェント(AIエージェント本体)が存在する場合、DeleteAgentAsync
でAzure上からエージェントを削除します。
public async ValueTask DisposeAsync()
{
if (Thread is not null)
{
await AgentClient.Threads.DeleteThreadAsync(Thread.Id);
}
if (Agent is not null)
{
await AgentClient.Administration.DeleteAgentAsync(Agent.Id);
}
}
以降は各チャットのタイプ毎に個別の実装内容を解説します。
5.2 Function Calling
Function Callingのチャットのサンプルでは現在日付から演算を行う処理をクライアントで行います。これはInstructionで定義したpromptの中で以下のように記載しています。
## Tools
- Use the provided tool:
- **Tool:** `CalculateDateTime`
- **Parameters Construction:**
- **Info:** Number of days specified by the user"
エージェントは日付の計算が必要だと判断した場合、クライアントに対してCalculateDateTimeの処理要求を行います。クライアント側はではCalculateDateTimeに対応する処理を実施し、エージェントに結果を返します。
5.2.1 Function Callingの定義と実装
Function Callingを利用する場合は、エージェントの生成時にFunction Callingをtoolとして登録する必要があります。その実装部分は以下のようになります。Function CallinはFunctionToolDefinitionクラスに関数の仕様を設定します。
今回はinstructionにも書いた以下の仕様で登録します。
- 関数名 : CalculateDateTime
- パラメータ : 数値型のinfoプロパティ
protected override IEnumerable<ToolDefinition> IntializeTools() => [
new FunctionToolDefinition(
name: functionName,
description: "This function is used to answer user questions about calculate Date Time.",
parameters: BinaryData.FromObjectAsJson(new {
Type = "object",
Properties = new {
Info = new {
Type = "integer",
Description = "The input should be a integer value to extract information based on the user's question. The result will be returned as a JSON object."
}
},
Required = new[] { "info" }
}, options)
)
];
5.2.2 Function Callingの実行と応答
実際にエージェントから関数呼び出しの要求があった場合、処理を行います。エージェントからは要求するアクションの関数名、パラメータが情報として含まれています。これらの情報を参照し、クライアント側で関数名に合わせた処理を実行します。Toolで処理した結果はSubmitToolOutputsToStreamAsyncメソッドを使って、クライアント側からエージェントに情報を送り返します。
protected override AsyncCollectionResult<StreamingUpdate> HandleToolAction(RequiredActionUpdate requiredActionUpdate)
{
if (requiredActionUpdate.FunctionName != functionName)
{
throw new InvalidOperationException($"Invalid function name: {requiredActionUpdate.FunctionName}");
}
SampleToolArgs sampleToolArgs = JsonSerializer.Deserialize<SampleToolArgs>(requiredActionUpdate.FunctionArguments, options) ?? throw new InvalidOperationException("Failed to parse JSON object.");
var result = DateTime.Now.AddDays(sampleToolArgs.Info).ToString("yyyy-MM-dd HH:mm:ss");
return AgentClient.Runs.SubmitToolOutputsToStreamAsync(
requiredActionUpdate.Value,
new List<ToolOutput>([new ToolOutput(requiredActionUpdate.ToolCallId, result)])
);
}
5.3 Files Search
ファイル検索用のサンプルはmd形式のファイルをベクター検索可能な情報としてエージェントに登録します。ユーザからの要求の中でエージェントが適宜ファイル内の知識を利用して応答することができます。ファイル検索をするためにはいくつか処理が必要になります。
- Azure AI Foundry内のエージェントの領域にファイルをアップロードする
- アップロードしたファイルをベンター検索用のストアを作成する
- エージェントのリソースとして2で作成したストアを登録する
Azure AI FoundryのAIエージェントで知識として利用できるファイルの形式は公式サイトに記載があります。
Azure AI Foundry Agent Service ファイル検索ツール - サポートされているファイルの種類
5.3.1 ベクトルストアの作成とファイルアップロード
エージェントでファイル検索するためには、Azure AI Foundryプロジェクトに対象ファイルをアップロードし、VectorStoreを作成します。これにより、アップロードしたファイルの内容を検索情報として活用できる準備ができました。
protected override async Task InitializeAgentAsync(PersistentAgentsClient agentClient)
{
string datasheet = Path.Combine(SharedPath, "files", "HomeAppliances.md");
await Console.Out.WriteLineAsync($"\u001b[32mUploading file: {datasheet}\u001b[0m");
var file = await agentClient.Files.UploadFileAsync(
filePath: datasheet,
purpose: PersistentAgentFilePurpose.Agents
);
await Console.Out.WriteLineAsync($"\u001b[32mFile uploaded: {file.Value.Id}\u001b[0m");
var result = await agentClient.VectorStores.CreateVectorStoreAsync(
fileIds: [file.Value.Id],
name: "Home Appliances Information Vector Store"
);
vectorStore = result.Value;
await Console.Out.WriteLineAsync($"Vector store created: {vectorStore.Id}");
}
5.3.2 ファイル検索ツールの初期化
次にエージェントが先ほど作成したVectorStoreの情報をつかえるようにtoolを追加します。追加するtoolはFileSeqrchToolDefinitionクラスになります。
protected override IEnumerable<ToolDefinition> IntializeTools() => [new FileSearchToolDefinition()];
5.3.3 ツールリソースの初期化
このエージェントが利用するtoolの情報源としてVectoreStoreを登録します。これはToolResourcesに追加します。今回はファイル検索なのでFileSearchToolResourceを利用し、情報として最初に作成したVectoreStoreのIDを渡します。
protected override ToolResources InitializeToolResources()
{
return new ToolResources
{
FileSearch = new FileSearchToolResource([vectorStore.Id], null)
};
}
以上で、エージェントに任意のファイルの情報をエージェントの知識として活用できます。
5.4 Grounding with Bing Search
エージェントにBing検索ツールを導入します。Bing検索を追加する場合にはAzure AI Foundry以外にGrounding with Bing Searchリソースを追加する必要があります。登録後にAzure AI Foundryプロジェクトに追加しておく必要があります。
5.4.1 Grounding with Bing Searchの追加
Azureポータルにログインしリソースの追加を選択し検索ボックスにGrounding with Bing Searchを入力します。
プロジェクトの詳細を設定します。公式ドキュメント(以前見た記憶はあるのですが。。。)に記載を見つけられないのですが無料枠のあるサブスクリプション配下にはGrounding with Bing Searchをデプロイすることは出来ません。いわゆる従量課金設定されているサブスクリプションを選択する必要があります。
もし無料枠のあるサブスクリプションでデプロイすると以下のようなエラーが発生します。
The subscription is not eligible for the selected SKU G1
作成後Azure AI Foundryポータルを開き、メニューからManagement centerを選択します。Management centerが開いたらProject配下のConnected resourcesを選択します。
Connected resourcesの左上にnew connectionをクリックすると他サービスのリソースを追加することが可能です。
Grounding With Bing Searchを選択すると自動的に先ほどデプロイしたリソースが表示されているはずです。Add connectionでプロジェクトに追加します。
connection Idのフォーマット
/subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.CognitiveServices/accounts/[ai_service_name]/projects/[project_name]/connections/[connection_name]
5.4.2 Bing検索ツールの定義
Bing検索ツールをエージェントに登録するためにはBingGroundingToolDefinitionをツールに追加します。この際先ほど追加したGrounding With Bing SearchのConnection Idを設定します。
今回のサンプルではAzure AI FoundryプロジェクトのConnected resourcesの一覧を取得し、Grounding With Bing Searchの情報からIdを取得しています。
Grounding With Bing SearchはConnected resourcesのタイプbing_groundingです。
protected override IEnumerable<ToolDefinition> IntializeTools()
{
var connectionsClient = ProjectClient.GetConnectionsClient();
var connections = connectionsClient.GetConnections();
string connectionId = "";
foreach (var connection in connections)
{
if (connection.Metadata.TryGetValue("type", out var type) && type == "bing_grounding")
{
connectionId = connection.Id;
}
}
if (string.IsNullOrEmpty(connectionId))
{
throw new NoNullAllowedException("Bing Grounding connection not found");
}
return [
new FileSearchToolDefinition(),
new BingGroundingToolDefinition(
new BingGroundingSearchToolParameters(
new[]
{
new BingGroundingSearchConfiguration(connectionId)
}
)
)
];
}