Spaces:
Runtime error
title: ExecChat
emoji: 📊
colorFrom: red
colorTo: yellow
sdk: docker
pinned: false
ExecChat — генератор ответов поддержки домовят
Веб-приложение для помощи операторам поддержки сервиса уборки «Домовёнок»: по вопросу исполнителя (домовёнка) строится ответ на основе базы знаний и при необходимости вызываются инструменты (доходы, качество, штрафы).
Как устроена система
База знаний — текст подгружается из Google Doc, режется на чанки (
CHUNK_SIZE=800, перекрытиеCHUNK_OVERLAP=200,RecursiveCharacterTextSplitter) и индексируется в FAISS (эмбеддинги OpenAI). При первом запуске, изменении документа или версии индекса (INDEX_VERSION) индекс пересобирается; хэш документа хранится вdoc_hash.pkl.System prompt — загружается из отдельного Google Doc (редактирование только там; версии — через историю документа). На странице — read-only просмотр и кнопка «Обновить промпт из документа». При
ENABLE_TOOLS=1к промпту из документа дописывается блок про инструменты из кода.Классификатор social-intent (
gpt-4.1-nano) — чистые вежливые реплики (спасибо/привет/прощание/комплимент) получают короткий вежливый ответ и не уходят в эскалацию и RAG.Переписывание запроса (
QUERY_REWRITE_MODEL, по умолчаниюgpt-4.1-mini) — шумное сообщение исполнителя превращается в чистый короткий поисковый запрос (опечатки, местоимения, учёт последних реплик). Качество rerank сильно зависит от этого шага.Retrieval + rerank — FAISS отдаёт
RETRIEVAL_CANDIDATES=30кандидатов, затем cross-encoderBAAI/bge-reranker-v2-m3переранжирует их; в контекст идут чанки из топ-RERANK_TOP_N=8с rerank-скором ≥RERANK_SCORE_THRESHOLD=0.1. Чанки обрезаются доMAX_CHUNK_CHARS=800. Если порог не прошёл ни один чанк — бот не эскалирует, а вежливо отвечает, что вопрос вне темы сервиса (без вызова менеджера); эскалация остаётся только при сбое генерации или при явном провале guardrails (ENABLE_OUTPUT_GUARDRAILS=1).Ответ генерирует OpenAI GPT-4.1 mini (
LLM_MODEL, по умолчаниюgpt-4.1-mini): system prompt + контекст из RAG + история диалога (последние 3 пары) + текущий вопрос.max_tokens=512. Temperature настраивается на странице (кнопка «Применить temperature»).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).Инструменты (tools) — при
ENABLE_TOOLS=1модель может вызывать функции изtools.py. Вapp_2.pyреализован цикл: запрос к модели → при наличииtool_callsвыполняются инструменты → результаты передаются обратно в модель → ответ без вызовов возвращается пользователю.Многоходовый диалог — в контекст ответа передаётся весь диалог, который был до сих пор (полная история сессии). Переписывание запроса при этом по-прежнему смотрит только на последние
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 или БД и возвращайте строку с результатом (она попадёт в контекст модели).
Как добавить новый инструмент:
- В
tools.pyописать функцию с типами аргументов и docstring (по нему модель понимает, когда инструмент вызывать). - Обернуть функцию в
@toolизlangchain_core.tools. - Добавить её в список возврата функции
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):
- Создайте Google-таблицу. Первая вкладка будет использоваться целиком (шапка пишется автоматически).
- Расшарьте её на
client_emailтого же сервисного аккаунта, что и для Google Docs, с правом Редактор. - В Google Cloud проекте включите Google Sheets API.
- 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).
Запуск
Локально:
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_kuejP0x09VqIG3KJGMfVxh8PROMPT_DOCUMENT_ID(system prompt) —1hk1PS4hkvp8MFDe0gGIDfYNsgzJCN5gMmyjMG0pkggMFAISS_INDEX_PATH— каталог для сохранения индекса FAISS.HASH_FILE/PROMPT_HASH_FILE— хэши документов для кэширования.
Перед деплоем расшарьте оба документа на client_email сервисного аккаунта (доступ «Просмотр»).
Кнопка «Обновить базу знаний» сбрасывает кэш FAISS. После правки промпта в Google Doc нажмите «Обновить промпт из документа». Если промпт-doc пуст, используется DEFAULT_SYSTEM_TEMPLATE из кода.
Деплой на Hugging Face
- Задать секреты в Space Settings → Variables and secrets:
OPENAI_API_KEY,GOOGLE_CREDENTIALS. Для admin-панели проверок такжеEVAL_PASSWORDиSCENARIOS_SHEET_ID(см. раздел «Проверка сценариев»). - Удалить
ANTHROPIC_API_KEYиз секретов Space (больше не используется) - Загрузить файлы через
hf upload(из-за.docxв git history обычныйgit pushможет быть заблокирован):
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