| name | bx-ai-rag |
| description | Use this skill when building RAG (Retrieval-Augmented Generation) systems with BoxLang AI: aiDocuments() for loading and chunking documents, aiEmbed() for embeddings, vector memory providers, ingesting documents into vector stores, and wiring RAG into agents. |
bx-ai: RAG (Retrieval-Augmented Generation)
RAG enhances AI responses by grounding them in your own documents, reducing hallucinations and keeping answers current without model retraining.
RAG Workflow
Documents → Load → Chunk → Embed → Vector DB
↓
User Query → Embed → Vector Search → Retrieve → Inject into Context → AI
Quick Start: Complete RAG System
vectorMemory = aiMemory( "chroma", config: {
collection : "knowledge_base",
embeddingProvider: "openai",
embeddingModel : "text-embedding-3-small",
serverUrl : "http://localhost:8000"
})
result = aiDocuments( "/path/to/docs", {
type : "directory",
recursive : true,
extensions: [ "md", "txt", "pdf" ]
}).toMemory(
memory = vectorMemory,
options = { chunkSize: 1000, overlap: 200 }
)
println( "Ingested #result.documentsIn# docs — #result.chunksOut# chunks" )
agent = aiAgent(
name : "KnowledgeBot",
description : "AI assistant with access to company knowledge base",
instructions: "Answer questions using only the provided documentation. If unsure, say so.",
memory : vectorMemory
)
response = agent.run( "How do I configure the ORM datasource?" )
println( response )
aiDocuments() — Loading Documents
doc = aiDocuments( "BoxLang is a modern JVM language", { type: "text" } )
doc = aiDocuments( expandPath( "./docs/readme.md" ), { type: "file" } )
docs = aiDocuments( expandPath( "./docs" ), {
type : "directory",
recursive : true,
extensions: [ "md", "txt", "html" ]
})
docs = aiDocuments( "https://boxlang.ortusbooks.com", { type: "url" } )
docs = aiDocuments( expandPath( "./manual.pdf" ), { type: "pdf" } )
Chunking Options
docs.toMemory(
memory = vectorMemory,
options = {
chunkSize : 1000,
overlap : 200,
chunkSeparator: "\n\n"
}
)
| Option | Recommended | Description |
|---|
chunkSize | 500–1500 | Larger = more context per chunk; smaller = more precise retrieval |
overlap | 10–20% of chunkSize | Prevents splitting mid-sentence at chunk boundaries |
chunkSeparator | "\n\n" | Natural split point for prose |
aiEmbed() — Generating Embeddings
embedding = aiEmbed( "BoxLang is a JVM language", {
provider: "openai",
model : "text-embedding-3-small"
})
embeddings = aiEmbed( [ "text one", "text two", "text three" ], {
provider: "openai",
model : "text-embedding-3-small"
})
Vector Memory Providers
mem = aiMemory( "in-memory-vector", config: {
embeddingProvider: "openai"
})
mem = aiMemory( "chroma", config: {
collection : "my_collection",
embeddingProvider: "openai",
serverUrl : "http://localhost:8000"
})
mem = aiMemory( "pinecone", config: {
index : "my-index",
embeddingProvider: "openai",
apiKey : server.system.environment.PINECONE_KEY,
environment : "us-east1-gcp"
})
mem = aiMemory( "weaviate", config: {
class : "Document",
embeddingProvider: "openai",
serverUrl : "http://localhost:8080"
})
Manual Retrieval
vectorMemory.add( "BoxLang supports closures, lambdas, and functional programming" )
vectorMemory.add( "The ORM module uses Hibernate under the hood" )
results = vectorMemory.getRelevant( "How do I use functional programming?", 3 )
context = results.map( r -> r.content ).toList( "\n\n" )
response = aiChat(
"Answer using only this context:\n\n#context#\n\nQuestion: How do I use functional programming in BoxLang?",
{ temperature: 0.2 }
)
Re-indexing / Updating Documents
vectorMemory.clear()
aiDocuments( "/docs", { type: "directory", recursive: true } )
.toMemory( memory = vectorMemory, options = { chunkSize: 800 } )
Multi-Tenant RAG
userMemory = aiMemory( "chroma",
key : createUUID(),
userId: auth.userId,
config: {
collection : "user_documents",
embeddingProvider: "openai"
}
)
aiDocuments( userUploadedFile, { type: "file" } )
.toMemory( memory = userMemory )
agent = aiAgent( name: "PersonalBot", memory: userMemory )
Best Practices
- ✅ Set
chunkSize based on your model's context window — stay well under the limit
- ✅ Use
overlap: 10–20% of chunkSize to avoid mid-sentence cuts
- ✅ Use smaller, focused embedding models (
text-embedding-3-small) for cost efficiency
- ✅ Re-ingest documents on a schedule (cron) when source documents update frequently
- ✅ Use multi-tenant isolation in any app where different users upload different docs
- ❌ Avoid vectorizing very short strings (< 50 chars) — embeddings lose quality
- ❌ Do NOT mix unrelated domains in one vector collection without metadata filtering