AI-бот — пользовательские инструменты

:bookmark: Это руководство объясняет, как создавать, настраивать и интегрировать пользовательские инструменты ИИ в плагине Discourse AI, позволяя администраторам расширять возможности бота с помощью определенных пользователем функций JavaScript.

:person_raising_hand: Требуемый уровень доступа пользователя: Администратор

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

Краткое содержание

В этой документации рассматриваются следующие темы:

  • Создание нового пользовательского инструмента ИИ
  • Настройка параметров и скриптов инструмента
  • Доступные API для скриптов инструментов
  • Интеграция пользовательских инструментов с персонажами ИИ
  • Тестирование и устранение неполадок пользовательских инструментов

Создание нового пользовательского инструмента ИИ

Чтобы создать новый инструмент ИИ:

  1. Перейдите в Панель администратора > Плагины > Discourse AI > Инструменты
  2. Нажмите «Новый инструмент» (вы можете использовать существующие предустановки, чтобы ознакомиться с возможностями)
  3. Заполните следующие поля:
    • Имя: Название инструмента, отображаемое для LLM
    • Описание: Описание инструмента, отображаемое для LLM
    • Краткое описание: Краткое описание того, что делает инструмент для помощи пользователям (отображается в деталях)
    • Параметры: Определите входные данные, необходимые вашему инструменту, как они будут представлены LLM
    • Скрипт: Код JavaScript, обеспечивающий работу вашего инструмента
  4. Нажмите «Сохранить»

Настройка скриптов инструментов

Доступные API

