ACAI — Chapter 20: Data Engineering, Knowledge Systems, RAG, Vector Search, Memory, Data Pipelines, and Knowledge Quality

Image
  20.1 Objective An advanced AI system is only as useful as the information it can reliably access. ACAI therefore needs a complete knowledge architecture: DATA ↓ INGESTION ↓ PROCESSING ↓ STORAGE ↓ INDEXING ↓ RETRIEVAL ↓ RERANKING ↓ CONTEXT ↓ MODEL ↓ VERIFICATION ↓ ANSWER The purpose of this chapter is to explain how ACAI can turn raw information into searchable, trustworthy context. 20.2 Data Sources ACAI may receive information from many sources: Documents Web pages Databases APIs User uploads Internal knowledge Application records Structured datasets Images Audio Video Different sources require different processing pipelines. 20.3 Data Ingestion Ingestion means bringing information into the system. SOURCE ↓ INGESTION SERVICE ↓ RAW DATA ↓ PROCESSING PIPELINE The ingestion layer should record where the information came from. Example metadata: { "source_id": "source_001", "source_type": "document", "created_at": ...

ACAI — Chapter 3: Retrieval-Augmented Generation (RAG)

 

Post cover


3.1 Objective

Chapter 2 introduced the Planner.

The system can now determine the type and complexity of a request:

User
 ↓
API
 ↓
Orchestrator
 ↓
Planner
 ↓
Plan
 ↓
Model
 ↓
Response

The next problem is that the model may not have the information required to answer a specific question.

Chapter 3 introduces a Retrieval-Augmented Generation (RAG) layer.

The new architecture becomes:

User
 ↓
API
 ↓
Orchestrator
 ↓
Planner
 ↓
Retrieval
 ↓
Relevant Context
 ↓
Model
 ↓
Response

The goal is to allow ACAI to work with external documents instead of relying exclusively on the model's built-in knowledge.


3.2 What Is RAG?

RAG stands for:

Retrieval-Augmented Generation.

The basic idea is:

Documents
   ↓
Split into chunks
   ↓
Index
   ↓
Search
   ↓
Retrieve relevant chunks
   ↓
Give context to model
   ↓
Generate response

For example, suppose the user uploads a document containing:

ACAI Architecture Specification

The system can search that document when the user asks:

"What does the architecture say about model routing?"

Instead of asking the model to answer from memory alone, ACAI first retrieves relevant information.


3.3 Why Retrieval Is Important

Without retrieval:

Question
   ↓
Model
   ↓
Possible answer

With retrieval:

Question
   ↓
Search knowledge
   ↓
Relevant evidence
   ↓
Model
   ↓
Evidence-based answer

This can be particularly useful for:

  • Private documents

  • Project documentation

  • Research papers

  • Internal knowledge bases

  • Product documentation

  • User-uploaded files


3.4 Chapter 3 Architecture

                         USER
                           │
                           ▼
                      FastAPI API
                           │
                           ▼
                   ACAI Orchestrator
                           │
                           ▼
                        Planner
                           │
                           ▼
                      Retrieval
                           │
                 ┌─────────┴─────────┐
                 ▼                   ▼
            User Query          Knowledge Base
                                      │
                                      ▼
                               Relevant Chunks
                                      │
                 └─────────┬─────────┘
                           ▼
                     Model Service
                           │
                           ▼
                        Response

3.5 Practical Chapter 3 Strategy

For the first implementation, we will deliberately avoid introducing a large external vector database.

Instead, we will create a simple local retrieval engine.

This gives us:

✓ Document storage
✓ Text chunking
✓ Keyword retrieval
✓ Context construction
✓ Orchestrator integration
✓ Tests

Later, this can be replaced by an embedding/vector database implementation.

This approach keeps the architecture understandable and runnable.


3.6 Updated Project Structure

Add the following files:

ACAI/
└── backend/
    ├── app/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── config.py
    │   ├── schemas.py
    │   ├── orchestrator.py
    │   │
    │   └── services/
    │       ├── __init__.py
    │       ├── model_service.py
    │       ├── planner.py
    │       └── retrieval.py
    │
    ├── data/
    │   └── documents/
    │
    └── tests/
        ├── test_api.py
        ├── test_planner.py
        └── test_retrieval.py

3.7 Retrieval Data Model

Create app/services/retrieval.py.

from dataclasses import dataclass


@dataclass
class DocumentChunk:
    document_id: str
    chunk_id: str
    text: str

A document can contain multiple chunks:

Document
   │
   ├── Chunk 1
   ├── Chunk 2
   ├── Chunk 3
   └── Chunk 4

3.8 Document Chunking

Add:

class DocumentStore:

    def __init__(self) -> None:
        self.chunks: list[DocumentChunk] = []

    def add_document(
        self,
        document_id: str,
        text: str,
        chunk_size: int = 500,
    ) -> None:

        words = text.split()

        for index in range(0, len(words), chunk_size):
            chunk_words = words[index:index + chunk_size]

            if not chunk_words:
                continue

            chunk = DocumentChunk(
                document_id=document_id,
                chunk_id=f"{document_id}-{index}",
                text=" ".join(chunk_words),
            )

            self.chunks.append(chunk)

    def clear(self) -> None:
        self.chunks.clear()

This is a simple word-based chunker.

It is intentionally not pretending to be a sophisticated semantic chunking algorithm.


3.9 Keyword Retrieval

Add:

class RetrievalService:

    def __init__(self) -> None:
        self.store = DocumentStore()

    def add_document(
        self,
        document_id: str,
        text: str,
    ) -> None:

        self.store.add_document(
            document_id=document_id,
            text=text,
        )

    def search(
        self,
        query: str,
        top_k: int = 3,
    ) -> list[DocumentChunk]:

        query_words = self._tokenize(query)

        if not query_words:
            return []

        scored_chunks = []

        for chunk in self.store.chunks:

            chunk_words = self._tokenize(chunk.text)

            score = sum(
                word in chunk_words
                for word in query_words
            )

            if score > 0:
                scored_chunks.append(
                    (score, chunk)
                )

        scored_chunks.sort(
            key=lambda item: item[0],
            reverse=True,
        )

        return [
            chunk
            for _, chunk in scored_chunks[:top_k]
        ]

    def build_context(
        self,
        query: str,
        top_k: int = 3,
    ) -> str:

        chunks = self.search(
            query=query,
            top_k=top_k,
        )

        if not chunks:
            return ""

        return "\n\n".join(
            f"[Source: {chunk.document_id}]\n"
            f"{chunk.text}"
            for chunk in chunks
        )

    @staticmethod
    def _tokenize(text: str) -> set[str]:

        return {
            word.strip(".,!?;:()[]{}\"'").lower()
            for word in text.split()
            if word.strip()
        }


retrieval_service = RetrievalService()

3.10 Understanding the Retrieval Process

Suppose the knowledge base contains:

ACAI uses a model router to select an appropriate
model according to task requirements, latency,
cost, and model availability.

User query:

"How does ACAI select models?"

The retrieval system extracts terms such as:

how
does
acai
select
models

It searches the stored chunks and identifies the chunk containing matching terms.

The result is then provided to the model.


3.11 Improving the Model Prompt

The model service should now be able to receive optional context.

Update app/services/model_service.py:

from app.config import settings


class ModelService:

    def __init__(self) -> None:
        self.provider = settings.model_provider
        self.model_name = settings.model_name

    async def generate(
        self,
        prompt: str,
        context: str = "",
    ) -> str:

        if self.provider == "mock":
            return self._mock_generate(
                prompt=prompt,
                context=context,
            )

        raise RuntimeError(
            f"Unsupported model provider: {self.provider}"
        )

    def _mock_generate(
        self,
        prompt: str,
        context: str = "",
    ) -> str:

        if context:
            return (
                "ACAI Demo Model Response\n\n"
                f"Question:\n{prompt}\n\n"
                f"Retrieved Context:\n{context}\n\n"
                "The response was generated using "
                "retrieved context."
            )

        return (
            "ACAI Demo Model Response\n\n"
            f"Question:\n{prompt}\n\n"
            "No relevant context was retrieved."
        )


model_service = ModelService()

3.12 Integrating Retrieval into the Orchestrator

Update app/orchestrator.py:

from app.services.model_service import model_service
from app.services.planner import planner
from app.services.retrieval import retrieval_service


class ACAIOrchestrator:

    async def process(self, message: str) -> dict:

        cleaned_message = message.strip()

        if not cleaned_message:
            raise ValueError(
                "Message cannot be empty."
            )

        plan = planner.create_plan(
            cleaned_message
        )

        context = retrieval_service.build_context(
            query=cleaned_message,
            top_k=3,
        )

        response = await model_service.generate(
            prompt=cleaned_message,
            context=context,
        )

        return {
            "response": response,
            "plan": {
                "task_type": plan.task_type,
                "complexity": plan.complexity,
                "steps": plan.steps,
            },
            "retrieval": {
                "used": bool(context),
                "context": context,
            },
        }


orchestrator = ACAIOrchestrator()

3.13 Update the API Schema

Update app/schemas.py:

from pydantic import BaseModel, Field


class ChatRequest(BaseModel):

    message: str = Field(
        ...,
        min_length=1,
        max_length=10000,
    )


class PlanResponse(BaseModel):

    task_type: str
    complexity: str
    steps: list[str]


class RetrievalResponse(BaseModel):

    used: bool
    context: str


class ChatResponse(BaseModel):

    success: bool
    response: str
    model: str
    mode: str
    plan: PlanResponse
    retrieval: RetrievalResponse

3.14 Add a Knowledge Document

Create:

data/documents/acai.txt

Example:

ACAI is a modular AI architecture.

The architecture contains an orchestrator responsible
for coordinating system components.

The planner analyzes incoming requests and determines
the task type and complexity.

The retrieval layer searches a knowledge base and
provides relevant context to the model.

The model router can select different models according
to task requirements.

The verification layer evaluates generated results
before the final response is returned.

The system should be evaluated using benchmarks,
baselines, controlled experiments, and ablation studies.

3.15 Load the Document

For the initial prototype, add a simple startup loader.

Create app/services/knowledge_loader.py:

from pathlib import Path

from app.services.retrieval import retrieval_service


def load_knowledge_base() -> None:

    documents_path = Path("data/documents")

    if not documents_path.exists():
        return

    for file_path in documents_path.glob("*.txt"):

        text = file_path.read_text(
            encoding="utf-8"
        )

        retrieval_service.add_document(
            document_id=file_path.name,
            text=text,
        )

3.16 Load Knowledge at Startup

Update app/main.py:

from fastapi import FastAPI, HTTPException

from app.config import settings
from app.orchestrator import orchestrator
from app.schemas import ChatRequest, ChatResponse
from app.services.knowledge_loader import load_knowledge_base


app = FastAPI(
    title=settings.app_name,
    version=settings.app_version,
    description="Adaptive Cognitive AI Architecture",
)


@app.on_event("startup")
async def startup_event():
    load_knowledge_base()


@app.get("/")
async def root():

    return {
        "name": settings.app_name,
        "version": settings.app_version,
        "status": "online",
    }


@app.get("/health")
async def health():

    return {
        "status": "healthy",
        "environment": settings.environment,
    }


@app.post(
    "/api/chat",
    response_model=ChatResponse,
)
async def chat(request: ChatRequest):

    try:

        result = await orchestrator.process(
            request.message
        )

        return ChatResponse(
            success=True,
            response=result["response"],
            model=settings.model_name,
            mode=settings.model_provider,
            plan=result["plan"],
            retrieval=result["retrieval"],
        )

    except ValueError as exc:

        raise HTTPException(
            status_code=400,
            detail=str(exc),
        )

    except Exception as exc:

        raise HTTPException(
            status_code=500,
            detail=f"ACAI processing error: {exc}",
        )

3.17 Test Retrieval

Create tests/test_retrieval.py:

from app.services.retrieval import RetrievalService


def test_document_storage():

    service = RetrievalService()

    service.add_document(
        document_id="test",
        text=(
            "ACAI uses a planner to analyze "
            "complex tasks."
        ),
    )

    results = service.search(
        "planner complex tasks"
    )

    assert len(results) > 0


def test_relevant_context():

    service = RetrievalService()

    service.add_document(
        document_id="research",
        text=(
            "The verification layer evaluates "
            "generated results."
        ),
    )

    context = service.build_context(
        "verification generated results"
    )

    assert "verification" in context.lower()


def test_no_results():

    service = RetrievalService()

    service.add_document(
        document_id="test",
        text="ACAI has a planner.",
    )

    results = service.search(
        "completely unrelated topic"
    )

    assert results == []

3.18 Test the Complete Pipeline

Start the server:

uvicorn app.main:app --reload

Open:

http://127.0.0.1:8000/docs

Send:

{
  "message": "How does ACAI use a planner?"
}

The system should now return information showing that retrieval was performed.

Conceptually:

{
  "success": true,
  "retrieval": {
    "used": true,
    "context": "[Source: acai.txt] ..."
  }
}

3.19 Complete Chapter 3 Flow

The architecture is now:

                         USER
                           │
                           ▼
                      FastAPI API
                           │
                           ▼
                   ACAI Orchestrator
                           │
                           ▼
                        Planner
                           │
                           ▼
                      Retrieval
                           │
                    ┌──────┴──────┐
                    ▼             ▼
                 Query       Knowledge Base
                                  │
                                  ▼
                           Relevant Chunks
                                  │
                    └──────┬──────┘
                           ▼
                     Model Service
                           │
                           ▼
                        Response

3.20 Why This Is Only Version 1 of RAG

The current implementation uses keyword matching.

It does not yet use:

Embeddings
Vector Database
Semantic Similarity
Reranking
Hybrid Search
Metadata Filtering

That is intentional.

The architecture can later evolve:

Keyword Search
      ↓
Embedding Search
      ↓
Vector Database
      ↓
Hybrid Retrieval
      ↓
Reranking
      ↓
Context Optimization

3.21 Future Vector Retrieval Architecture

A production-oriented version may eventually look like:

                 USER QUERY
                     │
                     ▼
                 Embedding
                     │
                     ▼
              Vector Database
                     │
              Top-K Candidates
                     │
                     ▼
                 Reranker
                     │
                     ▼
              Relevant Context
                     │
                     ▼
                   Model

This should be implemented only after the simpler retrieval system has been tested.


3.22 Retrieval Quality Metrics

Retrieval itself should be evaluated.

Important metrics include:

Precision
Recall
Top-K Retrieval Accuracy
Context Relevance
Context Coverage
Latency

For example:

Query
 ↓
Expected Relevant Chunk
 ↓
Retriever
 ↓
Top-3 Results

The benchmark can determine whether the relevant chunk appears in those results.


3.23 Retrieval Ablation Experiment

A useful experiment is:

Experiment A
Model Only

vs

Experiment B
Model + Retrieval

Then compare:

Accuracy
Completeness
Factuality
Latency
Cost

If retrieval does not provide meaningful improvement for a particular task, the architecture should not automatically use it.


3.24 Chapter 3 Success Criteria

Chapter 3 is complete when:

[✓] Retrieval service exists
[✓] Documents can be stored
[✓] Documents are chunked
[✓] Queries can retrieve chunks
[✓] Context can be constructed
[✓] Orchestrator uses retrieval
[✓] Knowledge files can be loaded
[✓] Retrieval tests pass
[✓] API exposes retrieval information

3.25 Important Engineering Principle

RAG should not be treated as:

"Put documents into AI."

A better mental model is:

Question
   ↓
Find evidence
   ↓
Select useful evidence
   ↓
Give evidence to model
   ↓
Generate
   ↓
Verify

This becomes particularly important when Chapter 6 introduces the verification layer.


3.26 Chapter 3 Summary

ACAI now has three fundamental layers:

Chapter 1
Core API
     ↓
Chapter 2
Planning
     ↓
Chapter 3
Retrieval

The resulting architecture is:

User
 ↓
API
 ↓
Orchestrator
 ├── Planner
 ├── Retrieval
 └── Model
 ↓
Response

The next major capability is persistent context.

That leads to:

Chapter 4 — Adaptive Memory System

The next chapter will introduce:

Short-Term Memory
Long-Term Memory
Memory Retrieval
Memory Importance
Memory Updates
Memory Tests

and integrate memory directly into the ACAI orchestration pipeline.

End of Chapter 3

Comments

Popular posts from this blog

Adaptive Cognitive AI (ACAI): Chapter 1 — Introduction & System Vision

Chapter 2 (Part 2) Knowledge Retrieval Engine

Adaptive Cognitive AI (ACAI) Chapter 2 (Part 1).