Поделиться через


Создание бота RAG в Teams

Расширенные чат-боты Q&A — это мощные приложения, созданные с помощью больших языковых моделей (LLM). Чат-боты отвечают на вопросы, извлекая информацию из определенных источников с помощью метода с именем Получение дополненного поколения (RAG). Архитектура RAG состоит из двух main потоков:

  • Прием данных. Конвейер для приема данных из источника и их индексирования. Обычно это происходит в автономном режиме.

  • Получение и создание. Цепочка RAG, которая принимает пользовательский запрос во время выполнения и извлекает соответствующие данные из индекса, а затем передает их модели.

Microsoft Teams позволяет создать бота для общения с RAG, чтобы создать улучшенный интерфейс для повышения производительности. Набор средств Teams предоставляет серию готовых к использованию шаблонов приложений в категории Чат с данными , которая сочетает в себе функции поиска Azure AI, Microsoft 365 SharePoint и пользовательского API в качестве разных источников данных и LLM для создания взаимодействия с поиском в Teams.

Предварительные условия

Установка Для использования...
Visual Studio Code Среды сборки JavaScript, TypeScript или Python. Используйте последнюю версию.
Набор средств Teams Расширение Microsoft Visual Studio Code, которое создает шаблон проекта для вашего приложения. Используйте последнюю версию.
Node.js Серверной среды выполнения JavaScript. Дополнительные сведения см . вNode.js таблице совместимости версий для типа проекта.
Microsoft Teams Microsoft Teams позволяет сотрудничать со всеми пользователями, с которыми вы работаете, с помощью приложений для чата, собраний и звонков в одном месте.
Azure OpenAI Сначала создайте ключ API OpenAI, чтобы использовать генеративный предварительно обученный преобразователь OpenAI (GPT). Если вы хотите разместить приложение или получить доступ к ресурсам в Azure, необходимо создать службу Azure OpenAI.

Создание нового базового проекта чат-бота ИИ

  1. Откройте Visual Studio Code.

  2. Щелкните значок Набора средств Teams на панели действий Visual Studio Code.

  3. Выберите Создать новое приложение.

    Снимок экрана: расположение ссылки

  4. Выберите Агент пользовательского обработчика.

    Снимок экрана: параметр выбора пользовательского агента обработчика в качестве нового проекта для создания.

  5. Выберите Чат с данными.

    Снимок экрана: выбор функций приложения с помощью списка библиотек ИИ.

  6. Выберите Настроить.

    Снимок экрана: параметр выбора настройки данных для загрузки.

  7. Выберите JavaScript.

    Снимок экрана: выбор языка программирования.

  8. Выберите Azure OpenAI или OpenAI.

    Снимок экрана: параметр выбора LLM.

  9. Введите учетные данные Azure OpenAI или OpenAI в зависимости от выбранной службы. Выберите ВВОД.

    Снимок экрана: расположение для ввода открытого ключа API Azure.

  10. Выберите Папка по умолчанию.

    Снимок экрана: папка приложения расположения для сохранения.

    Чтобы изменить расположение по умолчанию, выполните следующие действия.

    1. Нажмите кнопку Обзор.
    2. Выберите расположение рабочей области проекта.
    3. Выберите Выбрать папку.
  11. Введите имя приложения, а затем нажмите клавишу ВВОД .

    Снимок экрана: параметр для ввода подходящего имени.

    Вы успешно создали рабочую область проекта "Чат с данными ".

    Снимок экрана: созданный чат-бот ИИ и файл сведений доступен.

  12. В разделе EXPLORER перейдите к файлу env.env.testtool.user>.

  13. Обновите следующие значения:

    • SECRET_AZURE_OPENAI_API_KEY=<your-key>
    • AZURE_OPENAI_ENDPOINT=<your-endpoint>
    • AZURE_OPENAI_DEPLOYMENT_NAME=<your-deployment>

    Снимок экрана: сведения, обновленные в файле env.

  14. Чтобы выполнить отладку приложения, нажмите клавишу F5 или в левой области выберите Запуск и отладка (CTRL+SHIFT+D), а затем в раскрывающемся списке выберите Отладка в средстве тестирования (предварительная версия).

    Снимок экрана: выбор параметра отладки из списка параметров.

