MODULO 4.7

🎨 Materializacao de Artefatos

Sistema transforma estrutura da IA em podcasts, resumos, flashcards e outros formatos.

8
Topicos
~50
Minutos
Avanc.
Nivel
Pratico
Tipo
1

🔀 Principio das Duas Fases

IA gera estrutura JSON, Sistema materializa artefato final.

Fase 1: IA Estrutura

# IA retorna sempre JSON
{
    "type": "podcast",
    "title": "Resumo do Documento",
    "segments": [
        {
            "speaker": "host",
            "text": "Bem-vindos...",
            "duration_estimate": 30
        }
    ]
}

Fase 2: Sistema Materializa

# Sistema gera arquivo final
def materialize(structure):
    if structure["type"] == "podcast":
        return PodcastMaterializer()
    elif structure["type"] == "summary":
        return SummaryMaterializer()
    elif structure["type"] == "flashcards":
        return FlashcardMaterializer()
2

🎙️ Materializador de Podcast

Converte estrutura em audio usando TTS.

class PodcastMaterializer:
    def __init__(self):
        self.tts = TTSService()
        self.voices = {
            "host": "pt-BR-FranciscaNeural",
            "guest": "pt-BR-AntonioNeural"
        }

    async def materialize(self, structure: dict) -> Artifact:
        audio_segments = []

        for segment in structure["segments"]:
            # Gera audio para cada segmento
            audio = await self.tts.synthesize(
                text=segment["text"],
                voice=self.voices[segment["speaker"]]
            )
            audio_segments.append(audio)

        # Concatena e adiciona transicoes
        final_audio = self._concatenate(audio_segments)
        final_audio = self._add_music(final_audio, structure.get("music"))

        return Artifact(
            type="podcast",
            content=final_audio,
            format="mp3",
            metadata={"duration": len(final_audio) / 1000}
        )
3

📄 Materializador de Resumo

Gera documentos formatados em varios formatos.

class SummaryMaterializer:
    async def materialize(
        self,
        structure: dict,
        output_format: str = "markdown"
    ) -> Artifact:
        # Estrutura da IA
        sections = structure["sections"]

        if output_format == "markdown":
            content = self._to_markdown(sections)
        elif output_format == "html":
            content = self._to_html(sections)
        elif output_format == "pdf":
            md = self._to_markdown(sections)
            content = await self._markdown_to_pdf(md)

        return Artifact(
            type="summary",
            content=content,
            format=output_format,
            metadata={
                "word_count": len(content.split()),
                "sections": len(sections)
            }
        )

    def _to_markdown(self, sections: list) -> str:
        md = ""
        for section in sections:
            md += f"## {section['title']}\n\n"
            md += f"{section['content']}\n\n"
        return md
4

🎴 Materializador de Flashcards

Cria decks de estudo em formato Anki.

class FlashcardMaterializer:
    async def materialize(self, structure: dict) -> Artifact:
        cards = structure["cards"]

        # Cria deck Anki
        deck = genanki.Deck(
            deck_id=random.randrange(1 << 30, 1 << 31),
            name=structure["deck_name"]
        )

        model = genanki.Model(
            model_id=random.randrange(1 << 30, 1 << 31),
            name="GIPM Card",
            fields=[{"name": "Front"}, {"name": "Back"}],
            templates=[{
                "name": "Card 1",
                "qfmt": "{{Front}}",
                "afmt": "{{FrontSide}}
{{Back}}" }] ) for card in cards: note = genanki.Note( model=model, fields=[card["question"], card["answer"]] ) deck.add_note(note) # Exporta como .apkg output = io.BytesIO() genanki.Package(deck).write_to_file(output) return Artifact( type="flashcards", content=output.getvalue(), format="apkg", metadata={"card_count": len(cards)} )
5

🏭 Factory de Materializadores

Pattern para selecionar o materializador correto.

class MaterializerFactory:
    """Factory que retorna o materializador apropriado"""

    _materializers = {
        "podcast": PodcastMaterializer,
        "summary": SummaryMaterializer,
        "flashcards": FlashcardMaterializer,
        "faq": FAQMaterializer,
        "timeline": TimelineMaterializer,
        "mindmap": MindmapMaterializer,
    }

    @classmethod
    def get(cls, artifact_type: str) -> BaseMaterializer:
        if artifact_type not in cls._materializers:
            raise UnsupportedArtifactError(artifact_type)
        return cls._materializers[artifact_type]()

    @classmethod
    def register(cls, artifact_type: str, materializer: type):
        """Permite adicionar novos materializadores"""
        cls._materializers[artifact_type] = materializer
6

✅ Validacao de Estrutura

Schemas que validam a estrutura antes de materializar.

from pydantic import BaseModel, validator
from typing import List, Literal

class PodcastSegment(BaseModel):
    speaker: Literal["host", "guest"]
    text: str
    duration_estimate: int

    @validator("text")
    def text_not_empty(cls, v):
        if len(v.strip()) < 10:
            raise ValueError("Segmento muito curto")
        return v

class PodcastStructure(BaseModel):
    type: Literal["podcast"]
    title: str
    segments: List[PodcastSegment]

    @validator("segments")
    def min_segments(cls, v):
        if len(v) < 2:
            raise ValueError("Podcast precisa de ao menos 2 segmentos")
        return v
7

💾 Armazenamento de Artefatos

Persistencia dos artefatos gerados.

class ArtifactStorage:
    def __init__(self, base_path: str = "./artifacts"):
        self.base_path = Path(base_path)

    async def save(self, artifact: Artifact, notebook_id: int) -> str:
        # Gera path unico
        filename = f"{notebook_id}_{artifact.type}_{uuid4().hex[:8]}.{artifact.format}"
        path = self.base_path / str(notebook_id) / filename

        # Cria diretorio se necessario
        path.parent.mkdir(parents=True, exist_ok=True)

        # Salva conteudo
        if isinstance(artifact.content, bytes):
            path.write_bytes(artifact.content)
        else:
            path.write_text(artifact.content)

        # Registra no banco
        await self._register_artifact(artifact, path, notebook_id)

        return str(path)
8

🔗 Integracao com Pipeline

Como a materializacao se encaixa no pipeline.

class GenerationPipeline(UniversalPipeline):
    """Pipeline especializado para geracao de artefatos"""

    async def phase_output(self, context, ai_response) -> PipelineResult:
        # Passo 7: Materializar (especializado)
        structure = ai_response.content
        materializer = MaterializerFactory.get(structure["type"])

        # Valida estrutura
        schema = self._get_schema(structure["type"])
        validated = schema.parse_obj(structure)

        # Materializa
        artifact = await materializer.materialize(validated.dict())

        # Passo 8: Persistir
        artifact_path = await self.storage.save(artifact, context.notebook_id)

        # Passo 9: Retornar
        return PipelineResult(
            request_id=context.request_id,
            artifact_type=artifact.type,
            artifact_path=artifact_path,
            metadata=artifact.metadata
        )

📝 Resumo do Modulo

Duas Fases - IA estrutura, Sistema materializa
Materializadores - Podcast, Summary, Flashcards
Factory - Extensivel para novos tipos
Validacao - Schemas garantem qualidade