ADR-021: RAG Architecture for Knowledge-Augmented AI Assistance

<!-- MADR 3.0 Template - Markdown Any Decision Records --> <!-- Reference: https://adr.github.io/madr/ -->

Status

Implemented

Date

2025-12-07

Deciders

Context and Problem Statement

The idempiere-cli AI assistant (ADR-013) currently relies solely on the LLM's training data for iDempiere knowledge. This has limitations:

  1. Outdated Knowledge: LLM training cutoffs may miss recent iDempiere features
  2. Incomplete Coverage: Not all wiki pages, KB articles are in training data
  3. No Internal Knowledge: CloudEmpiere-specific K_Entry articles are proprietary
  4. No AD Context: Application Dictionary metadata (tables, windows, processes) not available

We need Retrieval-Augmented Generation (RAG) to provide the AI with up-to-date, contextual knowledge from multiple sources.

Decision Drivers

Considered Options

  1. PGVector + Local Embeddings (bge-small-en-q) - PostgreSQL vector extension with in-process ONNX model
  2. Qdrant Cloud - Managed vector database with API-based embeddings
  3. ChromaDB + OpenAI Embeddings - Lightweight local store with cloud embeddings
  4. Pinecone - Fully managed serverless vector database

Decision Outcome

Chosen option: "PGVector + Local Embeddings (bge-small-en-q)", because:

Confirmation

The decision is confirmed when:

Pros and Cons of the Options

Option 1: PGVector + Local Embeddings (chosen)

PostgreSQL vector extension with bge-small-en-q (384 dimensions, ~25MB ONNX model).

Option 2: Qdrant Cloud

Managed vector database with cloud-based embeddings.

Option 3: ChromaDB + OpenAI Embeddings

Lightweight local vector store with cloud embeddings.

Option 4: Pinecone

Fully managed serverless vector database.

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        CLI / AI Agent                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                  │
│  │ knowledge   │  │   ask       │  │ MCP Server  │                  │
│  │   command   │  │  command    │  │  (future)   │                  │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘                  │
│         │                │                │                          │
│         ▼                ▼                ▼                          │
│  ┌──────────────────────────────────────────────┐                   │
│  │              RagService (Facade)              │                   │
│  │  - ingest()  - search()  - getStatistics()   │                   │
│  └──────────────────────────────────────────────┘                   │
│         │                │                │                          │
│         ▼                ▼                ▼                          │
│  ┌────────────┐  ┌────────────┐  ┌────────────────┐                 │
│  │   Wiki     │  │  K_Entry   │  │  AD Metadata   │                 │
│  │  Ingestor  │  │  Ingestor  │  │   Ingestor     │                 │
│  └──────┬─────┘  └──────┬─────┘  └───────┬────────┘                 │
│         │                │                │                          │
│         └────────────────┼────────────────┘                          │
│                          ▼                                           │
│  ┌──────────────────────────────────────────────┐                   │
│  │       EmbeddingStoreProvider (PGVector)       │                   │
│  │  ┌──────────────────────────────────────┐    │                   │
│  │  │      EmbeddingModelProvider          │    │                   │
│  │  │   (bge-small-en-q, 384 dimensions)   │    │                   │
│  │  └──────────────────────────────────────┘    │                   │
│  └──────────────────────────────────────────────┘                   │
│                          │                                           │
└──────────────────────────┼───────────────────────────────────────────┘
                           ▼
┌──────────────────────────────────────────────────┐
│          PostgreSQL + pgvector                    │
│  ┌────────────────────────────────────────────┐  │
│  │          cli_embeddings table              │  │
│  │  - embedding_id (UUID)                     │  │
│  │  - text_segment (TEXT)                     │  │
│  │  - embedding (vector(384))                 │  │
│  │  - metadata (JSONB)                        │  │
│  │  - source_type (VARCHAR)                   │  │
│  │  - source_id (VARCHAR)                     │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘

Knowledge Sources

1. iDempiere Wiki (source_type: wiki)

External documentation from wiki.idempiere.org using MediaWiki API.

Discovery: Uses MediaWiki API (/w-en/api.php) for intelligent page discovery:

Default categories:

Ingestion: MediaWiki API discovery → HTTP fetch → Apache Tika (HTML→text) → Document splitter → Embeddings

Configuration:

idempiere.cli.rag.wiki.enabled=true
idempiere.cli.rag.wiki.api-url=https://wiki.idempiere.org/w-en/api.php
idempiere.cli.rag.wiki.base-url=https://wiki.idempiere.org/en/
idempiere.cli.rag.wiki.discover-pages=true
idempiere.cli.rag.wiki.categories=Developer,New_Features

2. CloudEmpiere K_Entry (source_type: k_entry)

Internal knowledge base articles (AD_Client_ID = 1000014).

SQL Query:

SELECT k_entry_id, name, keywords, textmsg, descriptionurl
FROM k_entry
WHERE ad_client_id = 1000014 AND isactive = &#39;Y&#39;

Document format: {Name}\n{Keywords}\n{TextMsg}

3. Application Dictionary (source_type: ad_table, ad_window, ad_process, ad_tab, ad_field)

Metadata about iDempiere tables, windows, processes, tabs, and fields.

Source types:

Note: AD_Element ingestion is disabled. AD_Field with DISTINCT ON columnname provides better context-specific help without duplicates.

Examples:

Configuration:

idempiere.cli.rag.ad-metadata.enabled=true
# Multiple languages can be ingested in same cycle (default: en_US,sk_SK,hu_HU)
idempiere.cli.rag.ad-metadata.languages=en_US,sk_SK,hu_HU

