ExecChat / README.md
guilsyTrue's picture
Out-of-scope reply when RAG has no context
0a9953f verified
|
Raw
History Blame Contribute Delete
20.5 kB
metadata
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).


Запуск

Локально:

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
  • 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 может быть заблокирован):
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