Перейти к основному содержанию

Обзор

Навыки и агенты сейчас проходят альфа-тестирование. Функция работает, но продолжает развиваться.
Приложения могут определять возможности ИИ, которые находятся внутри рабочего пространства — повторно используемые инструкции для навыков и агенты с настраиваемыми системными подсказками.
Навыки определяют многократно используемые инструкции и возможности, которые агенты ИИ могут использовать в вашем рабочем пространстве. Используйте defineSkill() для определения навыков со встроенной валидацией:
src/skills/example-skill.ts
Основные моменты:
  • name — уникальная строка-идентификатор навыка (рекомендуется kebab-case).
  • label — читаемое человеком отображаемое имя, показываемое в UI.
  • content содержит инструкции навыка — это текст, который использует агент ИИ.
  • icon (необязательно) задаёт значок, отображаемый в UI.
  • description (необязательно) предоставляет дополнительный контекст о назначении навыка.
Агенты — это ИИ-помощники, работающие в вашем рабочем пространстве. Используйте defineAgent() для создания агентов с пользовательским системным промптом:
src/agents/example-agent.ts
Основные моменты:
  • name — уникальная строка-идентификатор агента (рекомендуется kebab-case).
  • label — отображаемое имя, показываемое в UI.
  • prompt — это системный промпт, определяющий поведение агента.
  • description (необязательно) предоставляет контекст о том, что делает агент.
  • icon (необязательно) задаёт значок, отображаемый в UI.
  • modelId (необязательно) переопределяет модель ИИ по умолчанию, используемую агентом.
  • responseFormat (необязательно) задаёт формат вывода агента. По умолчанию для свободного текста используется { type: 'text' }. Используйте { type: 'json', schema }, чтобы принудительно получать структурированный JSON-вывод. Вызов runAgent() или ИИ-шаг рабочего процесса может запросить другой формат, который имеет приоритет.
  • roleUniversalIdentifier (необязательно) связывает агента с ролью, определённой вашим приложением. Эта роль не должна предоставлять больше прав, чем роль приложения, иначе сборка завершится с ошибкой; см. Роли агентов.
  • modelConfiguration (необязательный параметр) включает встроенные инструменты модели, как на странице настроек агента: { webSearch: { enabled: true }, twitterSearch: { enabled: true } }. Каждый инструмент работает только с моделями, которые его поддерживают. Не указывайте его, чтобы они оставались отключенными.
  • isSystem (необязательный параметр, по умолчанию false) скрывает агента из списков агентов, как агентов, которых Twenty создает для шагов ИИ в рабочих процессах. Он изменяет только отображаемое: агентов приложения можно изменять только через приложение, независимо от значения этого флага.
По умолчанию агент возвращает свободный текст. Чтобы получить структурированный вывод, установите для responseFormat значение { type: 'json' } и укажите schema:
src/agents/structured-agent.ts
Примечания к схеме:
  • Схема — это плоский объект: type каждого свойства должен быть примитивом (string, number или boolean). Вложенные объекты и массивы не поддерживаются.
  • description (необязательно) для каждого свойства подсказывает модели, что туда нужно поместить.
  • required (необязательно) перечисляет свойства, которые модель всегда должна возвращать.
  • additionalProperties: false (необязательно) запрещает любые свойства, не объявленные в properties.
MCP-серверы находятся на стадии бета-тестирования и ещё не запущены: вы можете объявить их, но рабочие пространства не смогут использовать их инструменты, пока эта функция не будет выпущена.
Используйте defineMcpServer() для развертывания удалённого MCP-сервера (Streamable HTTP) вместе с вашим приложением, чтобы ИИ рабочего пространства мог использовать его инструменты без синхронизации этих данных с Twenty:
src/mcp-servers/github.ts
Основные моменты:
  • name имеет формат snake_case (строчные буквы, цифры и символы подчёркивания, начинается с буквы, не более 32 символов) и уникален в рабочем пространстве. Он добавляется в начало имён инструментов сервера.
  • url должен быть URL-адресом HTTP или HTTPS.
  • connectionProvider (необязательно) ссылается на поставщика подключения, объявленного тем же приложением. Запросы отправляют токен доступа этого подключения в качестве токена Bearer. Не указывайте его для общедоступного сервера.
  • URL-адрес нельзя изменить в более поздней версии, пока подключён поставщик подключения, поэтому токены никогда не отправляются на другой хост.
  • Рабочие пространства могут деактивировать сервер; более поздняя версия приложения сохранит этот выбор.
  • Требуется "engines": { "twenty": ">=2.53.0" }.
