Adds documentation and fixes
This commit is contained in:
@@ -12,10 +12,12 @@ import uuid
|
|||||||
import difflib
|
import difflib
|
||||||
import re
|
import re
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
import os
|
||||||
|
secrets=json.loads(secrets.get_secret())
|
||||||
langfuse = Langfuse(
|
langfuse = Langfuse(
|
||||||
public_key=json.loads(secrets.get_secret())['api-langfuse-public'],
|
public_key=secrets['api-langfuse-public'],
|
||||||
secret_key=json.loads(secrets.get_secret())['api-langfuse-secret'],
|
secret_key=secrets['api-langfuse-secret'],
|
||||||
host="http://3.218.244.68:3000"
|
host=os.environ["LANGFUSE_HOST"]
|
||||||
)
|
)
|
||||||
@tool
|
@tool
|
||||||
def out_of_scope_and_dont_know_answer():
|
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.
|
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>
|
<\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}
|
{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?"}]
|
#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=""
|
response=""
|
||||||
started=False
|
started=False
|
||||||
finished=False
|
|
||||||
firstspan=[]
|
|
||||||
for step in agent_executor.stream({"messages": input_message}, config, stream_mode="values"):
|
for step in agent_executor.stream({"messages": input_message}, config, stream_mode="values"):
|
||||||
if not started:
|
if not started:
|
||||||
with langfuse.start_as_current_span(name="my-operation") as span:
|
with langfuse.start_as_current_span(name="my-operation") as span:
|
||||||
firstspan=span
|
|
||||||
span.score_trace(
|
span.score_trace(
|
||||||
name="Origem",
|
name="Origem",
|
||||||
value=origem,
|
value=origem,
|
||||||
|
|||||||
131
docs/README.md
Normal file
131
docs/README.md
Normal 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
273
docs/api-reference.md
Normal 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
269
docs/frontend-app.md
Normal 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
319
docs/langfuse-guide.md
Normal 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)
|
||||||
@@ -4,6 +4,66 @@ import conf as config
|
|||||||
import iam
|
import iam
|
||||||
import ecs
|
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
|
# ECS Cluster Setup
|
||||||
app_ecs_cluster = aws.ecs.Cluster(f"{config.project_name}-ecs-cluster",
|
app_ecs_cluster = aws.ecs.Cluster(f"{config.project_name}-ecs-cluster",
|
||||||
|
|||||||
Reference in New Issue
Block a user