Azure Bot Framework SDK から Microsoft 365 エージェント SDK への Node.js 移行ガイダンス

この記事では、Bot Framework SDK for Node.jsから移行するために必要な変更について説明します。

前提条件

  • Node.js バージョン 20 以降
  • 既存の Bot Framework SDK プロジェクト
  • Azure Bot Service リソース (移行中は変更されません)

NodeJS SDK コードの変更

このセクションの変更点は、Azure Bot Framework SDKMicrosoft 365 エージェント SDK JavaScript の違いによるものです。

Azureのリソース

Azure リソースは変更されません。 MicrosoftAppTypeMicrosoftAppIdMicrosoftAppPasswordMicrosoftAppTenantIdの appsettings プロパティを参照する必要があります。 ただし、これらの設定名は使用されなくなり、後で削除できます。 環境の構成の詳細

最初のステップ

最初に次の変更を適用して、ほとんどの違いに対処します。 これらの変更を適用した後も、デバッグして他の違いを確認する必要があります。

パッケージの依存関係を更新する

この変更では、必要なすべての名前空間が解決されるわけではありませんが、その大部分が対象となります。

Scenario Bot Framework SDK(ボット フレームワーク ソフトウェア開発キット) エージェント SDK
コア ホスティング botbuilder @microsoft/agents-hosting
アクティビティ スキーマ botframework-schema @microsoft/agents-activity
Dialogs botbuilder-dialogs @microsoft/agents-hosting-dialogs
Azure Cosmos DB botbuilder-azure @microsoft/agents-hosting-storage-cosmos
Azure Blob Storage botbuilder-azure-blobs @microsoft/agents-hosting-storage-blob
高速サーバー ユーティリティ 手動セットアップ @microsoft/agents-hosting-express

Teams を使用するボット

ボットで Teams を使用している場合は、次のパッケージ依存関係を追加します。 @microsoft/agents-hosting-extensions-teams

インポートの更新/必須

検索と置換を使用して、次の変更を行います。

Bot Framework エージェント SDK
require('botframework-schema'); require('@microsoft/agents-activity')
require('botbuilder'); require('@microsoft/agents-hosting')
require('botbuilder-dialogs'); require('@microsoft/agents-hosting-dialogs')

Agents SDK アクティビティ クラス

@microsoft/agents-activity パッケージには、zod に基づいて解析を実行する Activity クラスが含まれています。 カスタム アクティビティは、 Activity.fromJson() を使用した JSON から、または Activity.fromObject() を使用したリテラル JavaScript オブジェクトから解析および検証できます。

さらに、 Activity クラスは、アクティビティ ペイロードに関連するすべての操作 ( getConversationReferenceなど) を一元化します。 次の表のメソッドは、 TurnContext から移動され、現在のアクティビティ インスタンスで動作するようになりました。

Bot Framework 静的メソッド Agents SDK インスタンス メソッド
TurnContext.applyConversationReference activity.applyConversationReference
TurnContext.getConversationReference activity.getConversationReference
TurnContext.getReplyConversationReference activity.getReplyConversationReference
TurnContext.removeRecipientMention activity.removeRecipientMention
TurnContext.getMentions activity.getMentions
TurnContext.removeMentionText activity.removeMentionText

スタートアップと構成

Agents SDK 構成システムは、 ConfigurationBotFrameworkAuthentication クラスを AuthConfiguration インターフェイスに置き換えます。

既定の .env ファイルから構成を読み込むには、 loadAuthConfigFromEnvを使用します。

Important

構成変数については、「 JavaScript での認証の構成」を参照してください。

環境の構成

次の変数を含む .env ファイルを作成します。

# Required for Azure Bot Service
clientId=your-app-id
clientSecret=your-app-secret  
tenantId=your-tenant-id

# Optional - for local debugging
PORT=3978
DEBUG=true

移行に関する注: 次の表に示すように、環境変数の名前を更新します。

Bot Framework SDK(ボット フレームワーク ソフトウェア開発キット) エージェント SDK
MicrosoftAppId clientId
MicrosoftAppPassword clientSecret
MicrosoftAppTenantId tenantId

認証とセキュリティ

