File size: 20,514 Bytes
791c906
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5d3bc65
26ee4b3
5d3bc65
 
0a9953f
5d3bc65
f9fb344
5d3bc65
f6540c6
 
 
791c906
 
 
 
 
 
 
b3cfb25
 
a978f09
b3cfb25
791c906
26ee4b3
 
791c906
 
 
 
 
 
 
5d3bc65
 
 
 
791c906
b3cfb25
791c906
 
 
 
 
 
 
 
 
 
 
a978f09
 
 
 
 
 
aba65d5
a978f09
 
aba65d5
3e178d6
 
 
 
 
 
 
a978f09
aba65d5
 
a978f09
 
 
 
 
 
 
 
 
 
 
 
 
791c906
 
 
 
b3cfb25
791c906
5d3bc65
 
 
 
aba65d5
f9fb344
a978f09
 
 
791c906
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37ca7e3
26ee4b3
 
791c906
26ee4b3
791c906
26ee4b3
 
 
791c906
 
 
b3cfb25
 
a978f09
b3cfb25
 
 
 
 
a978f09
b3cfb25
 
 
 
 
 
 
 
 
 
 
 
791c906
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
---
title: ExecChat
emoji: 📊
colorFrom: red
colorTo: yellow
sdk: docker
pinned: false
---

# ExecChat — генератор ответов поддержки домовят

Веб-приложение для помощи операторам поддержки сервиса уборки «Домовёнок»: по вопросу исполнителя (домовёнка) строится ответ на основе базы знаний и при необходимости вызываются инструменты (доходы, качество, штрафы).

---

## Как устроена система

1. **База знаний** — текст подгружается из Google Doc, режется на чанки (`CHUNK_SIZE=800`, перекрытие `CHUNK_OVERLAP=200`, `RecursiveCharacterTextSplitter`) и индексируется в FAISS (эмбеддинги OpenAI). При первом запуске, изменении документа или версии индекса (`INDEX_VERSION`) индекс пересобирается; хэш документа хранится в `doc_hash.pkl`.
2. **System prompt** — загружается из отдельного Google Doc (редактирование только там; версии — через историю документа). На странице — read-only просмотр и кнопка «Обновить промпт из документа». При `ENABLE_TOOLS=1` к промпту из документа дописывается блок про инструменты из кода.
3. **Классификатор social-intent** (`gpt-4.1-nano`) — чистые вежливые реплики (спасибо/привет/прощание/комплимент) получают короткий вежливый ответ и **не** уходят в эскалацию и RAG.
4. **Переписывание запроса** (`QUERY_REWRITE_MODEL`, по умолчанию `gpt-4.1-mini`) — шумное сообщение исполнителя превращается в чистый короткий поисковый запрос (опечатки, местоимения, учёт последних реплик). Качество rerank сильно зависит от этого шага.
5. **Retrieval + rerank** — FAISS отдаёт `RETRIEVAL_CANDIDATES=30` кандидатов, затем cross-encoder **`BAAI/bge-reranker-v2-m3`** переранжирует их; в контекст идут чанки из топ-`RERANK_TOP_N=8` с rerank-скором ≥ `RERANK_SCORE_THRESHOLD=0.1`. Чанки обрезаются до `MAX_CHUNK_CHARS=800`. Если порог не прошёл ни один чанк — бот **не эскалирует**, а вежливо отвечает, что вопрос вне темы сервиса (без вызова менеджера); эскалация остаётся только при сбое генерации или при явном провале guardrails (`ENABLE_OUTPUT_GUARDRAILS=1`).
6. **Ответ генерирует OpenAI GPT-4.1 mini** (`LLM_MODEL`, по умолчанию `gpt-4.1-mini`): system prompt + контекст из RAG + история диалога (последние 3 пары) + текущий вопрос. `max_tokens=512`. **Temperature** настраивается на странице (кнопка «Применить temperature»).
7. **Guardrails на выходе** — детерминированный `enforce_format` (≤2 абзацев, срез «приглашений задавать вопросы»). Проверка **grounding** (цельный ответ vs контекст, `gpt-4.1-nano`) и корректирующая регенерация **выключены по умолчанию** (`ENABLE_OUTPUT_GUARDRAILS=0`): давали ложные NOT_GROUNDED на корректных перефразах. При повторении галлюцинаций имеет смысл вернуть проверку в виде **claim-level** (по отдельным утверждениям), а не одного вердикта на весь ответ — тогда можно включить `ENABLE_OUTPUT_GUARDRAILS=1`. Модерация токсичности есть в коде, но по умолчанию выключена (`ENABLE_OUTPUT_MODERATION=False`).
8. **Инструменты (tools)** — при `ENABLE_TOOLS=1` модель может вызывать функции из `tools.py`. В `app_2.py` реализован цикл: запрос к модели → при наличии `tool_calls` выполняются инструменты → результаты передаются обратно в модель → ответ без вызовов возвращается пользователю.
9. **Многоходовый диалог** — в контекст ответа передаётся **весь диалог**, который был до сих пор (полная история сессии). Переписывание запроса при этом по-прежнему смотрит только на последние `MAX_HISTORY_TURNS=3` пары.

   > **Пометка для prod (coconut):** сейчас история берётся из `st.session_state` (одна Streamlit-сессия = один диалог). При реализации в **coconut** контекст нужно передавать **в рамках `dialog_id`** — собирать все сообщения конкретного диалога по его идентификатору, а не полагаться на сессию. Для длинных историй стоит делать суммаризацию старых сообщений, чтобы не упираться в контекст/стоимость.