runAgent() позволяет логической функции запустить одного из агентов вашего приложения (с его навыками и инструментами). Идентифицируйте агента по universalIdentifier, который вы передали в defineAgent(), и передайте в качестве input то, над чем он должен работать:
src/logic-functions/run-enricher.ts
Основные моменты:
  • input — это строка или список из 1–100 сообщений ({ role: 'user' | 'assistant', content: string }). Сообщение user также может содержать вложения файлов (см. ниже). Чтобы поддерживать диалог, передавайте thread, а не воспроизводите предыдущие ходы самостоятельно.
  • По умолчанию вызов ожидает агента, который может самостоятельно читать и обновлять записи с помощью собственных инструментов — runAgent() возвращает результат после завершения запуска. Передайте waitForCompletion: false, чтобы вызов вернул результат сразу после запуска: status будет иметь значение RUNNING, а result: null; ответ поступит в беседу threadId. Запуск, длящийся более 15 минут, также завершается со значением RUNNING.
  • Агент также может приостановить работу, пока не изменится запись или не пройдет некоторое время. Затем runAgent() завершается со значениями success: true, status: 'SUSPENDED' и result: null; позже агент самостоятельно продолжает работу, а его ответ появляется в беседе threadId. Вызов thread, агент которого все еще ожидает, завершается ошибкой, пока агент не завершит работу.
  • Каждый вызов сохраняется вместе с его входными данными и статусом, а также с его ответом и использованием после завершения, поэтому он отображается в запусках агента. Без thread вызов получает собственный разговор: threadId задаёт его, и последующий вызов его никогда не видит.
  • Приложение может запускать только собственных агентов.
  • Роль по умолчанию приложения должна предоставлять флаг разрешения AI — добавьте SystemPermissionFlag.AI в permissionFlagUniversalIdentifiers (или установите canAccessAllTools: true). Без этого runAgent() завершится с ошибкой прав доступа.
  • Установите достаточно большое значение timeoutSeconds для логической функции — выполнение агента может занимать несколько секунд.
  • status имеет значение COMPLETED и не равный null result, когда запуск завершается без ожидания; SUSPENDED со значением success: true и result: null, пока он ожидает; RUNNING со значением success: true и result: null, когда вызов не ожидал запуска; FAILED со значением success: false и error, содержащим причину (например, когда в рабочем пространстве во время запуска закончились кредиты ИИ).
src/roles/default-role.ts
Избегайте циклов: если вы вызываете runAgent() из триггера события базы данных *.updated, и агент обновляет ту же запись, ограничьте триггер, указав в updatedFields поле, которое агент никогда не изменяет (например, исходный URL), или добавьте проверку, что хотя бы одно целевое поле всё ещё пусто, прежде чем вызывать runAgent().

Отправка файлов вместе с сообщением

Сообщение user может содержать attachments, поэтому агент видит снимок экрана или PDF, а не только его имя. Каждая запись ссылается на файл, который ваше приложение уже загрузило в папку agent-chat:
src/logic-functions/describe-screenshot.ts
Для загрузки требуется флаг разрешения UPLOAD_FILE в роли приложения по умолчанию наряду с AI. filename является необязательным и служит только для обозначения файла для модели.
  • Вложения могут содержать только сообщения user. Их нельзя представить в ходе assistant, поэтому runAgent() отклоняет их, а не удаляет.
  • Не более 10 вложений на сообщение.
  • fileId должен указывать на полностью загруженный файл в том же рабочем пространстве, в папке agent-chat. В противном случае вызов завершается ошибкой вместо того, чтобы незаметно ответить без файла.

