ISD-e-Frames / app.py
AniseF's picture
Update app.py
0840a2f verified
Raw
History Blame Contribute Delete
12.7 kB
# ==============================================================================
# Projeto: Analisador Multilíngue ISD & FrameNet
# Desenvolvimento Colaborativo: Anise Ferreira & Google Gemini (IA Generativa / LLM)
# Papel da IA: Assistente de Codificação e Engenharia de Prompts
# Data: Junho de 2026
# ==============================================================================
import os
import json
import streamlit as st
from transformers import pipeline
from huggingface_hub import InferenceClient
# 1. Configuração da página do Streamlit
st.set_page_config(page_title="Analisador Multilíngue ISD & FrameNet", layout="wide")
st.title("🔬 Analisador Linguístico Avançado (PT / FR)")
st.subheader("Arquitetura Textual do ISD (Bronckart) + Semântica de Frames (Fillmore)")
# 2. Configurar o cliente da API do Hugging Face usando o Token Seguro
token_seguro = os.environ.get("HF_TOKEN")
client = InferenceClient(
model="Qwen/Qwen2.5-7B-Instruct",
token=token_seguro
)
# 3. Inicialização do modelo complementar (Mundos Discursivos)
@st.cache_resource
def load_models():
classifier = pipeline(
"zero-shot-classification",
model="MoritzLaurer/mDeBERTa-v3-base-mnli-xnli"
)
return classifier
classifier = load_models()
# 4. Interface do Usuário na Barra Lateral (Fontes Ampliadas)
st.sidebar.header("📝 Contexto Inicial Declarado")
# --- AJUSTE: Idioma do Corpus ---
st.sidebar.markdown("<p style='font-size: 16px; font-weight: bold; margin-bottom: -10px;'>Idioma do Corpus</p>", unsafe_allow_html=True)
idioma = st.sidebar.selectbox("", ["Português", "Français"], label_visibility="collapsed")
# --- AJUSTE: Estatuto do Autor/Emissor ---
st.sidebar.markdown("<p style='font-size: 16px; font-weight: bold; margin-bottom: -10px;'>Estatuto do Autor/Emissor</p>", unsafe_allow_html=True)
autor = st.sidebar.text_input("", placeholder="Ex: Jornalista, Cientista...", label_visibility="collapsed")
# --- AJUSTE: Objetivo Estimado ---
st.sidebar.markdown("<p style='font-size: 16px; font-weight: bold; margin-bottom: -10px;'>Objetivo Estimado</p>", unsafe_allow_html=True)
objetivo_input = st.sidebar.text_input("", placeholder="Ex: Persuadir, Denunciar...", label_visibility="collapsed")
# >>> COLE AS LINHAS DE CRÉDITO EXATAMENTE AQUI <<<
st.sidebar.markdown("---")
st.sidebar.caption("Idealizado por Anise Ferreira. Desenvolvido em colaboração com IA Generativa (LLM Gemini 1.5 Pro).")
# 5. Entrada do Texto Principal (Fonte Ampliada)
# --- AJUSTE: Insira o fragmento de texto... ---
st.markdown("<p style='font-size: 18px; font-weight: bold; margin-bottom: 5px;'>Insira o fragmento de texto para análise em Português ou Francês</p>", unsafe_allow_html=True)
texto_input = st.text_area(
"",
height=200,
placeholder="Digite ou cole seu texto aqui...",
label_visibility="collapsed"
)
# 6. O Botão que Dispara a Análise Combinada
if st.button("Analisar Texto Completo"):
if texto_input:
col1, col2 = st.columns(2)
# ----------------------------------------------------
# COLUNA 1: Relatório Completo de Bronckart (ISD)
# ----------------------------------------------------
with col1:
st.header("📊 Camada de Bronckart (ISD)")
labels = ["Discurso Teórico / Discours Théorique", "Narração / Narration", "Discurso Interativo / Discours Interactif"]
res = classifier(texto_input, candidate_labels=labels)
# Prompt blindado com definições teóricas do ISD (Bronckart e Adam)
# Prompt avançado com desdobramento rigoroso entre Textualização e Enunciação
prompt_isd = (
"Você é um linguista sênior especialista no Interacionismo Sociodiscursivo (ISD) de Jean-Paul Bronckart e Jean-Michel Adam.\n"
f"Analise o texto considerando que o autor declarado é '{autor}' e o objetivo estimado é '{objetivo_input}'.\n"
"Responda EXCLUSIVAMENTE em um formato JSON estrito, sem markdown, sem introduções. "
"Siga rigorosamente as seguintes diretrizes teóricas estruturadas em camadas para preencher o JSON:\n\n"
"1. 'contexto_produção': Mapeie o emissor (estatuto social), receptor (público-alvo), suporte (onde circula), o objetivo (efeito na sociedade) e o 'genero_textual' (identifique o gênero específico do texto com base nas suas características sociodiscursivas, ex: Artigo de Opinião, Relatório Científico, Editorial, etc.).\n\n"
"2. 'sequencias_textuais': Atribua porcentagens (0 a 100) para a presença das 5 sequências de Jean-Michel Adam no texto: narrativa, descritiva, argumentativa, explicativa e injuntiva.\n\n"
"3. 'mecanismos_textualizacao':\n"
" - Focus apenas na amarração linear do texto. Não misture com vozes ou opiniões.\n"
" - 'coesao_nominal': Analise as cadeias de referência anafórica. Como pronomes, sinônimos, repetições ou elipses são usados para retomar os referentes textuais sem gerar ambiguidade. Dê exemplos exatos do texto.\n"
" - 'coesao_verbal': Analise a ordenação e a correlação dos tempos verbais (ex: presente do indicativo para exposição, alternância de pretérito perfeito/imperfeito para eixos temporais). Explique o papel deles na progressão textual.\n\n"
"4. 'mecanismos_enunciativos':\n"
" - Identifique como a subjetividade e as perspectivas são encenadas no texto.\n"
" - 'gerenciamento_vozes': Identifique rigorosamente a polifonia textual. Classifique a presença e a alternância das vozes textuais de acordo com o ISD: Voz do Narrador/Expositor, Voz de Personagem, Voz de Instância Social (leis, ciência, senso comum/doxa) ou Voz do Autor Empírico. Dê exemplos de trechos textuais e cite como são introduzidas (direta, indireta ou implícita).\n"
" - 'modalizacoes': Rastreie as marcas de avaliação presentes. Classifique-as estritamente em suas subcategorias se houverem: Modalizações Apreciativas (julgamentos de valor/afetivos), Lógicas (certeza, probabilidade, possibilidade), Deônticas (dever, obrigação, permissão) ou Pragmáticas (utilidade, eficácia, responsabilidade). Indique os itens lexicais exatos extraídos do texto original.\n\n"
"Estrutura exata do JSON esperado:\n"
"{\n"
' "contexto_produção": {"emissor": "", "receptor": "", "suporte_circulacao": "", "objetivo_sociedade": "", "genero_textual": ""},\n'
' "sequencias_textuais": {"narrativa": 0, "descritiva": 0, "argumentativa": 0, "explicativa": 0, "injuntiva": 0},\n'
' "mecanismos_textualizacao": {"coesao_nominal": "", "coesao_verbal": ""},\n'
' "mecanismos_enunciativos": {"gerenciamento_vozes": "", "modalizacoes": ""}\n'
"}"
)
with st.spinner("Decodificando camadas do ISD..."):
try:
resposta_isd = client.chat_completion(
messages=[{"role": "system", "content": prompt_isd}, {"role": "user", "content": texto_input}],
max_tokens=800
)
texto_isd = resposta_isd.choices[0].message.content.strip()
if texto_isd.startswith("```"):
texto_isd = texto_isd.replace("```json", "").replace("```", "").strip()
dados_isd = json.loads(texto_isd)
st.subheader("1. Contexto de Produção Pragmático")
cp = dados_isd.get('contexto_produção', {})
# NOVA LINHA: Exibição do Gênero Textual Detectado
st.write(f"📂 **Gênero Textual Detectado:** {cp.get('genero_textual', 'Não identificado')}")
st.write(f"✍️ **Emissor (Estatuto):** {cp.get('emissor', 'Não identificado')}")
st.write(f"👥 **Receptor (Público-Alvo):** {cp.get('receptor', 'Não identificado')}")
st.write(f"📍 **Suporte e Circulação:** {cp.get('suporte_circulacao', 'Não identificado')}")
st.write(f"🎯 **Efeito Pretendido na Sociedade:** {cp.get('objetivo_sociedade', 'Não identificado')}")
st.divider()
st.subheader("2. Infraestrutura: Mundos & Sequências")
st.write(f"🔮 **Mundo Discursivo Predominante:** {res['labels'][0]} ({res['scores'][0]*100:.1f}%)")
st.write("**Tipologia de Sequências (Adam):**")
for seq, valor in dados_isd.get('sequencias_textuais', {}).items():
st.text(f"- Sequência {seq.capitalize()}:")
st.progress(float(valor)/100 if isinstance(valor, (int, float)) else 0.0)
st.divider()
st.subheader("3. Mecanismos de Textualização")
mt = dados_isd.get('mecanismos_textualizacao', {})
st.write(f"🔗 **Coesão Nominal:** {mt.get('coesao_nominal', 'Não analisado')}")
st.write(f"⏳ **Coesão Verbal:** {mt.get('coesao_verbal', 'Não analisado')}")
st.divider()
st.subheader("4. Mecanismos Enunciativos")
me = dados_isd.get('mecanismos_enunciativos', {})
st.write(f"🗣️ **Gerenciamento de Vozes:** {me.get('gerenciamento_vozes', 'Não analisado')}")
st.write(f"🦉 **Modalizações Detectadas:** {me.get('modalizacoes', 'Não analisado')}")
except Exception as e:
st.error(f"Erro ao processar camada ISD: {e}")
# ----------------------------------------------------
# COLUNA 2: Análise de Semântica de Frames Universal (Fillmore)
# ----------------------------------------------------
with col2:
st.header("🧠 Semântica de Frames Universal")
prompt_frame = (
"Você é um linguista computacional especialista na Semântica de Frames de Fillmore, "
"utilizando estritamente as taxonomias oficiais da Berkeley FrameNet (para inglês/geral) "
"e da Asfalda French FrameNet (para termos em francês).\n\n"
"Instruções de análise:\n"
"1. Identifique a Unidade Lexical (palavra-gatilho) principal do texto.\n"
"2. Determine o nome oficial do Frame ativado (ex: Commerce_buy, Statement, Motion).\n"
"3. Mapeie os Elementos de Frame (FEs) extraindo as palavras exatas do texto original.\n"
"4. Se o texto for em francês, use a correspondência conceitual validada pela Asfalda.\n\n"
"Responda EXCLUSIVAMENTE com um objeto JSON válido, sem comentários e sem marcações markdown:\n"
"{\n"
' "frame": "NOME_OFICIAL_DO_FRAME",\n'
' "unidade_lexical": "palavra_gatilho",\n'
' "elementos": {"Nome_Do_Elemento_Oficial": "trecho do texto"}\n'
"}"
)
with st.spinner("Analisando frames..."):
try:
resposta_frame = client.chat_completion(
messages=[{"role": "system", "content": prompt_frame}, {"role": "user", "content": texto_input}],
max_tokens=400
)
texto_frame = resposta_frame.choices[0].message.content.strip()
if texto_frame.startswith("```"):
texto_frame = texto_frame.replace("```json", "").replace("```", "").strip()
dados_frame = json.loads(texto_frame)
st.success(f"🔓 **Frame Detectado:** `{dados_frame.get('frame', 'Desconhecido')}`")
st.write("**Elementos mapeados no texto:**")
for elemento, valor in dados_frame.get('elementos', {}).items():
st.write(f"- *{elemento}:* {valor}")
except Exception as e:
st.error(f"Erro ao processar Frame: {e}")
else:
st.error("Por favor, insira um texto para análise.")