| """Gradio UI para Hugging Face Spaces. |
| |
| Este é o entry point do Space. O arquivo é detectado automaticamente pelo HF |
| quando se chama `app.py` na raiz do Space. |
| |
| Dois modos de execução expostos via abas: |
| · Single ReAct → rápido, trace visível |
| · Multi LangGraph → mais lento, mostra plan/context/draft/critique |
| |
| A OPENAI_API_KEY vem como Secret do Space (NÃO commitar no código). |
| A variável PRODUCT vem como Variable do Space (educiacao ou designmind). |
| """ |
| from __future__ import annotations |
|
|
| import json |
| import os |
| import sys |
| from pathlib import Path |
|
|
| |
| ROOT = Path(__file__).resolve().parent.parent |
| if str(ROOT) not in sys.path: |
| sys.path.insert(0, str(ROOT)) |
|
|
| import gradio as gr |
|
|
| from src.agents.multi_agent import run_multi_agent |
| from src.agents.single_agent import SingleAgent |
| from src.config import get_product_config |
|
|
| |
| _PRODUCT_CONFIG = get_product_config() |
| _PRODUCT_NAME = _PRODUCT_CONFIG["name"] |
| _EXAMPLE_PROMPTS = _PRODUCT_CONFIG["example_prompts"] |
|
|
|
|
| |
| |
| |
| def handle_single(query: str) -> tuple[str, str]: |
| """Roda agente single ReAct. Retorna (resposta_final, trace_markdown).""" |
| if not query or len(query.strip()) < 3: |
| return "Digite uma pergunta com pelo menos 3 caracteres.", "" |
|
|
| agent = SingleAgent() |
| result = agent.run(query, verbose=False) |
|
|
| |
| trace_lines = ["### Trace do agente\n"] |
| for s in result.steps: |
| if s.is_final: |
| trace_lines.append(f"**Passo {s.step_number} · FINAL**") |
| trace_lines.append(f"_resposta direta ao usuário (mostrada acima)_\n") |
| else: |
| args_str = json.dumps(s.tool_args or {}, ensure_ascii=False, indent=2) |
| result_str = (s.tool_result or "")[:500] |
| trace_lines.append(f"**Passo {s.step_number} · TOOL**") |
| trace_lines.append(f"- ferramenta: `{s.tool_name}`") |
| trace_lines.append(f"- argumentos: `{args_str}`") |
| trace_lines.append(f"- resultado: {result_str}\n") |
|
|
| return result.final_answer, "\n".join(trace_lines) |
|
|
|
|
| def handle_multi(query: str) -> tuple[str, str, str, str, str]: |
| """Roda multi-agente. Retorna (final, plan, context, draft, critique).""" |
| if not query or len(query.strip()) < 3: |
| empty = "Digite uma pergunta com pelo menos 3 caracteres." |
| return empty, "", "", "", "" |
|
|
| state = run_multi_agent(query, max_iterations=2) |
| return ( |
| state.get("final_answer", state.get("draft_answer", "")), |
| state.get("plan", ""), |
| state.get("context", "")[:2000], |
| state.get("draft_answer", ""), |
| state.get("critique", ""), |
| ) |
|
|
|
|
| |
| |
| |
| INTRO = f""" |
| # {_PRODUCT_NAME} |
| |
| Agente construído na Aula 07 do curso de Engenharia de IA. |
| Função calling nativa OpenAI + LangGraph + avaliação BFCL. |
| |
| **Modelo**: {os.getenv("OPENAI_MODEL", "gpt-4.1-mini")} · |
| **Produto ativo**: `{os.getenv("PRODUCT", "educiacao")}` |
| |
| > Limitações deste Space gratuito: timeout de 30 segundos por requisição, |
| > sem persistência entre sessões, latência variável. |
| """ |
|
|
|
|
| with gr.Blocks(title=f"{_PRODUCT_NAME} · Agente") as demo: |
| gr.Markdown(INTRO) |
|
|
| with gr.Tabs(): |
| |
| |
| |
| with gr.Tab("Single ReAct · rápido"): |
| gr.Markdown( |
| "**Loop ReAct**: o LLM raciocina, chama tool, recebe resultado, " |
| "itera até resposta final. Limite: 8 iterações." |
| ) |
| with gr.Row(): |
| with gr.Column(scale=2): |
| s_input = gr.Textbox( |
| label="Sua pergunta", |
| placeholder=_EXAMPLE_PROMPTS[0], |
| lines=3, |
| ) |
| gr.Examples( |
| examples=[[p] for p in _EXAMPLE_PROMPTS], |
| inputs=s_input, |
| label="Exemplos · clique para preencher", |
| ) |
| s_button = gr.Button("Perguntar", variant="primary") |
| with gr.Column(scale=3): |
| s_answer = gr.Textbox( |
| label="Resposta final", |
| lines=8, |
| interactive=False, |
| ) |
| s_trace = gr.Markdown(label="Trace") |
|
|
| s_button.click( |
| fn=handle_single, |
| inputs=s_input, |
| outputs=[s_answer, s_trace], |
| ) |
|
|
| |
| |
| |
| with gr.Tab("Multi LangGraph · estruturado"): |
| gr.Markdown( |
| "**Plan-and-execute com self-critique**: 4 nós sequenciais " |
| "(planner → researcher → writer → critic). Mais lento, " |
| "mais estruturado. Padrão usado por Devin e Agentforce." |
| ) |
| with gr.Row(): |
| with gr.Column(scale=2): |
| m_input = gr.Textbox( |
| label="Sua pergunta", |
| placeholder=_EXAMPLE_PROMPTS[0], |
| lines=3, |
| ) |
| m_button = gr.Button("Executar pipeline", variant="primary") |
| with gr.Column(scale=3): |
| m_answer = gr.Textbox( |
| label="Resposta final", |
| lines=6, |
| interactive=False, |
| ) |
|
|
| gr.Markdown("### Trace dos 4 nós") |
| with gr.Row(): |
| m_plan = gr.Textbox(label="1. PLAN", lines=4, interactive=False) |
| m_context = gr.Textbox(label="2. CONTEXT", lines=4, interactive=False) |
| with gr.Row(): |
| m_draft = gr.Textbox(label="3. DRAFT", lines=4, interactive=False) |
| m_critique = gr.Textbox(label="4. CRITIQUE", lines=4, interactive=False) |
|
|
| m_button.click( |
| fn=handle_multi, |
| inputs=m_input, |
| outputs=[m_answer, m_plan, m_context, m_draft, m_critique], |
| ) |
|
|
| gr.Markdown( |
| """ |
| --- |
| Engenharia de IA · Prof. Sergio Gaiotto · 2026. |
| Código completo em [GitHub](https://github.com/) · Model Card em `docs/MODEL_CARD_v4.md`. |
| """ |
| ) |
|
|
|
|
| |
| if __name__ == "__main__": |
| demo.queue() |
| demo.launch(theme=gr.themes.Soft()) |
|
|