Spaces:
Runtime error
Runtime error
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
|