Spaces:
Running
Running
| # ============================================================================== | |
| # 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) | |
| 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.") |