Comprehensive documentation for the AI module covering architecture, API design, data model, RAG pipeline, configuration, integration points, and frontend integration guide. Includes IDA5 extraction points reference and AGENTS.md with project conventions and coding standards.
16 KiB
Pontos de Integração — Módulo AI com Sistema Existente
Visão Geral
O módulo ai/ não existe isoladamente — ele se integra profundamente com os módulos existentes (workspace, analysis, job) através de eventos de domínio, dependências Modulith e jobs assíncronos.
Este documento descreve cada ponto de integração, o fluxo de dados e as modificações necessárias nos módulos existentes.
1. Diagrama de Sequência Completo
Usuário Controller AnalysisSvc JobModule AI Module Ollama/LLM
│ │ │ │ │ │
│ POST /upload │ │ │ │ │
├────────────────►│ │ │ │ │
│ │ save │ │ │ │
│ ├─────────────►│ │ │ │
│ │ │ BinaryUp- │ │ │
│ │ │ loadedEvent │ │ │
│ │ ├─────────────►│ │ │
│ │ │ │ ANALYZE_FILE │ │
│ │ │ │ (DiE Docker) │ │
│ │ │ │ │ │
│ POST /analyze │ │ │ │ │
├────────────────►│ │ │ │ │
│ │ │ StaticAnlys │ │ │
│ │ │ RequestedEvt │ │ │
│ │ ├─────────────►│ │ │
│ │ │ │ STATIC_ANALYS │ │
│ │ │ │ (IDA5 Docker) │ │
│ │ │◄─────────────┤ saveResult() │ │
│ │ │ │ │ │
│ │ │ StaticAnlys │ │ │
│ │ │ CompletedEvt │ │ │
│ │ ├─────────────►│ │ │
│ │ │ │ GENERATE_EMB │ │
│ │ │ ├──────────────►│ │
│ │ │ │ │ indexBinary() │
│ │ │ │ ├──────────────►│
│ │ │ │ │◄──────────────┤
│ │ │ │◄──────────────┤ embeddings │
│ │ │ │ │ │
│ │ │ │ (binário indexado) │
│ │ │ │ │ │
│ POST /chat/... │ │ │ │ │
├────────────────►│ │ │ │ │
│ │ │ │ │ chat() │
│ │ │ │ ├──────────────►│
│ │◄─────────────┼──────────────┼───────────────┤ SSE stream │
│◄────────────────┤ │ │ │ │
2. Eventos
2.1 Novo Evento: StaticAnalysisCompletedEvent
Definição: analysis/event/StaticAnalysisCompletedEvent.java
package ai.decompile.analysis.event;
import java.util.UUID;
/**
* Published when a static analysis job completes successfully.
* Triggers the embedding generation pipeline.
*/
public record StaticAnalysisCompletedEvent(
UUID binaryId,
UUID projectId,
UUID workspaceId,
UUID analysisId,
String engine
) {}
Quem publica: StaticAnalysisHandler.process() no módulo job
Quem consome: EmbeddingEventSubscriber no módulo job
Momento da publicação: Após analysisService.saveResult() e binaryService.markStaticAnalysisDone() concluírem com sucesso.
2.2 Publicação no StaticAnalysisHandler
Arquivo a modificar: job/service/handler/StaticAnalysisHandler.java
Mudança: Adicionar publicação do evento após salvar resultados:
// Dentro de process(), após saveResult() e markStaticAnalysisDone():
@Autowired
private ApplicationEventPublisher eventPublisher;
// ... no final de process():
var binary = binaryService.getBinary(job.getBinaryId());
eventPublisher.publishEvent(
new StaticAnalysisCompletedEvent(
binary.getId(),
binary.getProject().getId(),
binary.getProject().getWorkspace().getId(),
analysisResponse.id(),
engineName
)
);
Alternativa com Spring Modulith: Usar
@ApplicationModuleListenerexige que o evento seja publicado viaApplicationEventPublisherdo Spring. O Spring Modulith intercepta e persiste na tabelaevent_publicationpara garantia de entrega.
2.3 Novo Subscriber: EmbeddingEventSubscriber
Arquivo a criar: job/service/EmbeddingEventSubscriber.java
package ai.decompile.job.service;
import ai.decompile.analysis.event.StaticAnalysisCompletedEvent;
import ai.decompile.job.messaging.JobMessagePublisher;
import ai.decompile.job.model.entity.Job;
import ai.decompile.job.model.enums.JobStatus;
import ai.decompile.job.model.enums.JobType;
import ai.decompile.job.model.repository.JobRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.modulith.events.ApplicationModuleListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Log4j2
@Component
@RequiredArgsConstructor
public class EmbeddingEventSubscriber {
private final JobRepository jobRepository;
private final JobMessagePublisher jobMessagePublisher;
@ApplicationModuleListener
public void on(StaticAnalysisCompletedEvent event) {
log.info(">>> EmbeddingEventSubscriber: Received StaticAnalysisCompletedEvent binary={}", event.binaryId());
var job = createJob(event);
log.info(">>> EmbeddingEventSubscriber: Created GENERATE_EMBEDDINGS job {} for binary {}", job.getId(), event.binaryId());
jobMessagePublisher.publish(job.getId());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
Job createJob(StaticAnalysisCompletedEvent event) {
return jobRepository.save(
Job.builder()
.type(JobType.GENERATE_EMBEDDINGS)
.status(JobStatus.ENQUEUED)
.projectId(event.projectId())
.binaryId(event.binaryId())
.payload("{\"analysisId\":\"" + event.analysisId() + "\"}")
.retryCount(0)
.build()
);
}
}
3. Jobs
3.1 Novo JobType
Arquivo a modificar: job/model/enums/JobType.java
public enum JobType {
ANALYZE_FILE,
STATIC_ANALYSIS,
GENERATE_EMBEDDINGS // <-- novo
}
3.2 Novo Handler: EmbeddingGenerationHandler
Arquivo a criar: job/service/handler/EmbeddingGenerationHandler.java
package ai.decompile.job.service.handler;
import ai.decompile.ai.service.EmbeddingService;
import ai.decompile.job.model.entity.Job;
import ai.decompile.job.model.enums.JobStatus;
import ai.decompile.job.model.enums.JobType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class EmbeddingGenerationHandler implements JobHandler {
private final EmbeddingService embeddingService;
private final ObjectMapper objectMapper;
public EmbeddingGenerationHandler(EmbeddingService embeddingService, ObjectMapper objectMapper) {
this.embeddingService = embeddingService;
this.objectMapper = objectMapper;
}
@Override
public void process(Job job) {
log.info("Processing GENERATE_EMBEDDINGS job {} for binary {}", job.getId(), job.getBinaryId());
try {
int chunks = embeddingService.indexBinary(job.getBinaryId());
job.setResult("{\"chunkCount\":" + chunks + "}");
job.setStatus(JobStatus.COMPLETED);
log.info("Job {} completed. Generated {} embedding chunks for binary {}",
job.getId(), chunks, job.getBinaryId());
} catch (Exception e) {
log.error("Job {} failed: {}", job.getId(), e.getMessage(), e);
job.setStatus(JobStatus.FAILED);
job.setErrorMessage(e.getMessage());
}
}
@Override
public JobType supportedType() {
return JobType.GENERATE_EMBEDDINGS;
}
}
Nota:
EmbeddingGenerationHandlernão precisa de@ConditionalOnPropertycomoStaticAnalysisHandler, pois embeddings não dependem do Docker. Ollama roda no host.
3.3 Atualizar JobHandlerFactory
Nenhuma mudança necessária — o factory já descobre automaticamente todos os beans JobHandler via injeção de lista.
4. Dependências Modulith
4.1 Atualizar job/package-info.java
Arquivo a modificar: job/package-info.java
@org.springframework.modulith.ApplicationModule(
displayName = "Job",
allowedDependencies = {
"workspace::events",
"workspace::entities",
"workspace::services",
"die::service",
"die::model",
"analysis::events",
"analysis::services",
"analysis::dto",
"engine::services",
"ai::services", // <-- novo: EmbeddingService
"ai::model::entity", // <-- novo: EmbeddingChunk (se necessário)
"common"
})
package ai.decompile.job;
4.2 analysis/package-info.java
Não precisa ser alterado. O evento StaticAnalysisCompletedEvent é definido no próprio módulo analysis (analysis.event), então não há nova dependência.
4.3 ai/package-info.java (novo)
@org.springframework.modulith.ApplicationModule(
displayName = "AI",
allowedDependencies = {
"workspace::entities", // Binary, Project (para JPA relationships)
"workspace::services", // BinaryService (para buscar filePath, metadados)
"analysis::entities", // StaticFunction, StaticAnalysis, StaticXref, StaticLabel
"analysis::services", // AnalysisService (para buscar funções/xrefs)
"common" // exceções, config
})
package ai.decompile.ai;
5. Modificações em Arquivos Existentes
5.1 pom.xml
Adicionar dependências Spring AI (Spring AI BOM, ollama, openai, pgvector-store).
5.2 application.yml
Adicionar blocos spring.ai.* e app.ai.* (ver configuration.md).
5.3 job/model/enums/JobType.java
Adicionar GENERATE_EMBEDDINGS.
5.4 job/package-info.java
Adicionar ai::services e ai::model::entity aos allowedDependencies.
5.5 job/service/handler/StaticAnalysisHandler.java
Adicionar publicação de StaticAnalysisCompletedEvent ao final de process().
5.6 .gitignore
Adicionar .env.
6. Arquivos Novos (por módulo)
Módulo ai/
src/main/java/ai/decompile/ai/
├── package-info.java
├── config/
│ └── AiConfig.java
├── model/
│ ├── entity/
│ │ ├── package-info.java
│ │ ├── EmbeddingChunk.java
│ │ ├── ChatSession.java
│ │ └── ChatMessage.java
│ ├── repository/
│ │ ├── package-info.java
│ │ ├── EmbeddingChunkRepository.java
│ │ ├── ChatSessionRepository.java
│ │ └── ChatMessageRepository.java
│ └── dto/
│ ├── package-info.java
│ ├── ChatRequest.java
│ ├── SessionResponse.java
│ └── MessageResponse.java
├── service/
│ ├── package-info.java
│ ├── EmbeddingService.java
│ ├── RagService.java
│ └── ChatService.java
└── controller/
├── package-info.java
└── ChatController.java
Módulo analysis/
src/main/java/ai/decompile/analysis/
└── event/
└── StaticAnalysisCompletedEvent.java # novo
Módulo job/
src/main/java/ai/decompile/job/
├── service/
│ ├── EmbeddingEventSubscriber.java # novo
│ └── handler/
│ └── EmbeddingGenerationHandler.java # novo
└── model/enums/
└── JobType.java # modificado (+ GENERATE_EMBEDDINGS)
Database
src/main/resources/db/migration/
└── V008__ai_embedding_and_chat.sql # novo
Raiz do projeto
backend/
├── .env # novo
├── .env.example # novo (template sem secrets)
└── .gitignore # modificado (+ .env)
7. Ordem de Implementação Recomendada
| Ordem | Tarefa | Depende de |
|---|---|---|
| 1 | V008__ai_embedding_and_chat.sql |
Nada |
| 2 | Dependências no pom.xml |
Nada |
| 3 | application.yml — blocos AI |
Nada |
| 4 | .env + .env.example + .gitignore |
Nada |
| 5 | Entidades JPA (EmbeddingChunk, ChatSession, ChatMessage) |
V008, pom.xml |
| 6 | Repositórios JPA | Entidades |
| 7 | AiConfig.java |
pom.xml, application.yml |
| 8 | EmbeddingService |
Repositórios, AiConfig |
| 9 | RagService |
EmbeddingService, AnalysisService |
| 10 | ChatService |
RagService, AiConfig |
| 11 | ChatController |
ChatService |
| 12 | StaticAnalysisCompletedEvent |
Nada |
| 13 | EmbeddingEventSubscriber |
Evento, JobService |
| 14 | EmbeddingGenerationHandler |
EmbeddingService |
| 15 | JobType.GENERATE_EMBEDDINGS |
Nada |
| 16 | job/package-info.java update |
Dependências ai |
| 17 | StaticAnalysisHandler — publish event |
Evento |
| 18 | ai/package-info.java |
Dependências workspace, analysis |
| 19 | Testes de integração | Tudo acima |
8. Verificação de Integridade
Após implementar todos os pontos de integração, verificar:
# 1. Compilação limpa
./mvnw clean compile
# 2. Migration roda sem erros
./mvnw flyway:migrate
# 3. Testes de módulo Modulith
./mvnw test -Dtest="*Modulith*"
# 4. Fluxo completo (manual):
# - Upload de binário
# - Análise estática
# - Verificar job GENERATE_EMBEDDINGS criado e concluído
# - Verificar registros em embedding_chunk
# - Criar sessão de chat
# - Enviar mensagem e receber SSE stream