# 语义相似度分析模块

## 功能概述

提供基于 AI Agent 的语义相似度分析功能，支持缓存机制以提高性能和降低 API 调用成本。

## 主要功能

### 1. 核心函数

- `difference_between_phrases()` - 返回原始 AI 响应
- `difference_between_phrases_parsed()` - 返回解析后的 JSON 字典
- `compare_phrases()` - `difference_between_phrases_parsed()` 的别名

### 2. 缓存系统设计

#### 缓存文件名设计

**方案：可读文件名 + 哈希后缀**

```
cache/semantic_similarity/
├── 宿命感_vs_余华的小说_gpt-4.1-mini_t0.0_a7f3e2d9.json
├── 人工智能_vs_机器学习_claude-sonnet-4.5_t0.0_b8e4f3e0.json
├── 深度学习_vs_神经网络_gemini-2.5-pro_t0.2_c9f5g4h1.json
└── 创意写作_vs_AI生成_gpt-4.1-mini_t0.7_d0a6h5i2.json
```

**文件名格式：**
```
{phrase_a}_vs_{phrase_b}_{model}_t{temp}_{hash[:8]}.json
```

- `phrase_a`: 第一个短语（最长20字符，特殊字符转为下划线）
- `phrase_b`: 第二个短语（最长20字符，特殊字符转为下划线）
- `model`: 模型简称（提取 `/` 后部分，最长20字符）
- `t{temp}`: 温度参数（格式化为1位小数，如 t0.0, t0.2, t0.7）
- `hash[:8]`: 完整哈希的前8位

**哈希生成逻辑：**
- 基于所有影响结果的参数生成唯一 MD5 哈希：
  - `phrase_a` - 第一个短语
  - `phrase_b` - 第二个短语
  - `model_name` - 模型名称
  - `temperature` - 温度参数
  - `max_tokens` - 最大 token 数
  - `prompt_template` - 提示词模板

**缓存文件格式（结构化 JSON）：**

```json
{
  "input": {
    "phrase_a": "宿命感",
    "phrase_b": "余华的小说",
    "model_name": "openai/gpt-4.1-mini",
    "temperature": 0.0,
    "max_tokens": 65536,
    "prompt_template": "从语意角度,判断【{phrase_a}】和【{phrase_b}】..."
  },
  "output": {
    "result": "{\n  \"说明\": \"...\",\n  \"相似度\": 0.75\n}"
  },
  "metadata": {
    "timestamp": "2025-11-19 14:30:45",
    "cache_key": "a7f3e2d9c1b4a5f8e6d7c9b2a1f3e5d7",
    "cache_file": "宿命感_vs_余华的小说_gpt-4.1-mini_t0.0_a7f3e2d9.json"
  }
}
```

#### 缓存特性

1. **自动缓存**：默认启用，首次调用保存结果
2. **智能匹配**：相同参数自动从缓存读取
3. **可控性**：支持 `use_cache=False` 强制重新请求
4. **可追溯**：缓存文件包含完整元数据和时间戳
5. **自定义目录**：支持通过 `cache_dir` 参数自定义缓存位置

## 使用示例

### 基本使用（自动缓存）

```python
from lib.semantic_similarity import compare_phrases

# 第一次调用 - 请求 API 并缓存
result = await compare_phrases("宿命感", "余华的小说")
# 输出: ✓ 已缓存: 宿命感_vs_余华的小说_gpt-4.1-mini_t0.0_a7f3e2d9.json

# 第二次调用相同参数 - 从缓存读取
result = await compare_phrases("宿命感", "余华的小说")
# 输出: ✓ 使用缓存: 宿命感_vs_余华的小说_t0.0_a7f3e2d9.json

print(result['相似度'])  # 0.3
print(result['说明'])    # "两个概念..."
```

### 禁用缓存

```python
# 强制重新请求 API
result = await compare_phrases(
    "人工智能",
    "机器学习",
    use_cache=False
)
```

### 自定义缓存目录

```python
# 使用自定义缓存目录
result = await compare_phrases(
    "深度学习",
    "神经网络",
    cache_dir="my_cache/similarity"
)
```

### 自定义提示词模板

