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.
425 lines
16 KiB
Markdown
425 lines
16 KiB
Markdown
# 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`
|
|
|
|
```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:
|
|
|
|
```java
|
|
// 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 `@ApplicationModuleListener` exige que o evento seja publicado via `ApplicationEventPublisher` do Spring. O Spring Modulith intercepta e persiste na tabela `event_publication` para garantia de entrega.
|
|
|
|
### 2.3 Novo Subscriber: `EmbeddingEventSubscriber`
|
|
|
|
**Arquivo a criar**: `job/service/EmbeddingEventSubscriber.java`
|
|
|
|
```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`
|
|
|
|
```java
|
|
public enum JobType {
|
|
ANALYZE_FILE,
|
|
STATIC_ANALYSIS,
|
|
GENERATE_EMBEDDINGS // <-- novo
|
|
}
|
|
```
|
|
|
|
### 3.2 Novo Handler: `EmbeddingGenerationHandler`
|
|
|
|
**Arquivo a criar**: `job/service/handler/EmbeddingGenerationHandler.java`
|
|
|
|
```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**: `EmbeddingGenerationHandler` não precisa de `@ConditionalOnProperty` como `StaticAnalysisHandler`, 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`
|
|
|
|
```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)
|
|
|
|
```java
|
|
@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:
|
|
|
|
```bash
|
|
# 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
|
|
```
|