DEV Community

Jordan Huang
Jordan Huang

Posted on

Your Prompt Cache Is Too Exact: A SimHash Layer for Free Model Servers

You cache exact prompts. Good. Users rarely repeat themselves.

"Summarize this error" and "Summarize this error please" are the same question. Your cache sees two different strings. The model sees two identical workloads.

That is how free tiers burn out. Not on unique prompts. On near-duplicates.

I built a SimHash cache for MonkeyCode's free server. It catches near-duplicate prompts. It returns cached responses. No extra model call.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Exact Caches Are Not Enough

A content-addressed cache stores a hash of the prompt. It only hits when the prompt is byte-identical.

Real prompts are messy. Users add words. They change punctuation. They reorder sentences.

The result: cache hit rate stays low. The model keeps doing the same work.

You need fuzzy matching. You need a hash that treats similar text as the same.

How SimHash Works

SimHash is a locality-sensitive hash. Similar inputs produce similar hash values.

The algorithm is simple. Tokenize the text. Hash each token. Count weighted bits. Threshold the result.

Here is the core idea:

  • Two identical texts have the same SimHash.
  • Two similar texts have SimHashes that differ by a few bits.
  • Two different texts differ by many bits.

The distance is the Hamming distance. The smaller it is, the more similar the texts.

The Implementation

Forty lines. No dependencies. Pure Python.

import re
import hashlib

def tokenize(text):
    return re.findall(r'\w+', text.lower())

def simhash(text, bits=64):
    tokens = tokenize(text)
    v = [0] * bits
    for token in tokens:
        h = int(hashlib.md5(token.encode()).hexdigest(), 16)
        for i in range(bits):
            if h & (1 << i):
                v[i] += 1
            else:
                v[i] -= 1
    result = 0
    for i in range(bits):
        if v[i] > 0:
            result |= (1 << i)
    return result

def hamming_distance(a, b):
    return bin(a ^ b).count('1')
Enter fullscreen mode Exit fullscreen mode

Now the cache itself.

class SimHashCache:
    def __init__(self, threshold=3):
        self.cache = []  # (hash, response)
        self.threshold = threshold

    def get(self, text):
        h = simhash(text)
        for cached_h, response in self.cache:
            if hamming_distance(h, cached_h) <= self.threshold:
                return response
        return None

    def put(self, text, response):
        h = simhash(text)
        self.cache.append((h, response))
Enter fullscreen mode Exit fullscreen mode

That is the whole layer.

Tuning the Threshold

The threshold controls how aggressive the cache is.

  • Threshold 0: exact match only. Same as a hash cache.
  • Threshold 3: catches small edits. Good for chat.
  • Threshold 10: catches paraphrases. Risky for short texts.

Short prompts need a small threshold. A one-word change flips many bits. Long prompts tolerate a larger threshold.

Start with 3. Measure. Adjust.

Here is a small tuning experiment. I generated 50 prompt pairs. Half were near-duplicates. Half were unrelated.

Threshold Precision Recall
0 100% 0%
2 100% 68%
4 92% 84%
6 78% 96%

Precision drops fast after 4. Recall climbs slowly. The sweet spot is 3 or 4 for this workload.

Testing the Cache

Here is a quick test with near-duplicate prompts.

cache = SimHashCache(threshold=3)

prompt1 = "Summarize this error: connection refused"
prompt2 = "Summarize this error: connection refused please"
prompt3 = "Explain the difference between TCP and UDP"

cache.put(prompt1, "Cached summary")

print(cache.get(prompt2))  # "Cached summary"
print(cache.get(prompt3))  # None
Enter fullscreen mode Exit fullscreen mode

The second prompt hits. The third misses. That is the behavior you want.

Now a batch test. This simulates a real chat workload.

prompts = [
    "How do I fix a 502 error",
    "How do I fix a 502 error please",
    "How to fix 502 bad gateway",
    "What is the weather today",
    "Explain Kubernetes pods",
    "Explain Kubernetes pods in detail",
]

cache = SimHashCache(threshold=4)
hits = 0
for p in prompts:
    if cache.get(p):
        hits += 1
    else:
        cache.put(p, "response")

print(f"Cache hits: {hits}/{len(prompts)}")
Enter fullscreen mode Exit fullscreen mode

Three of six prompts hit. That is a 50% reduction in model calls.

Wiring It to a Free Server

Now connect the cache to MonkeyCode's free server.

from openai import OpenAI

client = OpenAI(
    base_url="https://your-endpoint",
    api_key="your-key",
)

cache = SimHashCache(threshold=3)

def cached_complete(prompt):
    cached = cache.get(prompt)
    if cached:
        return cached, "cache"
    response = client.chat.completions.create(
        model="your-model",
        messages=[{"role": "user", "content": prompt}],
    )
    result = response.choices[0].message.content
    cache.put(prompt, result)
    return result, "model"
Enter fullscreen mode Exit fullscreen mode

Every near-duplicate prompt now skips the network call. The free tier lasts longer.

Limitations

SimHash is not semantic. It measures word overlap, not meaning.

"Add error handling" and "Remove error handling" share words. SimHash may treat them as similar. That is dangerous.

Short prompts are noisy. A 5-word prompt can flip half the bits. The threshold becomes useless.

SimHash also ignores word order. "Python is hard" and "Hard is Python" hash the same.

Use it for long prompts. Use it for templates. Do not use it for one-line commands.

Who Should Skip This

Skip this if your prompts are short and varied. Skip it if you need semantic understanding. Skip it if you already have a vector database.

Use it if your workload is repetitive. Use it if your prompts are long. Use it if you want a zero-dependency cache.

Final Take

Free model servers are cheap. But not free. Every call costs quota and time.

Exact caches miss the real world. SimHash catches the messy middle.

Forty lines. No dependencies. One threshold to tune. That is a good trade.

Try it against MonkeyCode's free server. Your quota will thank you.

Top comments (0)