この記事では、Microsoft Bot Frameworkを使用してビルドするカスタム エンジン エージェントに非同期およびプロアクティブ メッセージング パターンを実装する方法について説明します。 これらのパターンを使用すると、エージェントは遅延後、またはユーザーが開始したメッセージなしでユーザーに応答できます。
非同期メッセージングとプロアクティブ メッセージングを使用して、カスタム エンジン エージェントで次のことができます。
- バックグラウンド処理を続行しながら、遅延後に応答します。
- ユーザー入力なしでメッセージを開始します (システムによってトリガーされる更新プログラムなど)。
各ユーザー クエリは、15 秒以内に最初の応答を受け取る必要があります。 実行時間の長いタスクの場合、エージェントはフォローアップ メッセージを送信できます。 ストリーミング更新の間に 45 秒のタイムアウトが適用されます。
非同期メッセージ
非同期メッセージは、エージェントがユーザーによって開始されたバックグラウンド タスクを完了した後に送信されます。 このパターンは、注文の追跡や状態の更新などのシナリオに役立ちます。
たとえば、ユーザーがノート PC を注文した場合、エージェントは要求を確認し、注文が行われたときに後でフォローアップ メッセージをユーザーに送信できます。 次の例は、 Bot Framework を使用してノート PC の注文に関する非同期メッセージを送信する方法を示しています。
app.message(
CustomMessageTypes.orderLaptopSelected.toString(),
async (context: TurnContext, _state) => {
return new Promise(async (resolve) => {
await context.sendActivity({
text: "Thank you for order laptop. I will keep you posted with updates.",
});
setTimeout(async () => {
await context.sendActivity({
text: "Great! I have successfully placed your order #1292. I'll notify you when it's delivered.",
attachments: [
{
contentType: "application/vnd.microsoft.card.adaptive",
content: deliveredCard,
},
],
});
resolve();
}, 10 * 1000);
});
}
);
次の表は、非同期メッセージ プロセスをまとめたものです。
| タスク | 説明 |
|---|---|
| ✅ 初期確認 | 要求を確認するメッセージを送信します。 |
| ✅ バックグラウンド処理 | タスクを非同期的に実行します。 |
| ✅ フォローアップ メッセージ | タスクが完了したら、ユーザーに通知します。 |
プロアクティブ メッセージ
プロアクティブ メッセージは、ユーザーではなくシステムによって開始されます。 これらのメッセージは、専用の会話スレッドを介して送信されます。
たとえば、エージェントは、イベントに関する通知をユーザーに送信したり、ユーザー クエリなしで更新したりできます。 次の例は、 createConversation API を使用して会話情報をフェッチし、専用スレッド経由でプロアクティブ メッセージを送信する方法を示しています。
export async function getToken() {
const url =
"https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token";
const params = new URLSearchParams();
params.append("grant_type", "client_credentials");
params.append("client_id", config.MicrosoftAppId);
params.append("client_secret", config.MicrosoftAppPassword);
params.append("scope", "https://api.botframework.com/.default");
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
});
if (!response.ok) {
throw new Error(`Error! status: ${response.status}`);
}
const data = await response.json();
return data;
}
let accessToken;
try {
accessToken = getToken();
if (!accessToken) {
console.log("No access token found, fetching a new one");
const tokenResponse = await getToken();
accessToken = tokenResponse.access_token;
if (!accessToken) {
throw new Error("Failed to obtain access token");
}
setAccessToken(accessToken);
}
} catch (error) {
console.error("Error retrieving access token:", error);
await context.sendActivity(
"Failed to send proactive message due to authentication error"
);
return;
}
const createConversationBody = {
members: [{ id: context.activity.from.aadObjectId }],
tenantId: context.activity.conversation.tenantId,
channelData: {
productContext: "Copilot",
conversation: {
conversationSubType: "AgentProactive",
},
},
};
const createConversationResponse = await fetch(
"https://canary.botapi.skype.com/teams/v3/conversations",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(createConversationBody),
}
);
const createConversationResponseData =
await createConversationResponse.json();
console.log("Create conversation response", createConversationResponseData);
const body = {
text: "Hello proactive world",
type: "message",
};
const response = await fetch(
`https://canary.botapi.skype.com/teams/v3/conversations/${createConversationResponseData.id}/activities`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(body),
}
);
次の表は、プロアクティブ メッセージ プロセスをまとめたものです。
| タスク | 説明 |
|---|---|
| ✅ トークンの取得 | OAuth2 を使用して認証します。 |
| ✅ 会話を作成する | Bot Framework API を使用して会話を開始します。 |
| ✅ メッセージの送信 | メッセージを会話に投稿します。 |