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.
9.6 KiB
API Design — Módulo AI
Endpoints
Todos os endpoints são prefixados com o contexto da aplicação e seguem as convenções REST do projeto (OpenAPI 3.1 via SpringDoc).
| Método | Path | Descrição | Tag |
|---|---|---|---|
POST |
/binaries/{binaryId}/chat/sessions |
Criar nova sessão de chat | Chat |
GET |
/binaries/{binaryId}/chat/sessions |
Listar sessões do binário | Chat |
GET |
/chat/sessions/{sessionId} |
Detalhes da sessão | Chat |
DELETE |
/chat/sessions/{sessionId} |
Deletar sessão | Chat |
GET |
/chat/sessions/{sessionId}/messages |
Histórico de mensagens | Chat |
POST |
/chat/sessions/{sessionId}/messages |
Enviar mensagem (SSE stream) | Chat |
1. Criar Sessão
POST /binaries/{binaryId}/chat/sessions
Cria uma nova sessão de chat vinculada a um binário.
Query Parameters:
| Parâmetro | Tipo | Obrigatório | Default | Descrição |
|---|---|---|---|---|
model |
String | Não | app.ai.default-chat-provider |
Modelo LLM: gemma4:12b, gemma4:e4b, deepseek-chat |
Request Body: Vazio
Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"binaryId": "660e8400-e29b-41d4-a716-446655440001",
"projectId": "770e8400-e29b-41d4-a716-446655440002",
"title": "malware.exe — 2026-06-08T15:30:00Z",
"chatModel": "gemma4:12b",
"messageCount": 0,
"createdAt": "2026-06-08T15:30:00Z",
"updatedAt": "2026-06-08T15:30:00Z"
}
Erros:
| Código | Condição |
|---|---|
404 |
Binário não encontrado |
400 |
Modelo não suportado (ex: ?model=invalid-model) |
Exemplo curl:
curl -X POST "http://localhost:8080/binaries/{binaryId}/chat/sessions?model=deepseek-chat"
2. Listar Sessões
GET /binaries/{binaryId}/chat/sessions
Lista todas as sessões de chat de um binário, ordenadas por data de atualização (mais recente primeiro).
Response (200 OK):
[
{
"id": "550e8400-...",
"binaryId": "660e8400-...",
"projectId": "770e8400-...",
"title": "Analisando funções de rede",
"chatModel": "deepseek-chat",
"messageCount": 12,
"createdAt": "2026-06-08T15:30:00Z",
"updatedAt": "2026-06-08T16:45:00Z"
},
{
"id": "550e8401-...",
"binaryId": "660e8400-...",
"projectId": "770e8400-...",
"title": "Investigando crypto",
"chatModel": "gemma4:12b",
"messageCount": 5,
"createdAt": "2026-06-08T14:00:00Z",
"updatedAt": "2026-06-08T14:20:00Z"
}
]
Exemplo curl:
curl "http://localhost:8080/binaries/{binaryId}/chat/sessions"
3. Detalhes da Sessão
GET /chat/sessions/{sessionId}
Retorna os detalhes de uma sessão específica.
Response (200 OK):
{
"id": "550e8400-...",
"binaryId": "660e8400-...",
"projectId": "770e8400-...",
"title": "Analisando funções de rede",
"chatModel": "deepseek-chat",
"messageCount": 12,
"createdAt": "2026-06-08T15:30:00Z",
"updatedAt": "2026-06-08T16:45:00Z"
}
Erros: 404 se sessão não encontrada.
4. Deletar Sessão
DELETE /chat/sessions/{sessionId}
Deleta a sessão e todas as suas mensagens (cascade).
Response: 204 No Content
Exemplo curl:
curl -X DELETE "http://localhost:8080/chat/sessions/{sessionId}"
5. Histórico de Mensagens
GET /chat/sessions/{sessionId}/messages
Retorna o histórico de mensagens da sessão, ordenado cronologicamente (mais antigo primeiro).
Response (200 OK):
[
{
"id": "aa0e8400-...",
"sessionId": "550e8400-...",
"role": "USER",
"content": "Quais funções lidam com rede?",
"tokenCount": 12,
"createdAt": "2026-06-08T15:30:30Z"
},
{
"id": "bb0e8400-...",
"sessionId": "550e8400-...",
"role": "ASSISTANT",
"content": "O binário importa funções de rede da WS2_32.dll. As funções sub_401200 e sub_401300 utilizam send() e recv()...",
"tokenCount": 85,
"createdAt": "2026-06-08T15:30:35Z"
}
]
Erros: 404 se sessão não encontrada.
6. Enviar Mensagem (SSE Streaming)
POST /chat/sessions/{sessionId}/messages
Envia uma mensagem do usuário e retorna a resposta da LLM em streaming via Server-Sent Events (SSE).
Headers:
Content-Type: application/json
Accept: text/event-stream
Request Body:
{
"content": "Quais funções nesse binário lidam com criptografia?"
}
Response: 200 OK com Content-Type: text/event-stream
Protocolo SSE
O stream consiste em uma sequência de eventos data::
Evento: chunk
Fragmento de texto da resposta, enviado assim que gerado pela LLM:
data: {"type":"chunk","content":"O binário"}
data: {"type":"chunk","content":" utiliza "}
data: {"type":"chunk","content":"as seguintes"}
data: {"type":"chunk","content":" funções"}
data: {"type":"chunk","content":" relacionadas"}
data: {"type":"chunk","content":" a criptografia:"}
data: {"type":"chunk","content":"\n\n1. **sub_405000**"}
data: {"type":"chunk","content":" em 0x405000"}
...
Evento: done
Indica o fim do stream, com metadados da mensagem:
data: {"type":"done","messageId":"cc0e8400-e29b-...","sessionId":"550e8400-...","tokenCount":142}
Evento: error
Indica um erro durante o processamento:
data: {"type":"error","code":"EMBEDDING_FAILED","message":"Failed to generate embedding: Ollama connection refused"}
Após um evento error, a conexão é fechada. Nenhum evento done é enviado.
Tipos de Evento SSE
type |
Descrição | Campos |
|---|---|---|
chunk |
Fragmento de texto da resposta | content: String |
done |
Stream concluído com sucesso | messageId: UUID, sessionId: UUID, tokenCount: Integer |
error |
Erro durante o processamento | code: String, message: String |
Fluxo Interno Durante o SSE
1. Salva UserMessage (role=USER) no banco
2. Embedda a query do usuário (qwen3-embedding)
3. Busca similaridade no pgvector (top-K chunks)
4. (Opcional) Expande call graph
5. Constrói system prompt com contexto RAG
6. Monta lista de mensagens: [SystemPrompt, history, UserMessage]
7. Chama StreamingChatClient da LLM
8. Para cada token gerado: envia SSE "chunk"
9. Ao finalizar: salva AssistantMessage (role=ASSISTANT) no banco
10. Envia SSE "done" com messageId + tokenCount
11. Se erro em qualquer passo: envia SSE "error" e fecha conexão
Exemplo curl (com streaming)
curl -X POST "http://localhost:8080/chat/sessions/{sessionId}/messages" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"content": "Explique a função sub_401000"}'
Implementação no Controller
@PostMapping(value = "/chat/sessions/{sessionId}/messages",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@Operation(summary = "Send a message and stream the AI response",
tags = {"Chat"})
public Flux<ServerSentEvent<String>> sendMessage(
@PathVariable UUID sessionId,
@Valid @RequestBody ChatRequest request) {
return chatService.chat(sessionId, request.content())
.map(token -> ServerSentEvent.<String>builder()
.data("{\"type\":\"chunk\",\"content\":\"" + escapeJson(token) + "\"}")
.build())
.concatWith(chatService.finalizeMessage(sessionId)
.map(msg -> ServerSentEvent.<String>builder()
.data("{\"type\":\"done\",\"messageId\":\"" + msg.id() + "\","
+ "\"sessionId\":\"" + sessionId + "\","
+ "\"tokenCount\":" + msg.tokenCount() + "}")
.build()))
.onErrorResume(e -> Flux.just(
ServerSentEvent.<String>builder()
.data("{\"type\":\"error\",\"code\":\"INTERNAL\","
+ "\"message\":\"" + escapeJson(e.getMessage()) + "\"}")
.build()));
}
DTOs
ChatRequest
public record ChatRequest(
@NotBlank @Size(min = 1, max = 4000)
String content
) {}
ChatResponse (evento done)
public record ChatResponse(
UUID messageId,
UUID sessionId,
int tokenCount
) {}
SessionResponse
public record SessionResponse(
UUID id,
UUID binaryId,
UUID projectId,
String title,
String chatModel,
int messageCount,
Instant createdAt,
Instant updatedAt
) {}
MessageResponse
public record MessageResponse(
UUID id,
UUID sessionId,
String role,
String content,
Integer tokenCount,
Instant createdAt
) {}
StreamChunk (evento SSE chunk)
public record StreamChunk(
String type, // "chunk", "done", "error"
String content // null para "done" e "error"
) {}
Tratamento de Erros
| Código HTTP | type SSE |
Condição |
|---|---|---|
400 |
— | Request body inválido (content vazio, >4000 chars) |
404 |
— | Sessão não encontrada |
404 |
— | Binário não encontrado |
503 |
error |
Ollama indisponível (embeddings ou chat) |
503 |
error |
DeepSeek API indisponível |
500 |
error |
Erro interno (pgvector, RabbitMQ, etc.) |
402 |
error |
DeepSeek API: saldo insuficiente / quota excedida |
Resposta de erro padrão (não-SSE)
{
"status": 404,
"message": "Chat session with id '550e8400-...' not found",
"timestamp": "2026-06-08T15:30:00Z"
}
OpenAPI / Swagger
Todos os endpoints são documentados com @Operation e aparecem no Swagger UI (/swagger-ui.html).
Tag: Chat
Schemas:
ChatRequestSessionResponseMessageResponseStreamChunk(não documentado como schema, pois é SSE)