Skip to content

Commit

Permalink
Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Данила Беляков authored and Данила Беляков committed Jul 30, 2024
1 parent 550642d commit 4f8e86d
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 4 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center">
<a href="https://github.com/danbeldev/alice-ktx/tree/master">
<img width="200px" height="200px" alt="alice-skill" src="/docs/kotlin_logo.png">
<img width="200px" height="200px" alt="alice-skill" src="/docs/resources/kotlin_logo.png">
</a>
</p>
<h1 align="center">
Expand Down Expand Up @@ -59,8 +59,9 @@ fun main() {
```

## Документация
- [Примеры](https://github.com/danbeldev/alice-ktx/tree/master/examples/src/main/kotlin/com/github/examples)
- [Туториал](docs/)
- [Документация](https://danbeldev.github.io/alice-ktx/)
- [Примеры](https://github.com/danbeldev/alice-ktx/tree/master/examples/src/main/kotlin/com/github/examples)


## Связь
Expand Down
Binary file added documentation/resources/create_dialogue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added documentation/resources/ngrok_runnable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/resources/storage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/resources/type_dialogue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/resources/webhook_url.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions documentation/Машина состояний.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## Машина состояний

> Finite-state machine (FSM) или finite-state automation (FSA), конечный автомат — это математическая модель вычислений.
>
> Это абстрактная машина, которая в любой момент времени может находиться ровно в одном из конечного числа состояний. Конечный автомат может переходить из одного состояния в другое в ответ на некоторые входные данные; переход из одного состояния в другое называется переходом.
>
> Конечный автомат определяется списком его состояний, его начальным состоянием и входными данными, которые запускают каждый переход.
>
> [Википедия](https://en.wikipedia.org/wiki/Finite-state_machine)
### Проблема

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

### Решение

Для начала определите возможные состояния в вашем навыке:
```kotlin
enum class InfoState {
SET_NAME,
SET_AGE,
SET_INFO
}
```

Начальное Состояние
Когда начинается новая сессия, установите начальное состояние:
```kotlin
message({ message.session.new }) {
state.setState(InfoState.SET_NAME.name)
response {
text = "Добро пожаловать в навык, как вас зовут?"
}
}
```
- `Условие`: Обрабатывается только при начале новой сессии.
- `Действие`: Устанавливает состояние в `SET_NAME` и запрашивает у пользователя имя.

После получения имени от пользователя, сохраняем его и переходим к следующему состоянию:
```kotlin
message({ state == InfoState.SET_NAME.name }) {
val username = message.request.originalUtterance.toString()
state.updateData("name" to username)
state.setState(InfoState.SET_AGE.name)
response {
text = "Рад познакомиться $username, сколько вам лет?"
}
}
```
- `Условие`: Обрабатывается, если текущее состояние `SET_NAME`.
- `Действие`: Сохраняет имя в состоянии, устанавливает следующее состояние `SET_AGE`, и запрашивает возраст.

После получения возраста от пользователя, сохраняем его и переходим к последнему состоянию:
```kotlin
message({ state == InfoState.SET_AGE.name }) {
val age = message.request.originalUtterance.toString()
state.updateData("age" to age)
state.setState(InfoState.SET_INFO.name)
response {
text = "Супер, расскажите о себе"
}
}
```

- `Условие`: Обрабатывается, если текущее состояние `SET_AGE`.
- `Действие`: Сохраняет возраст в состоянии, устанавливает следующее состояние `SET_INFO`, и запрашивает дополнительную информацию.

На заключительном этапе, после получения дополнительной информации, формируем окончательный ответ и завершаем сессию:
```kotlin
message({state == InfoState.SET_INFO.name}) {
val info = message.request.originalUtterance.toString()
val data = state.getData()
state.clear()
response {
text = "Вот что мне удалось узнать\n\nИмя-${data["name"]}\nВозраст-${data["age"]}\nИнформация-$info"
endSession = true
}
}
```

- `Условие`: Обрабатывается, если текущее состояние `SET_INFO`.
- `Действие`: Формирует текст ответа на основе собранной информации, очищает состояние и завершает сессию.

> ⚠️ **Внимание**
> Чтобы использовать хранилище на стороне Алисы, включите его в настройках навыка.
>
> ![storage](resources/storage.png)
### Заключение
Использование машины состояний позволяет структурировать взаимодействие с пользователем и управлять процессом сбора информации через последовательные шаги. Это особенно полезно для сложных сценариев, требующих многократного взаимодействия и сохранения состояния между шагами.

### Примеры
- [Info.kt](../examples/src/main/kotlin/com/github/examples/Info.kt)
- [FsmForm.kt](../examples/src/main/kotlin/com/github/examples/FsmForm.kt)
43 changes: 43 additions & 0 deletions documentation/Мидлвари.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## Мидлвари

Мидлварь - это код, который активируется при каждом событии, полученном от API Алисы.

### Теория
> Мидлвари — это повторно используемое программное обеспечение, которое использует шаблоны и платформы для устранения разрыва между функциональными требованиями приложений и базовыми операционными системами, стеками сетевых протоколов и базами данных
#### Есть два типа Мидлвари

1. Внешняя (outer) область - вызывается перед обработкой фильтров
2. Внутренняя (inner) область - вызывается после обработки фильтров, но перед обработчиком

> **Важно**
> Мидлварь должен всегда возвращать `null` чтобы передать событие следующему мидлварю/хэндлеру.
> Если вы хотите завершить обработку события, вы должны вернуть `MessageResponse`
### Практика

```kotlin
fun main() {
skill {
id = "..."
webServer = ktorWebServer {
port = 8080
path = "/alice"
}
dispatch {
outerMiddleware {
if(message.session.user == null) {
return@outerMiddleware response {
text = "У вас нет аккаунта в Яндексе"
}
}
null
}
}
}.run()
}
```

В этом примере показано, как использовать внешнюю мидлварь, чтобы проверять, есть ли у пользователя аккаунт в Яндексе.
Если аккаунта нет, мидлварь возвращает ответ с сообщением "У вас нет аккаунта в Яндексе".
В противном случае, мидлварь возвращает null, позволяя передать событие следующему мидлварю или обработчику.
40 changes: 40 additions & 0 deletions documentation/Начало.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Начало

Для того чтобы интерактивно работать с примерами фреймворка, необходимо создать тестовый навык в Алисе.

### Создание навыка

1. Зарегистрируйтесь на Яндексе или войдите в свою учетную запись.
2. Перейдите в [панель разработчика](https://dialogs.yandex.ru/developer/).
3. Нажмите на кнопку "Создать диалог".
![create_dialogue](resources/create_dialogue.png)
4. Выберите "Навык в Алисе".
![type_dialogue](resources/type_dialogue.png)
5. Заполните все необходимые поля и в поле Backend выберите Webhook URL.
### Ngrok

Для первого запуска необходимо подключить вебхук к Диалогам. Это можно сделать с помощью ngrok — сервиса, который позволяет сделать локальный порт доступным из интернета без настройки NAT, роутера, DDNS и других протоколов. Программа создает туннель между вашим компьютером и удалённым сервером, предоставляя доступ через уникальный домен.

- [Установите](https://dashboard.ngrok.com/get-started/setup), разархивируйте и запустите ngrok.
- Добавьте ваш [auth-token](https://dashboard.ngrok.com/get-started/your-authtoken) в консоль ngrok.
- Запустите ngrok с командой:
```shell
ngrok http 8080
```
- Через несколько секунд появится длинная ссылка. Укажите её как URL вебхука и добавьте /alice в конце ссылки:

> ⚠️ **Внимание**
> Не выключайте ngrok, иначе туннель закроется.
- ![ngrok](resources/ngrok_runnable.png)
![webhook_url.png](resources/webhook_url.png)

6. Сохраните изменения внизу страницы.

### ID навыка
Скоро вам понадобится ID навыка. Его можно найти на вкладке "Общие сведения" в панели разработчика.

> ℹ️ **Примечание**
> Для запуска в прод вам нужен свой домен и [SSL сертификат](https://wiki.yaboard.com/s/zc), об этом подробнее в [официальной документации](https://yandex.ru/dev/dialogs/alice/doc/deploy-overview.html).
### [Первый навык](Первый_навык.md)
69 changes: 69 additions & 0 deletions documentation/Первый_навык.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Первый навык

Прежде чем приступить к разработке навыка для Алисы, важно ознакомиться с [официальной документацией](https://yandex.ru/dev/dialogs/alice/doc/) и понять принцип работы навыков.

### Первые шаги
Этот кодовый фрагмент иллюстрирует создание и запуск навыка с использованием библиотеки для работы с сервером на базе [Ktor](https://ktor.io).
Навык обрабатывает сообщения и отвечает на них в зависимости от состояния сессии.

```kotlin
fun main() {
val skill = skill {
id = ""
webServer = ktorWebServer {
port = 8080
path = "/alice"
}

dispatch {
message({ message.session.new }) {
response {
text = "Привет!"
}
}

message {
response {
text = message.request.originalUtterance.toString()
}
}
}
}
}
```

- `id` - Уникальный идентификатор скилла. Оставьте пустым, если не требуется уникальный идентификатор.
- `webServer` - Конфигурация веб-сервера с использованием Ktor.
- `port` - Порт, на котором будет запущен сервер. В данном случае используется порт 8080.
- `path` - Путь, по которому сервер будет доступен. В данном случае это /alice.

#### Диспетчеризация сообщений
```kotlin
message({ message.session.new }) {
response {
text = "Привет!"
}
}
```
Этот блок кода обрабатывает новые сессии. Если сессия новая `message.session.new`, то в ответ отправляется текст "Привет!".

#### Обработка всех остальных сообщений
```kotlin
message {
response {
text = message.request.originalUtterance.toString()
}
}
```
Этот блок кода обрабатывает все остальные сообщения. В ответ отправляется оригинальный текст запроса пользователя.

#### Запускает сконфигурированный скилл.

```kotlin
skill.run()
```

### [Мидлвари](Мидлвари.md)

### Примеры
- [Echo.kt](../examples/src/main/kotlin/com/github/examples/Echo.kt)
2 changes: 1 addition & 1 deletion examples/src/main/kotlin/com/github/examples/Echo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.github.alice.ktx.skill

fun main() {
skill {
id = "2e3e39c3-9fea-4d55-a754-9fa54b0d5502"
id = "..."
webServer = ktorWebServer {
port = 8080
path = "/alice"
Expand Down
2 changes: 1 addition & 1 deletion examples/src/main/kotlin/com/github/examples/FsmForm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum class FormState {

fun main() {
skill {
id = "2e3e39c3-9fea-4d55-a754-9fa54b0d5502"
id = "..."
webServer = ktorWebServer {
port = 8080
path = "/alice"
Expand Down
57 changes: 57 additions & 0 deletions examples/src/main/kotlin/com/github/examples/Info.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.github.examples

import com.github.alice.ktx.dispatch
import com.github.alice.ktx.handlers.message
import com.github.alice.ktx.models.response.response
import com.github.alice.ktx.server.impl.ktorWebServer
import com.github.alice.ktx.skill

private enum class InfoState {
SET_NAME,
SET_AGE,
SET_INFO
}

fun main() {

skill {
id = "..."
webServer = ktorWebServer {
port = 8080
path = "/alice"
}
dispatch {
message({ message.session.new }) {
state.setState(InfoState.SET_NAME.name)
response {
text = "Добро пожаловать в навык, как вас зовут?"
}
}
message({ state == InfoState.SET_NAME.name }) {
val username = message.request.originalUtterance.toString()
state.updateData("name" to username)
state.setState(InfoState.SET_AGE.name)
response {
text = "Рад познакомиться $username, сколько вам лет?"
}
}
message({ state == InfoState.SET_AGE.name }) {
val age = message.request.originalUtterance.toString()
state.updateData("age" to age)
state.setState(InfoState.SET_INFO.name)
response {
text = "Супер, расскажите о себе"
}
}
message({state == InfoState.SET_INFO.name}) {
val info = message.request.originalUtterance.toString()
val data = state.getData()
state.clear()
response {
text = "Вот что мне удалось узнать\n\nИмя-${data["name"]}\nВозраст-${data["age"]}\nИнформация-$info"
endSession = true
}
}
}
}.run()
}

0 comments on commit 4f8e86d

Please sign in to comment.