Translation support: Uses iDempiere's _Trl tables with COALESCE(trl.column, base.column) pattern for fallback to base language.

CLI Commands

# Initialize database (creates pgvector extension + embeddings table)
idempiere-cli knowledge init
idempiere-cli knowledge init --force  # Drop and recreate table

# Ingest all knowledge sources
idempiere-cli knowledge ingest --source all

# Ingest specific source
idempiere-cli knowledge ingest --source wiki
idempiere-cli knowledge ingest --source k_entry
idempiere-cli knowledge ingest --source ad_metadata

# Force re-ingestion (clear existing first)
idempiere-cli knowledge ingest --source all --force

# Search knowledge base
idempiere-cli knowledge search &quot;how to create a callout&quot;
idempiere-cli knowledge search &quot;invoice process&quot; --source k_entry

# Show statistics
idempiere-cli knowledge status

# Validate sources
idempiere-cli knowledge validate

# Clear embeddings
idempiere-cli knowledge clear --source wiki -y

LangChain4j Tools

The following @Tool methods are exposed to the AI agent:

Tool Description
searchKnowledge(query, sourceFilter) Semantic search across all knowledge
getKnowledgeStats() Get embedding counts by source
findADEntity(name, type) Look up specific AD table/window/process
lookupDevelopmentDocs(topic) Find development documentation

Configuration

Environment Variables

The CLI uses TWO separate PostgreSQL databases:

# 1. iDempiere Database (IDEMPIERE_DB_*) - Application data
export IDEMPIERE_DB_HOST=localhost      # PostgreSQL host
export IDEMPIERE_DB_PORT=5433           # PostgreSQL port (iDempiere default)
export IDEMPIERE_DB_NAME=idempiere      # Database name
export IDEMPIERE_DB_USER=adempiere      # Database user
export IDEMPIERE_DB_PASSWORD=adempiere  # Database password

# 2. RAG Vector Database (RAG_VECTORDB_PG_*) - Semantic search embeddings
#    NOTE: &quot;PG&quot; suffix clarifies this is PostgreSQL with pgvector extension
export RAG_VECTORDB_PG_HOST=localhost   # PostgreSQL host
export RAG_VECTORDB_PG_PORT=5432        # PostgreSQL port
export RAG_VECTORDB_PG_NAME=vector      # Database name
export RAG_VECTORDB_PG_USER=postgres    # Database user
export RAG_VECTORDB_PG_PASSWORD=postgres # Database password

Application Properties

# Embedding Store (PGVector)
idempiere.cli.rag.embedding.table=cli_embeddings
idempiere.cli.rag.embedding.dimension=1024
idempiere.cli.rag.embedding.create-table=true

# Wiki Ingestion (MediaWiki API)
idempiere.cli.rag.wiki.enabled=true
idempiere.cli.rag.wiki.api-url=https://wiki.idempiere.org/w-en/api.php
idempiere.cli.rag.wiki.base-url=https://wiki.idempiere.org/en/
idempiere.cli.rag.wiki.discover-pages=true
idempiere.cli.rag.wiki.categories=Developer,New_Features

# K_Entry Ingestion
idempiere.cli.rag.k-entry.enabled=true
idempiere.cli.rag.k-entry.client-id=1000014

# AD Metadata Ingestion
idempiere.cli.rag.ad-metadata.enabled=true

# Retrieval Settings
idempiere.cli.rag.retrieval.max-results=5
idempiere.cli.rag.retrieval.min-score=0.7

# Document Splitting
idempiere.cli.rag.splitter.max-segment-size=500
idempiere.cli.rag.splitter.max-overlap-size=50

Database Schema

-- Enable pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Embeddings table (auto-created if create-table=true)
CREATE TABLE IF NOT EXISTS cli_embeddings (
    embedding_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    text_segment TEXT NOT NULL,
    embedding vector(384),
    metadata JSONB,
    source_type VARCHAR(50) NOT NULL,
    source_id VARCHAR(255),
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Indexes for efficient retrieval
CREATE INDEX idx_cli_embeddings_vector ON cli_embeddings
    USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
CREATE INDEX idx_cli_embeddings_source ON cli_embeddings (source_type, source_id);

Prerequisites

  1. PostgreSQL with pgvector: Target database must have pgvector extension
  2. Network Access: For wiki ingestion, requires internet access to wiki.idempiere.org
  3. Database Access: For K_Entry, requires access to CloudEmpiere database

Dependencies Added

&lt;!-- pom.xml additions --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-pgvector&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-embeddings-bge-small-en-q&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-document-parser-apache-tika&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-easy-rag&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;

Implementation Files

File Purpose
rag/RagConfig.java Configuration properties
rag/RagService.java Main facade
rag/embedding/EmbeddingStoreProvider.java PGVector store
rag/embedding/EmbeddingModelProvider.java BGE model (ONNX)
rag/ingest/KnowledgeIngestor.java Ingestor interface
rag/ingest/WikiIngestor.java Wiki pages via MediaWiki API
rag/ingest/MediaWikiClient.java MediaWiki API client (discovery, timestamps)
rag/ingest/KEntryIngestor.java K_Entry table
rag/ingest/ADMetadataIngestor.java AD metadata (tables, windows, processes, tabs, fields)
ai/langchain/tools/RagTools.java @Tool methods
commands/RagCommand.java CLI commands

Path: /docs/developers/architecture/idempiere-hub/021-rag-architecture