Copilot Studioでエージェントと外部API呼び出しを組み合わせてみる ― カスタムコードでツール拡張
X-tech推進本部 川端前回記事のおさらいと今回のテーマ
以前執筆した「Copilot Studioでエージェントと外部API呼び出しを組み合わせてみる」では、Copilot Studioから外部サービスを呼び出す方法として、REST APIツールを作成し、エージェントに組み込むまでの流れを紹介しました。
本記事では、前回紹介しなかったカスタムコードを追加することで、ツールをどのように拡張できるのかにフォーカスします。
カスタムコードとは
ツール(カスタムコネクタ)の以下のセクションで設定するコードのことで、C#で記載することができます。カスタムコードを使うことで、以下のようなことができます。
- APIリクエスト / レスポンスの加工
- 外部APIを呼ばず、コードだけで処理を完結させる

具体的な利用方法について、サンプルコードをベースに見ていきます。
パターン① レスポンスの加工(正規化:必要部分の抽出)
目的:APIから返却された結果を加工し、Copilotが扱いやすい形に整形する
public override async Task<HttpResponseMessage> ExecuteAsync()
{
// OperationId を判定
if (this.Context.OperationId == "sample_backlogapi_wikipages")
{
// カスタム処理に転送します
return await this.HandleForwardAndTransformOperation().ConfigureAwait(false);
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = CreateJsonContent($"不明な Operation ID '{this.Context.OperationId}'");
return response;
}
private async Task<HttpResponseMessage> HandleForwardAndTransformOperation()
{
// リクエスト結果を取得します
HttpResponseMessage response =
await this.Context.SendAsync(this.Context.Request, this.CancellationToken)
.ConfigureAwait(false);
// リクエスト成功の場合
if (response.IsSuccessStatusCode)
{
// リクエストBodyをJSONオブジェクトに変換
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JObject.Parse(responseString);
// JSONオブジェクトの content プロパティを書き換えした結果を、文字列としてレスポンスに格納します
result["content"] = Regex.Replace(result["content"].ToString(), @"ここから(.*)ここまで", "$1", RegexOptions.Singleline);
response.Content = CreateJsonContent(result.ToString());
}
return response;
}
パターン② "コードそのものを実行する" タイプ
Microsoft公式GitHubでも紹介されているカスタムパターンです。
特定APIに依存せず、HTMLが入力されたらタグを削除して返却する処理のサンプルになっています。
このコネクタは、HTMLテキストを入力として受け取り、タグを除去した結果をレスポンスとして返します。その際に外部APIへのアクセスは発生していません。
このように、カスタムコードを使うことで「APIコネクタ」というよりも「入力と出力を持つ関数」としてツールを設計することもできるようになります。
例えば、以下のような感じです。
例)入力として正規表現と、テキストを与えて、正規表現をテキストに適用した結果を出力
private async Task<HttpResponseMessage> HandleHtmlToTextOperation()
{
HttpResponseMessage response;
// リクエストボディとして以下のJSONフォーマットが入力される:
// {
// "start": "<some start text>"
// "end": "<some end text>"
// "text": "<some full text>"
// }
var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);
// レスポンスBodyをJSONオブジェクトに変換
var contentAsJson = JObject.Parse(contentAsString);
var start = (string)contentAsJson["start"];
var end = (string)contentAsJson["end"];
var text = (string)contentAsJson["text"];
var pattern = $"{Regex.Escape(start)}(.*)?{Regex.Escape(end)}";
// 正規表現でテキストから抽出する
string outText;
outText = Regex.Replace(text, pattern, "$1", RegexOptions.Singleline);
response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(outText);
return response;
}
※ 実運用では、ReDoS(正規表現による高負荷)を避けるために、正規表現パターンがユーザー入力にならないよう注意が必要です。
制約事項
カスタムコードの制約は公式サイトの解説にも記載の通り、実行時間は2分以内、スクリプトサイズは1MB以下にする必要がある点で注意が必要です。
この制約から、カスタムコードは以下の用途に向いています。
- 軽量な変換・正規化処理
- Copilot向けのレスポンス整形
- 小規模なユーティリティ関数
一方で、大量データ処理や長時間実行が必要な処理には向いていません。
まとめ
Copilot Studioのカスタムコードを使うことで、外部APIの呼び出しだけでなく、
- Copilotが理解しやすい形にデータを整形する
- APIに依存しない「関数的」なツールを作る
といった柔軟な設計ができます。
「APIはあるが、そのままCopilotに渡すと使いづらい」、そんな場面では、ぜひカスタムコードの活用を検討してみてください。