Запрос структурированного результата

Передайте responseFormat, чтобы задать формат только для этого вызова. Он имеет приоритет над собственным responseFormat агента, поэтому один агент может обслуживать вызывающих, которым нужны разные результаты. Он использует ту же схему, что и defineAgent():
src/logic-functions/score-lead.ts

Продолжение диалога

Передайте thread, и Twenty сохранит диалог: при каждом вызове отправляется только новый ввод, а агент видит предыдущие ходы, включая то, что вернули его инструменты. Используйте в качестве ключа любой стабильный идентификатор со своей стороны, например Slack- канал и временную метку цепочки сообщений:
src/logic-functions/reply-in-thread.ts
  • Ключи ограничены вашим приложением и агентом, поэтому два приложения никогда не используют один диалог. Передавать thread могут только токены доступа приложения.
  • input может содержать только сообщения user: в диалоге уже есть ответы агента.
  • additionalInstructions передаются модели только для этого вызова и не сохраняются, что подходит для указаний для каждого запроса, например правил форматирования или крайнего срока. Они также работают без thread.
  • Каждый запуск сохраняется как ход с указанием его статуса, включая неудачные. Его использование сохраняется при возврате агента, даже если у него закончились кредиты. Неудачный запуск не является частью разговора, поэтому следующий вызов его не видит.
  • Вызовы в одной и той же цепочке выполняются по одному: второй вызов ожидает завершения первого, поэтому каждый из них видит предшествующие ему ходы.
  • Когда ходы выполняются от имени разных участников (см. ниже), каждый из них видит только текст ходов остальных, но никогда не видит результаты их инструментов, поскольку они были получены с разрешениями другого пользователя.

Запуск от имени участника рабочего пространства

Передавайте runAsWorkspaceMemberId, когда запуск инициирован человеком — например, чат-ботом, отвечающим на сообщение, — чтобы агент действовал как этот участник, а не как приложение:
src/logic-functions/answer-question.ts
Затем запуск выполняется с собственной ролью участника: он может делать всё, что может делать этот участник, и не больше; созданные им записи приписываются этому участнику, и к ним применяются его разрешения на уровне строк. Роль агента в этом не участвует — это роль по умолчанию приложения для запусков, за которыми никто не стоит. Опускайте это поле для автономных запусков (плановые задания, триггеры событий базы данных): в таких случаях сохраняется собственная роль агента. Действующее приложение остаётся привязанным к контексту запуска для отслеживания происхождения, не сужая при этом полномочий участника.Ваше приложение отвечает за сопоставление человека, который инициировал запуск, с участником рабочего пространства. Чтобы указать участника, требуется токен доступа приложения; то, кого можно указать с помощью токена, зависит от того, привязан ли к нему пользователь:
  • Токен без привязанного пользователя может указать любого участника. Логическая функция выполняется с таким токеном; то же относится к токенам, выпущенным через client_credentials или на основе API‑ключа.
  • Токен, выданный от имени пользователя (как у фронтенд‑компонента), может указывать только на участника самого этого пользователя.
Все остальное — обычная пользовательская сессия, API‑ключ без токена приложения — вообще не может указывать участника.
runAgent() генерирует исключение, если не удаётся определить участника рабочего пространства — неизвестного или удалённого участника, либо участника без роли. При этом никогда не используется роль самого агента, так как это предоставило бы больше прав доступа, чем запросил вызывающий код.
sendInboxMessage() отправляет сообщение из вашего приложения в чаты одного или нескольких участников рабочего пространства. Это может завершиться вызовом инструмента, на который участник отвечает из чата, после чего разговор продолжается с ассистентом Twenty. См. Входящие, чтобы узнать о его параметрах, вспомогательных функциях упоминаний и о том, как выполнять публикации из фронтенд-компонента.