Adds documentation and fixes

This commit is contained in:
2026-01-23 14:49:44 -03:00
parent 9eecd617b3
commit 4e9e18faea
6 changed files with 1058 additions and 7 deletions

View File

@@ -12,10 +12,12 @@ import uuid
import difflib
import re
from collections import Counter
import os
secrets=json.loads(secrets.get_secret())
langfuse = Langfuse(
public_key=json.loads(secrets.get_secret())['api-langfuse-public'],
secret_key=json.loads(secrets.get_secret())['api-langfuse-secret'],
host="http://3.218.244.68:3000"
public_key=secrets['api-langfuse-public'],
secret_key=secrets['api-langfuse-secret'],
host=os.environ["LANGFUSE_HOST"]
)
@tool
def out_of_scope_and_dont_know_answer():
@@ -277,7 +279,7 @@ Na prática, isso significa que o Comm.Pix monetiza o fluxo de pagamento, mas se
O Comm.Pix ganha nas taxas já embutidas no valor pago pelo cliente, e não do parceiro. Assim, o vendor opera sem custos diretos e com total transparência sobre o valor que vai receber.
<\general_info>
Answer the following questions as best you can, and use the """+language+"""language. You have access to the following tools:
Answer the following questions as best you can,and use the """+language+"""language. You have access to the following tools:
{tools}
@@ -289,12 +291,9 @@ Chat History:"""+str(history)
#input_message=[{"role":"user","content":"aluno superior, nunca recebi auxilio, campus são paulo, Meu pai não é registrado, como faço para ganhar auxilio?"}]
response=""
started=False
finished=False
firstspan=[]
for step in agent_executor.stream({"messages": input_message}, config, stream_mode="values"):
if not started:
with langfuse.start_as_current_span(name="my-operation") as span:
firstspan=span
span.score_trace(
name="Origem",
value=origem,

131
docs/README.md Normal file
View File

@@ -0,0 +1,131 @@
# Assistente Projeto Produto - Documentacao
Esta documentacao cobre o sistema de assistente de IA construido com LangChain/LangGraph para suporte ao Comm.Pix.
## Indice
- [Visao Geral](#visao-geral)
- [Arquitetura](#arquitetura)
- [Componentes](#componentes)
- [Configuracao](#configuracao)
- [Uso](#uso)
- [Documentacao Adicional](#documentacao-adicional)
## Visao Geral
O Assistente e um agente RAG (Retrieval-Augmented Generation) que fornece suporte tecnico para usuarios da plataforma Comm.Pix. Ele utiliza:
- **Claude Sonnet 4** via Amazon Bedrock para capacidades de LLM
- **Amazon Knowledge Bases** para recuperacao de documentos
- **DynamoDB** para persistencia de memoria de conversas
- **Langfuse** para observabilidade e scoring
- **LangGraph** para orquestracao do agente
## Arquitetura
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Entrada User │────▶│ agent_call() │────▶│ Claude Sonnet │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
│ ▼
┌──────┴──────┐ ┌─────────────────┐
│ │ │ Knowledge Base │
▼ ▼ │ Retriever │
┌──────────┐ ┌──────────┐ └─────────────────┘
│ DynamoDB │ │ Langfuse │
│ Memoria │ │ Tracking │
└──────────┘ └──────────┘
```
## Componentes
### agent.py
Modulo principal de orquestracao do agente contendo:
- `agent_call(event, context)` - Ponto de entrada principal para a funcao Lambda
- `out_of_scope_and_dont_know_answer()` - Ferramenta para lidar com perguntas desconhecidas
**Estrutura do Evento de Entrada:**
```json
{
"username": "usuario123",
"email": "usuario@exemplo.com",
"language": "Portuguese",
"message": [{"role": "user", "content": "Pergunta aqui"}],
"chat_history": [],
"origem": "web"
}
```
**Estrutura da Resposta:**
```json
{
"json": "Texto da resposta do assistente",
"dynamo_response": {...},
"chat_history": [...]
}
```
### tools/dynamo.py
Operacoes DynamoDB para memoria de conversas:
- `write_memory(user, timestamp, role, content)` - Armazena mensagens da conversa
- `read_memory(userid)` - Recupera as ultimas 30 mensagens de um usuario
### tools/secrets.py
Integracao com AWS Secrets Manager:
- `get_secret()` - Recupera chaves de API do Langfuse do AWS Secrets Manager
## Configuracao
### Recursos AWS Necessarios
1. **Tabela DynamoDB**: `assistente-produtos-servicos-memoria`
- Partition Key: `UserId` (String)
- Sort Key: `Timestamp` (Number)
2. **Secret no AWS Secrets Manager**: `assistente-produtos-servicos`
- Chaves: `api-langfuse-public`, `api-langfuse-secret`
3. **Amazon Knowledge Base**: ID `PETAZDUOFZ`
### Requisitos de Ambiente
- Credenciais AWS configuradas (via IAM role ou variaveis de ambiente)
- Python 3.9+
- Pacotes necessarios no `requirements.txt`
## Uso
### Uso Basico
```python
from agent import agent_call
event = {
"username": "usuario123",
"email": "usuario@exemplo.com",
"language": "Portuguese",
"message": [{"role": "user", "content": "Como funciona o Pix?"}],
"chat_history": [],
"origem": "web"
}
response = agent_call(event, None)
print(response["json"])
```
### Como AWS Lambda
A funcao `agent_call` foi projetada para ser usada como handler de AWS Lambda.
## Documentacao Adicional
- [Guia de Integracao Langfuse](./langfuse-guide.md) - Como usar Langfuse para scoring e observabilidade
- [Referencia da API](./api-reference.md) - Documentacao detalhada da API
- [Melhorias de Codigo](./code-improvements.md) - Sugestoes de melhorias para o codigo

273
docs/api-reference.md Normal file
View File

@@ -0,0 +1,273 @@
# Referencia da API
Documentacao detalhada de todas as funcoes e modulos do assistente.
## Indice
- [agent.py](#agentpy)
- [tools/dynamo.py](#toolsdynamopy)
- [tools/secrets.py](#toolssecretspy)
---
## agent.py
Modulo principal que contem a logica do agente de IA.
### agent_call(event, context)
Funcao principal que processa perguntas do usuario e retorna respostas do assistente.
**Parametros:**
| Parametro | Tipo | Obrigatorio | Descricao |
|-----------|------|-------------|-----------|
| `event` | dict | Sim | Evento contendo dados da requisicao |
| `context` | object | Nao | Contexto do Lambda (pode ser None) |
**Estrutura do `event`:**
```python
{
"username": str, # Identificador do usuario
"email": str, # Email do usuario
"language": str, # Idioma desejado (ex: "Portuguese", "English")
"message": list, # Lista de mensagens no formato LangChain
"chat_history": list, # Historico de chat ([] para nova conversa)
"origem": str # (Opcional) Canal de origem da pergunta
}
```
**Formato das mensagens:**
```python
# Mensagem do usuario
{"role": "user", "content": "Texto da pergunta"}
# Mensagem do assistente
{"role": "assistant", "content": "Texto da resposta"}
```
**Retorno:**
```python
{
"json": str, # Resposta do assistente
"dynamo_response": dict, # Resposta da operacao DynamoDB
"chat_history": list # Historico atualizado
}
```
**Exemplo de Uso:**
```python
from agent import agent_call
# Nova conversa
event = {
"username": "joao.silva",
"email": "joao@empresa.com",
"language": "Portuguese",
"message": [{"role": "user", "content": "O que e o Comm.Pix?"}],
"chat_history": [],
"origem": "web"
}
response = agent_call(event, None)
print(response["json"]) # Resposta do assistente
```
**Codigos de Erro:**
| Erro | Causa | Solucao |
|------|-------|---------|
| KeyError | Campo obrigatorio ausente no event | Verificar estrutura do event |
| BedrockException | Erro na API do Bedrock | Verificar credenciais AWS |
| DynamoDBException | Erro ao acessar DynamoDB | Verificar permissoes IAM |
---
### out_of_scope_and_dont_know_answer()
Tool do LangChain para respostas padrao quando o agente nao encontra a resposta.
**Parametros:** Nenhum
**Retorno:** `str` - Mensagem padrao orientando o usuario a abrir ticket de suporte
**Comportamento:**
- Registra um score "Miss" no Langfuse
- Retorna mensagem com link para portal de suporte
**Quando e Chamada:**
- Perguntas fora do escopo do Comm.Pix
- Perguntas dentro do escopo mas sem resposta nos documentos
---
## tools/dynamo.py
Modulo para operacoes de memoria no DynamoDB.
### write_memory(user, timestamp, role, content)
Armazena uma mensagem da conversa no DynamoDB.
**Parametros:**
| Parametro | Tipo | Obrigatorio | Descricao |
|-----------|------|-------------|-----------|
| `user` | str | Sim | Identificador do usuario (UserId) |
| `timestamp` | int | Sim | Timestamp Unix da mensagem |
| `role` | str | Sim | Papel da mensagem ("user" ou "assistant") |
| `content` | str | Sim | Conteudo da mensagem |
**Retorno:** `None`
**Exemplo:**
```python
from tools import dynamo
import time
dynamo.write_memory(
user="joao.silva",
timestamp=int(time.time()),
role="user",
content="Como funciona o Finance Batch?"
)
```
**Estrutura no DynamoDB:**
```json
{
"UserId": "joao.silva",
"Timestamp": 1704067200,
"role": "user",
"content": "Como funciona o Finance Batch?"
}
```
---
### read_memory(userid)
Recupera as ultimas 30 mensagens de um usuario.
**Parametros:**
| Parametro | Tipo | Obrigatorio | Descricao |
|-----------|------|-------------|-----------|
| `userid` | str | Sim | Identificador do usuario |
**Retorno:** `list` - Lista de mensagens ordenadas por timestamp (mais recentes primeiro)
**Exemplo:**
```python
from tools import dynamo
history = dynamo.read_memory("joao.silva")
for msg in history:
print(f"{msg['role']}: {msg['content']}")
```
**Retorno Exemplo:**
```python
[
{"UserId": "joao.silva", "Timestamp": 1704067300, "role": "assistant", "content": "..."},
{"UserId": "joao.silva", "Timestamp": 1704067200, "role": "user", "content": "..."},
# ... ate 30 mensagens
]
```
---
## tools/secrets.py
Modulo para recuperacao de segredos do AWS Secrets Manager.
### get_secret()
Recupera as credenciais armazenadas no Secrets Manager.
**Parametros:** Nenhum
**Retorno:** `str` - String JSON contendo os segredos
**Exemplo:**
```python
from tools import secrets
import json
secret_string = secrets.get_secret()
secrets_data = json.loads(secret_string)
langfuse_public = secrets_data['api-langfuse-public']
langfuse_secret = secrets_data['api-langfuse-secret']
```
**Estrutura do Segredo:**
```json
{
"api-langfuse-public": "pk-lf-xxxxxxxx",
"api-langfuse-secret": "sk-lf-xxxxxxxx"
}
```
**Erros Possiveis:**
| Erro | Causa | Solucao |
|------|-------|---------|
| `ResourceNotFoundException` | Segredo nao encontrado | Verificar nome do segredo |
| `AccessDeniedException` | Sem permissao | Verificar politica IAM |
| `DecryptionFailure` | Erro de descriptografia | Verificar chave KMS |
---
## Fluxo Completo de Execucao
```
1. agent_call() recebe o evento
2. Inicializa Langfuse handler
3. Configura ChatBedrock (Claude Sonnet 4)
4. Configura Knowledge Base Retriever
5. Carrega historico
│ ├── Se chat_history vazio: dynamo.read_memory('frente')
│ └── Se nao: usa chat_history do evento
6. Cria agente ReAct com tools:
│ ├── retriever.as_tool() - Busca documentos
│ └── out_of_scope_and_dont_know_answer - Resposta padrao
7. Executa agente com streaming
│ ├── Registra scores no Langfuse (Origem, Email)
│ └── Processa cada step do agente
8. Salva mensagem no DynamoDB
9. Retorna resposta
```
---
## Variaveis de Configuracao
| Variavel | Local | Valor Padrao | Descricao |
|----------|-------|--------------|-----------|
| `model_id` | agent.py | `us.anthropic.claude-sonnet-4-20250514-v1:0` | Modelo Claude |
| `region_name` | agent.py | `us-east-1` | Regiao AWS |
| `knowledge_base_id` | agent.py | `PETAZDUOFZ` | ID da Knowledge Base |
| `temperature` | agent.py | `0.1` | Temperatura do modelo |
| `max_tokens` | agent.py | `2000` | Maximo de tokens |
| `numberOfResults` | agent.py | `4` | Documentos retornados |
| `table_name` | dynamo.py | `assistente-produtos-servicos-memoria` | Tabela DynamoDB |
| `secret_name` | secrets.py | `assistente-produtos-servicos` | Nome do segredo |

269
docs/frontend-app.md Normal file
View File

@@ -0,0 +1,269 @@
# Documentação da Aplicação Frontend
Este documento descreve a aplicação web Streamlit localizada na pasta `front/app`, que fornece uma interface de chat conversacional para o assistente de produtos e serviços baseado em IA.
## Estrutura de Diretórios
```
front/app/
├── front.py # Aplicação principal Streamlit (74 linhas)
├── st_auth.py # Módulo de autenticação AWS Secrets Manager (28 linhas)
└── __pycache__/ # Bytecode Python compilado
```
## Visão Geral da Arquitetura
```
┌─────────────────────────────────────────────────────────────┐
│ Interface Web Streamlit │
│ (Executa na porta 8501 via Docker) │
├─────────────────────────────────────────────────────────────┤
│ │
│ [Autenticação de Usuário via AWS Cognito OIDC] │
│ (Token JWT no header x-amzn-oidc-data) │
│ ↓ │
│ [Seletor de Idioma] [Campo de Entrada do Chat] │
│ ↓ │
│ ┌──────────────────────────────────────────┐ │
│ │ Gerenciamento de Estado da Sessão │ │
│ │ - user_prompt_history │ │
│ │ - chat_answer_history │ │
│ │ - chat_history (conversa completa) │ │
│ └──────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────┐ │
│ │ Manipulador de Requisições API │ │
│ │ - Busca chave API do AWS Secrets Mgr │ │
│ │ - POST para AWS API Gateway │ │
│ │ - Inclui contexto do usuário e histórico│ │
│ └──────────────────────────────────────────┘ │
│ ↓ │
├─────────────────────────────────────────────────────────────┤
│ Backend AWS (Serviço Externo) │
│ API Gateway → Lambda → Assistente IA │
│ (Retorna JSON com chat_history e mensagem) │
└─────────────────────────────────────────────────────────────┘
```
## Descrição dos Arquivos
### st_auth.py - Integração com AWS Secrets Manager
**Propósito:** Recupera chaves de API e segredos sensíveis do AWS Secrets Manager.
#### Função: `get_secret()`
Conecta ao AWS Secrets Manager e recupera os segredos da aplicação.
```python
def get_secret() -> str:
"""
Recupera o segredo 'assistente-produtos-servicos' do AWS Secrets Manager.
Retorna:
str: A string do segredo (formato JSON) contendo chaves de API e credenciais.
Exceções:
ClientError: Se o segredo não puder ser recuperado da AWS.
"""
```
**Detalhes Importantes:**
- Conecta ao AWS Secrets Manager na região `us-east-1`
- Recupera o segredo chamado `"assistente-produtos-servicos"`
- Retorna o segredo como string (formato JSON esperado)
- Usa sessão boto3 para operações do SDK AWS
- Inclui tratamento de erros para exceções `ClientError`
**Dependências:**
- `boto3` - SDK AWS para Python
- `botocore.exceptions` - Tratamento de erros para operações AWS
---
### front.py - Aplicação Principal Streamlit
**Propósito:** Interface de chatbot interativa para assistência de produtos/serviços com autenticação AWS Cognito e respostas baseadas em IA.
#### Componentes
##### 1. Autenticação e Contexto do Usuário (Linhas 1-27)
Extrai informações do usuário da autenticação AWS ALB OIDC:
- Extrai token JWT do header `x-amzn-oidc-data`
- Decodifica token JWT sem verificação de assinatura
- Extrai identificação do usuário de múltiplos campos JWT:
- `sub` (Subject - campo JWT padrão)
- `cognito:username` (nome de usuário Cognito)
- `username` (campo alternativo de nome de usuário)
- `user_id` (campo customizado)
- Recupera email do usuário do token decodificado
##### 2. Elementos da Interface
| Elemento | Descrição |
|----------|-----------|
| Seletor de Idioma | Dropdown com opções English, Portuguese, Spanish |
| Cabeçalho Principal | "Assistente Produtos Servicos" |
| Entrada do Chat | Campo de texto com placeholder em português |
##### 3. Gerenciamento de Estado da Sessão
A aplicação mantém três variáveis de estado de sessão:
| Variável | Tipo | Descrição |
|----------|------|-----------|
| `user_prompt_history` | `List[str]` | Lista de perguntas do usuário |
| `chat_answer_history` | `List[str]` | Lista de respostas da IA |
| `chat_history` | `List[dict]` | Histórico completo da conversa com papéis |
##### 4. Função Utilitária: `create_sources_string()`
```python
def create_sources_string(source_urls: Set[str]) -> str:
"""
Formata fontes de citação para exibição.
Args:
source_urls: Conjunto de URLs de fontes únicas para formatar.
Retorna:
str: Lista numerada de fontes para exibição.
"""
```
*Nota: Atualmente definida mas não utilizada ativamente no código.*
##### 5. Fluxo Principal do Chat
Quando um usuário envia uma mensagem, a seguinte sequência ocorre:
1. **Exibe mensagem do usuário** - Mostra a pergunta na interface imediatamente
2. **Faz requisição à API:**
- Constrói payload com mensagem, histórico do chat, nome de usuário, email, origem, idioma
- Envia requisição POST para endpoint AWS API Gateway
- Inclui chave API nos headers obtida do AWS Secrets Manager
3. **Processa resposta:**
- Analisa resposta JSON
- Atualiza histórico do chat da resposta se fornecido
- Extrai conteúdo da mensagem da estrutura de resposta
- Trata erros de timeout com mensagem amigável ao usuário
4. **Atualiza estado:**
- Adiciona pergunta do usuário ao histórico
- Adiciona resposta da IA ao histórico
- Atualiza histórico do chat com registros de conversa baseados em papéis
## Fluxo de Dados
```
1. Acesso do Usuário → AWS ALB injeta token JWT nos headers
2. Autenticação → Extrai user_id e email do JWT
3. Entrada do Usuário → Mensagem de chat com preferência de idioma
4. Chamada API → POST para AWS Lambda via API Gateway com:
- Conteúdo da mensagem
- Histórico do chat
- Metadados do usuário (username, email, origin, language)
5. Processamento Backend → Assistente IA gera resposta
6. Resposta → JSON com chat_history e conteúdo da mensagem
7. Exibição → Atualiza interface e mantém estado da sessão
```
## Comunicação com API
### Endpoint
```
POST https://xexm2wsz07-vpce-05915540d0592b921.execute-api.us-east-1.amazonaws.com/dev
```
### Payload da Requisição
```json
{
"message": "Pergunta ou mensagem do usuário",
"chat_history": [
{"role": "user", "content": "Mensagem anterior do usuário"},
{"role": "assistant", "content": "Resposta anterior do assistente"}
],
"username": "user_id_do_jwt",
"email": "usuario@exemplo.com",
"origin": "streamlit",
"language": "Portuguese"
}
```
### Headers da Requisição
```
x-api-key: <Chave API do AWS Secrets Manager>
```
### Formato da Resposta
```json
{
"chat_history": [...],
"message": {
"content": "Texto de resposta do assistente"
}
}
```
## Dependências
| Pacote | Versão | Propósito |
|--------|--------|-----------|
| `streamlit` | ^1.49.1 | Framework de interface web |
| `boto3` | ^1.40.37 | SDK AWS |
| `requests` | - | Cliente HTTP para chamadas API |
| `PyJWT` | - | Decodificação de tokens JWT |
| `pyyaml` | - | Parsing de YAML |
## Configuração
### Ambiente
- **Porta:** 8501 (porta padrão do Streamlit)
- **Região AWS:** us-east-1
- **Nome do Segredo:** `assistente-produtos-servicos`
### Idiomas Suportados
- English (Inglês)
- Portuguese (Português)
- Spanish (Espanhol)
## Tratamento de Erros
| Cenário | Comportamento |
|---------|---------------|
| Timeout da API | Exibe: "Desculpe, a busca pode ter demorado mais que o esperado. Por favor tente novamente." |
| Erros gerais da API | Resposta pode não ser exibida corretamente |
## Comportamento da Sessão
- Histórico do chat persiste dentro de uma única sessão do navegador
- Histórico é limpo ao atualizar a página ou fechar a aba
- Não há armazenamento persistente de conversas
## Notas de Implantação
A aplicação foi projetada para executar atrás de um AWS Application Load Balancer (ALB) com autenticação Cognito configurada. O ALB injeta o header `x-amzn-oidc-data` contendo o token JWT para usuários autenticados.
### Docker
A aplicação executa em um container Docker (veja `front/Dockerfile`) e expõe a porta 8501.
## Considerações Conhecidas
1. **Endpoint de API Hardcoded:** A URL do AWS API Gateway está hardcoded no código fonte
2. **Funcionalidades Não Utilizadas:** Dependências `streamlit-authenticator` e `sseclient` são importadas mas não utilizadas
3. **Sem Validação de Entrada:** Perguntas dos usuários e respostas da API não são validadas antes do uso
4. **Persistência Apenas na Sessão:** Conversas não persistem entre sessões

319
docs/langfuse-guide.md Normal file
View File

@@ -0,0 +1,319 @@
# Guia de Integracao Langfuse
Este guia explica como o Langfuse esta integrado ao assistente e como utilizar suas funcionalidades de scoring e observabilidade.
## Indice
- [O que e Langfuse](#o-que-e-langfuse)
- [Configuracao Inicial](#configuracao-inicial)
- [Sistema de Scores](#sistema-de-scores)
- [Adicionando Novos Scores](#adicionando-novos-scores)
- [Visualizando Metricas](#visualizando-metricas)
- [Boas Praticas](#boas-praticas)
## O que e Langfuse
Langfuse e uma plataforma de observabilidade para aplicacoes LLM que permite:
- Rastrear todas as chamadas ao modelo
- Adicionar scores e metricas customizadas
- Analisar performance e custos
- Debugar conversas problematicas
## Configuracao Inicial
### 1. Credenciais
As credenciais do Langfuse sao armazenadas no AWS Secrets Manager:
```python
# Recuperando credenciais
from tools import secrets
import json
secrets_data = json.loads(secrets.get_secret())
public_key = secrets_data['api-langfuse-public']
secret_key = secrets_data['api-langfuse-secret']
```
### 2. Inicializacao do Cliente
```python
from langfuse import Langfuse
from langfuse.langchain import CallbackHandler
# Cliente principal para spans e scores
langfuse = Langfuse(
public_key=public_key,
secret_key=secret_key,
host="http://SEU_HOST_LANGFUSE:3000"
)
# Callback handler para integracao automatica com LangChain
langfuse_handler = CallbackHandler()
```
### 3. Integracao com LangGraph
```python
# Adicionar o handler na configuracao do agente
config = {
"configurable": {"thread_id": "abc123"},
"callbacks": [langfuse_handler]
}
# Executar o agente
for step in agent_executor.stream({"messages": input_message}, config, stream_mode="values"):
# processamento...
pass
# IMPORTANTE: Flush para garantir envio dos dados
langfuse.flush()
```
## Sistema de Scores
### Scores Atuais
O sistema atual utiliza os seguintes scores:
| Score | Tipo | Descricao |
|-------|------|-----------|
| `Miss` | CATEGORICAL | Indica quando o agente nao encontrou a resposta |
| `Origem` | CATEGORICAL | Canal de origem da pergunta (web, app, etc.) |
| `Email` | CATEGORICAL | Email do usuario para rastreabilidade |
### Como os Scores sao Registrados
```python
# Score de "Miss" - quando nao encontra resposta
@tool
def out_of_scope_and_dont_know_answer():
with langfuse.start_as_current_span(name="my-operation") as span:
span.score_trace(
name="Miss",
value="True",
data_type="CATEGORICAL"
)
return "Mensagem de resposta padrao..."
# Scores de metadados - origem e email
with langfuse.start_as_current_span(name="my-operation") as span:
span.score_trace(
name="Origem",
value=origem,
data_type="CATEGORICAL"
)
span.score_trace(
name="Email",
value=email,
data_type="CATEGORICAL"
)
```
## Adicionando Novos Scores
### Tipos de Score Disponiveis
1. **CATEGORICAL** - Para valores de categorias (string)
2. **NUMERIC** - Para valores numericos (int/float)
3. **BOOLEAN** - Para valores verdadeiro/falso
### Exemplos de Novos Scores
#### Score de Satisfacao do Usuario
```python
def add_user_satisfaction_score(span, satisfaction_rating):
"""
Adiciona score de satisfacao do usuario (1-5)
"""
span.score_trace(
name="user_satisfaction",
value=satisfaction_rating,
data_type="NUMERIC"
)
```
#### Score de Tempo de Resposta
```python
import time
def add_response_time_score(span, start_time):
"""
Adiciona score de tempo de resposta em segundos
"""
response_time = time.time() - start_time
span.score_trace(
name="response_time_seconds",
value=response_time,
data_type="NUMERIC"
)
```
#### Score de Documentos Consultados
```python
def add_documents_used_score(span, num_documents):
"""
Registra quantos documentos foram consultados
"""
span.score_trace(
name="documents_consulted",
value=num_documents,
data_type="NUMERIC"
)
```
#### Score de Qualidade da Resposta
```python
def add_response_quality_score(span, quality_level):
"""
Classifica qualidade da resposta
Valores: "excellent", "good", "fair", "poor"
"""
span.score_trace(
name="response_quality",
value=quality_level,
data_type="CATEGORICAL"
)
```
#### Score de Feedback Explicito
```python
def add_feedback_score(span, was_helpful):
"""
Registra se o usuario achou a resposta util
"""
span.score_trace(
name="user_found_helpful",
value="True" if was_helpful else "False",
data_type="CATEGORICAL"
)
```
### Implementacao Completa de Scoring
```python
class LangfuseScorer:
"""
Classe utilitaria para gerenciar scores do Langfuse
"""
def __init__(self, langfuse_client):
self.langfuse = langfuse_client
def score_interaction(self, span, metrics: dict):
"""
Adiciona multiplos scores de uma vez
Args:
span: Span atual do Langfuse
metrics: Dicionario com metricas
{
"origem": "web",
"email": "user@example.com",
"response_time": 2.5,
"documents_used": 3,
"found_answer": True
}
"""
# Scores categoricos
if "origem" in metrics:
span.score_trace(name="Origem", value=metrics["origem"], data_type="CATEGORICAL")
if "email" in metrics:
span.score_trace(name="Email", value=metrics["email"], data_type="CATEGORICAL")
# Scores numericos
if "response_time" in metrics:
span.score_trace(name="response_time", value=metrics["response_time"], data_type="NUMERIC")
if "documents_used" in metrics:
span.score_trace(name="documents_used", value=metrics["documents_used"], data_type="NUMERIC")
# Scores booleanos (como categoricos)
if "found_answer" in metrics:
span.score_trace(
name="found_answer",
value="True" if metrics["found_answer"] else "False",
data_type="CATEGORICAL"
)
```
## Visualizando Metricas
### Acessando o Dashboard
1. Acesse a interface web do Langfuse: `http://SEU_HOST:3000`
2. Navegue ate "Traces" para ver todas as execucoes
3. Use filtros por score para analises especificas
### Queries Uteis
#### Encontrar conversas sem resposta
- Filtrar por: `score.Miss = "True"`
#### Analise por canal de origem
- Agrupar por: `score.Origem`
#### Metricas de performance
- Ordenar por: `score.response_time`
## Boas Praticas
### 1. Sempre fazer Flush
```python
# No final de cada execucao
langfuse.flush()
```
### 2. Usar Nomes Consistentes
```python
# BOM - nomes consistentes e descritivos
span.score_trace(name="user_satisfaction", value=5, data_type="NUMERIC")
span.score_trace(name="response_quality", value="good", data_type="CATEGORICAL")
# EVITAR - nomes inconsistentes
span.score_trace(name="sat", value=5, data_type="NUMERIC")
span.score_trace(name="Quality", value="good", data_type="CATEGORICAL")
```
### 3. Documentar Valores Possiveis
Para scores CATEGORICAL, documente os valores possiveis:
```python
# response_quality: "excellent" | "good" | "fair" | "poor"
# user_type: "vendor" | "subvendor" | "supervendor" | "support"
# channel: "web" | "api" | "whatsapp" | "slack"
```
### 4. Tratar Erros
```python
try:
with langfuse.start_as_current_span(name="operation") as span:
span.score_trace(name="metric", value=value, data_type="CATEGORICAL")
except Exception as e:
print(f"Erro ao registrar score: {e}")
# Continua execucao sem falhar
```
### 5. Nao Bloquear por Metricas
```python
# Scores nao devem impactar a resposta ao usuario
# Se o Langfuse falhar, a conversa deve continuar
```
## Referencias
- [Documentacao Oficial Langfuse](https://langfuse.com/docs)
- [Langfuse Python SDK](https://langfuse.com/docs/sdk/python)
- [Integracao LangChain](https://langfuse.com/docs/integrations/langchain)

View File

@@ -4,6 +4,66 @@ import conf as config
import iam
import ecs
import pulumi
import pulumi_aws as aws
import conf as config
import iam
import ecs
# ECS Cluster Setup
app_ecs_cluster = aws.ecs.Cluster(f"{config.project_name}-ecs-cluster",
configuration=aws.ecs.ClusterConfigurationArgs(
execute_command_configuration=aws.ecs.ClusterConfigurationExecuteCommandConfigurationArgs(
logging="DEFAULT",
),
),
settings=[aws.ecs.ClusterSettingArgs(
name="containerInsights",
value="disabled",
)],
tags={"Name": f"{config.project_name}-{config.stack_name}"},
)
ecs_cluster_capacity_providers = aws.ecs.ClusterCapacityProviders(f"{config.project_name}-cluster-capacity-providers",
cluster_name=app_ecs_cluster.name,
capacity_providers=["FARGATE", "FARGATE_SPOT"],
)
# Security Group Setup
alb_security_group = aws.ec2.SecurityGroup(f"{config.project_name}-security-group",
vpc_id=config.network["vpc_id"],
ingress=[aws.ec2.SecurityGroupIngressArgs(
protocol="-1",
from_port=0,
to_port=0,
cidr_blocks=config.network["alb_allow_ingress_cidr"],
),
],
egress=[aws.ec2.SecurityGroupEgressArgs(
protocol="-1",
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"],
)],
)
# Load Balancer Setup
app_load_balancer = aws.lb.LoadBalancer(
f"alb-{config.project_name}",
load_balancer_type="application",
security_groups=[alb_security_group.id],
subnets=config.network["alb_subnet_ids"],
idle_timeout=(1200),
internal=config.network['alb_internal'],
)
for ecs_app in config.ecs:
ecs.deploy_app(ecs_app, app_ecs_cluster, alb_security_group, app_load_balancer.arn)
# Export the ALB DNS Name
pulumi.export("url", app_load_balancer.dns_name.apply(lambda dns_name: f"http://{dns_name}"))
# ECS Cluster Setup
app_ecs_cluster = aws.ecs.Cluster(f"{config.project_name}-ecs-cluster",