

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# マルチターン会話キャッシュ
<a name="semantic-caching-multi-turn"></a>

マルチターン会話を使用するアプリケーションの場合、同じユーザーメッセージはコンテキストに応じて異なる意味を持つ可能性があります。例えば、Valkey に関する会話の「Tell me more」とは、Python に関する会話の「Tell me more」とは別の意味です。

## チャレンジ
<a name="semantic-caching-multi-turn-challenge"></a>

シングルプロンプトキャッシュは、ステートレスクエリに適しています。マルチターン会話では、最後のメッセージだけでなく、会話コンテキスト全体をキャッシュする必要があります。

```
# "Tell me more" means nothing without context
# Conversation A: "What is Valkey?" -> "Tell me more"  (about Valkey)
# Conversation B: "What is Python?" -> "Tell me more"  (about Python)
```

## 戦略: コンテキスト対応キャッシュキー
<a name="semantic-caching-context-aware-keys"></a>

最後のユーザーメッセージのみを埋め込む代わりに、会話コンテキスト全体の概要を埋め込みます。これにより、同様の会話フローで同様のフォローアップの質問がキャッシュされた回答を再利用できます。

```
def build_context_string(messages: list) -> str:
    """Build a cacheable context string from conversation messages."""
    # Use last 3 turns (6 messages: user + assistant pairs)
    recent = messages[-6:]
    parts = []
    for msg in recent:
        role = msg["role"]
        content = msg["content"][:200]  # Truncate long messages
        parts.append(f"{role}: {content}")
    return " | ".join(parts)
```

## TAG フィルターを使用したユーザーごとのキャッシュ分離
<a name="semantic-caching-tag-filters"></a>

TAG フィールドを使用して、キャッシュされた会話をユーザー、セッション、またはその他のディメンション別に分離します。これにより、あるユーザーのキャッシュされた会話が別のユーザーに返されるのを防ぐことができます。

```
# Create index with TAG field for per-user isolation
valkey_client.execute_command(
    "FT.CREATE", "conv_cache_idx",
    "SCHEMA",
    "context_summary", "TEXT",
    "response", "TEXT",
    "user_id", "TAG",
    "turn_count", "NUMERIC",
    "embedding", "VECTOR", "HNSW", "6",
    "TYPE", "FLOAT32",
    "DIM", "1024",
    "DISTANCE_METRIC", "COSINE",
)
```

ハイブリッドフィルタリングで検索 (TAG \+ KNN):

```
def lookup_conversation_cache(messages: list, user_id: str, threshold: float = 0.12):
    """Search cache for similar conversation contexts, scoped to a user.

    Note: FT.SEARCH with COSINE distance returns a distance score where
    0 = identical and 2 = opposite. A lower score means higher similarity.
    The threshold here is a maximum distance: only return results closer
    than this value.
    """
    context = build_context_string(messages)
    query_vec = get_embedding(context)

    # Hybrid search: filter by user_id TAG + KNN on context embedding
    results = valkey_client.execute_command(
        "FT.SEARCH", "conv_cache_idx",
        f"@user_id:{{{user_id}}}=>[KNN 1 @embedding $query_vec]",
        "PARAMS", "2", "query_vec", query_vec,
        "DIALECT", "2",
    )

    if results[0] > 0:
        fields = results[2]
        field_dict = {fields[j]: fields[j+1] for j in range(0, len(fields), 2)}
        distance = float(field_dict.get("__embedding_score", "999"))
        if distance < threshold:  # Lower distance = more similar
            return {"hit": True, "response": field_dict.get("response", ""), "distance": distance}

    return {"hit": False}
```

**注記**  
`@user_id:{user_123}` TAG フィルターは、ユーザー A のキャッシュされた会話がユーザー B にリークされないようにします。ハイブリッドクエリ (TAG \+ KNN) は 1 つのアトミックオペレーションとして実行され、ユーザーによって事前フィルタリングされた後、最も近い会話コンテキストを見つけます。

## キャッシュ分離戦略
<a name="semantic-caching-isolation-strategies"></a>


| 方針 | タグフィルター | 次の用途に適しています | 
| --- | --- | --- | 
| ユーザーごと | @user\_id:{user\_123} | パーソナライズされたアシスタント | 
| セッションごと | @session\_id:{sess\_abc} | 存続期間の短いチャット | 
| グローバル (共有) | フィルターなし (\*) | よくある質問ボット、一般的なクエリ | 
| モデルごと | @model:{gpt-4} | マルチモデルデプロイ | 
| 製品ごと | @product\_id:{prod\_456} | e コマースアシスタント | 