haiku.rag/docs/rlm.md
Yiorgis Gozadinos 56cf6ebfb6
Docs
2026-02-06 12:07:18 +01:00

5.9 KiB

RLM Agent (Recursive Language Model)

The RLM agent enables complex analytical tasks by writing and executing Python code in a sandboxed environment. It solves problems that traditional RAG struggles with:

  • Aggregation: "How many documents mention security vulnerabilities?"
  • Computation: "What's the average revenue across all quarterly reports?"
  • Multi-document analysis: "Compare the key findings between Report A and Report B"
  • Structured data extraction: "Extract all tables from the document and summarize them"

How It Works

  1. The agent receives a question
  2. It writes Python code to explore the knowledge base
  3. Code executes in a sandboxed environment with access to haiku.rag functions
  4. The agent iterates: run code, examine results, refine approach
  5. Final answer is synthesized from the gathered data

CLI Usage

# Basic usage
haiku-rag rlm "How many documents are in the database?"

# With document filter (restricts what the agent can access)
haiku-rag rlm "Summarize the key points" --filter "uri LIKE '%report%'"

# Pre-load specific documents
haiku-rag rlm "Compare these two reports" --document "Q1 Report" --document "Q2 Report"

Python Usage

from haiku.rag.client import HaikuRAG

async with HaikuRAG(path_to_db) as client:
    # Basic question
    answer = await client.rlm("How many documents mention 'security'?")
    print(answer)

    # With filter (agent can only see filtered documents)
    answer = await client.rlm(
        "What is the total revenue?",
        filter="title LIKE '%Financial%'"
    )

    # Pre-load specific documents
    answer = await client.rlm(
        "Compare the conclusions",
        documents=["Report A", "Report B"]
    )

Available Functions

Inside the sandbox, these functions are available (no imports needed):

search(query, limit=10)

Search the knowledge base using hybrid search (vector + full-text).

results = search("climate change impacts", limit=20)
for r in results:
    print(r['document_title'], r['score'])
    print(r['content'][:200])

Returns list of dicts with keys: chunk_id, content, document_id, document_title, document_uri, score, page_numbers, headings

list_documents(limit=10, offset=0)

List available documents in the knowledge base.

docs = list_documents(limit=100)
for doc in docs:
    print(doc['id'], doc['title'])

Returns list of dicts with keys: id, title, uri, created_at

get_document(id_or_title)

Get the full text content of a document by ID, title, or URI.

content = get_document("Q1 Report")
if content:
    print(len(content), "characters")

Returns the document content as a string, or None if not found.

get_docling_document(id_or_title)

Get the structured DoclingDocument object for advanced analysis of tables, figures, and document structure.

doc = get_docling_document("Technical Manual")
if doc:
    print(f"Tables: {len(doc.tables)}")
    print(f"Pictures: {len(doc.pictures)}")

    # Extract table data
    for table in doc.tables:
        for cell in table.data.table_cells:
            print(f"Row {cell.start_row_offset_idx}, Col {cell.start_col_offset_idx}: {cell.text}")

llm(prompt)

Call an LLM directly for classification, summarization, or extraction tasks.

content = get_document("Q1 Report")
sentiment = llm(f"Classify the sentiment as positive, negative, or mixed: {content}")
print(sentiment)

Use this when you have content and need LLM reasoning without RAG search.

Pre-loaded Documents

When documents are pre-loaded via the documents parameter, they're available as a documents variable:

# Available when documents are pre-loaded
for doc in documents:
    print(doc['title'], len(doc['content']))

Each document dict has keys: id, title, uri, content

Allowed Imports

The following standard library modules can be imported:

  • json - JSON encoding/decoding
  • re - Regular expressions
  • math - Mathematical functions
  • statistics - Statistical functions
  • collections - Specialized containers
  • itertools - Iterator utilities
  • functools - Higher-order functions
  • datetime - Date and time handling
  • typing - Type hints
import re
import json
from collections import Counter

# Extract and count patterns
results = search("error", limit=50)
error_types = []
for r in results:
    matches = re.findall(r'Error: (\w+)', r['content'])
    error_types.extend(matches)

print(Counter(error_types).most_common(10))

Security

The sandbox enforces several security measures:

  • Blocked builtins: eval, exec, compile, open, input, __import__, globals, locals, getattr, setattr, delattr
  • Blocked imports: os, sys, subprocess, shutil, socket, requests, builtins
  • Private attribute access blocked: Cannot access __dunder__ attributes (except common ones like __init__, __str__)
  • Execution timeout: Code execution times out after configurable limit (default 60s)
  • Output truncation: Large outputs are truncated to prevent memory issues

Context Filter

The filter parameter restricts what documents the agent can access. Unlike tool parameters, the filter is applied automatically and cannot be bypassed by the LLM:

# Agent can only see documents with "confidential" in the URI
answer = await client.rlm(
    "Summarize all findings",
    filter="uri LIKE '%confidential%'"
)

This is useful for:

  • Scoping to specific document sets
  • Enforcing access control
  • Limiting context for focused analysis

Configuration

RLM settings can be configured in haiku.rag.yaml:

rlm:
  model:
    provider: anthropic
    name: claude-sonnet-4-20250514
  code_timeout: 60.0      # Max seconds for code execution
  max_tool_calls: 20      # Max execute_code calls per question
  max_output_chars: 50000 # Truncate output after this many chars