Средство тестирования открывает бот на веб-странице.

Экраны: чат с окончательными выходными данными.

Обзор исходного кода приложения бота

Folder Содержание
.vscode Visual Studio Code файлы для отладки.
appPackage Шаблоны для манифеста приложения Teams.
env Файлы среды.
infra Шаблоны для подготовки ресурсов Azure.
src Исходный код приложения.
src/index.js Настройка сервера приложений бота.
src/adapter.js Настраивает адаптер бота.
src/config.js Определяет переменные среды.
src/prompts/chat/skprompt.txt Определяет запрос.
src/prompts/chat/config.json Настраивает запрос.
src/app/app.js Обрабатывает бизнес-логику для бота RAG.
src/app/myDataSource.js Определяет источник данных.
src/data/*.md Источники необработанных текстовых данных.
teamsapp.yml Это файл проекта набора средств Teams main. Файл проекта определяет свойства и определения этапов конфигурации.
teamsapp.local.yml Это переопределяет teamsapp.yml действия, которые обеспечивают локальное выполнение и отладку.
teamsapp.testtool.yml Это переопределяет teamsapp.yml действия, которые обеспечивают локальное выполнение и отладку в средстве тестирования приложений Teams.

Сценарии RAG для ИИ Teams

В контексте ИИ векторные базы данных широко используются в качестве хранилищ RAG, которые хранят данные внедрения и обеспечивают поиск по сходству векторов. Библиотека ИИ Teams предоставляет служебные программы, помогающие создавать внедрения для заданных входных данных.

Совет

Библиотека ИИ Teams не предоставляет реализацию векторной базы данных, поэтому необходимо добавить собственную логику для обработки созданных внедрений.

// create OpenAIEmbeddings instance
const model = new OpenAIEmbeddings({ ... endpoint, apikey, model, ... });

// create embeddings for the given inputs
const embeddings = await model.createEmbeddings(model, inputs);

// your own logic to process embeddings

На следующей схеме показано, как библиотека ИИ Teams предоставляет функциональные возможности для упрощения каждого этапа процесса получения и создания.

Снимок экрана: сценарий RAG.

  1. Обработка входных данных. Самый простой способ — передать входные данные пользователя в извлечение без каких-либо изменений. Однако если вы хотите настроить входные данные перед извлечением, можно добавить обработчик действий к определенным входящим действиям.

  2. Извлечение dataSource. Библиотека ИИ Teams предоставляет DataSource интерфейс, который позволяет добавлять собственную логику извлечения. Необходимо создать собственный DataSource экземпляр, и библиотека ИИ Teams вызывает его по запросу.

    class MyDataSource implements DataSource {
      /**
        * Name of the data source.
        */
      public readonly name = "my-datasource";
    
      /**
        * Renders the data source as a string of text.
        * @param context Turn context for the current turn of conversation with the user.
        * @param memory An interface for accessing state values.
        * @param tokenizer Tokenizer to use when rendering the data source.
        * @param maxTokens Maximum number of tokens allowed to be rendered.
        * @returns The text to inject into the prompt as a `RenderedPromptSection` object.
        */
      renderData(
        context: TurnContext,
        memory: Memory,
        tokenizer: Tokenizer,
        maxTokens: number
      ): Promise<RenderedPromptSection<string>> {
        ...
      }
    }
    
  3. Вызов ИИ с помощью командной строки. В системе командной строки ИИ Teams можно легко внедрить DataSource , настроив раздел конфигурации augmentation.data_sources . При этом запрос соединяется с оркестратором DataSource библиотеки и , чтобы вставить DataSource текст в окончательный запрос. Дополнительные сведения см. в разделе authorprompt. Например, в файле запроса:config.json

    {
        "schema": 1.1,
        ...
        "augmentation": {
            "data_sources": {
                "my-datasource": 1200
            }
        }
    }
    
  4. Ответ сборки. По умолчанию библиотека ИИ Teams отвечает на созданный ИИ ответ в виде текстового сообщения пользователю. Если вы хотите настроить ответ, можно переопределить действия SAY по умолчанию или явно вызвать модель ИИ для создания ответов, например с помощью адаптивных карточек.