```python
custom_template = """
请详细分析【{phrase_a}】和【{phrase_b}】的语义关系
输出格式：
```json
{{
  "说明": "详细分析",
  "相似度": 0.5,
  "关系类型": "相关/包含/对立/无关"
}}
```
"""

result = await compare_phrases(
    "机器学习",
    "深度学习",
    prompt_template=custom_template
)
```

### 配置不同模型

```python
# 使用 Claude 模型
result = await compare_phrases(
    "人工智能",
    "深度学习",
    model_name='anthropic/claude-sonnet-4.5',
    temperature=0.2
)
```

## 缓存管理

### 查看缓存

```bash
# 查看缓存目录
ls cache/semantic_similarity/

# 查看特定缓存文件
cat cache/semantic_similarity/a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6.json
```

### 清理缓存

```bash
# 清理所有缓存
rm -rf cache/semantic_similarity/

# 清理特定缓存文件
rm cache/semantic_similarity/a1b2c3d4*.json
```

### 缓存统计

```python
from pathlib import Path
import json

cache_dir = Path("cache/semantic_similarity")
cache_files = list(cache_dir.glob("*.json"))

print(f"缓存文件总数: {len(cache_files)}")

# 统计各模型使用情况
model_stats = {}
for file in cache_files:
    with open(file, 'r') as f:
        data = json.load(f)
        model = data.get('model_name', 'unknown')
        model_stats[model] = model_stats.get(model, 0) + 1

print("各模型缓存数量:")
for model, count in model_stats.items():
    print(f"  {model}: {count}")
```

## 参数说明

### 所有函数共享参数

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `phrase_a` | str | 必填 | 第一个短语 |
| `phrase_b` | str | 必填 | 第二个短语 |
| `model_name` | str | `'openai/gpt-4.1-mini'` | 使用的 AI 模型 |
| `temperature` | float | `0.0` | 温度参数（0.0-1.0） |
| `max_tokens` | int | `65536` | 最大生成 token 数 |
| `prompt_template` | str | `None` | 自定义提示词模板 |
| `use_cache` | bool | `True` | 是否启用缓存 |
| `cache_dir` | str | `'cache/semantic_similarity'` | 缓存目录路径 |

### 支持的模型

- `'google/gemini-2.5-pro'`
- `'anthropic/claude-sonnet-4.5'`
- `'google/gemini-2.0-flash-001'`
- `'openai/gpt-5-mini'`
- `'anthropic/claude-haiku-4.5'`
- `'openai/gpt-4.1-mini'` (默认)

## 性能优化

### 缓存命中率优化

1. **参数标准化**：确保相同语义使用相同参数
2. **批量处理**：对相同短语对只调用一次
3. **预热缓存**：提前为常用短语对生成缓存

### 示例：批量处理

```python
phrase_pairs = [
    ("宿命感", "余华的小说"),
    ("人工智能", "机器学习"),
    ("深度学习", "神经网络"),
]

for phrase_a, phrase_b in phrase_pairs:
    result = await compare_phrases(phrase_a, phrase_b)
    print(f"{phrase_a} vs {phrase_b}: {result['相似度']}")
```

## 注意事项

1. **参数敏感性**：任何参数变化都会导致新的缓存键
2. **存储空间**：长期使用可能积累大量缓存文件
3. **缓存一致性**：模型更新后建议清理旧缓存
4. **并发安全**：当前实现不支持并发写入同一缓存文件

## 故障排查

### 缓存未命中

**问题**：相同参数调用但未使用缓存

**可能原因**：
- 参数细微差异（如空格、换行）
- `prompt_template` 不一致
- 缓存文件损坏或被删除

**解决方案**：
```python
# 检查缓存键
from lib.semantic_similarity import _generate_cache_key, DEFAULT_PROMPT_TEMPLATE

key = _generate_cache_key(
    "宿命感", "余华的小说",
    "openai/gpt-4.1-mini", 0.0, 65536,
    DEFAULT_PROMPT_TEMPLATE
)
print(f"缓存键: {key}")
```

### 缓存损坏

**问题**：缓存文件存在但无法加载

**解决方案**：
```bash
# 删除损坏的缓存文件
rm cache/semantic_similarity/{cache_key}.json
```

## 版本历史

- **v1.0** - 初始版本，支持基本语义相似度分析
- **v1.1** - 添加缓存系统
- **v1.2** - 支持自定义提示词模板
- **v1.3** - 优化缓存文件格式，添加元数据
