次の方法で共有


Foundry Agent Service で App Service アプリをツールとして追加する (Node.js)

このチュートリアルでは、OpenAPI を使用して Express.js アプリの機能を公開し、Foundry Agent Service にツールとして追加し、エージェントプレイグラウンドで自然言語を使用してアプリと対話する方法について説明します。

Web アプリケーションにショッピング、ホテル予約、データ管理などの便利な機能が既にある場合は、Foundry Agent Service の AI エージェントでそれらの機能を簡単に使用できます。 OpenAPI スキーマをアプリに追加するだけで、エージェントがユーザーのプロンプトに応答したときにアプリの機能を理解して使用できるようになります。 つまり、アプリでできることは何でも、AI エージェントでも実行でき、アプリの OpenAPI エンドポイントを作成する以外の労力は最小限です。 このチュートリアルでは、簡単な to-do リスト アプリから始めます。 最終的には、会話型 AI を使用して、エージェントでタスクを作成、更新、管理できるようになります。

OpenAPI ツールを使用してアクションを実行する会話の途中にあるエージェントのプレイグラウンドを示すスクリーンショット。

  • Web アプリに OpenAPI 機能を追加します。
  • OpenAPI スキーマが Foundry Agent Service と互換性があることを確認します。
  • Foundry Agent Service でアプリを OpenAPI ツールとして登録します。
  • エージェントのプレイグラウンドでエージェントをテストします。

Prerequisites

このチュートリアルでは、「 チュートリアル: Node.js + MongoDB Web アプリを Azure にデプロイする」で使用するサンプルを使用していることを前提としています。

少なくとも、GitHub Codespaces で サンプル アプリケーション を開き、 azd upを実行してアプリをデプロイします。

GitHub codespaces で開く で開く