Ниже приведен минимальный набор реализаций для добавления RAG в приложение. Как правило, он реализует DataSource внедрение в knowledge запрос, чтобы ИИ смог создать ответ на knowledgeоснове .

  • Создайте myDataSource.ts файл для реализации DataSource интерфейса:

    export class MyDataSource implements DataSource {
      public readonly name = "my-datasource";
      public async renderData(
        context: TurnContext,
        memory: Memory,
        tokenizer: Tokenizer,
        maxTokens: number
      ): Promise<RenderedPromptSection<string>> {
        const input = memory.getValue('temp.input') as string;
        let knowledge = "There's no knowledge found.";
    
        // hard-code knowledge
        if (input?.includes("shuttle bus")) {
          knowledge = "Company's shuttle bus may be 15 minutes late on rainy days.";
        } else if (input?.includes("cafe")) {
          knowledge = "The Cafe's available time is 9:00 to 17:00 on working days and 10:00 to 16:00 on weekends and holidays."
        }
    
        return {
          output: knowledge,
          length: knowledge.length,
          tooLong: false
        }
      }
    }
    
  • DataSource Зарегистрируйте в app.ts файле :

      // Register your data source to prompt manager
      planner.prompts.addDataSource(new MyDataSource());
    

  • prompts/qa/skprompt.txt Создайте файл и добавьте следующий текст:

    The following is a conversation with an AI assistant. The assistant is helpful, creative, clever, and very friendly to answer user's question.
    
    Base your answer off the text below:
    
  • prompts/qa/config.json Создайте файл и добавьте следующий код для подключения к источнику данных:

    {
        "schema": 1.1,
        "description": "Chat with QA Assistant",
        "type": "completion",
        "completion": {
            "model": "gpt-35-turbo",
            "completion_type": "chat",
            "include_history": true,
            "include_input": true,
            "max_input_tokens": 2800,
            "max_tokens": 1000,
            "temperature": 0.9,
            "top_p": 0.0,
            "presence_penalty": 0.6,
            "frequency_penalty": 0.0,
            "stop_sequences": []
        },
        "augmentation": {
            "data_sources": {
                "my-datasource": 1200
            }
        }
    }
    

Выбор источников данных

В сценариях Чат с данными или RAG набор средств Teams предоставляет следующие типы источников данных:

  • Настройка. Позволяет полностью контролировать прием данных, чтобы создать собственный векторный индекс и использовать его в качестве источника данных. Дополнительные сведения см. в статье Создание собственного приема данных.

    Вы также можете использовать расширение векторной базы данных Azure Cosmos DB или расширение вектора сервера Azure PostgreSQL в качестве векторных баз данных или API Bing для веб-поиска, чтобы получить последнее веб-содержимое для реализации любого экземпляра источника данных для подключения к собственному источнику данных.

  • Поиск Azure AI. Предоставляет пример для добавления документов в Службу поиска Azure AI, а затем используйте индекс поиска в качестве источника данных.

  • Пользовательский API. Позволяет чат-боту вызывать API, определенный в описании OpenAPI, для получения данных домена из службы API.

  • Microsoft Graph и SharePoint. Предоставляет пример использования содержимого Microsoft 365 из API поиска Microsoft Graph в качестве источника данных.

Создание собственного приема данных

