Outcome Recorder - Integração com Aplicação
Visão Geral
O módulo outcome_recorder.py foi criado para integrar o registro de outcomes diretamente no código da aplicação, usando a procedure sp_ctx_record_outcome que garante:
- ✅ Preenchimento direto de
decision_log_ideaction_id - ✅ Score 100 garantido quando links estão disponíveis
- ✅ Metadados de matching preenchidos automaticamente
- ✅ Zero necessidade de backfill futuro
Estrutura
Módulo: outcome_recorder.py
Localização: csuite-executive/csuite-api/app/policy_engine/outcome_recorder.py
Funções principais:
record_policy_outcome()- Função completa para registrar qualquer outcome
- Recebe todos os parâmetros necessários
-
Chama
sp_ctx_record_outcome -
record_outcome_from_decision() - Função de conveniência
- Registra outcome a partir de uma decisão
- Simplifica uso quando já temos
decision_log_id
Integração Atual
1. Policy Engine Instrumentation
Arquivo: csuite-executive/csuite-api/app/policy_engine/instrumentation.py
O que foi adicionado:
Após cada decisão ser logada, um outcome inicial é criado automaticamente:
# Após instrument_decision() criar o decision_log
record_outcome_from_decision(
db=db,
decision_log_id=decision_log_id,
org_id=org_id,
policy_code=policy_code,
entity_type=subject_type,
entity_ref=subject_ref,
action_taken=f"DECISION_{resp.outcome}",
action_id=None, # Será preenchido quando ação for executada
created_by=source_app
)
Status inicial:
- status = 'PENDING'
- decision_log_id preenchido (score 100)
- action_id = NULL (será preenchido quando ação for executada)
- match_method = 'DIRECT'
- match_score = 100
Próximos Passos de Integração
2. Execução de Ações
Onde: Código que executa ações (ex: csuite-operations)
O que fazer:
Quando uma ação é executada e registrada em execution_action_ledger, atualizar o outcome:
from csuite_executive.csuite_api.app.policy_engine.outcome_recorder import record_policy_outcome
# Após executar ação e ter action_id
# Atualizar outcome existente com action_id
db.execute(text("""
UPDATE csuite_context.ctx_policy_outcomes
SET
action_id = :action_id,
match_method = 'ACTION_ID',
match_score = 100,
matched_at = NOW(),
matched_by = 'action_executor'
WHERE decision_log_id = :decision_log_id
AND action_id IS NULL
"""), {
"action_id": action_id,
"decision_log_id": decision_log_id
})
Ou criar novo outcome se não existir:
record_policy_outcome(
db=db,
org_id=org_id,
policy_code=policy_code,
entity_type=entity_type,
entity_ref=entity_ref,
action_taken=action_type,
decision_log_id=decision_log_id,
action_id=action_id, # ✅ Link direto
created_by="action_executor"
)
3. Observação de Resultados
Onde: Código que observa resultados (ex: jobs de análise, feedback loops)
O que fazer:
Quando o resultado é observado, atualizar o outcome:
db.execute(text("""
UPDATE csuite_context.ctx_policy_outcomes
SET
observed_outcome = :observed_outcome,
metric_after = :metric_after,
delta_score = :metric_after - metric_before,
observed_at = NOW(),
status = CASE
WHEN :metric_after > metric_before THEN 'SUCCESS'
WHEN :metric_after < metric_before THEN 'FAILURE'
ELSE 'INCONCLUSIVE'
END
WHERE outcome_id = :outcome_id
"""), {
"outcome_id": outcome_id,
"observed_outcome": observed_outcome,
"metric_after": metric_after
})
Ou usar a procedure existente:
db.execute(text("""
CALL csuite_context.sp_ctx_close_pending_outcomes(:org_id)
"""), {"org_id": org_id})
Exemplos de Uso
Exemplo 1: Outcome Completo na Criação
from csuite_executive.csuite_api.app.policy_engine.outcome_recorder import record_policy_outcome
outcome_id = record_policy_outcome(
db=db,
org_id=1,
policy_code="INV_LOW_TURN",
entity_type="PRODUCT",
entity_ref="PROD-12345",
entity_name="Produto XYZ",
action_taken="BUNDLE_CREATED",
expected_outcome="Aumentar giro em 30 dias",
metric_before=0.5, # Giro atual
decision_log_id=decision_log_id, # ✅ Link direto
action_id=action_id, # ✅ Link direto
created_by="policy_engine"
)
Exemplo 2: Outcome a partir de Decisão
from csuite_executive.csuite_api.app.policy_engine.outcome_recorder import record_outcome_from_decision
outcome_id = record_outcome_from_decision(
db=db,
decision_log_id=123,
org_id=1,
policy_code="INV_LOW_TURN",
entity_type="PRODUCT",
entity_ref="PROD-12345",
action_taken="BUNDLE_CREATED",
action_id=456, # Opcional, mas recomendado
created_by="policy_engine"
)
Exemplo 3: Atualizar Outcome com Resultado
# Após observar resultado
db.execute(text("""
UPDATE csuite_context.ctx_policy_outcomes
SET
observed_outcome = :observed_outcome,
metric_after = :metric_after,
delta_score = :metric_after - COALESCE(metric_before, 0),
observed_at = NOW(),
status = CASE
WHEN :metric_after > COALESCE(metric_before, 0) THEN 'SUCCESS'
WHEN :metric_after < COALESCE(metric_before, 0) THEN 'FAILURE'
ELSE 'INCONCLUSIVE'
END,
closed_at = NOW()
WHERE outcome_id = :outcome_id
"""), {
"outcome_id": outcome_id,
"observed_outcome": "Giro aumentou de 0.5 para 0.8",
"metric_after": 0.8
})
Benefícios
✅ Zero Backfill Necessário
Com decision_log_id e action_id preenchidos diretamente:
- Não há necessidade de backfill futuro
- Score 100 garantido
- Links diretos desde a criação
✅ Rastreabilidade Completa
Cada outcome tem:
- decision_log_id: Link para a decisão
- action_id: Link para a ação executada
- match_method: Como foi vinculado ('DIRECT', 'ACTION_ID')
- match_score: Score de confiança (100 quando direto)
✅ Auditoria e Debug
match_debug_json: Detalhes do matching (quando aplicável)matched_at,matched_by: Quando e quem fez o match- Histórico completo do ciclo de vida
Monitoramento
Ver Outcomes Criados Recentemente
SELECT
outcome_id,
policy_code,
entity_type,
entity_ref,
action_taken,
decision_log_id,
action_id,
match_method,
match_score,
status,
created_at
FROM csuite_context.ctx_policy_outcomes
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY created_at DESC;
Ver Outcomes sem action_id
SELECT
outcome_id,
policy_code,
decision_log_id,
action_taken,
created_at
FROM csuite_context.ctx_policy_outcomes
WHERE action_id IS NULL
AND decision_log_id IS NOT NULL
AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY created_at DESC;
Estatísticas de Matching
SELECT * FROM csuite_context.vw_outcomes_matching_stats;
Troubleshooting
Outcome não está sendo criado
- Verificar logs do
policy_engine - Verificar se
decision_log_idestá sendo gerado - Verificar se há erros no
outcome_recorder
action_id não está sendo preenchido
- Verificar se código de execução está atualizado
- Verificar se
execution_action_ledgerestá sendo populado - Executar backfill se necessário:
sp_ctx_backfill_outcome_decision_log_id_v2
Score baixo
- Scores baixos (< 80) indicam matching retrospectivo
- Verificar se
decision_log_ideaction_idestão sendo preenchidos - Considerar atualizar código para preencher diretamente