Commit Graph

15 Commits

Author SHA1 Message Date
Rodribm10
b457e84c2f fix(captain): route embeddings to legacy OpenAI + retry transient errors
Resolve duas camadas de problema identificadas em teste end-to-end:

1. Embeddings falhavam com HTTP 404 (/codex/v1/embeddings não existe).
   Solução: Captain::Llm::EmbeddingService sempre usa OpenAI tradicional
   via Llm::Config.with_api_key(legacy_settings). ProviderConfig expõe
   legacy_openai_settings pra isso.

2. Servidor Codex ocasionalmente responde com response.failed +
   code=server_error (instabilidade transitória). Client agora retenta
   até 2x com backoff exponencial (0.5s, 1.5s) em erros retryable:
   HTTP 5xx, server_error no response.failed, ou stream inacabado.

Outras correções nesta etapa:
- Scenario#agent_model: em modo Codex, ignora CAPTAIN_OPEN_AI_MODEL_SCENARIO
  (que pode ter gpt-4o legado) e usa ProviderConfig.model.
- ExtractionService/ContradictionCheckerService/TranslateQueryService:
  trocam constantes hardcoded gpt-4o-mini/gpt-4.1-nano por
  ProviderConfig.light_model (respeitando o provider ativo).
- ProviderConfig.DEFAULT_CODEX_MODEL agora é gpt-5.2 (reconhecido pelo
  RubyLLM; gpt-5.4 não está no catalog do gem).

Validado ponta-a-ponta: WhatsApp → Chatwoot → Jasmine → handoff Daniela
→ faq_lookup com embedding OK → resposta com preços corretos.

Docs em docs/captain-codex-oauth.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:42:31 -03:00
Rodribm10
08a06c6528 fix(captain): memory allows 'Solicitou Pix ..., aguardando pagamento'
Some checks failed
Build and Push to GHCR (multi-arch) / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Build and Push to GHCR (multi-arch) / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Build and Push to GHCR (multi-arch) / merge (push) Has been cancelled
Previous commit made the extractor reject any reservation-shaped fact
without a literal payment confirmation in the conversation. That
killed the useful middle ground: a customer who requests a Pix and
hasn't paid yet is still a concrete signal worth remembering (for
follow-up, interest mapping, CRM). We were going from "hallucinated
reservation" to "nothing remembered".

Add the intermediate pattern:
- Payment confirmed → "Reservou X para Y em DD/MM/AAAA"
- Pix generated, no payment yet → "Solicitou Pix para X em DD/MM/AAAA, aguardando pagamento"
- Just a price quote → nothing

The "aguardando pagamento" suffix is required so the downstream recall
never confuses it with a closed reservation.
2026-04-22 05:01:24 -03:00
Rodribm10
d2c2c6b7fe fix(captain): pre-reservation semantics + no duplicate pix links
Three UX bugs from staging testing:

1. Duplicate Pix link in WhatsApp — the tool's formatted_message
   embedded the full link + instructions, so the LLM copied it into
   its own response on top of the dedicated link message sent by
   dispatch_direct_link_message. The tool now returns a short
   summary with no URL; dispatch is the single source of the link.

2. "Reserva confirmada!" sent before payment — the scenario prompt
   used the word "confirmação" loosely, which the LLM read as the
   reservation being closed. Now the prompt forces "pré-reserva /
   aguardando pagamento" until the Pix is actually paid, and the
   dispatched link message explains that the reservation is only
   secured after payment clears.

3. Memory extraction wrote "Reservou Hidromassagem para pernoite
   em 22/04/2026" when the customer only received a Pix link and
   replied "obrigado". Tightened the extraction prompt so
   padrao_comportamental of a reservation requires a literal
   payment confirmation — Pix generated alone no longer qualifies.
2026-04-22 04:19:39 -03:00
Rodribm10
cfffea9c16 feat(captain): semantic memory fixes + roleta + reclamações + analytics
Consolida o trabalho desta branch de abril/2026 em um bloco pronto pra
testar em staging antes do merge pra main.

## Correções de memória semântica
- ExtractionService: Princípio Zero + Regra de Ouro (ação consumada vs intenção).
- Cenário Daniela_Reservas: Passo 0 de classificação (consulta/intenção/fora).

## Roleta da Sorte (end-to-end)
- Schema Supabase + 7 RPCs atômicas (server-side, idempotentes).
- Services: Offer, Redeem, WeeklyReport.
- Jobs: OfferRouletteJob (hook em ConfirmationService após Pix pago),
  NotifyRevealed + Scheduler de fallback.
- Tool manual GenerateRoletaLinkTool + endpoint público /roleta/notify.
- Dashboard /captain/roleta com Resgate + Relatório + anomaly detection.

## Cenário Reclamacoes_Ouvidoria
- Triagem P1-P4, framework LAST, Three-level listening, Self-check.
- Sem compensação material, detecção de cliente frustrado eleva prioridade.

## Analytics
- Funil de conversão /captain/funnel: 5 etapas via regex, zero LLM.
- Detector de churn via ChurnOutreach* (cron dias úteis 10h-17h BRT).

## Trabalho pré-existente incluído
- Captain Executive Reports (ceo_digest, mattermost_delivery).
- get_reserva_preco_tool, Lifecycle ajustes, Reservations UI polimentos.