Чтобы создать прием данных, выполните следующие действия.

  1. Загрузка исходных документов. Убедитесь, что в документе есть осмысленный текст, так как модель внедрения принимает в качестве входных данных только текст.

  2. Разделение на блоки. Убедитесь, что документ разделен, чтобы избежать сбоев вызовов API, так как модель внедрения имеет ограничение на входной маркер.

  3. Вызов модели внедрения. Вызов API модели внедрения для создания внедрения для заданных входных данных.

  4. Хранить внедрения. Храните созданные внедрения в векторной базе данных. Кроме того, включите полезные метаданные и необработанное содержимое для дальнейшего использования.

Пример кода

  • loader.ts: обычный текст в качестве исходных входных данных.

    import * as fs from "node:fs";
    
    export function loadTextFile(path: string): string {
      return fs.readFileSync(path, "utf-8");
    }
    
  • splitter.ts: разбиение текста на блоки с перекрытием.

    
    // split words by delimiters.
    const delimiters = [" ", "\t", "\r", "\n"];
    
    export function split(content: string, length: number, overlap: number): Array<string> {
      const results = new Array<string>();
      let cursor = 0, curChunk = 0;
      results.push("");
      while(cursor < content.length) {
        const curChar = content[cursor];
        if (delimiters.includes(curChar)) {
          // check chunk length
          while (curChunk < results.length && results[curChunk].length >= length) {
            curChunk ++;
          }
          for (let i = curChunk; i < results.length; i++) {
            results[i] += curChar;
          }
          if (results[results.length - 1].length >= length - overlap) {
            results.push("");
          }
        } else {
          // append
          for (let i = curChunk; i < results.length; i++) {
            results[i] += curChar;
          }
        }
        cursor ++;
      }
      while (curChunk < results.length - 1) {
        results.pop();
      }
      return results;
    }
    
    
  • embeddings.ts: используйте библиотеку OpenAIEmbeddings ИИ Teams для создания внедрений.

    import { OpenAIEmbeddings } from "@microsoft/teams-ai";
    
    const embeddingClient = new OpenAIEmbeddings({
      azureApiKey: "<your-aoai-key>",
      azureEndpoint: "<your-aoai-endpoint>",
      azureDeployment: "<your-embedding-deployment, e.g., text-embedding-ada-002>"
    });
    
    export async function createEmbeddings(content: string): Promise<number[]> {
      const response = await embeddingClient.createEmbeddings(content);
      return response.output[0];
    }
    
  • searchIndex.ts: создание индекса поиска Azure AI.

    import { SearchIndexClient, AzureKeyCredential, SearchIndex } from "@azure/search-documents";
    
    const endpoint = "<your-search-endpoint>";
    const apiKey = "<your-search-key>";
    const indexName = "<your-index-name>";
    
    const indexDef: SearchIndex = {
      name: indexName,
      fields: [
        {
          type: "Edm.String",
          name: "id",
          key: true,
        },
        {
          type: "Edm.String",
          name: "content",
          searchable: true,
        },
        {
          type: "Edm.String",
          name: "filepath",
          searchable: true,
          filterable: true,
        },
        {
          type: "Collection(Edm.Single)",
          name: "contentVector",
          searchable: true,
          vectorSearchDimensions: 1536,
          vectorSearchProfileName: "default"
        }
      ],
      vectorSearch: {
        algorithms: [{
          name: "default",
          kind: "hnsw"
        }],
        profiles: [{
          name: "default",
          algorithmConfigurationName: "default"
        }]
      },
      semanticSearch: {
        defaultConfigurationName: "default",
        configurations: [{
          name: "default",
          prioritizedFields: {
            contentFields: [{
              name: "content"
            }]
          }
        }]
      }
    };
    
    export async function createNewIndex(): Promise<void> {
      const client = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
      await client.createIndex(indexDef);
    }
    
  • searchIndexer.ts: отправка созданных внедрений и других полей в индекс поиска Azure AI.

    import { AzureKeyCredential, SearchClient } from "@azure/search-documents";
    
    export interface Doc {
      id: string,
      content: string,
      filepath: string,
      contentVector: number[]
    }
    
    const endpoint = "<your-search-endpoint>";
    const apiKey = "<your-search-key>";
    const indexName = "<your-index-name>";
    const searchClient: SearchClient<Doc> = new SearchClient<Doc>(endpoint, indexName, new AzureKeyCredential(apiKey));
    
    export async function indexDoc(doc: Doc): Promise<boolean> {
      const response = await searchClient.mergeOrUploadDocuments([doc]);
      return response.results.every((result) => result.succeeded);
    }
    
  • index.ts: оркестрация указанных выше компонентов.

    import { createEmbeddings } from "./embeddings";
    import { loadTextFile } from "./loader";
    import { createNewIndex } from "./searchIndex";
    import { indexDoc } from "./searchIndexer";
    import { split } from "./splitter";
    
    async function main() {
      // Only need to call once
      await createNewIndex();
    
      // local files as source input
      const files = [`${__dirname}/data/A.md`, `${__dirname}/data/A.md`];
      for (const file of files) {
        // load file
        const fullContent = loadTextFile(file);
    
        // split into chunks
        const contents = split(fullContent, 1000, 100);
        let partIndex = 0;
        for (const content of contents) {
          partIndex ++;
          // create embeddings
          const embeddings = await createEmbeddings(content);
    
          // upload to index
          await indexDoc({
            id: `${file.replace(/[^a-z0-9]/ig, "")}___${partIndex}`,
            content: content,
            filepath: file,
            contentVector: embeddings,
          });
        }
      }
    }
    
    main().then().finally();
    