---

## Структура проекта

| Файл / папка | Назначение |
|--------------|------------|
| **`app_2.py`** | Точка входа: Streamlit-интерфейс, загрузка Google Doc, построение/загрузка FAISS, GPT-4.1 mini с инструментами, RAG, цикл вызова инструментов и формирование ответа. |
| **`tools.py`** | Определения инструментов для LLM (декоратор `@tool` из LangChain). Сюда добавлять новые инструменты и подключать реальные API/БД. |
| **`evals/`** | Встроенный харнесс проверок: прогон сценариев против **живого** промпта и базы знаний (`runtime_live.py`), детерминированные проверки (`checks.py`), LLM-судья (`judge.py`), чтение/запись сценариев в Google Sheet (`sheets.py`), стартовый набор (`scenarios_seed.yaml`). |
| **`requirements.txt`** | Зависимости Python (Streamlit, LangChain, OpenAI, FAISS, Google API и т.д.). |
| **`faiss_index/`** | Локальный индекс FAISS (создаётся при первом запуске). |
| **`doc_hash.pkl`** | Хэш документа базы знаний |
| **`prompt_hash.pkl`** | Хэш документа с system prompt |

---

## Инструменты (tools)

Все инструменты объявлены в **`tools.py`** и возвращаются функцией **`get_all_tools()`**.

- **`get_exec_next_orders`** — детали ближайших заказов исполнителя (`executive_id`).
- **`get_exec_quality_params`** — параметры качества работы исполнителя (`executive_id`, опционально `period`, по умолчанию 14 дней, макс. 30).
- **`get_exec_revenue_and_fines_feed`** — лента доходов и штрафов исполнителя (`executive_id`, опционально `period`, по умолчанию 7 дней, макс. 10).
- **`get_exec_orders_history`** — история выполненных заказов исполнителя (`executive_id`, опционально `period`, по умолчанию 7 дней, макс. 10).

Сейчас внутри — заглушки с текстом «Подключите ваш API». Чтобы подключить реальные данные: в каждой функции замените тело на вызов вашего API или БД и возвращайте строку с результатом (она попадёт в контекст модели).

**Как добавить новый инструмент:**

1. В `tools.py` описать функцию с типами аргументов и **docstring** (по нему модель понимает, когда инструмент вызывать).
2. Обернуть функцию в `@tool` из `langchain_core.tools`.
3. Добавить её в список возврата функции `get_all_tools()`.

Изменения в `app_2.py` не требуются — список инструментов подхватывается при инициализации.

---

## Проверка сценариев (evals)

На странице есть скрытая admin-панель **«🧪 Проверка сценариев»**, которая прогоняет наборы тест-сценариев против **текущего** (живого) промпта и базы знаний — удобно после правок промпта/документа убедиться, что ничего не сломалось. Сценарии и их ожидания (`expect`) хранятся в Google-таблице, поэтому их можно редактировать прямо из UI, не трогая код.

**Что умеет панель:**