## Outros
- .gitignore: patterns pra credenciais.
- Migrations de scenarios idempotentes.
- i18n completa pt_BR+en pra roleta/funnel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:36:25 -03:00
Rodribm10
6330bec857 fix(captain-memory): temporal memory model + aggressive dedup
User feedback revealed a fundamental design issue: the memory model was
accumulating contradictory "Prefere X" facts because a single choice was
being treated as a permanent preference. Result: 3 different
"Prefere suite X" entries coexisting, all at 90% confidence, with
reservation patterns over time (2hrs, 4hrs, pernoite) all claiming to be
the customer's "preferred" duration.

Corrections:

1. ExtractionService prompt — preferencia now requires EXPLICIT
  declaration words ("prefiro", "gosto mais de", "sempre escolho",
  "adoro", "favorita"). A mere choice in one conversation is NO LONGER
  extracted as preferencia — instead it goes to padrao_comportamental
  WITH THE DATE in the content (e.g. "Reservou Alexa para pernoite em
  23/05/2026"). This makes memory temporal and auditable instead of
  imposing fake consistency.

2. Reference date is passed to the LLM prompt via the latest message
  timestamp, used as the anchor date the LLM must embed in every
  padrao_comportamental content.

3. ContradictionCheckerService — dual threshold:
  - cosine < 0.15 → auto-supersede without LLM (pure duplicate)
  - 0.15 to 0.6 → ask LLM if contradicts, supersede if yes
  - > 0.6 → ignore, unrelated facts
  Previously only the middle band existed, so near-duplicate facts like
  two "aniversário 23/05" entries or three "prefere suite X" entries
  were never cleaned up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 08:30:42 -03:00
Rodribm10
b742d774c8 fix(captain-memory): block suite hallucinations + hardcode cadastral data exclusion
Real test revealed gpt-4o-mini was still:
- Hallucinating suite names ("Aluba" doesn't exist — we only have
  Alexa, Stilo, Hidromassagem)
- Extracting cadastral data as memory ("Rodrigo has a CPF", "Name is X")
  despite the per-type NÃO examples

Added two sections at the top of the prompt:
1. Business canonical data — explicit whitelist of suite names (Alexa,
  Stilo, Hidromassagem) and stay types. Anything else = discard, NO auto-
  normalization. LLM must not guess.
2. Cadastral data absolute rule — explicit list of fields that are
  profile data, not memory: name, CPF/RG/passport, email/phone/address,
  birth date. Plus 5 concrete  examples of what was being wrongly
  extracted in the wild.

Existing 9 specs still pass (stub at call_llm; prompt change is
semantic, not structural).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 08:06:31 -03:00
Rodribm10
4becfd0a57 fix(captain-memory): strict taxonomy definitions in ExtractionService prompt
Real-world test revealed the LLM extractor (gpt-4o-mini) was using type
labels too loosely: a customer's QUESTION about parking ("tem
estacionamento?") was classified as 'reclamacao'. Similarly cortesia
generica ("obrigado") was becoming 'feedback_positivo', and transactional
events (CPF informed, reservation made) were becoming memories when they
should be ignored.

Rewrote build_prompt with:
- Per-type strict definition (what it IS)
- YES/NO examples for each of the 9 types, with the most common pitfalls
  explicitly shown as NO
- 7 absolute rules, including: questions are never complaints, generic
  courtesy is never feedback, agent actions are never customer memory,
  transactional events are not long-term facts
- Confidence threshold guidance (>=0.9 only if totally explicit, 0.7-0.89
  for strong inference, <0.7 discard)
- "If in doubt, discard — quality > quantity. Most transactional
  conversations should return empty facts list"

Existing 9 specs still pass (stub call_llm, so prompt changes don't
affect unit test assertions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 07:44:26 -03:00
Rodribm10
5874029a03 fix(captain-memory): raise RecallService timeout 0.5s -> 2.0s
Real-world observation: OpenAI embedding API takes 200-400ms typical,
plus pgvector query overhead, the 500ms budget was being exceeded
frequently, silently dropping memory recall. Agent typing delay is
already 2-15s humanized, so a 2s recall budget is well within UX
tolerance and gives ~4-5x margin over typical embedding latency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 07:25:19 -03:00
Rodribm10
6723473fdc fix(captain-memory): ContradictionChecker exact-match parsing + rescue wrap + LLM failure test 2026-04-19 00:31:54 -03:00
Rodribm10
9bc6429b91 feat(captain-memory): add ContradictionCheckerService with LLM verification 2026-04-19 00:26:58 -03:00
Rodribm10
aec796ebfd fix(captain-memory): cap ExtractionService input, validate scope, filter failed msgs 2026-04-19 00:24:09 -03:00
Rodribm10
9d593757df feat(captain-memory): add ExtractionService with evidence+confidence guardrails 2026-04-19 00:18:32 -03:00
Rodribm10
0fee1b3c2f fix(captain-memory): strengthen RecallService logging context and document timeout tradeoff 2026-04-19 00:14:06 -03:00
Rodribm10
502c3d1698 feat(captain-memory): add RecallService with timeout and graceful degradation 2026-04-19 00:09:31 -03:00
Rodribm10
5d15f55a29 feat(captain-memory): add PromptInjectionService formatting memories as XML 2026-04-19 00:05:11 -03:00