Поиск ИИ Azure в качестве источника данных

В этом разделе вы узнаете, как:

Примечание.

Этот подход создает комплексный API чата, называемый моделью ИИ. Вы также можете использовать созданный ранее индекс в качестве источника данных, а также использовать библиотеку ИИ Teams для настройки получения и запроса.

Вы можете принимать документы знаний в Службу поиска Azure AI и создавать векторный индекс с помощью Azure OpenAI для своих данных. После приема индекс можно использовать в качестве источника данных.

  1. Подготовьте данные в Хранилище BLOB-объектов Azure.

    Снимок экрана: настройка помощник в Azure OpenAI Studio.

  2. В Azure OpenAI Studio выберите Добавить источник данных.

  3. Обновите обязательные поля.

    Снимок экрана: параметр для добавления источника данных.

  4. Нажмите кнопку Далее.

    Откроется страница Управление данными .

  5. Обновите обязательные поля.

  6. Нажмите кнопку Далее.

    Снимок экрана: выбор варианта добавления источника данных.

  7. Обновите обязательные поля. Нажмите кнопку Далее.

    Снимок экрана: параметр добавления управления данными.

  8. Выберите Сохранить и закрыть.

    Снимок экрана: параметр для проверки и завершения.

Использование источника данных индекса поиска ИИ Azure

После приема данных в поиск Azure AI вы можете реализовать собственные DataSource средства для получения данных из индекса поиска.

const { AzureKeyCredential, SearchClient } = require("@azure/search-documents");
const { DataSource, Memory, OpenAIEmbeddings, Tokenizer } = require("@microsoft/teams-ai");
const { TurnContext } = require("botbuilder");

// Define the interface for document
class Doc {
  constructor(id, content, filepath) {
    this.id = id;
    this.content = content; // searchable
    this.filepath = filepath;
  }
}

// Azure OpenAI configuration
const aoaiEndpoint = "<your-aoai-endpoint>";
const aoaiApiKey = "<your-aoai-key>";
const aoaiDeployment = "<your-embedding-deployment, e.g., text-embedding-ada-002>";

// Azure AI Search configuration
const searchEndpoint = "<your-search-endpoint>";
const searchApiKey = "<your-search-apikey>";
const searchIndexName = "<your-index-name>";

