Produzir um resumo estruturado de um documento

PythonBeginner
Pratique Agora

Introdução

Uma equipe de operações precisa de um resumo estruturado de um breve relatório de incidente. Um modelo pode produzir um texto plausível, mas uma aplicação precisa de campos previsíveis e deve rejeitar saídas inválidas. Você solicitará um resumo JSON com Bedrock Converse, fará sua análise em Python e testará a rejeição com dados de teste locais claramente identificados.

Você deve conhecer os fundamentos de Python, JSON e as solicitações Converse ensinadas no laboratório guiado anterior. Cada nova VM fornece seu próprio documento, identidade configurada e cota de inferência. Nenhum arquivo ou credencial de uma VM anterior é necessário.

Relevância para a certificação

Certificação Tarefa do exame Prática
AI Practitioner (AIF-C01) Tarefa 3.2 Especificar o formato de saída e validar a resposta do modelo antes de a aplicação usá-la.

Visão conceitual criada com draw.io: um documento entra no Bedrock Converse; a análise e a validação selecionam um resumo utilizável ou uma rejeição controlada.

Examinar o documento e os campos obrigatórios

Nesta etapa, você examinará um relatório sintético de incidente fornecido e definirá a estrutura esperada pela aplicação que consumirá o resultado.

Comece no ambiente de trabalho:

cd /home/labex/project

O documento fornecido é um arquivo de texto simples. Leia-o com cat, que imprime o arquivo:

cat incident.txt

Ele descreve o incidente INC-204: um erro de configuração do checkout causou 30 minutos de falhas nas finalizações de compra. A equipe reverteu a configuração e restaurou o serviço. Um teste de configuração de acompanhamento está planejado; ele ainda não foi concluído. Esses fatos delimitam seu resumo.

A aplicação espera exatamente três campos: incident_id, impact e next_action. Cada um deve conter uma string não vazia. Um esquema descreve essa estrutura; JSON válido, sozinho, é insuficiente, pois uma lista, um campo ausente ou um número ainda podem constituir JSON válido. Leia a referência de esquema fornecida:

cat summary-schema.json

A referência explica a saída desejada. Você imporá seus campos com código Python comum; ela não garante automaticamente que o modelo os siga. Solicitar JSON e validar JSON são responsabilidades distintas.

Gerar e validar um resumo estruturado

Nesta etapa, você criará um pequeno comando Python que solicita um resumo e valida o texto retornado antes de indicar sucesso.

boto3 é o SDK da AWS para Python. O ambiente Python fornecido já o tem instalado e usa as credenciais e o endpoint de serviço configurados. O código usa converse, a mesma operação praticada com a CLI. maxTokens limita a saída, e o cliente desativa novas tentativas automáticas para impedir que uma falha ambígua envie outra solicitação de inferência silenciosamente.

Crie summary.py com o código a seguir. A função parse_response verifica o motivo de conclusão, analisa o texto com json.loads, valida as chaves exatas e verifica seus tipos. ValueError representa uma rejeição controlada; um ClientError ou erro de conexão é informado sem expor credenciais nem a exceção completa do serviço. O modo opcional --response-file lê uma resposta local para testar o analisador e não envia solicitações de inferência:

cat > summary.py <<'EOF'
import argparse
import json
from pathlib import Path
import boto3
from botocore.config import Config
from botocore.exceptions import BotoCoreError, ClientError