Ваши скрипты инструментов имеют доступ к следующим API:

  1. HTTP-запросы:

    http.get(url, options)
    http.post(url, options)
    http.put(url, options)
    http.patch(url, options)
    http.delete(url, options)
    

    Используйте их для взаимодействия со внешними сервисами. Вы можете использовать options для указания заголовков и тела HTTP-запроса:

    http.get(url, { headers: { "Authorization": "Bearer key" } })
    http.post(url, { headers: { "Content-Type": "application/json" }, body: { key: "value" } })
    http.patch(url, { headers: { "Authorization": "Bearer key" }, body: "some body" })
    http.delete(url, { headers: { "Authorization": "Bearer key" } })
    http.put(url, { headers: { "Authorization": "Bearer key" }, body: "some body" })
    

    Все методы HTTP возвращают объект { status: number, body: string }.

  2. Интеграция с LLM (языковой моделью):

    llm.truncate(text, length)
    

    Обрезает текст до указанной длины в токенах на основе токенизатора настроенной LLM.

    llm.generate(prompt, options)
    

    Генерирует текст с использованием настроенной LLM. Промпт может быть простой строкой или структурированным объектом, например { messages: [{ type: "system", content: "..." }, { type: "user", content: "..." }] }. Опции включают json: true для запроса и автоматического парсинга вывода в формате JSON, а также temperature, top_p, max_tokens и stop_sequences.

  3. Интеграция пользовательских загрузок (RAG)

    index.search(query, { filenames: ["file.pdf"], limit: 10 })
    

    Ищет фрагменты документов RAG, индексированных и прикрепленных к этому инструменту. Возвращает Array<{ fragment: string, metadata: string | null }>, отсортированный по релевантности. Ограничение по умолчанию — 10, максимум — 200.

    index.getFile(filename)
    

    Извлекает полное содержимое загруженного файла RAG по его точному имени файла. Возвращает полный текст или null, если файл не найден.

  4. Поддержка загрузок

    upload.create(filename, base_64_content)
    

    Создает новую загрузку. Возвращает { id: number, url: string, short_url: string }.

    upload.getUrl(shortUrl)
    

    По короткому URL (например, upload://12345) возвращает полный URL, совместимый с CDN.

    upload.getBase64(uploadIdOrShortUrl, maxPixels)
    

    Получает содержимое существующей загрузки в кодировке base64. Принимает ID загрузки (число) или короткий URL (строку). Необязательный параметр maxPixels для автоматического изменения размера изображения (по умолчанию: 10 000 000).

  5. Контроль цепочки выполнения

    chain.setCustomRaw(raw)
    

    Устанавливает окончательное необработанное содержимое поста бота и останавливает цепочку выполнения инструмента. Полезно для инструментов, которые напрямую генерируют полный ответ (например, инструменты генерации изображений).

  6. Управление секретами

    secrets.get(alias)
    

    Возвращает значение учетных данных, связанное с указанным псевдонимом. Псевдонимы определяются в конфигурации контрактов секретов инструмента и привязываются к секретам ИИ в панели администратора. Выбрасывает ошибку, если псевдоним не объявлен, не привязан или отсутствуют учетные данные.

    const apiKey = secrets.get("my_api_key");
    
  7. Интеграция с Discourse

    Инструменты могут напрямую взаимодействовать с данными Discourse:

    discourse.baseUrl              // Базовый URL сайта
    discourse.search(params)       // Выполнить поиск в Discourse
    discourse.getPost(post_id)     // Получить детали поста (включая необработанное содержимое)
    discourse.getTopic(topic_id)   // Получить детали темы (теги, категория и т. д.)
    discourse.getUser(id_or_username)  // Получить детали пользователя
    discourse.createTopic(params)  // Создать новую тему
    discourse.createPost(params)   // Создать новый пост/ответ
    discourse.editPost(post_id, raw, options)    // Изменить содержимое поста
    discourse.editTopic(topic_id, updates, options) // Изменить свойства темы (теги, категория, видимость)
    discourse.createChatMessage(params) // Отправить сообщение в чате
    discourse.createStagedUser(params)  // Создать подготовленного пользователя
    discourse.getAgent(name)       // Получить другого агента ИИ (с методом respondTo)
    discourse.updateAgent(name, updates) // Обновить конфигурацию агента ИИ
    discourse.getCustomField(type, id, key)      // Читать пользовательское поле в посте/теме/пользователе
    discourse.setCustomField(type, id, key, value) // Устанавливать пользовательское поле в посте/теме/пользователе
    
  8. Объект контекста

    Объект context предоставляет информацию о том, где выполняется инструмент:

    • Контекст разговора бота: context.post_id, context.topic_id, context.private_message, context.participants, context.username, context.user_id
    • Контекст чата: context.message_id, context.channel_id, context.username
    • Контекст автоматизации: context.post_id, context.topic_id, context.username, context.user_id, context.feature_name, context.feature_context
    • Общие свойства: context.site_url, context.site_title, context.site_description

Обязательные функции

Ваш скрипт должен реализовывать:

  • invoke(params): Основная функция, которая выполняется при вызове инструмента

Он также может опционально реализовывать:

  • details(): Возвращает строку (может включать базовый HTML), описывающую выполнение инструмента, отображаемую в интерфейсе чата
  • customSystemMessage(): Вызывается во время сборки промпта (не во время вызова инструмента). Возвращает строку, добавляемую к системному промпту, или null/undefined для пропуска. Имеет доступ к объектам context, discourse и index.

Пример скрипта:

function invoke(params) {
  let result = http.get("https://api.example.com/data?query=" + params.query);
  return JSON.parse(result.body);
}

function details() {
  return "Получены данные из Example API";
}

Ограничения и безопасность

  • Тайм-аут выполнения: Тайм-аут по умолчанию составляет 2000 мс времени обработки скрипта. Таймер приостанавливается во время внешних HTTP-запросов (http.*) и вызовов LLM (llm.generate), поэтому учитывается только время обработки самого скрипта.
  • Память: Максимальный лимит кучи V8 — 10 МБ
  • HTTP-запросы: Максимум 20 запросов за одно выполнение инструмента
  • Песочница: Скрипты выполняются в ограниченной среде JavaScript V8 (через MiniRacer). Нет доступа к глобальным объектам браузера, файловой системе хоста или серверным библиотекам. Сетевые запросы проксируются через бэкенд Discourse.

Тестирование вашего инструмента

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

Интеграция инструментов с персонажами ИИ

Чтобы добавить свой пользовательский инструмент к персонажу ИИ:

  1. Перейдите в Панель администратора > Плагины > Discourse AI > Персонажи
  2. Отредактируйте существующий персонаж или создайте новый
  3. В разделе «Инструменты» вы увидите свои пользовательские инструменты в списке наряду со встроенными
  4. Выберите свой пользовательский инструмент, чтобы добавить его к персонажу

Пользовательские инструменты в действии

Как только вы предоставите свой пользовательский инструмент LLM, он сможет использовать его для улучшения разговора.

Устранение неполадок

Если ваш инструмент работает не так, как ожидалось:

  1. Используйте интерфейс Тестирования, чтобы убедиться, что он ведет себя ожидаемым образом для ваших входных данных.
  2. Убедитесь, что ваша группа включена в ai_bot_debugging_allowed_groups. Участники этой группы имеют полный доступ к транскриптам бота; вы можете просмотреть логи ИИ там.
  3. Если происходит что-то неожиданное, перейдите на https://SITENAME/logs, чтобы проверить наличие ошибок.

Дополнительные ресурсы

14 лайков

Я только начинаю создавать инструменты для AI-персонажей в Discourse, отчасти чтобы иметь возможность сосредоточиться на этом; это кажется очень полезным, учитывая, сколько существует полезных и публичных API!

Например, я получаю такие результаты:

Понимание принятия решений через стратегии покера AI

maiki:

Создай инфокарточку для Breath of the Wild.

gpt-4o:

Вот информационная карточка для «The Legend of Zelda: Breath of the Wild»:


Название: The Legend of Zelda: Breath of the Wild
Разработчик: Nintendo
Издатель: Nintendo
Дата выхода: 3 марта 2017 года
Платформы: Nintendo Switch, Wii U
Жанр: Приключенческий экшен
Режим игры: Одиночный

Сюжет:
Действие происходит в огромном открытом мире, где игроки управляют Линком, который пробуждается от глубокого сна, чтобы победить Каламити Ганона и спасти королевство Хайрул. Игра получила высокую оценку за свой захватывающий мир, позволяющий игрокам свободно исследовать и взаимодействовать с окружением и его системами.

Награды:
Игра завоевала множество наград, включая несколько премий «Игра года», благодаря инновационному дизайну, обширному миру и увлекательному геймплею.

Подробнее: Запись в Wikidata


«Breath of the Wild» известна тем, что переопределила жанр игр с открытым миром и подарила захватывающий опыт, поощряющий исследование и открытия.

maiki:

То же самое для Animal Crossing: New Horizons.

gpt-4o:

Вот информационная карточка для «Animal Crossing: New Horizons»:


Название: Animal Crossing: New Horizons
Разработчик: Nintendo
Издатель: Nintendo
Дата выхода: 20 марта 2020 года
Платформа: Nintendo Switch
Жанр: Социальная симуляция
Режим игры: Одиночный, Мультиплеер

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

Награды:
Игра была высоко оценена за творческую свободу и своевременный выход, получив награды «Игра года» и признание за вклад в социальные игры во время глобальной пандемии.

Подробнее: Запись в Wikidata


«Animal Crossing: New Horizons» стала культурным феноменом, прославленной за увлекательные социальные функции и способность снимать стресс в непростые времена.

maiki:

Далеко от Чикаго

gpt-4o:

Вот информационная карточка для книги «Далеко от Чикаго»:


Название: Далеко от Чикаго
Автор: Ричард Пик
Год публикации: 1998
Жанр: Историческая проза, Молодёжная литература
ISBN: 978-0141303529

Сюжет:
Этот роман, удостоенный почётного упоминания премии Newbery, представляет собой серию коротких рассказов, повествующих о ежегодных летних поездках Джо и его сестры Мэри Элис из Чикаго в маленький городок в сельской местности Иллинойса, где они навещают свою эксцентричную бабушку Доудел в конце 1920-х и 1930-х годов. Каждая глава представляет собой забавный и трогательный эпизод, несущий жизненные уроки на фоне Великой депрессии.

Награды:
Книга получила почётное упоминание Newbery в 1999 году и прославлена за юмор, глубину и изображение сельской жизни в США начала XX века.

Подробнее: Запись в Wikidata


Эта книга ценится за ностальгическое и трогательное повествование, передающее суть семейных уз и простых времён.

maiki:

Мысли в ставках

gpt-4o:

Ранее я нашёл запись в Wikidata для «Мысли в ставках», но в данный момент не могу получить дополнительные конкретные детали напрямую из Wikidata. Однако вот краткая карточка на основе имеющейся информации:


Название: Мысли в ставках: Принятие умных решений, когда у вас нет всех фактов
Автор: Энни Дьюк
Год публикации: 2018
Жанр: Нон-фикшн, Принятие решений, Психология
ISBN: 978-0735216358

Сюжет:
Книга использует идеи из покера, чтобы помочь читателям понять процесс принятия решений в условиях неопределённости. Энни Дьюк, сочетая свой опыт профессиональной игрока в покер и когнитивного психолога, предлагает framework для принятия более умных решений, оценки результатов и формирования вероятностного мышления.

Подробнее: Запись в Wikidata


Для получения более детальной информации рекомендуется обратиться к дополнительным ресурсам или базам данных, посвящённым книжным публикациям.


Хорошо, это всё в порядке, но я заметил следующее описание…

…и задаюсь вопросом: стоит ли мне строить свои промпты, ссылаясь напрямую на эти инструменты для более эффективного использования?

Мой общий подход к написанию промптов заключается в постепенной доработке и затем закреплении паттерна поведения, который я хочу, чтобы инструменты AI采用了. Однако, если бы я мог добавлять конкретные инструкции, например, когда обращаться к сущности в Wikidata, а когда перечислять все утверждения о конкретной сущности (два разных API), то я чувствую, что смогу оптимизировать весь рабочий процесс так, чтобы он протекал именно так, как я задумал… :star_struck:

4 лайка

Действительно, четкое объяснение инструмента и предоставление примеров в системном промпте полезно.

2 лайка

Возможно ли в пользовательском инструменте вставить API-ключ и проект OpenAI из настроек администратора?

1 лайк

Вы можете выполнять REST-запросы из пользовательского инструмента и указывать все заголовки.

2 лайка

только что понял, ха-ха… извини за глупость

1 лайк

Я загрузил несколько документов в персону, она сгенерировала эмбеддинги и теперь может выполнять семантический поиск по ним. Однако в некоторых случаях семантический поиск не идеален, поэтому я хочу его улучшить и реализовать гибридный поиск: например, сохранить текущую функциональность и добавить поиск по ключевым словам. На данный момент, если я хочу это сделать, мне нужно написать собственный инструмент, верно?
Я знаю, что мог бы просто опубликовать документы как темы, и тогда всё заработало бы «из коробки» с помощью встроенного поиска Discourse, но в данный момент это не вариант.

При добавлении пользовательского инструмента с параметром массива возникает ошибка схемы инструмента. Ошибка при запуске диалога:

{
"error": {
"code": 400,
"message": "* GenerateContentRequest.tools[0].function_declarations[3].parameters.properties[properties].items: отсутствует поле.\n",
"status": "INVALID_ARGUMENT"
}
}

Что я пробовал:

  • Создал пользовательский инструмент с параметром под названием properties типа массив.
  • Интерфейс списка параметров не позволяет указать items.
  • Экспортировал/импортировал полный JSON инструмента, включающий items: { type: “string” } для properties.
  • После импорта ошибка сохраняется сразу после включения инструмента для персоны. Если удалить инструмент, бот работает.

Ожидаемое поведение:

Либо интерфейс списка параметров должен позволять определять типы элементов массива, либо импорт должен учитывать items, чтобы схема валидировалась.

Столкнулся ли кто-нибудь с этим? Есть ли известное ограничение или требуемый путь в интерфейсе для определения параметров массива?

1 лайк