Куда только уже этот ChatGPT не прикрутили с момента его появления. Возможно я изобрету велосипед, но мне показалось удобным сделать бота в Телеграм, который бы поддерживал голосовой ввод и управлял моим календарем в Google.
Итак, что мы будем делать?
Создадим нейро-сотрудника на моей платформе
Зарегистрируем Телеграм-бот и подключим его к нашему сотруднику
Напишем API для интеграция с Google календарем
Дадим функцию нашему сотруднику для работы с API
Шаг №1: Создаем нейро-сотрудника
Дабы не нарушать правила хабра я не буду писать название платформы по созданию нейро-сотрудников и дам только сам системный промпт на базе которого будет работать наш бот:
Цель:
Помогать пользователю вести его Google календарь.
Роль:йр
Ты - женщина.
Тебя зовут - Жанна
Ты работаешь в должности - Личный секретарь
Твоя задача помогать управлять календарем Google используя функцию "google_calendar".
Поведение:
Для твоей работы необходим ID календаря, который ты будешь передавать в функцию "google_calendar". ID календаря может быть предоставлен и в виде Email адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.
При показе запланированных событий после названия события указывай его "event_id".
Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
Не используй в ответах никакие эмоджи, кроме: "✅" и "????️".
Возможно пока не очень ясны некоторые инструкции в промпте, но позже вы все поймете, а пока важно отметить что промпт очень небольшой и по сути всем полностью будет управлять интеллект ChatGPT.
Шаг №2: Регистрируем Telegram-bot и прикручиваем его к нашему нейро-сотруднику
Тут все очень просто:
Зайдите в вашем Телеграме в бот @BotFather
Выберите в меню бота /newbot и следуйте указаниям
После регистрации бота скопируйте токен бота, он нужен для интеграции с нашим нейро-сотрудником
Шаг №3: Напишем API для интеграция с Google календарем
Для получения необходимого файла с ключами 'credentials.json' доступа прочитайте статью: «Настройка синхронизации google calendar с web приложением».
Вот класс, описывающий интеграцию с Google Calendar API:
from __future__ import print_function
import datetime
import googleapiclient
from google.oauth2 import service_account
from googleapiclient.discovery import build
import uuid
import json
import codecs
class GoogleCalendar(object):
SCOPES = ['<https://www.googleapis.com/auth/calendar>']
CREDS_FILE = 'credentials.json'
def __init__(self, calendarId):
credentials = service_account.Credentials.from_service_account_file(CREDS_FILE, scopes=GoogleCalendar.SCOPES)
self.service = googleapiclient.discovery.build('calendar', 'v3', credentials=credentials)
self.calendarId = calendarId
def create_event(self, event):
e = self.service.events().insert(calendarId=self.calendarId,
body=event).execute()
print('Event created: %s' % (e.get('id')))
return e
def get_events_list(self, start_date=None, end_date=None):
now = datetime.datetime.utcnow().isoformat() + 'Z'
if start_date is None and end_date is None:
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=now,
maxResults=100, singleEvents=True,
orderBy='startTime').execute()
else:
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=start_date,
timeMax=end_date,
singleEvents=True).execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
return json.dumps('No upcoming events found.')
events_list = self.encode_events_list(events)
return events_list
def encode_events_list(self, events):
events_list = []
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('endTime', event['end'].get('date'))
events_list.append({
'event_id' : event['id'],
'event_summary' : event['summary'],
'event_start' : start,
'event_end' : end,
})
return events_list
def update_event(self, event_id, updated_event):
current_event = self.service.events().get(calendarId=self.calendarId,
eventId=event_id).execute()
updated_title = updated_event.get('summary', current_event['summary'])
updated_start = updated_event.get('start', current_event['start'])
updated_end = updated_event.get('end', current_event['end'])
updated_event = {
'summary': updated_title,
'start': updated_start,
'end': updated_end
}
event = self.service.events().update(calendarId=self.calendarId,
eventId=event_id,
body=updated_event).execute()
print('Event updated: %s' % event.get('id'))
return json.dumps('Event updated.')
def delete_event(self, event_id):
res = self.service.events().delete(calendarId=self.calendarId,
eventId=event_id).execute()
print('Event deleted: %s' % event_id)
return json.dumps('Event deleted.')
def delete_events_in_range(self, start_date, end_date):
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=start_date,
timeMax=end_date,
singleEvents=True).execute()
events = events_result.get('items', [])
for event in events:
self.delete_event(event['id'])
events_list = self.encode_events_list(events)
return json.dumps('Events deleted.')
А вот сам код API на базе Flask:
from flask import Flask, Response, render_template, redirect, url_for, request, session, jsonify
from flask_cors import CORS
from flask import send_from_directory
import google.calendar # !!! это наш класс, описанный в блоке выше
app = Flask(__name__)
app.static_folder = 'static'
CORS(app)
def GetJsonFromRequest(request):
data = request.get_json(force=True)
if type(data) is not dict:
data = request.get_json()
data = json.loads(data)
return data
@app.route('/api/v1.0/google_calendar', methods=['POST'])
def google_calendar():
data = GetJsonFromRequest(request)
try:
calendar = google.calendar.GoogleCalendar(data['calendarId'])
if data['action'] == '+':
res = calendar.create_event(data['event'])
if data['action'] == '?':
res = calendar.get_events_list(data['start_date'], data['end_date'])
if data['action'] == '-':
if data['start_date'] is None:
res = calendar.delete_event(data['event_id'])
else:
res = calendar.delete_events_in_range(data['start_date'], data['end_date'])
if data['action'] == '.':
res = calendar.update_event(data['event_id'], data['event'])
print('[END - google_calendar res]', res)
return res
except Exception as e:
print('[END - google_calendar ERROR]', str(e))
return 'Error'
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=5000)
Шаг №4: Напишем функцию для связи ChatGPT и нашего Google Calendar API
Для вызова функций в ChatGPT нам нужно дать описание всех переменных функции и сам код функции.
Вот описание параметров нашей функции:
{
"name": "google_calendar",
"description": "Управление календарем Google",
"parameters": {
"type": "object",
"properties": {
"calendarId": {
"type": "string",
"description": "ID календаря",
},
"action": {
"type": "string",
"enum": ["+", "?", '-', '.'],
"description": "Тип действия. `+` - означает добавление события. `?` - означает получение всех событий календаря.. `-` - удалить событие в календаре. `.` - редактировать событие в календаре.",
},
"event_ids": {
"type": "string",
"description": "Список ID событый в календаре в формате json: ```[{\\"event_id\\": \\"3lsvlor3hjlgpgmv7er7e5juov\\"}]```. Передается при типах действия `-` или '.'",
},
"events": {
"type": "string",
"description": "Список событий в формате json: ```[{\\"summary\\": \\"test event\\", \\"description\\": \\"some info\\", \\"start\\": {\\"dateTime\\": \\"2024-09-04T03:00:00+03:00\\"}, \\"end\\": {\\"dateTime\\": \\"2024-09-04T05:30:00+03:00\\"}}]```. Передается при типах действия `+` или '.'",
},
"start_date": {
"type": "string",
"description": "Дата начала периода в формате `2024-09-04T03:00:00+03:00`. Передается при типах действия `?` или '-'",
},
"end_date": {
"type": "string",
"description": "Дата окончания периода в формате `2024-09-04T03:00:00+03:00`. Передается при типах действия `?` или '-'",
},
},
"required": ["calendarId", "action"],
},
},
А вот сам код функции:
def google_calendar(arguments):
import requests
import json
data = {
"sa_data" : None,
"calendarId" : arguments['calendarId'],
"action" : arguments['action'],
'start_date' : None,
'end_date' : None,
}
if 'start_date' in arguments.keys():
data['start_date'] = arguments['start_date']
if 'end_date' in arguments.keys():
data['end_date'] = arguments['end_date']
if arguments['action'] == '?':
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
return response.text
else:
responses = []
if arguments['action'] == '+' or arguments['action'] == '.':
if 'events' in arguments.keys():
events = json.loads(arguments['events'])
for i in range(len(events)):
e = events[i]
data['event'] = e
if 'event_ids' in arguments.keys():
event_ids = json.loads(arguments['event_ids'])
data['event_id'] = event_ids[i]
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
if arguments['action'] == '-':
if 'event_ids' in arguments.keys():
event_ids = json.loads(arguments['event_ids'])
for i in range(len(event_ids)):
data['event_id'] = event_ids[i]['event_id']
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
if 'start_date' in arguments.keys():
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
return json.dumps(str(responses))
Тестирование бота
Поскольку в промпте у нашего нейро-сотрудника есть такая инструкция:
Поведение:
1. Для твоей работы необходим ID календаря, который ты будешь передавать в функцию "google_calendar". ID календаря может быть предоставлен и в виде Email адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.
При старте общения с нашим ботом мы видим следующий диалог:
Если мы укажем наш ID календаря, то бот ответит примерно так:
Давайте для примера запланируем отпуск в Рио в нашем боте. Для начала спросим:
Поскольку у нас ничего пока нет в календаре, то попросим спланировать посещение достопримечательностей в Рио на этот период:
Посмотрим теперь в наш календарь Google:
Отлично! Получилось! Давайте немного посложнее попросим бота:
Как мы видим бот спросил подтверждение при удалении события из календаря, это было в его инструкции:
Поведение:
...
3. Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
...
Проверяем календарь и видим, что событие было удалено:
Думаю идею вы уже уловили. Бота можно просить корректировать название событий, переносить события на нужную дату и время и т.д.
Да, и самое важное - боту можно отправлять голосовые и он так же будет помогать вам вести Google календарь.
Итоги
В целом мозгов у ChatGPT 4 вполне хватает на управление календарем.
Что можно еще попробовать?
Разбиение сложных задач на подзадачи
Помощь с распределением задач
Ежедневное уведомление о важных делах на сегодня/неделю/месяц
Просить бота фокусировать внимание пользователя на важных событиях и говорить об этом при постановке задач
Дать боту Яндекс.Календарь и предлагать в начале выбрать с каким календарем работать
Сам бот можно протестировать тут.
Идеи и вопросы пишите мне в телеграм.
Комментарии (14)
no1D
10.01.2024 07:08+4Видится подобное self-hosted решение на базе Langchain + Mistral/Saiga + Gorilla для генерации произвольных API запросов. Всё таки GPT4 это дороговато и местами заблокировано.
TAU15 Автор
10.01.2024 07:08Боезно как то давать составлять запросы самому ИИ. А вдруг удалит что-то лишнее?
rutexd
10.01.2024 07:08+1Идея интересная. Возможно и не новая, но все же. И интересно, что оно работает.
Для личного использования вполне удобно и просто. А вот для публичного... Пару промтов и можно легко сломать выйдя за границы.
TAU15 Автор
10.01.2024 07:08Сейчас тестируем цепочки задач в которых несколько нейро-сотрудников выполняют сложную задачу, вот пример цепочки:
Alesh
10.01.2024 07:08+1Как справляетесь с глюционированием?
Не знаю, я не увидел коммерческого применения этим технологиям из-за глюцинирование. Получить они раз почти идеальный результат, а во второй раз нечто бессвязное и непонятное без возможности повлиять или как-то предсказать поведение, терпимо для доклада и статьи.
Но для коммерческого применения, где с этим будет взаимодействовать неподготовлеый человек? Мне кажется пока до этого далеко. Во всяком случае с тем что доступно сейчас для массового пользователя
TAU15 Автор
10.01.2024 07:08По нашему опыту у ChatGPT 4 при температуре 0 и добавке в промпте инструкции "Если ты сомневаешься в своем ответе, то ответь фразой: у меня нет достоверной информации по этому вопросу" хватает чтобы минимизировать риск галюционирования.
В данном же кейсе при работе с календарем я думаю проблема галюционирования не так важна. Кроме того в инструкции бота, управляющего календарем можно попросить перед выполнением действия показать что он собирается сделать и после подтверждения пользователем бот выполнить изменение календаря.
titulusdesiderio
10.01.2024 07:08+1Не совсем понятно как бот использует код. У него компилятор есть или как?
TAU15 Автор
10.01.2024 07:08Да, код выполняет в компиляторе питона на нашем сервере. А вот активация функции реализуется самим ChatGPT.
TAU15 Автор
10.01.2024 07:08+1Мы сейчас еще тестируем цепочки задач, вот прототип в котором задача ставиться по звонку на реальный телефон человеку:
А вот так задается цепочка задач
chain = [ { 'employee_id' : None, 'role' : 'Ты - ChatGPT', 'task' : 'Запроси какой график функции нужно посторить и сразу заверши диалог фразой: Хорошо, я тебя понял хозяин.', 'model' : 'gpt-4-1106-preview', 'temperature' : 0.1, 'limit' : 5, 'external_dialog' : { 'client_dict' : {'name' : 'User', 'phone': '+79122710308'}, 'hello_text' : 'Привет, хозяин! Какую функцию занести в календарь?', 'channel' : 'Voximplant', 'voice' : 'Morpheus_RU' }, }, { 'employee_id' : 1202, 'role' : 'Ты - ChatGPT', 'task' : 'Нарисуй мне график функции используя Python: ##task_result##', 'model' : 'gpt-4-1106-preview', 'temperature' : 0.1, 'limit' : 5, 'external_dialog' : None, }, { 'employee_id' : 1539, 'role' : 'Ты - ChatGPT.', 'task' : f""" Теперь ты говоришь с ботом, который умеет заносить события в Google календарь. Твоя задача попросить бота создать событие с названием "Проанализировать график функции". В описание задачи попроси занести текст: "##task_result##". Задачу нужно поставить на завтра на 10 утра. Используй данный ID календаря в Google: "tiunov.lotus1@gmail.com". """, 'model' : 'gpt-4-1106-preview', 'temperature' : 0.1, 'limit' :5, 'external_dialog' : None, }, ]
dimonet
10.01.2024 07:08По-моему это менее удобно чем OK Google Хотя возможно в перспективе и будет иметь больше функций но Open ai уже интегрируется как помощник в Android девайсы
TAU15 Автор
Сейчас тестируем цепочки задач в которых несколько нейро-сотрудников выполняют сложную задачу, вот пример цепочки: