1414import os
1515import sys
1616import time
17+ import uuid
1718
1819from dotenv import load_dotenv
1920from openai import AsyncOpenAI
@@ -35,7 +36,7 @@ def _require(name: str) -> str:
3536MOSS_PROJECT_KEY = _require ("MOSS_PROJECT_KEY" )
3637_require ("OPENAI_API_KEY" ) # read by AsyncOpenAI from the environment
3738
38- # cosine similarity above which a cached answer is "close enough" to reuse.
39+ # semantic similarity above which a cached answer is "close enough" to reuse.
3940# too low -> you answer questions people didn't quite ask; too high -> you miss.
4041THRESHOLD = 0.92
4142
@@ -50,10 +51,12 @@ def __init__(self, store):
5051 self .store = store
5152
5253 async def ask (self , question : str ) -> tuple [str , bool ]:
53- # 1. look for the closest question we've already answered
54- hit = await self .store .query (question , QueryOptions (top_k = 1 ))
54+ # 1. look for the closest question we've already answered.
55+ # alpha=1.0 -> pure semantic (embedding) match, so the score reflects
56+ # meaning rather than keyword overlap.
57+ hit = await self .store .query (question , QueryOptions (top_k = 1 , alpha = 1.0 ))
5558 if hit .docs and hit .docs [0 ].score >= THRESHOLD :
56- answer = hit .docs [0 ].metadata .get ("answer" )
59+ answer = ( hit .docs [0 ].metadata or {}) .get ("answer" )
5760 if answer is not None :
5861 return answer , True # cache hit — no LLM call
5962
@@ -62,7 +65,7 @@ async def ask(self, question: str) -> tuple[str, bool]:
6265 model = "gpt-4o-mini" ,
6366 messages = [{"role" : "user" , "content" : question }],
6467 )
65- answer = resp .choices [0 ].message .content
68+ answer = resp .choices [0 ].message .content or ""
6669
6770 # 3. remember it so any wording of it is instant next time
6871 await self .store .add_docs (
@@ -72,12 +75,12 @@ async def ask(self, question: str) -> tuple[str, bool]:
7275
7376
7477async def main ():
75- # A Moss session is the cache store. It holds the entries in memory for this
76- # run, so the demo is deterministic: the first question is a MISS and its
77- # paraphrase is a HIT. To make the cache persist across runs and processes,
78- # call `await store.push_index()` to upload it as a cloud index (it will then
79- # auto-load by name on the next run) .
80- store = await moss .session (index_name = "qa-cache" )
78+ # A Moss session is the cache store. We use a unique name per run so the demo
79+ # always starts empty and shows a clean MISS -> HIT (a session auto-loads an
80+ # existing cloud index of the same name, which would otherwise make the first
81+ # question a HIT). In production, use a stable name and call
82+ # `await store.push_index()` to persist the cache across runs and processes .
83+ store = await moss .session (index_name = f "qa-cache-demo- { uuid . uuid4 (). hex [: 8 ] } " )
8184 cache = SemanticCache (store )
8285
8386 # the 2nd question means the same as the 1st, phrased differently -> cache hit
0 commit comments