- **Прогон** — выбрать сценарии, запустить, увидеть таблицу PASS/FAIL с детализацией каждой проверки и ответом бота. При `ENABLE_TOOLS=1` тул-сценарии используют mock-инструменты с реальными именами (реальные API не вызываются).
- **Редактор** — добавлять/редактировать/удалять сценарии и сохранять их в Google Sheet. Если таблица пустая — кнопка «Засеять стандартным набором» из `evals/scenarios_seed.yaml`.

Сценарий идентифицируется по полю **`id`** (короткое уникальное имя латиницей).

**Проверки (`expect`):** `escalate`, `format_ok`, `no_prompt_leak`, `tools_any`, `tools_none` (детерминированные) и `judge` — критерий на естественном языке, который оценивает LLM-судья (`JUDGE_MODEL`, по умолчанию `gpt-4o-mini`). Сценарий проходит, если прошли все заполненные проверки. `format_ok` и `no_prompt_leak` включены принудительно во всех сценариях, поэтому в UI их нет.

**Инструменты — два независимых понятия (для агентов с tools):**

- **Доступ** (`use_tools`, вход) — даём ли боту инструменты в этом сценарии: `Да` / `Нет` / `Авто`. `Да` даёт доступ **независимо** от глобальной галочки прогона; `Авто` берёт значение из настроек прогона («По умолчанию давать инструменты»); `Нет` — никогда. Жёсткое условие одно: сам агент должен поддерживать инструменты (для RAG-only агентов раздел скрыт).
- **Проверка** (`tools_any` / `tools_none`, ассерт) — что бот сделал по факту: должен ли был вызвать инструмент (`tools_any` — достаточно любого из перечисленных) или не должен был (`tools_none`). Чтобы проверка `tools_any` могла пройти, в сценарии должен быть разрешён доступ.

> **Инструменты в харнессе завязаны на `ENABLE_TOOLS`** (тот же флаг, что и для бота). Пока `ENABLE_TOOLS=0` (значение по умолчанию на проде), инструментов «нет» и для бота, и для проверок: тул-виджеты скрыты, mock-инструменты не подставляются, бот в евалах ведёт себя как tool-less. Когда инструменты будут реализованы — поставьте `ENABLE_TOOLS=1`, и тестирование инструментов вернётся без правок кода. На время `ENABLE_TOOLS=0` тул-сценарии (с проверкой `tools_any`) будут падать — не выбирайте их в прогоне или временно удалите из таблицы.

**Доступ — за паролем.** Раздел появляется только если задан секрет `EVAL_PASSWORD`. Схема простая: вы сами придумываете пароль, кладёте его в секреты Space (`EVAL_PASSWORD`) и раздаёте доверенным людям. Без правильного пароля панель не разворачивается; это не полноценная авторизация, а лёгкий барьер, чтобы случайные пользователи не запускали платные прогоны.

**Настройка хранилища (Google Sheet):**

1. Создайте Google-таблицу. Первая вкладка будет использоваться целиком (шапка пишется автоматически).
2. Расшарьте её на `client_email` того же сервисного аккаунта, что и для Google Docs, с правом **Редактор**.
3. В Google Cloud проекте включите **Google Sheets API**.
4. ID таблицы (из её URL) положите в секрет `SCENARIOS_SHEET_ID`.

> Сервисному аккаунту нужен scope `https://www.googleapis.com/auth/spreadsheets` — он запрашивается в коде (`evals/sheets.py`), отдельной настройки не требует, важно лишь дать аккаунту доступ к самой таблице (шаг 2) и включить API (шаг 3).

---

## Переменные окружения и секреты