// Define MyDataSource class implementing DataSource interface
class MyDataSource extends DataSource {
  constructor() {
    super();
    this.name = "my-datasource";
    this.embeddingClient = new OpenAIEmbeddings({
      azureEndpoint: aoaiEndpoint,
      azureApiKey: aoaiApiKey,
      azureDeployment: aoaiDeployment
    });
    this.searchClient = new SearchClient(searchEndpoint, searchIndexName, new AzureKeyCredential(searchApiKey));
  }

  async renderData(context, memory, tokenizer, maxTokens) {
    // use user input as query
    const input = memory.getValue("temp.input");

    // generate embeddings
    const embeddings = (await this.embeddingClient.createEmbeddings(input)).output[0];

    // query Azure AI Search
    const response = await this.searchClient.search(input, {
      select: [ "id", "content", "filepath" ],
      searchFields: ["rawContent"],
      vectorSearchOptions: {
        queries: [{
          kind: "vector",
          fields: [ "contentVector" ],
          vector: embeddings,
          kNearestNeighborsCount: 3
        }]
      },
      queryType: "semantic",
      top: 3,
      semanticSearchOptions: {
        // your semantic configuration name
        configurationName: "default",
      }
    });

    // Add documents until you run out of tokens
    let length = 0, output = '';
    for await (const result of response.results) {
      // Start a new doc
      let doc = `${result.document.content}\n\n`;
      let docLength = tokenizer.encode(doc).length;
      const remainingTokens = maxTokens - (length + docLength);
      if (remainingTokens <= 0) {
          break;
      }

      // Append doc to output
      output += doc;
      length += docLength;
    }
    return { output, length, tooLong: length > maxTokens };
  }
}

Добавление дополнительных API для пользовательского API в качестве источника данных

Выполните следующие действия, чтобы расширить настраиваемый агент обработчика из шаблона Пользовательского API дополнительными API.

  1. Обновите ./appPackage/apiSpecificationFile/openapi.*.

    Скопируйте соответствующую часть API, которую вы хотите добавить из спецификации, и добавьте в ./appPackage/apiSpecificationFile/openapi.*.

  2. Обновите ./src/prompts/chat/actions.json.

    Обновите необходимые сведения и свойства для пути, запроса и текста ДЛЯ API в следующем объекте:

    {
      "name": "${{YOUR-API-NAME}}",
      "description": "${{YOUR-API-DESCRIPTION}}",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "object",
            "properties": {
              "${{YOUR-PROPERTY-NAME}}": {
                "type": "${{YOUR-PROPERTY-TYPE}}",
                "description": "${{YOUR-PROPERTY-DESCRIPTION}}",
              }
              // You can add more query properties here
            }
          },
          "path": {
            // Same as query properties
          },
          "body": {
            // Same as query properties
          }
        }
      }
    }
    
  3. Обновите ./src/adaptiveCards.

    Создайте новый файл с именем ${{YOUR-API-NAME}}.json и заполните адаптивную карточку для ответа API api api.

  4. Обновите./src/app/app.js файл.

    Добавьте следующий код перед module.exports = app;:

    app.ai.action(${{YOUR-API-NAME}}, async (context: TurnContext, state: ApplicationTurnState, parameter: any) => {
      const client = await api.getClient();
    
      const path = client.paths[${{YOUR-API-PATH}}];
      if (path && path.${{YOUR-API-METHOD}}) {
        const result = await path.${{YOUR-API-METHOD}}(parameter.path, parameter.body, {
          params: parameter.query,
        });
        const card = generateAdaptiveCard("../adaptiveCards/${{YOUR-API-NAME}}.json", result);
        await context.sendActivity({ attachments: [card] });
      } else {
        await context.sendActivity("no result");
      }
      return "result";
    });
    

Microsoft 365 в качестве источника данных

