Фото сгенерировано в OpenAI по запросу: "сгенери фото для статьи "ChatGPT управляет вашим календарем Google через персональный бот в Telegram"
Фото сгенерировано в OpenAI по запросу: "сгенери фото для статьи "ChatGPT управляет вашим календарем Google через персональный бот в Telegram"

Куда только уже этот ChatGPT не прикрутили с момента его появления. Возможно я изобрету велосипед, но мне показалось удобным сделать бота в Телеграм, который бы поддерживал голосовой ввод и управлял моим календарем в Google.

Итак, что мы будем делать?

  1. Создадим нейро-сотрудника на моей платформе

  2. Зарегистрируем Телеграм-бот и подключим его к нашему сотруднику

  3. Напишем API для интеграция с Google календарем

  4. Дадим функцию нашему сотруднику для работы с API

Шаг №1: Создаем нейро-сотрудника

На скриншоте начало квиза по созданию нейро-сотрудника
На скриншоте начало квиза по созданию нейро-сотрудника

Дабы не нарушать правила хабра я не буду писать название платформы по созданию нейро-сотрудников и дам только сам системный промпт на базе которого будет работать наш бот:

Цель:
Помогать пользователю вести его Google календарь.
Роль:йр
Ты - женщина.
Тебя зовут - Жанна
Ты работаешь в должности - Личный секретарь
Твоя задача помогать управлять календарем Google используя функцию "google_calendar".
Поведение:

Для твоей работы необходим ID календаря, который ты будешь передавать в функцию "google_calendar". ID календаря может быть предоставлен и в виде Email адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.
При показе запланированных событий после названия события указывай его "event_id".
Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
Не используй в ответах никакие эмоджи, кроме: "✅" и "????️".

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

Шаг №2: Регистрируем Telegram-bot и прикручиваем его к нашему нейро-сотруднику

Тут все очень просто:

  1. Зайдите в вашем Телеграме в бот @BotFather

  2. Выберите в меню бота /newbot и следуйте указаниям

  3. После регистрации бота скопируйте токен бота, он нужен для интеграции с нашим нейро-сотрудником

На скриншоте блок по настройке бота в Телеграм для работы нашего нейро-сотрудника
На скриншоте блок по настройке бота в Телеграм для работы нашего нейро-сотрудника
Так же вы можете сделать бота условно бесплатным и ограничить количество бесплатных токенов
Так же вы можете сделать бота условно бесплатным и ограничить количество бесплатных токенов

Шаг №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 адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.

При старте общения с нашим ботом мы видим следующий диалог:

На скрине начало диалога с нейро-сотрудником, управляющим Google календарем
На скрине начало диалога с нейро-сотрудником, управляющим Google календарем

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

После получения ID Google календаря наш нейро-сотрудник готов им управлять
После получения ID Google календаря наш нейро-сотрудник готов им управлять

Давайте для примера запланируем отпуск в Рио в нашем боте. Для начала спросим:

Просим нейро-сотрудника проверить события за заданный период
Просим нейро-сотрудника проверить события за заданный период

Поскольку у нас ничего пока нет в календаре, то попросим спланировать посещение достопримечательностей в Рио на этот период:

Просим нашего бота с помощью ChatGPT придумать интересные для посещения места в Рио и сформировать наш план посещения их во время отпуска
Просим нашего бота с помощью ChatGPT придумать интересные для посещения места в Рио и сформировать наш план посещения их во время отпуска

Посмотрим теперь в наш календарь Google:

Проверяем как события опубликовались в Google календаре
Проверяем как события опубликовались в Google календаре

Отлично! Получилось! Давайте немного посложнее попросим бота:

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

Как мы видим бот спросил подтверждение при удалении события из календаря, это было в его инструкции:

Поведение:
...
3. Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
...

Проверяем календарь и видим, что событие было удалено:

Видно, что нужное событие было удалено
Видно, что нужное событие было удалено

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

Да, и самое важное - боту можно отправлять голосовые и он так же будет помогать вам вести Google календарь.

Наш получившийся бот/сотрудник понимает команды голосом и даже озвучивает свой ответ
Наш получившийся бот/сотрудник понимает команды голосом и даже озвучивает свой ответ

Итоги

В целом мозгов у ChatGPT 4 вполне хватает на управление календарем.

