From ad9830fe354787ec91f7e33610efc9eb9db482b0 Mon Sep 17 00:00:00 2001 From: SaintDemon Date: Tue, 12 May 2026 21:19:39 +0300 Subject: [PATCH 1/2] feat(summarizer): add sales template for B2B call analysis Structured prompt with 5 sections: summary, next steps, objections, client signals, deal assessment. Used by CoPilot Sales PostMeeting. Co-Authored-By: Claude Opus 4.6 --- gigaam_transcriber/summarizer.py | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gigaam_transcriber/summarizer.py b/gigaam_transcriber/summarizer.py index 9a21ad0..56c0d95 100644 --- a/gigaam_transcriber/summarizer.py +++ b/gigaam_transcriber/summarizer.py @@ -154,6 +154,41 @@ def _default_api_key() -> str: "Формат: Markdown. Будь точным и объективным." ), }, + "sales": { + "label": "💼 Продажи", + "system_prompt": ( + "Ты — аналитик B2B-продаж. Проанализируй транскрипцию звонка менеджера с клиентом " + "и составь структурированный отчёт на русском языке.\n\n" + "СТРОГО используй эти заголовки второго уровня (##):\n\n" + "## Резюме\n" + "2-4 предложения: что обсуждали, чем закончили, общий тон встречи.\n\n" + "## Следующие шаги\n" + "Конкретные задачи. Каждый пункт строго в формате:\n" + "- **[Ответственный]** Задача · Приоритет: высокий/средний/низкий\n" + "Если указан срок, добавь его после задачи через · _до ДД.ММ_\n\n" + "## Возражения\n" + "Каждое возражение клиента и реакция менеджера:\n" + "- **Возражение**: текст возражения → **Ответ**: как менеджер ответил (или «не отработано»)\n" + "Если возражений не было — напиши «Возражений не выявлено».\n\n" + "## Сигналы клиента\n" + "Разбей на категории:\n" + "- **Интерес**: к чему клиент проявил интерес\n" + "- **Боль**: какие проблемы или потребности озвучил\n" + "- **Готовность**: покупательские сигналы (запрос цены, сроков, деталей)\n" + "- **Сомнения**: что вызвало настороженность\n\n" + "## Оценка сделки\n" + "- **Этап воронки**: квалификация / выявление потребностей / презентация / " + "работа с возражениями / согласование условий / закрытие\n" + "- **Вероятность закрытия**: X% — коротко почему\n" + "- **Риски**: что может помешать сделке\n" + "- **Рекомендация**: 1-2 предложения что делать дальше\n\n" + "Правила:\n" + "- Будь конкретен, ссылайся на слова из транскрипта\n" + "- Не выдумывай то, чего нет в разговоре\n" + "- Если данных для раздела нет — напиши «Не выявлено»\n" + "- Формат: Markdown" + ), + }, } From 82df50bad9a5a39795af28bd1367e2fb8e083f1a Mon Sep 17 00:00:00 2001 From: SaintDemon Date: Thu, 14 May 2026 13:35:07 +0300 Subject: [PATCH 2/2] feat(live-advisor): improve cascade prompts, timing, and confidence threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sales-focused classifier with buying_signal/need_expression categories, concise actionable advisor prompt, confidence threshold 0.5→0.3, cascade interval 20s→8s, fallback throttle 10s→15s. Co-Authored-By: Claude Opus 4.6 --- gigaam_transcriber/llm_cascade.py | 59 ++++++++++++++++--------------- pyproject.toml | 2 +- routers/live_hints.py | 4 +-- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/gigaam_transcriber/llm_cascade.py b/gigaam_transcriber/llm_cascade.py index 9ab6635..984ae88 100644 --- a/gigaam_transcriber/llm_cascade.py +++ b/gigaam_transcriber/llm_cascade.py @@ -26,49 +26,50 @@ # ─── Layer 1: Classification ──────────────────────────────────── -CLASSIFIER_SYSTEM_PROMPT = """Ты — классификатор фрагментов делового диалога. -Проанализируй фрагмент транскрипта и классифицируй его. +CLASSIFIER_SYSTEM_PROMPT = """Ты — классификатор фрагментов B2B-продажного диалога. +Определи, содержит ли фрагмент значимое для менеджера по продажам событие. Категории: -- "neutral" — рутина, small talk, обычное обсуждение без значимых событий -- "objection" — возражение клиента (дорого, не подходит, подумаем, уже работаем с другими) -- "price_question" — вопрос или обсуждение цены, скидок, бюджета, условий оплаты -- "competitor_mention" — упоминание конкурента или альтернативного поставщика -- "clarification" — запрос уточнения от клиента (важно, но не возражение) -- "commitment_signal" — сигнал готовности к сделке (договорились, ок, согласен) -- "silence" — пауза в разговоре +- "neutral" — рутина, small talk, пустая фраза, обычное изложение без триггера +- "objection" — возражение: дорого, не подходит, подумаем, не готовы, не уверены, у других лучше +- "price_question" — обсуждение цены, бюджета, скидок, условий оплаты, ROI, сроков окупаемости +- "competitor_mention" — упоминание конкурента, альтернативы, сравнение с другим продуктом +- "buying_signal" — клиент спрашивает о деталях внедрения, сроках, демо, следующих шагах, договоре +- "need_expression" — клиент описывает свою проблему, боль, потребность, чего ему не хватает +- "clarification" — запрос уточнения, технический вопрос, сомнение в понимании -ВАЖНО: Классифицируй как НЕ-neutral ТОЛЬКО если есть конкретное значимое событие. -Нейтральные обсуждения, приветствия, small talk — всегда "neutral". +Классифицируй щедро: если есть хоть намёк на значимое событие — НЕ neutral. +Приветствия, прощания, «да-да», «понял» без контекста — neutral. -Ответь ТОЛЬКО валидным JSON: -{"classification": "", "confidence": 0.0-1.0, "excerpt": "<ключевая фраза>"}""" +Ответь ТОЛЬКО JSON: {"classification": "", "confidence": 0.0-1.0, "excerpt": "<фраза>"}""" # ─── Layer 2: Advisory ────────────────────────────────────────── -ADVISOR_SYSTEM_PROMPT = """Ты — ассистент менеджера B2B-продаж. На основе контекста встречи и фрагмента диалога дай подсказку. +ADVISOR_SYSTEM_PROMPT = """Ты — live-советник менеджера B2B-продаж. Менеджер сейчас на звонке с клиентом. Дай ОДНУ короткую, конкретную подсказку. -Типы подсказок: -- "tactical" — немедленное действие (что сказать/спросить прямо сейчас) -- "strategic" — направление разговора (куда вести диалог) -- "warning" — предупреждение (риск, конкурент, возражение) -- "analytical" — аналитическое наблюдение (тенденция, сдвиг в обсуждении) +Типы: +- "tactical" — что СКАЗАТЬ или СПРОСИТЬ прямо сейчас (конкретная фраза или вопрос) +- "strategic" — куда ВЕСТИ разговор дальше (переход к теме, техника закрытия) +- "warning" — ОПАСНОСТЬ: необработанное возражение, уход клиента, конкурент, ошибка менеджера +- "analytical" — НАБЛЮДЕНИЕ: клиент проявил интерес, сменил тон, подал покупательский сигнал -Приоритеты: "critical", "high", "medium", "low" +Приоритеты: +- "critical" — клиент уходит, сделка под угрозой, нужно действовать СЕЙЧАС +- "high" — возражение, конкурент, покупательский сигнал — важно не упустить +- "medium" — полезный совет, можно учесть +- "low" — фоновое наблюдение -ПРАВИЛА: -- Не повторяй подсказки, которые уже давались (см. историю) -- Учитывай фазу переговоров и накопленные факты -- Не выдумывай данные — опирайся только на транскрипт и контекст -- Не давай юридических/финансовых гарантий -- Не рекомендуй манипулятивные техники +ФОРМАТ подсказки: +- 1-2 предложения максимум. Менеджер читает на ходу — ему некогда +- Для tactical: дай ГОТОВУЮ фразу в кавычках или конкретный вопрос +- Ссылайся на слова клиента из транскрипта {feedback_bias} -Ответь ТОЛЬКО валидным JSON: -{"type": "", "text": "<подсказка>", "priority": "", "rationale": "<почему эта подсказка>"}""" +Ответь ТОЛЬКО JSON: +{{"type": "", "text": "<подсказка>", "priority": "", "rationale": "<почему>"}}""" @dataclass @@ -81,7 +82,7 @@ class ClassificationResult: @property def is_significant(self) -> bool: """Whether this fragment should trigger Layer 2.""" - return self.classification != "neutral" and self.confidence >= 0.5 + return self.classification != "neutral" and self.confidence >= 0.3 class LLMCascade: diff --git a/pyproject.toml b/pyproject.toml index 80f17bb..8d0bcda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" license = {text = "MIT"} requires-python = ">=3.10" authors = [ - {name = "GigaAM Transcriber", email = ""} + {name = "GigaAM Transcriber"} ] keywords = [ "transcription", diff --git a/routers/live_hints.py b/routers/live_hints.py index 56511fe..65c43b7 100644 --- a/routers/live_hints.py +++ b/routers/live_hints.py @@ -246,7 +246,7 @@ async def live_hints_ws(ws: WebSocket): # ── Live Advisor: cascade hint generation ─────── cascade_hint_sent = False - min_cascade_interval = 20.0 # seconds between cascade hints + min_cascade_interval = 8.0 if trigger_event and event_detector.should_trigger(trigger_event) and (now - last_hint_time) >= min_cascade_interval: event_detector.mark_event_processed() event_detector.reset_timer() @@ -291,7 +291,7 @@ async def live_hints_ws(ws: WebSocket): if ( not cascade_hint_sent and session_config - and now - last_hint_time >= 10.0 + and now - last_hint_time >= 15.0 ): last_hint_time = now full_transcript = "\n".join(transcript_segments)