

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 멀티턴 대화 캐싱
<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)는 사용자별로 사전 필터링한 다음 가장 가까운 대화 컨텍스트를 찾는 단일 원자 작업으로 실행됩니다.

## 캐시 격리 전략
<a name="semantic-caching-isolation-strategies"></a>


| 전략 | TAG 필터 | 최적의 용도 | 
| --- | --- | --- | 
| 사용자당 | @user\_id:{user\_123} | 개인 맞춤형 어시스턴트 | 
| 세션당 | @session\_id:{sess\_abc} | 단기 채팅 | 
| 글로벌(공유) | 필터 없음(\*) | FAQ 봇, 일반 쿼리 | 
| 모델별 | @model:{gpt-4} | 다중 모델 배포 | 
| 제품별 | @product\_id:{prod\_456} | 전자 상거래 도우미 | 