Keyword search is fine until someone types what they mean instead of what you named it. A user searches "how do I control who can access content" and your docs call it "RBAC." Keyword search shrugs. The words don't match.
I wanted barakoCMS to do better than that without turning into a service that phones home to an API and bills me per query. So the AI module does semantic search with embeddings, and the embeddings come from a model I run myself.
The short version of how it works: you turn each piece of content into a vector, a list of numbers that captures its meaning. You turn the search query into a vector the same way. Then you find the stored vectors closest to the query's. "Access control" and "RBAC" land near each other in that number-space even though they share no letters, because the model learned they mean similar things.
The part I'm happiest about is where the model runs. Ollama, on the same modest server that runs everything else. No API key, nothing leaves the box. I pull an embedding model once, and indexing a piece of content is a local call. When I tested it against the real docs, "control who can access content" came back with the RBAC guide first, then external auth, then the endpoints reference. That's the answer a human would give.
I did learn where the limits are, the hard way. Embeddings are cheap because they're a single pass through the model. Generating text is not. When I tried to also run a chat model on that same server for an "ask the docs" feature, it fell over. The box is a four-core machine already busy running a dozen containers, and text generation is hundreds of sequential passes fighting for a CPU that's already pinned. Even a tiny model timed out. So search stays self-hosted and fast, and the chat idea waits for either a spare GPU or a quieter machine. Knowing which half of a feature your hardware can actually carry is its own kind of engineering.
One thing I was careful about: the search only ever indexes and returns content that's already public. It runs each result through the same published-and-public check as the rest of the delivery API before handing it back. A clever query can't use search as a side door into a draft. Fast is nice. Not leaking is the requirement.