Что можно еще попробовать?

  1. Разбиение сложных задач на подзадачи

  2. Помощь с распределением задач

  3. Ежедневное уведомление о важных делах на сегодня/неделю/месяц

  4. Просить бота фокусировать внимание пользователя на важных событиях и говорить об этом при постановке задач

  5. Дать боту Яндекс.Календарь и предлагать в начале выбрать с каким календарем работать

Сам бот можно протестировать тут.

Идеи и вопросы пишите мне в телеграм.

Комментарии (14)


  1. TAU15 Автор
    10.01.2024 07:08

    Сейчас тестируем цепочки задач в которых несколько нейро-сотрудников выполняют сложную задачу, вот пример цепочки:

    Сначала просим первого нейро-сотрудника создать нам график функции x = y^2
    Сначала просим первого нейро-сотрудника создать нам график функции x = y^2
    Полученный график в ответе первого нейро-сотрудника мы просим добавить нашего бота интегрированного с Google календарем
    Полученный график в ответе первого нейро-сотрудника мы просим добавить нашего бота интегрированного с Google календарем
    В результате мы видим выполненную задачу в виде события в Google календаре
    В результате мы видим выполненную задачу в виде события в Google календаре
    Результат работы цепочки задач
    Результат работы цепочки задач


  1. no1D
    10.01.2024 07:08
    +4

    Видится подобное self-hosted решение на базе Langchain + Mistral/Saiga + Gorilla для генерации произвольных API запросов. Всё таки GPT4 это дороговато и местами заблокировано.


    1. TAU15 Автор
      10.01.2024 07:08

      Боезно как то давать составлять запросы самому ИИ. А вдруг удалит что-то лишнее?


  1. rutexd
    10.01.2024 07:08
    +1

    Идея интересная. Возможно и не новая, но все же. И интересно, что оно работает.

    Для личного использования вполне удобно и просто. А вот для публичного... Пару промтов и можно легко сломать выйдя за границы.


    1. TAU15 Автор
      10.01.2024 07:08

      Бот же общается только с вами один на один, зачем его ломать?


      1. rutexd
        10.01.2024 07:08
        +1

        Любителей халявы и бесплатному доступа к чатгпт можно всегда быстро найти.)


  1. obvia
    10.01.2024 07:08
    +1

    Бот очень удобный. Удивился, когда задал ему запрос с Markdown разметкой, правда, он очень долго заносил, но справился. Жалко, что 50000 тысяч токинов как-то быстро исчерпали себя.


    1. TAU15 Автор
      10.01.2024 07:08

      Покажите скрин результата если можно )


  1. TAU15 Автор
    10.01.2024 07:08

    Сейчас тестируем цепочки задач в которых несколько нейро-сотрудников выполняют сложную задачу, вот пример цепочки:

    Сначала просим первого нейро-сотрудника создать нам график функции x = y^2
    Сначала просим первого нейро-сотрудника создать нам график функции x = y^2
    Полученный график в ответе первого нейро-сотрудника мы просим добавить нашего бота интегрированного с Google календарем
    Полученный график в ответе первого нейро-сотрудника мы просим добавить нашего бота интегрированного с Google календарем
    В результате мы видим выполненную задачу в виде события в Google календаре
    В результате мы видим выполненную задачу в виде события в Google календаре
    Результат работы цепочки задач
    Результат работы цепочки задач


  1. Alesh
    10.01.2024 07:08
    +1

    Как справляетесь с глюционированием?

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

    Но для коммерческого применения, где с этим будет взаимодействовать неподготовлеый человек? Мне кажется пока до этого далеко. Во всяком случае с тем что доступно сейчас для массового пользователя


    1. TAU15 Автор
      10.01.2024 07:08

      По нашему опыту у ChatGPT 4 при температуре 0 и добавке в промпте инструкции "Если ты сомневаешься в своем ответе, то ответь фразой: у меня нет достоверной информации по этому вопросу" хватает чтобы минимизировать риск галюционирования.

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


  1. titulusdesiderio
    10.01.2024 07:08
    +1

    Не совсем понятно как бот использует код. У него компилятор есть или как?


    1. TAU15 Автор
      10.01.2024 07:08

      Да, код выполняет в компиляторе питона на нашем сервере. А вот активация функции реализуется самим ChatGPT.


  1. 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,
        },
    ]

    Лог диалога по телефонной линии
    Лог диалога по телефонной линии
    Результат работы цепочки задач
    Результат работы цепочки задач


  1. dimonet
    10.01.2024 07:08

    По-моему это менее удобно чем OK Google Хотя возможно в перспективе и будет иметь больше функций но Open ai уже интегрируется как помощник в Android девайсы