def parse_response(response):
    if not isinstance(response, dict):
        raise ValueError("invalid_response")
    if response.get("stopReason") != "end_turn":
        raise ValueError("incomplete_response")
    try:
        text = response["output"]["message"]["content"][0]["text"]
        value = json.loads(text)
        expected = {"incident_id", "impact", "next_action"}
        if not isinstance(value, dict) or set(value) != expected:
            raise ValueError("invalid_fields")
        if any(not isinstance(v, str) or not v.strip() for v in value.values()):
            raise ValueError("invalid_field_type")
        if value["incident_id"] != "INC-204":
            raise ValueError("unexpected_incident")
        return value
    except (KeyError, IndexError, TypeError, json.JSONDecodeError) as error:
        raise ValueError("invalid_response") from error


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--response-file", type=Path)
    args = parser.parse_args()
    try:
        if args.response_file:
            response = json.loads(args.response_file.read_text())
        else:
            client = boto3.client("bedrock-runtime", config=Config(
                connect_timeout=5, read_timeout=150,
                retries={"total_max_attempts": 1}))
            response = client.converse(
                modelId="labex.text-v1:0",
                system=[{"text": "Summarize only facts supplied in the incident document. Return exactly one JSON object with string fields incident_id, impact and next_action. No Markdown fences, extra keys or surrounding prose. Do not turn planned work into completed work."}],
                messages=[{"role": "user", "content": [
                    {"text": Path("incident.txt").read_text()}]}],
                inferenceConfig={"maxTokens": 768, "temperature": 0})
            Path("response.json").write_text(json.dumps(response, indent=2))
        value = parse_response(response)
    except (ValueError, KeyError, TypeError):
        print(json.dumps({"status": "rejected", "reason": "invalid_model_output"}))
        return 2
    except (BotoCoreError, ClientError):
        print(json.dumps({"status": "unavailable", "reason": "inference_failed"}))
        return 3
    print(json.dumps({"status": "ok", "summary": value}, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
EOF

Execute o script com o ambiente Python da AWS instalado. O trecho final > summary.json salva o resultado impresso:

/opt/labex/aws/venv/bin/python summary.py > summary.json

Aguarde o retorno do prompt e examine o resultado:

cat summary.json

Uma execução bem-sucedida apresenta "status": "ok" e um objeto summary com os três campos de string obrigatórios. Leia também a resposta Converse bruta:

python3 -m json.tool response.json

response.json registra a saída real do modelo, o motivo de parada e o uso de tokens. No AWS View superior, expanda a solicitação concluída e compare seu texto. As palavras geradas podem variar. Verifique se impact representa as falhas de checkout e se next_action descreve um trabalho planejado. Passar na verificação do esquema não prova que essas afirmações estejam corretas.

AWS View real de uma VM: a solicitação concluída contém um resumo de incidente em JSON, o uso de tokens e a cota restante. O texto gerado, o tempo e a cota podem variar.

Se o script informar rejeição, leia a resposta bruta antes de tentar novamente. Um modelo pode truncar a saída ou ignorar o formato solicitado; não substitua o resumo por um sucesso fixo no código. Uma inferência malsucedida pode consumir cota.

Testar o tratamento de respostas inválidas

Nesta etapa, você confirmará que respostas malformadas, com tipos incorretos e truncadas são rejeitadas sem outra solicitação ao modelo.

O diretório fixtures/ contém entradas deliberadas para testar o analisador. São dados locais de teste, não resultados de inferência. Liste-os:

ls fixtures

Execute o script com o arquivo de teste de JSON malformado:

/opt/labex/aws/venv/bin/python summary.py --response-file fixtures/malformed.json

A saída esperada é {"status": "rejected", "reason": "invalid_model_output"}. O código de saída 2 identifica essa rejeição controlada. echo $? imprime o código de saída do comando imediatamente anterior:

echo $?

Uma resposta pode ser JSON válido e ainda violar o esquema. Teste o arquivo com impact numérico:

/opt/labex/aws/venv/bin/python summary.py --response-file fixtures/wrong-type.json

Ele deve informar a mesma rejeição. Um objeto JSON aparentemente completo também precisa ser rejeitado se o modelo atingir o limite de tokens. Teste esse caso:

/opt/labex/aws/venv/bin/python summary.py --response-file fixtures/truncated.json

Ele deve indicar rejeição porque seu stopReason é max_tokens. Esses testes não imprimem um resumo bem-sucedido nem adicionam solicitações ao AWS View.

Por fim, analise novamente a resposta real salva:

/opt/labex/aws/venv/bin/python summary.py --response-file response.json

Ela ainda deve retornar status: ok. Você testou tanto a resposta real utilizável quanto entradas de falha controlada. Não confunda os dados de teste do analisador com uma prova de que a inferência funcionou; a solicitação concluída e a resposta bruta demonstram isso separadamente.

Remover os artefatos próprios do exercício

Nesta etapa, você removerá seu script e os dois arquivos de resultados gerados após as verificações funcionais passarem.

O documento, o esquema e os dados de teste do analisador fornecidos pertencem ao ambiente novo. Preserve-os, assim como o serviço de backend. rm remove apenas os três artefatos do aluno indicados:

rm summary.py summary.json response.json

Confirme que as entradas fornecidas permanecem:

ls

Converse não cria um servidor persistente na nuvem para excluir. Remover arquivos locais não devolve os créditos consumidos nem apaga os registros reais de inferência mantidos pelo AWS View.

Resumo

Você usou uma resposta real do Bedrock Converse para produzir um resumo estruturado de incidente, impôs os nomes e tipos dos campos, verificou o motivo de conclusão e rejeitou respostas inválidas controladas. Também revisou o significado factual separadamente da validade do esquema e removeu apenas seus próprios arquivos.

Para aprofundar o tema, consulte a documentação da AWS: Conteúdo da resposta, motivos de parada e uso do Converse.