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