Хватит использовать AI-плагины! Как получить доступ к LLM-преимуществам через API Jay Copilot
Меня зовут Алексей Петров, я solution-инженер в компании Just AI. Сегодня многие разработчики столкнулись с необходимостью внедрения LLM в свои корпоративные сервисы. В этой статье я попробую доказать, почему интеграция API в данном случае гораздо эффективнее использования AI-плагинов, а также расскажу о том, как интегрировать API Jay Copilot в бизнес-процессы на примере интеграции amoCRM.
AI-плагины не про автоматизацию
С раскрытием потенциала LLM моделей пользователи все чаще делятся историями о том, как быстро и удобно они теперь решают сложные и трудоемкие рабочие задачи — от написания кода до подготовки презентаций.
Кажется, что переключение между AI-сервисами и своими документами не составляет сложности, но все же занимает какое-то время. И рано или поздно кто-то точно придет к вам в бэклог с задачей автоматизации этого процесса, и вы, как разработчик, столкнетесь с необходимостью внедрения языковых моделей в свои корпоративные сервисы.
В идеальном мире это была бы разовая активность с нативной интеграцией одной модели, которая решит все потребности бизнеса. Но с большей вероятностью вам придется оценить и подробно изучить документацию к нескольким сервисам, сравнить их, понять, что не все из них имеют точки для интеграции, покопаться на гитхабе в поисках чего-то похожего, но условно-бесплатного, протестировать промпты, применить продуктовое мышление и понять, зачем мы вообще внедряем эти сервисы в наш старенький самописный сервис или CRM, а когда более важные задачи начнут поджимать по срокам, вы отправите вашего идейного вдохновителя в бесконечный цикл copy-paste из одного фронта в другой.
Мы решали похожие рутинные задачи и не переставали верить в идеальный мир с одним эндпоинтом. Как вы уже поняли из названия, решение нашлось, благодаря сервису Jay Copilot и его внешнему API.
Что такое Jay Copilot?
Это платформа для удобного использования генеративного ИИ в работе и повседневных задачах. Под капотом в Jay Copilot используются нейросети и сервисы, например: GPT-3.5 и GPT-4, YaGPT-2, GigaChat, Stable Diffusion и DALL·E 3, Whisper, Aimyvoice и собственная языковая модель Just AI, JustGPT.
Jay Copilot объединяет функции этих нейросетей в виде готовых приложений: Анализ файла, Подкасты, Создание документа по образцу, Иллюстратор, Краткое содержание, Поиск по сайту, Программист, Маркетинговое письмо и т.д. И несколько отраслевых бизнес-решений: Аналитик данных, База знаний, Написание вакансии и Анализ отзывов.
Про механику работы этих приложениями можно почитать в документации: https://help.jaycopilot.com/docs/
У сервиса также существует API, на базе которого можно реализовать сложные кейсы с интеграцией в сторонние системы и сервисы. API предоставляет возможность загружать, получать и удалять файлы, получать список шаблонов приложений, получать список приложений и избранных приложений, получать и удалять диалоги, обновлять диалоги, получать историю сообщений диалога, и отправлять сообщения в диалог.
C чего начать работу с API?
В первую очередь стоит начать с изучения возможностей: https://help.jaycopilot.com
Напишите нам в техподдержку: support@just-ai.com для получения ключа API Jay Copilot, указав ваш аккаунт на платформе.
Шаг 1: сделайте ваш первый гет-запрос и загрузите все шаблоны приложений: https://help.jaycopilot.com/api#tag/App-templates/operation/getTemplates
Метод [GET] https://app.jaycopilot.com/api/appsAdapter/templates вернет вам в ответ json ответ от сервиса, в котором максимально подробно описаны все навыки и все необходимые поля для создания приложений
Шаг 2: Получите информацию про приложения, используя методы из раздела Apps Вы можете получить:
1. GET https://app.jaycopilot.com/api/appsAdapter/apps — список всех созданных вами приложений
2. GET https://app.jaycopilot.com/api/appsAdapter/apps/favorite — список приложений только из списка избранных,
3. GET https://app.jaycopilot.com/api/appsAdapter/apps/{appId} само приложение если уже известен его уникальный ID*:*ID приложения можно узнать в строке браузера в интерфейсе Метод вернет информацию про приложение, его статус, название, уникальный ID и самое важное поле params/ или его уникальные параметры:
{
"id": "Идентификатор приложения",
"name": "Имя приложения",
"description": "Описание приложения",
"status": "READY",
"favorite": true,
"template": "Идентификатор шаблона приложения",
"params": {},
"meta": {},
"createdAt": 1700661509748,
"updatedAt": 1700661509748
}Шаг 3 (Опционально): при создании или редактировании приложений, может потребоваться файл, его можно загрузить методом загрузка файла:
POST https://app.jaycopilot.com/api/appsAdapter/files
— файл загрузится в хранилище и вы получите на него ссылку в ответе:
{
"id": "Идентификатор файла",
"name": "Имя файла",
"url": "https://example.com/file.pdf",
"contentType": "application/pdf",
"contentSize": 1024,
"createdAt": 1700661509748,
"meta": { }
}
Обратите внимание что в запросе на загрузку файла нужно поменять REQUEST BODY SCHEMA на multipart/form-data
Файлы также можно получить:
GET https://app.jaycopilot.com/api/appsAdapter/files/{fileId}Или удалить:
DEL https://app.jaycopilot.com/api/appsAdapter/files/{fileId}Шаг 4: Обновите существующий диалог с приложением вашими новыми параметрами (params):
PUT https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}Шаг 5: Диалог с приложением. Методы из раздела Conversations позволяют реализовать:
POST Создание диалога
https://app.jaycopilot.com/api/appsAdapter/conversationsGET Получение списка диалогов
https://app.jaycopilot.com/api/appsAdapter/conversationsGET Получение диалога
https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}DEL Удаление диалога
https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}PUT Обновление диалога (уже упоминалось выше) https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}
POST Удаление истории сообщений
https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}/clearGET Получение истории сообщений
https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}/historyPOST Отправка сообщения
https://app.jaycopilot.com/api/appsAdapter/conversations/{conversationId}/messageПожалуйста ознакомьтесь со схемами запросов в документации: https://help.jaycopilot.com/api#
Пример интеграции с CRM
Давайте разберем интеграцию с CRM и Jay Copilot на примере бизнес-кейса.
Описание:
Менеджер создает лид в CRM;
Документы загружены в раздел «Файлы» в CRM;
Cкрипт обращается в CRM и получает URL документа(ов);
В Jay создается навык, подходящий для анализа документа;
При создании навыка в параметрах указывается URL Документа из CRM и вопросы, на которые необходимо ответить навыку;
В результате выполнения скрипта в сделку записывается примечание с ответами на вопросы по документу.
Преднастройки и вспомогательные функции:
Python 3;
requests для отправки запросов в сервисы;
Функция для чтения токенов;
Доступ в amo crm и работа с token;
Доступ в Jay API и работа с token;
Опционально: используем библиотеку amocrm.v2 для работы с amocrm (в примере будет использоваться Tokens, Lead) https://github.com/andrey-tech/amocrm-api-v2-docs
Кодим?
Функции для работы с запросами:
import requests
def handle_response(response):
if response.status_code == 200:
return response.json()
else:
return response.text
## Блок кода проверяет ответ от сервера после отправки запроса.
## Если все прошло хорошо (код ответа 200), то он возвращает данные этого ответа в формате JSON.
## Если что-то пошло не так (код ответа отличается от 200), то возвращает текст ответа, который может содержать информацию об ошибке.
def read_token_from_file(filename):
with open(filename, 'r') as file:
return file.read().strip()
## Блок кода для работы с токенами которые хранятся в файлах: открывает файл с указанным именем и читает из него информацию.
## Информация, которую он прочитал из файла, возвращается как результат работы функции.
## Все лишние пробелы в начале и конце строки удаляются.
Сделаем доступ по API до CRM
Сначала преднастройки. В AMO можно создать тестовую учетку на 14 дней, и там все сразу доступно, включая интеграции. В админской учетке есть возможность провалиться в настройки и получить токен доступа для нашего кастомного приложения или выбрать какой-то готовый коннектор в маркетплейсе. В AMO CRM это настраивается в разделе амоМаркет.
Справа сверху есть раздел webhooks, нужно нажать на троеточие и выбрать «создать интеграцию» и выбрать «внешняя интеграция».
При создании из обязательных полей нам нужен только адрес. Можно даже написать localhost. Но мы возьмем адрес Jay Copilot. Так как AMO проверяет доступность домена, как обязательное условие для работы интеграции.
Нажимаем создать и видим, что у нас в установленных интеграциях появилась новая. Но этот коннектор пока отключен. Кликаем на него и проваливаемся в настройки. Видим наше описание и ключи доступа.
Кстати, небольшой оффтоп: вы можете также найти информацию про подключение своих интеграций в AMO, где говорится, что нужно идти в настройки ЛК и там выпускать bearer токен, но это чуть устаревшая информация. Раньше тут действительно был механизм API ключей, но со слов поддержки CRM он устарел и имел множество недостатков. Поэтому они перешли на механизм [[oAuth2.0]].
Ключи доступа нам чуть позже понадобятся, пока мы кликнем включить и отключить. Когда нажмем «установить», то появится статус интеграции «установлено», и это значит, что наш коннектор готов к использованию.
Далее мы уже можем через API интерфейс обращаться к нашей CRM. Но тут есть нюанс.
В документации разработчиков AMO CRM пишут, что нормальная библиотека, которую поддерживает вендор этой CRM самостоятельно, есть только для PHP, но, думаю, писать код сейчас на PHP мы не будем, поэтому для скорости демонстрации возьмем примеры на Python и готовую библиотеку. Благо, что комьюнити разработчиков у этой CRM действительно большое, и примеры всегда можно найти.
Например, ниже будет пару примеров из мануала, который я нашел на Youtube на канале Сеньор Джуниор: https://www.youtube.com/watch?v=rbwvOOwZxGo
Я возьму одноименную библиотеку amocrm-api https://pypi.org/project/amocrm-api/2.6.1/
Теперь кодим
Итак, давайте уже напишем немного кода а-ля приложение или AI-агента, который будет ходить как в CRM, так и в API Jay Copilot. Приложению нужно авторизоваться.
Далеко ходить не будем, возьмем метод с токенами из библиотеки для авторизации в amocrm:
## Данный блок кода предназначен для подключения к сервису AMO CRM через API.
## Переменные в начале кода - это уникальные ключи и адреса, которые нужны для того, чтобы программа могла войти в ваш аккаунт на сервисе AMO CRM.
## Библиотека amocrm.v2 помогает программе работать с этими ключами и авторизоваться в системе AMO CRM.
## Функция default_token_manager создает менеджер токенов, который используется для авторизации в AMO CRM.
## После выполнения кода, программа сохранит два ключа доступа в два файла: access_token.txt и refresh_token.txt.
## Эти ключи будут использоваться для авторизации в AMO CRM при следующих запросах к API.
subdomain="" ##субдомен
client_id="" ## ID интеграции
client_secret="" ##Секретный ключ
redirect_url="" ## URL который указан в интеграции (в нашем примере https://app.jaycopilot.com/)
refresh_token = ''
## в refresh_token - указывается тот длиииинный код авторизации (действительный 20 мин)"
from amocrm.v2 import tokens
if __name__ == '__main__':
tokens.default_token_manager( client_id, client_secret, subdomain, redirect_url,
storage=tokens.FileTokensStorage(), ## by default FileTokensStorage
)
tokens.default_token_manager.init(refresh_token, skip_error=False)
# Получение нового access token с использованием refresh token
def refresh_access_token():
token_url = f'https://{subdomain}.amocrm.ru/oauth2/access_token'
data = {
'client_id': client_id,
'client_secret': client_secret,
'grant_type': 'refresh_token',
'refresh_token': read_token_from_file('refresh_token.txt')
}
response = requests.post(token_url, data=data)
return response.json().get('access_token')
# Формируем заголовки для запроса в AMOCRM
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {read_token_from_file("access_token.txt")}'
}
Заполним параметры, где client id = ID интеграции, client_secret = Секретный ключ, subdomain = ваш домен, т.е. те данные, которые в браузере в строке написаны до .amocrm.ru/.., redirect_url это наш ранее указанный адрес, и самое основное это tokens.default_token_manager.init(code="" = временный код авторизации.
Например, так:
Когда первый раз запустите код, у вас в папке проекта появится access token и refresh token.
После этого нам уже можно будет не обновлять каждый раз 20-ти минутный токен, поэтому закомментим его:
## tokens.default_token_manager.init(refresh_token, skip_error=False)
Все, вы авторизовались. Можно написать первый тестовый запрос, допустим, проверить какие у нас там есть сделки.
## выполним простую проверку для того чтобы убедиться что мы получаем данные из AMO CRM.
## получим информацию о созданных сделках при помощи библиотеки amocrm.v2
from amocrm.v2 import Lead
leads = Lead.objects.all()
for _ in leads:
print(_.name, _.id)
Еще один пример:
# Получение сущности сделки по её ID
# lead_id = 888423 # Замените на реальный ID вашей сделки
# lead = Lead.objects.all(object_id=lead_id)
# Мы просто возьмем первый
lead = list(Lead.objects.all())[0]
lead_id = lead.id
# Вывод основной информации о сделке
print("ID сделки:", lead.id)
print("Название сделки:", lead.name)
print("Создана:", lead.created_at)
print("Обновлена:", lead.updated_at)
print("Стоимость:", lead.price)
Немного отвлечемся от кода. Давайте загрузим документы в сделку или убедимся, что они там есть:
Вернемся в код. Получим информацию о файлах в CRM:
# Формируем URL для запроса. Найдем файлы в сделке
url = f'https://{subdomain}.amocrm.ru/api/v4/leads/{lead_id}/files'
# Отправляем GET-запрос
response = requests.get(url, headers=headers)
response = handle_response(response)
response
Увидим на выходе что-то вроде:
{'_links': {'self': {'href': '
https://hostname.amocrm.ru/api/v4/leads/1738915/files?limit=50
'}},
'_embedded': {'files': [{'file_uuid': '8ab921e7-40c1-4323-9849-e87f48c5d9ad', 'id': 208995}]}}
# Получим uuid файлов
file_uuids = [file["file_uuid"] for file in response["_embedded"]["files"]]
file_uuids
# получаем домен сервиса файлов
url = f'https://{subdomain}.amocrm.ru/api/v4/account?with=drive_url'
response = requests.get(url, headers=headers)
response = handle_response(response)
drive_url = response['drive_url']
print(f"домен сервиса файлов: {drive_url}")
# получаем ссылочки на файлы по uuid
files = []
url = drive_url + '/v1.0/files/'
for uuid in file_uuids:
response = requests.get(url + uuid, headers=headers)
response = handle_response(response)
files.append({
'name': response['name'],
'url': response['_links']['download']['href']
})
files
Этот блок кода должен вернуть в ответ имя и точный Url Документа из сделки в CRM.
Если все успешно, можно идти дальше уже в Jay.
Получим краткое содержание файлов при помощи Jay:
### Настроим доступ к Jay Copilot API
BASE_URL = 'https://app.jaycopilot.com/api/appsAdapter/' ### base url прода
API_KEY = read_token_from_file('jay_api_key.txt') ## ключ
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': API_KEY
}
В этом коде токена нет.
Возьмите его у техподдержки Just AI: support@just-ai.com
Взяли? Отлично! Положите в txt файл в папку с проектом access_token.txt или укажите в коде.
Можно идти дальше.
Опциональный шаг:
# получим темплейты для приложений
response = requests.get(BASE_URL + "templates/", headers=headers)
print(response
response = handle_response(response)
print(str(response)[:500] + "<...>")
templates = response["templates"]
Еще один опциональный шаг:
# посмотрим список темплейтов
for (templateName, templateData) in templates.items():
print(templateName + ": " + templateData["info"]["title"])
Последний опциональный шаг, обещаю:
# Посмотрим настройки темплейта Jay: Краткое содержание.
templateName = "summarizer"
templates[templateName]
Все, можно создавать приложение с диалогом:
# создадим диалог с приложением "Краткий пересказ" и запустим приложение c первым файлом из сделки (Без предварительной загрузки файлов)
body = {
'app': {
'template': 'summarizer',
'params': {
'language': 'Russian',
'fields': 'Содержание',
'sentences': 4,
'documentToSummarize': [files[0]['url']]
}
}
}
response = requests.post(BASE_URL + "conversations/", headers=headers, json=body)
response = handle_response(response)
response
Видите ответ? Посмотрите внимательно на этот Json — это параметры. Далее:
# запомним id диалога и приложения
conversationId = response["id"]
appId = response["app"]["id"]
appId
# сохраним ответ приложения из response в переменную summary и немного отформатируем
summary = []
summary.append(files[0]["name"] + ":\n" + response["history"][0]["content"][0]["text"])
print(summary[0])
Получили саммари. Можно идти обратно в CRM и записывать его.
Уберите пальцы от ctrl+c, запишем по API :)
## Запишем ответ от Jay в notes нашей сделки
lead.notes(text="\n\n".join(summary)).save()
## Прочитаем записанное
list(lead.notes.objects.all())[-1]
Все, вы молодец! Вы получаете бонусный код.
Создайте новый имя-файл.py
Можно пообщаться напрямую с ChatGPT, как настоящий hackerman в командной строке.
import requests
import os
def read_token_from_file(filename):
with open(filename, 'r') as file:
return file.read().strip()
API_KEY = read_token_from_file('jay_api_key.txt') ## ключ
#или
#API_KEY = 'your token'
BASE_URL = 'https://app.jaycopilot.com/api/appsAdapter/'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': API_KEY
}
def handle_response(response):
if response.status_code == 200:
return response.json()
else:
return response.text
# проверим, есть ли уже диалог с приложением
conversations = requests.get(BASE_URL + "conversations", headers=headers)
conversations = handle_response(conversations)
gpt_conversations = list(filter(lambda conv: conv["app"]["template"] == "directLLM", conversations["conversations"]))
if len(gpt_conversations) > 0:
conversationId = gpt_conversations[0]["id"]
print("Найден существующий диалог. id: " + conversationId)
else:
# создадим диалог с приложением (запустим приложение)
body = {
"app": {
"template": "directLLM",
"params": {'model': 'gpt-3.5-turbo'}
}
}
response = requests.post(BASE_URL + "conversations/", headers=headers, json=body)
response = handle_response(response)
conversationId = response["id"]
print("Создан новый диалог. id: " + conversationId)
print("Напишите exit, чтобы выйти")
while True:
message = input("Клиент: ")
if message == "exit":
break
response = requests.post(BASE_URL + "conversations/" + conversationId + "/message", headers=headers, json={"text": message})
response = handle_response(response)
print("Бот: " + response["content"][0]["text"])
FAQ
Как ничего не работает? У меня на компьютере все работает :)
Но я могу поделиться jupyter notebook c этим кодом — пишите на почту: apetrov@just-ai.com или @palexe на Хабре.