Web アプリに OpenAPI 機能を追加する

  1. codespace ターミナルで、NuGet swagger-jsdoc NPM パッケージをプロジェクトに追加します。

    npm install swagger-jsdoc
    
  2. routes/index.js を開きます。 ファイルの下部に、上記 module.exports = router; 次の API 関数を追加します。 Foundry Agent Service と互換性を持たさせるには、operationId注釈で @swagger プロパティを指定する必要があります (OpenAPI 指定ツールで Foundry Agent Service を使用する方法: 前提条件を参照)。

    router.get('/schema', function(req, res, next) {
      try {
        const swaggerJsdoc = require('swagger-jsdoc');
    
        res.json(
          swaggerJsdoc(
            {
              definition: {
                openapi: '3.0.0',
                servers: [
                  {
                    url: `${req.protocol}://${req.get('host')}`,
                    description: 'Task API',
                  },
                ],
              },
              apis: ['./routes/*.js'],
            }
          )
        );
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks:
     *   get:
     *     summary: Get all tasks
     *     operationId: getAllTasks
     *     responses:
     *       200:
     *         description: List of tasks
     */
    router.get('/api/tasks', async function(req, res, next) {
      try {
        const tasks = await Task.find();
        res.json(tasks);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   get:
     *     summary: Get task by ID
     *     operationId: getTaskById
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     responses:
     *       200:
     *         description: Task details
     */
    router.get('/api/tasks/:id', async function(req, res, next) {
      try {
        const task = await Task.findById(req.params.id);
        res.json(task);
      } catch (error) {
        res.status(404).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks:
     *   post:
     *     summary: Create a new task
     *     operationId: createTask
     *     requestBody:
     *       required: true
     *       content:
     *         application/json:
     *           schema:
     *             type: object
     *             properties:
     *               taskName:
     *                 type: string
     *     responses:
     *       201:
     *         description: Task created
     */
    router.post('/api/tasks', async function(req, res, next) {
      try {
        // Set createDate to current timestamp when creating a task
        const taskData = {
          ...req.body,
          createDate: new Date()
        };
    
        const task = new Task(taskData);
        await task.save();
        res.status(201).json(task);
      } catch (error) {
        res.status(400).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   put:
     *     summary: Update a task
     *     operationId: updateTask
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     requestBody:
     *       required: true
     *       content:
     *         application/json:
     *           schema:
     *             type: object
     *             properties:
     *               taskName:
     *                 type: string
     *               completed:
     *                 type: boolean
     *     responses:
     *       200:
     *         description: Task updated
     */
    router.put('/api/tasks/:id', async function(req, res, next) {
      try {
        // If completed is being set to true, also set completedDate
        if (req.body.completed === true) {
          req.body.completedDate = new Date();
        }
    
        const task = await Task.findByIdAndUpdate(req.params.id, req.body, { new: true });
        res.json(task);
      } catch (error) {
        res.status(400).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   delete:
     *     summary: Delete a task
     *     operationId: deleteTask
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     responses:
     *       200:
     *         description: Task deleted
     */
    router.delete('/api/tasks/:id', async function(req, res, next) {
      try {
        const task = await Task.findByIdAndDelete(req.params.id);
        res.json({ message: 'Task deleted successfully', task });
      } catch (error) {
        res.status(404).json({ error: error.message });
      }
    });
    

    このコードは既存のルートの機能を複製していますが、これは不要ですが、わかりやすくするために保持します。 ベスト プラクティスは、アプリ ロジックを共有関数に移動してから、MVC ルートと OpenAPI ルートの両方から呼び出すことです。

  3. codespace ターミナルで、 npm startを使用してアプリケーションを実行します。

  4. [ブラウザーで開く] を選択します。

  5. URL に /schema を追加して、OpenAPI スキーマを表示します。

  6. codespace ターミナルに戻り、変更をコミットして変更をデプロイするか (GitHub Actions メソッド)、または azd up (Azure Developer CLI メソッド) を実行します。

  7. 変更がデプロイされたら、 https://<your-app's-url>/schema に移動し、後で使用できるようにスキーマをコピーします。

Microsoft Foundry でエージェントを作成する

これらの手順では、新しい Foundry ポータルを使用します。

  1. Foundry ポータルの右上のメニューで、[New Foundry] を選択します。

  2. 新しい Foundry ポータルでこれが初めての場合は、プロジェクト名を選択し、[ 新しいプロジェクトの作成] を選択します。

  3. プロジェクトに名前を付け、[ 作成] を選択します。

  4. [ ビルドの開始]、[ エージェントの作成] の順に選択します。

  5. エージェントに名前を付け、[ 作成] を選択します。 エージェントの準備ができたら、エージェントのプレイグラウンドが表示されます。

    使用できるモデルと使用可能なリージョンに注意してください。

  6. エージェントのプレイグラウンドで、[ ツール ] を展開し、[ 追加>Custom>OpenAPI ツール>Create を選択します。

  7. ツールに名前と説明を付けます。 OpenAPI 3.0 以降のスキーマ ボックスに、先ほどコピーしたスキーマを貼り付けます。

  8. [ 作成ツール] を選択します。

  9. 保存 を選択します。

ヒント

このチュートリアルでは、認証なしでアプリを匿名で呼び出すように OpenAPI ツールを構成します。 運用シナリオでは、マネージド ID 認証を使用してツールをセキュリティで保護する必要があります。 詳細な手順については、「 Foundry Agent Service の OpenAPI エンドポイントをセキュリティで保護する」を参照してください。

エージェントをテストする

  1. [手順] で、"todosApp ツールを使用してタスクを管理してください" などの簡単な手順を説明します。

  2. 次のプロンプトの提案を使用してエージェントとチャットします。

    • すべてのタスクを表示します。
    • 「3つのレタスジョークを思い付く」というタスクを作成します。
    • それを「3つのノックノックジョークを考えてください」に変えてください。

    OpenAPI ツールを使用してアクションを実行する会話の途中にあるエージェントのプレイグラウンドを示すスクリーンショット。

セキュリティのベスト プラクティス

Azure App Service で OpenAPI 経由で API を公開する場合は、次のセキュリティのベスト プラクティスに従います。

  • 認証と承認: Microsoft Entra 認証を使用して OpenAPI エンドポイントを保護します。 詳細な手順については、「 Foundry Agent Service の OpenAPI エンドポイントをセキュリティで保護する」を参照してください。 また、 Microsoft Entra ID を使用して Azure API Management の背後にあるエンドポイントを保護し、承認されたユーザーまたはエージェントのみがツールにアクセスできるようにすることもできます。
  • 入力データを検証します。 無効または悪意のある入力を防ぐために、常に受信データを検証します。 Node.js アプリの場合は、 高速検証 などのライブラリを使用して、データ検証規則を適用します。 ベスト プラクティスと実装の詳細については、ドキュメントを参照してください。
  • HTTPS を使用する: このサンプルは、既定で HTTPS を適用し、転送中のデータを暗号化するための無料の TLS/SSL 証明書を提供する Azure App Service に依存しています。
  • CORS の制限: クロスオリジン リソース共有 (CORS) を信頼されたドメインのみに制限します。 詳細については、「 CORS を有効にする」を参照してください。
  • レート制限の適用:API Management またはカスタム ミドルウェアを使用して、不正使用やサービス拒否攻撃を防ぎます。
  • 機密性の高いエンドポイントを非表示にする: OpenAPI スキーマで内部 API または管理者 API を公開しないようにします。
  • OpenAPI スキーマを確認します。 OpenAPI スキーマが機密情報 (内部 URL、シークレット、実装の詳細など) を漏らさないようにします。
  • 依存関係を更新したままにする: NuGet パッケージを定期的に更新し、セキュリティ アドバイザリを監視します。
  • アクティビティの監視とログ記録: ログ記録を有効にし、アクセスを監視して疑わしいアクティビティを検出します。
  • マネージド ID を使用する: 他の Azure サービスを呼び出す場合は、ハードコーディングされた資格情報の代わりにマネージド ID を使用します。

詳細については、 App Service アプリのセキュリティ保護REST API セキュリティのベスト プラクティスに関する説明を参照してください。

次のステップ

これで、App Service アプリを Foundry Agent Service のツールとして使用し、エージェントのプレイグラウンドで自然言語を使用してアプリの API と対話できるようになりました。 ここから、Foundry ポータルで引き続きエージェントに機能を追加したり、Microsoft Foundry SDK または REST API を使用して独自のアプリケーションに統合したり、大規模なソリューションの一部としてデプロイしたりできます。 Microsoft Foundry で作成されたエージェントは、クラウドで実行したり、チャットボットに統合したり、Web アプリやモバイル アプリに埋め込んだりすることができます。

次の手順を実行し、Azure App Service 内で直接エージェントを実行する方法については、次のチュートリアルを参照してください。

その他のリソース