Bot Framework SDK が受信要求を承認すると、JSON Web トークン (JWT) 承認トークンがスタックに含まれます。 エージェント SDK は動作しません。 Express などの Web サーバー ランタイムを使用する場合は、JWT ベアラー トークンに基づいて受信要求を承認するように JWT ミドルウェアを構成する必要があります。Agents SDK には、 authorizeJWT(AuthConfiguration) メソッドが用意されています。

JWT ミドルウェア (運用環境に必要):

import { authorizeJWT, loadAuthConfigFromEnv } from '@microsoft/agents-hosting'

const authConfig = loadAuthConfigFromEnv()
server.use(authorizeJWT(authConfig))

ローカル開発:

ローカル デバッグでは、JWT 検証を無効にすることができます。

// Only for local development - NEVER in production
if (process.env.NODE_ENV === 'development') {
    // JWT validation disabled for local testing
} else {
    server.use(authorizeJWT(authConfig))
}

サーバーセットアップオプション

Agents SDK には、サーバーを設定するための 2 つの方法が用意されています。

startServer メソッドを使用する

最小限のセットアップが必要で、カスタム ミドルウェアが必要ない場合は、この簡略化されたアプローチを新しいプロジェクトに使用します。

botbuilderから初期化コードを更新します。

const { EchoBot } = require('./bot');
const {
    CloudAdapter,
    ConfigurationBotFrameworkAuthentication
} = require('botbuilder');
const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env);
const adapter = new CloudAdapter(botFrameworkAuthentication);
const myBot = new EchoBot();
const server = express();
server.use(express.json());
server.post('/api/messages', async (req, res) => 
    await adapter.process(req, res, (context) => 
      myBot.run(context));
);

startServer() でのエージェント ホスティング:

const { EchoBot } = require('./bot');
const { startServer } = require('@microsoft/agents-hosting-express')
startServer(new EchoBot());

手動による高速セットアップ

既存のボットを移行する場合、カスタム ミドルウェアが必要な場合、Express 構成を完全に制御する場合は、この方法を使用します

const { EchoBot } = require('./bot');
const {
    CloudAdapter,
    loadAuthConfigFromEnv, // Update
    authorizeJWT // Update
} = require('@microsoft/agents-hosting'); // Update
const authConfig = loadAuthConfigFromEnv(); // Update
const adapter = new CloudAdapter(authConfig); // Update
const myBot = new EchoBot();
const server = express();
server.use(express.json());
server.use(authorizeJWT(authConfig)); // Update
server.post('/api/messages', async (req, res) => 
    await adapter.process(req, res, (context) => 
      myBot.run(context));
);

const port = process.env.PORT || 3978;
server.listen(port, () => {
    console.log(`Server listening on port ${port}`);
}).on('error', (err) => {
    console.error('Server failed to start:', err);
    process.exit(1);
});

ActivityHandler のサポート

Bot Framework SDK で作成されたほとんどのボットは、 botbuilder-core.ActivityHandler 基本クラスに基づいています。

Agents SDK には、移行を容易にするために同じ API サーフェスを維持する互換性のある agents-hosting.ActivityHandler が用意されています。

主な違い

Bot Framework SDK と Agents SDK の主な違いは次のとおりです。

ハンドラー パラメーター:

SDK ハンドラーの種類
Bot Framework SDK(ボット フレームワーク ソフトウェア開発キット) BotHandler
エージェント SDK AgentHandler

Agents SDK のその他のメソッド:

Method Description
onMessageDelete メッセージ削除アクティビティを処理する
onMessageUpdate メッセージ更新アクティビティを処理する
onSignInInvoke サインイン呼び出しアクティビティを処理します

Agents SDK にメソッドがありません:

Method 理由
onCommand コマンド アクティビティはサポートされていません
onCommandResult コマンド結果アクティビティはサポートされていません
onEvent 汎用イベント処理 ( onTokenResponseEvent などの特定のイベントの種類は引き続きサポートされています)
onTokenResponseEvent OAuth トークン応答イベント

メソッド シグネチャの変更:

すべてのハンドラー メソッドは、メソッド チェーンのActivityHandlerではなく、thisを返します。 ハンドラー関数は、同じシグネチャを持つ AgentHandler 型を使用します BotHandler

移行の例:

ボットフレームワークSDK:

const { ActivityHandler } = require('botbuilder');

class MyBot extends ActivityHandler {
    constructor() {
        super();
        this.onMessage(async (context, next) => {
            await context.sendActivity('Hello!');
            await next();
        });
    }
}

