【独立开发日记 006】Game-Odyssey 技术拆解:如何用 RAG + pgvector 构建语义搜索

【独立开发日记 006】Game-Odyssey 技术拆解:如何用 RAG + pgvector 构建语义搜索

以 Game Odyssey 为例,详解 RAG 系统的设计与实现。

什么是 RAG?

RAG(Retrieval-Augmented Generation,检索增强生成)是目前 AI 应用最主流的架构之一。

核心思想很简单:

先检索,再生成

LLM 的知识有截止日期,也不可能知道你的私有数据。RAG 的解决方案是:用户提问时,先从知识库检索相关内容,再把检索结果和问题一起喂给 LLM,让它基于这些上下文生成回答。

Game Odyssey 的 RAG 架构

简单的架构图

关键技术点

1. Embedding:把文字变成向量

Embedding 是 RAG 的基础。它把文字映射到高维向量空间,语义相似的文字在空间中距离更近。

我用的是 Qwen3-Embedding-4B,通过 MLX 在 Mac 上本地运行:

# 生成 embedding
response = await client.post("/embed", json={"texts": [query]})
query_vector = response.json()["embeddings"][0]  # 2560 维向量

为什么选这个模型?

  • 中文效果好(游戏数据大量中文)
  • MLX 在 Apple Silicon 上性能优秀
  • 2560 维够用,不会太慢

2. pgvector:PostgreSQL 的向量扩展

pgvector 让 PostgreSQL 支持向量类型和相似度搜索:

-- 创建向量列
CREATE TABLE game_embeddings (
    game_id INTEGER PRIMARY KEY,
    embedding_vector vector(2560)
);
 
-- 向量相似度搜索(余弦距离)
SELECT game_id, 
       1 - (embedding_vector <=> query_vector) as similarity
FROM game_embeddings
ORDER BY embedding_vector <=> query_vector
LIMIT 10;

<=> 是余弦距离操作符,返回值越小越相似。

为什么用 pgvector 而不是 Pinecone、Milvus?

  • 数据量不大(几千条游戏),pgvector 足够
  • 减少技术栈复杂度,一个数据库搞定
  • 免费、开源、易部署

3. Prompt 工程:让 LLM 听话

RAG 系统最头疼的问题是 LLM "自由发挥"。我遇到的坑:

问题 1:推荐了用户提到的游戏

用户: 推荐类似对马岛之魂的游戏

LLM: 我推荐对马岛之魂... // 不对!

解决:在 System Prompt 里明确约束

system_prompt = """你是游戏推荐助手。
 
【重要规则】
1. 只能推荐候选列表中的游戏
2. 如果用户提到"类似 XX",不要推荐 XX 本身
3. 推荐 3 款游戏并说明理由
"""

问题 2:输出格式不稳定

有时 LLM 按格式输出,有时不按,导致解析失败。

解决:多层 fallback

# 1. 尝试解析格式化输出
if "---游戏ID---" in response:
    ids = parse_formatted_ids(response)
 
# 2. fallback: 从文字中匹配游戏名
if not ids:
    for game in candidates:
        if game.title in response:
            ids.append(game.id)

4. 本地 LLM:Ollama + Qwen2.5

Ollama 让本地运行 LLM 变得很简单:

# 安装
brew install ollama
 
# 启动服务
ollama serve
 
# 下载模型
ollama pull qwen2.5:3b

API 调用:

response = await client.post("/api/chat", json={
    "model": "qwen2.5:3b",
    "messages": [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt}
    ]
})

为什么用 3B 模型?

  • Mac Pro M1 Pro 16GB 跑得动
  • 响应速度 2-5 秒,可接受
  • 中文理解能力够用

完整代码已开源:https://github.com/ICEMAN-CN/game-reco.git