Outcome Recorder Integration

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:

Estrutura

Módulo: outcome_recorder.py

Localização: csuite-executive/csuite-api/app/policy_engine/outcome_recorder.py

Funções principais:

  1. record_policy_outcome()
  2. Função completa para registrar qualquer outcome
  3. Recebe todos os parâmetros necessários
  4. Chama sp_ctx_record_outcome

  5. record_outcome_from_decision()

  6. Função de conveniência
  7. Registra outcome a partir de uma decisão
  8. 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

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

  1. Verificar logs do policy_engine
  2. Verificar se decision_log_id está sendo gerado
  3. Verificar se há erros no outcome_recorder

action_id não está sendo preenchido

  1. Verificar se código de execução está atualizado
  2. Verificar se execution_action_ledger está sendo populado
  3. Executar backfill se necessário: sp_ctx_backfill_outcome_decision_log_id_v2

Score baixo

🔊 Text-to-Speech

1.0x
1.0
Pronto para reproduzir