Skip to main content
Cache-Pot provides three ready-to-use building blocks for AI-powered applications: semantic caching to cut LLM costs by reusing answers to similar questions, vector search for similarity-based retrieval in RAG pipelines, and agent memory for stateful sessions that survive across turns. All three are exposed over the same RESP2 connection your existing Redis client already speaks — no extra services, no SDKs to install.

Pattern 1: Semantic response caching

Save LLM responses keyed by meaning, not by exact text. When a user asks something semantically equivalent to a question you have already answered — even if the wording is completely different — Cache-Pot returns the stored response instead of making another model call.
import redis
import openai

r = redis.Redis(host='localhost', port=6379)
oai = openai.OpenAI()

def ask(question: str) -> str:
    # Check semantic cache first
    cached = r.execute_command('SCACHE.GET', question)
    if cached:
        return cached.decode()

    # Cache miss — call the LLM
    response = oai.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': question}]
    )
    answer = response.choices[0].message.content

    # Store with 1-hour TTL
    r.execute_command('SCACHE.SET', question, answer, 'TTL', 3600)
    return answer
SCACHE.GET and SCACHE.SET require an embeddings provider. Set CACHEPOT_EMBED_URL and CACHEPOT_EMBED_KEY on the Cache-Pot server before using these commands. A free local Ollama instance or an OpenAI API key both work.
You can tune how closely a new question must match a stored one by passing an explicit THRESHOLD:
# Require a very close match (default is 0.9)
cached = r.execute_command('SCACHE.GET', question, 'THRESHOLD', '0.95')

# Accept looser matches to increase the hit rate
cached = r.execute_command('SCACHE.GET', question, 'THRESHOLD', '0.80')
Cache-Pot tracks hit and miss counts and surfaces a running hit ratio on the dashboard at http://localhost:8080 — every hit is a model call you did not pay for. Store document embeddings and retrieve the most relevant ones for retrieval-augmented generation (RAG). Your application produces the embeddings; Cache-Pot stores and searches them with cosine similarity.
def index_document(doc_id: str, text: str):
    # Generate an embedding with your model of choice
    resp = oai.embeddings.create(input=text, model='text-embedding-3-small')
    vec = resp.data[0].embedding

    # Store the vector with a metadata snippet
    args = ['VSET', 'documents', doc_id] + [str(f) for f in vec] + ['META', text[:100]]
    r.execute_command(*args)

def search_documents(query: str, top_k: int = 5):
    # Embed the query and find the closest documents
    resp = oai.embeddings.create(input=query, model='text-embedding-3-small')
    vec = resp.data[0].embedding

    args = ['VSEARCH', 'documents'] + [str(f) for f in vec] + ['TOPK', str(top_k), 'WITHSCORES']
    return r.execute_command(*args)
All vectors in a collection must share the same dimension, fixed by the first VSET call. VDIM documents returns the dimension of an existing collection. VCARD documents returns the number of stored vectors.
A full RAG loop — index at write time, search at query time, inject top results into the prompt — needs only these two functions plus your LLM call:
def rag_answer(user_query: str) -> str:
    # Retrieve relevant chunks
    results = search_documents(user_query, top_k=3)

    # results is a flat list: [id, meta, score, id, meta, score, ...]
    context_chunks = [results[i + 1].decode() for i in range(0, len(results), 3)]
    context = '\n'.join(context_chunks)

    response = oai.chat.completions.create(
        model='gpt-4o-mini',
        messages=[
            {'role': 'system', 'content': f'Answer using this context:\n{context}'},
            {'role': 'user', 'content': user_query},
        ]
    )
    return response.choices[0].message.content

Pattern 3: Agent session memory

Store per-user or per-session state that persists across agent turns. REMEMBER writes a named field into a session namespace; RECALL reads one field or the entire session back.
def agent_turn(session_id: str, user_message: str) -> str:
    # Recall what we know about this session
    memory = r.execute_command('RECALL', session_id)
    context = dict(zip(memory[0::2], memory[1::2])) if memory else {}

    # Build a system prompt from stored facts
    user_name = context.get(b'user_name', b'').decode()
    turn_count = int(context.get(b'turn_count', b'0'))

    system_prompt = f"You are a helpful assistant. The user's name is {user_name}." if user_name else \
                    "You are a helpful assistant."

    response = oai.chat.completions.create(
        model='gpt-4o-mini',
        messages=[
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': user_message},
        ]
    )
    answer = response.choices[0].message.content

    # Update memory for the next turn
    r.execute_command('REMEMBER', session_id, 'last_message', user_message)
    r.execute_command('REMEMBER', session_id, 'turn_count', str(turn_count + 1))

    return answer
Recall a single field instead of the whole session by passing the field name:
# Recall only the user's name
name = r.execute_command('RECALL', session_id, 'user_name')
REMEMBER and RECALL are backed by a per-session hash named mem:<session>. You can inspect or edit session memory directly with HGETALL mem:<session> using any Redis client or the Cache-Pot dashboard.

Putting it all together

Combine all three patterns in a single agent pipeline: check the semantic cache → recall session memory → search the vector store for context → generate a response → update the cache and memory. The entire loop runs over one RESP2 connection with no extra services.
def full_agent_turn(session_id: str, user_message: str) -> str:
    # 1. Check semantic cache — skip the LLM entirely if we have a close match
    cached = r.execute_command('SCACHE.GET', user_message, 'THRESHOLD', '0.92')
    if cached:
        return cached.decode()

    # 2. Recall session memory for personalisation
    memory = r.execute_command('RECALL', session_id)
    context = dict(zip(memory[0::2], memory[1::2])) if memory else {}

    # 3. Retrieve relevant documents from the vector store
    results = search_documents(user_message, top_k=3)
    context_chunks = [results[i + 1].decode() for i in range(0, len(results), 3)] if results else []

    # 4. Generate a response
    answer = oai.chat.completions.create(
        model='gpt-4o-mini',
        messages=[
            {'role': 'system', 'content': 'Context:\n' + '\n'.join(context_chunks)},
            {'role': 'user', 'content': user_message},
        ]
    ).choices[0].message.content

    # 5. Update the semantic cache and session memory
    r.execute_command('SCACHE.SET', user_message, answer, 'TTL', 3600)
    r.execute_command('REMEMBER', session_id, 'last_message', user_message)

    return answer