Узнайте, как использовать API поиска Microsoft Graph для запроса содержимого Microsoft 365 в качестве источника данных для приложения RAG. Дополнительные сведения об API поиска Microsoft Graph см. в статье Использование API поиска (Майкрософт) для поиска в OneDrive и SharePoint.

Предварительные требования. Необходимо создать клиент API Graph и предоставить ему Files.Read.All разрешение область на доступ к файлам, папкам, страницам и новостям SharePoint и OneDrive.

Прием данных

Доступен API поиска Microsoft Graph, который может искать содержимое SharePoint. Поэтому необходимо только убедиться, что документ отправлен в SharePoint или OneDrive без необходимости приема дополнительных данных.

Примечание.

Сервер SharePoint индексирует файл, только если его расширение указано на странице управления типами файлов. Полный список поддерживаемых расширений файлов см. в статье Расширения индексированных файлов по умолчанию и типы файлов, проанализированные на сервере SharePoint и SharePoint в Microsoft 365.

Реализация источника данных

Ниже приведен пример поиска текстовых файлов в SharePoint и OneDrive.

import {
  DataSource,
  Memory,
  RenderedPromptSection,
  Tokenizer,
} from "@microsoft/teams-ai";
import { TurnContext } from "botbuilder";
import { Client, ResponseType } from "@microsoft/microsoft-graph-client";

export class GraphApiSearchDataSource implements DataSource {
  public readonly name = "my-datasource";
  public readonly description =
    "Searches the graph for documents related to the input";
  public client: Client;

  constructor(client: Client) {
    this.client = client;
  }

  public async renderData(
    context: TurnContext,
    memory: Memory,
    tokenizer: Tokenizer,
    maxTokens: number
  ): Promise<RenderedPromptSection<string>> {
    const input = memory.getValue("temp.input") as string;
    const contentResults = [];
    const response = await this.client.api("/search/query").post({
      requests: [
        {
          entityTypes: ["driveItem"],
          query: {
            // Search for markdown files in the user's OneDrive and SharePoint
            // The supported file types are listed here:
            // https://learn.microsoft.com/sharepoint/technical-reference/default-crawled-file-name-extensions-and-parsed-file-types
            queryString: `${input} filetype:txt`,
          },
          // This parameter is required only when searching with application permissions
          // https://learn.microsoft.com/graph/search-concept-searchall
          // region: "US",
        },
      ],
    });
    for (const value of response?.value ?? []) {
      for (const hitsContainer of value?.hitsContainers ?? []) {
        contentResults.push(...(hitsContainer?.hits ?? []));
      }
    }

    // Add documents until you run out of tokens
    let length = 0,
      output = "";
    for (const result of contentResults) {
      const rawContent = await this.downloadSharepointFile(
        result.resource.webUrl
      );
      if (!rawContent) {
        continue;
      }
      let doc = `${rawContent}\n\n`;
      let docLength = tokenizer.encode(doc).length;
      const remainingTokens = maxTokens - (length + docLength);
      if (remainingTokens <= 0) {
        break;
      }

      // Append do to output
      output += doc;
      length += docLength;
    }
    return { output, length, tooLong: length > maxTokens };
  }

  // Download the file from SharePoint
  // https://docs.microsoft.com/en-us/graph/api/driveitem-get-content
  private async downloadSharepointFile(
    contentUrl: string
  ): Promise<string | undefined> {
    const encodedUrl = this.encodeSharepointContentUrl(contentUrl);
    const fileContentResponse = await this.client
      .api(`/shares/${encodedUrl}/driveItem/content`)
      .responseType(ResponseType.TEXT)
      .get();

    return fileContentResponse;
  }

  private encodeSharepointContentUrl(webUrl: string): string {
    const byteData = Buffer.from(webUrl, "utf-8");
    const base64String = byteData.toString("base64");
    return (
      "u!" + base64String.replace("=", "").replace("/", "_").replace("+", "_")
    );
  }
}

См. также

Библиотека ИИ Teams