backend/docs/ai/api-design.md
Rodrigo Verdiani 90ee74b3b9 docs: add AI module documentation and AGENTS.md
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.
2026-06-08 20:13:21 -03:00

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:

  • ChatRequest
  • SessionResponse
  • MessageResponse
  • StreamChunk (não documentado como schema, pois é SSE)