移行はほとんど簡単で、主な変更は import ステートメントとハンドラーの種類です。 既存の ActivityHandlerベースのボットのほとんどは、最小限の変更で動作する必要があります。

Important

新しいActivityHandler クラスを優先して、AgentApplicationは非推奨になりました

ActivityHandler から AgentApplication への移行

下位互換性のために ActivityHandler がサポートされていますが、 AgentApplicationを使用することをお勧めします。

ActivityHandlerの使用

import { ActivityHandler } from '@microsoft/agents-hosting'

class MyBot extends ActivityHandler {
    constructor() {
        super()
        this.onMessage(async (context, next) => {
            await context.sendActivity('Hello!')
            await next()
        })
        
        this.onMembersAdded(async (context, next) => {
            await context.sendActivity('Welcome!')
            await next()
        })
    }
}

主な違い

次の表では、 ActivityHandlerAgentApplicationの主な機能の違いについて説明します。

特徴 ActivityHandler AgentApplication
状態管理 手動による状態管理が必要 提供される組み込みの状態管理
イベント処理 汎用イベント ハンドラー ( onMembersAddedなど) より具体的なイベント ハンドラー ( membersAddedなど)
次の関数 ハンドラーには呼び出しが必要です next() ハンドラーは呼び出しを必要としません next()
Storage 手動ストレージ構成 自動状態永続化を使用した組み込みストレージのサポート

一般的な移行パターン

一般的な移行パターンは次のとおりです。

シンプルエコーボット

Bot Framework

const { ActivityHandler } = require('botbuilder')

class EchoBot extends ActivityHandler {
    constructor() {
        super()
        this.onMessage(async (context, next) => {
            await context.sendActivity(`You said: ${context.activity.text}`)
            await next()
        })
    }
}

状態管理

AgentApplicationの使用

import { AgentApplication, MemoryStorage } from '@microsoft/agents-hosting'

const agent = new AgentApplication({
    storage: new MemoryStorage()
})

agent.onMessage('/count', async (context, state) => {
    const count = state.conversation.count ?? 0
    state.conversation.count = count + 1
    await context.sendActivity(`Count: ${state.conversation.count}`)
})

AgentApplication からの継承

より複雑なシナリオでは、 AgentApplicationから継承するクラスを作成できます。

import { AgentApplication, MemoryStorage, MessageFactory } from '@microsoft/agents-hosting'

class MyAgent extends AgentApplication {
    constructor() {
        super({
            storage: new MemoryStorage()
        })
        this.setupRoutes()
    }
    
    setupRoutes() {
        this.onMessage('/help', this.handleHelp)
        this.onMessage('/status', this.handleStatus)
        this.onMessage('/reset', this.handleReset)
        
        this.onActivity('message', this.handleDefault)
        this.onConversationUpdate('membersAdded', this.handleWelcome)
    }
    
    handleHelp = async (context, state) => {
        const helpText = `
                Available commands:
                - /help - Show this help message
                - /status - Show current status
                - /reset - Reset conversation state
        `
        await context.sendActivity(MessageFactory.text(helpText))
    }
    
    handleStatus = async (context, state) => {
        const messageCount = state.conversation.messageCount ?? 0
        await context.sendActivity(`Messages processed: ${messageCount}`)
    }
    
    handleReset = async (context, state) => {
        state.deleteConversationState()
        await context.sendActivity('Conversation state has been reset.')
    }
    
    handleWelcome = async (context, state) => {
        const welcomeText = 'Welcome! Type /help to see available commands.'
        await context.sendActivity(MessageFactory.text(welcomeText))
    }
    
    handleDefault = async (context, state) => {
        // Increment message counter
        const messageCount = (state.conversation.messageCount ?? 0) + 1
        state.conversation.messageCount = messageCount
        
        const replyText = `Echo: ${context.activity.text} (Message #${messageCount})`
        await context.sendActivity(MessageFactory.text(replyText))
    }
}

export default new MyAgent()

このパターンの利点

Benefit Description
組織の改善 別々のハンドラー用のメソッド
再 利用 さらに簡単に拡張または継承できます
テスト可能性 個々のメソッドを単体テストできます
保守容易性 複雑なボットのよりクリーンなコード構造
矢印関数 this コンテキストを .bind() なしで自動的にバインドします