| Переменная | Назначение |
|------------|------------|
| **`OPENAI_API_KEY`** | Ключ OpenAI (эмбеддинги FAISS + генерация ответов). |
| **`GOOGLE_CREDENTIALS`** | JSON сервисного аккаунта Google (доступ к Google Doc). Либо в .env, либо в секретах Streamlit/HF под ключом `google`. |
| **`LLM_MODEL`** | (опционально) модель генерации ответа, по умолчанию `gpt-4.1-mini`. |
| **`QUERY_REWRITE_MODEL`** | (опционально) модель переписывания запроса, по умолчанию `gpt-4.1-mini`. |
| **`RERANK_MODEL`** | (опционально) cross-encoder для rerank, по умолчанию `BAAI/bge-reranker-v2-m3`. |
| **`SOCIAL_INTENT_MODEL`** | (опционально) модель классификатора social-intent, по умолчанию `gpt-4.1-nano`. |
| **`ENABLE_TOOLS`** | Включить инструменты (`1` или `true`). По умолчанию `0` — инструменты выключены и у бота, и в харнессе проверок (тул-виджеты скрыты, mock-инструменты не подставляются). **На Hugging Face не задавайте** (останется версия без tools). Локально для разработки с tools добавьте в .env: `ENABLE_TOOLS=1`. |
| **`ENABLE_OUTPUT_GUARDRAILS`** | (опционально) LLM-проверка grounding цельного ответа + регенерация при провале. По умолчанию `0` (выключено). Включать (`1`) только после перехода на claim-level проверку — см. раздел про guardrails выше. |
| **`EVAL_PASSWORD`** | Пароль для входа в admin-панель «Проверка сценариев». Без него раздел скрыт. Придумайте сами и раздайте доверенным людям. |
| **`SCENARIOS_SHEET_ID`** | ID Google-таблицы, где хранятся сценарии проверок (см. раздел ниже). |
| **`JUDGE_MODEL`** | (опционально) модель LLM-судьи для проверок, по умолчанию `gpt-4o-mini`. |

Локально достаточно создать `.env` с этими переменными. На Hugging Face Spaces секреты задаются в настройках Space (Settings → Variables and secrets).

---

## Запуск

**Локально:**

```bash
pip install -r requirements.txt
streamlit run app_2.py
```

Приложение откроется по адресу http://localhost:8501. При первом запуске подтягивается Google Doc и строится FAISS (1–2 минуты).

**На Hugging Face:** репозиторий разворачивается как Space (Docker); переменные окружения задаются в интерфейсе Space.

---

## Конфигурация в коде

В `app_2.py` в начале заданы:

- **`DOCUMENT_ID`** (база знаний) — `1p9J-knk-d7gIOnzDnjN_kuejP0x09VqIG3KJGMfVxh8`
- **`PROMPT_DOCUMENT_ID`** (system prompt) — `1hk1PS4hkvp8MFDe0gGIDfYNsgzJCN5gMmyjMG0pkggM`
  - https://docs.google.com/document/d/1hk1PS4hkvp8MFDe0gGIDfYNsgzJCN5gMmyjMG0pkggM/edit
- **`FAISS_INDEX_PATH`** — каталог для сохранения индекса FAISS.
- **`HASH_FILE`** / **`PROMPT_HASH_FILE`** — хэши документов для кэширования.

Перед деплоем расшарьте **оба** документа на `client_email` сервисного аккаунта (доступ «Просмотр»).

Кнопка «Обновить базу знаний» сбрасывает кэш FAISS. После правки промпта в Google Doc нажмите **«Обновить промпт из документа»**. Если промпт-doc пуст, используется `DEFAULT_SYSTEM_TEMPLATE` из кода.

---

## Деплой на Hugging Face

1. Задать секреты в Space Settings → Variables and secrets: `OPENAI_API_KEY`, `GOOGLE_CREDENTIALS`. Для admin-панели проверок также `EVAL_PASSWORD` и `SCENARIOS_SHEET_ID` (см. раздел «Проверка сценариев»).
2. Удалить `ANTHROPIC_API_KEY` из секретов Space (больше не используется)
3. Загрузить файлы через `hf upload` (из-за `.docx` в git history обычный `git push` может быть заблокирован):

```bash
hf upload DomovenokChatbots/ExecChat app_2.py README.md requirements.txt .gitignore --commit-message "GPT-4.1 mini + token optimization"
hf upload DomovenokChatbots/ExecChat evals/ evals/ --commit-message "in-app eval harness"
```

### Стоимость (ориентир)

| | Было (Sonnet 4.6) | Стало (GPT-4.1 mini) |
|--|-------------------|----------------------|
| Input / 1M tok | ~$3 | ~$0.40 |
| Output / 1M tok | ~$15 | ~$1.60 |
| Типичный запрос | ~$0.02 | ~$0.002 |

---

Конфигурация Space: https://huggingface.co/docs/hub/spaces-config-reference