docs: 添加 AI 智能搜索系统设计文档
This commit is contained in:
@@ -0,0 +1,502 @@
|
|||||||
|
# AI 智能搜索系统设计文档
|
||||||
|
|
||||||
|
**日期**: 2026-01-25
|
||||||
|
**作者**: Claude Code
|
||||||
|
**状态**: 设计阶段
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
为项目列表添加 AI 语义搜索功能,用户可通过自然语言描述需求,系统通过向量相似度匹配返回相关项目,而非传统的关键词模糊搜索。
|
||||||
|
|
||||||
|
### 核心目标
|
||||||
|
|
||||||
|
- ✅ 支持自然语言查询(如"帮我找做图像生成的项目")
|
||||||
|
- ✅ 基于向量相似度的语义匹配
|
||||||
|
- ✅ 完全在 n8n 中实现 AI 逻辑,Next.js 应用保持纯净
|
||||||
|
- ✅ 利用现有 Neon 数据库的 pgvector 扩展
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 架构设计
|
||||||
|
|
||||||
|
### 整体架构
|
||||||
|
|
||||||
|
```
|
||||||
|
前端(Next.js)
|
||||||
|
↓
|
||||||
|
API 代理(/api/search/ai)
|
||||||
|
↓
|
||||||
|
n8n 工作流(AI 逻辑)
|
||||||
|
↓
|
||||||
|
Neon 数据库(pgvector)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心组件
|
||||||
|
|
||||||
|
**数据层(Neon + pgvector)**
|
||||||
|
- `Project` 表添加 `embedding` 字段存储向量
|
||||||
|
- HNSW 索引加速相似度搜索
|
||||||
|
- 向量维度:1536(OpenAI text-embedding-3-small)
|
||||||
|
|
||||||
|
**服务层(n8n 工作流)**
|
||||||
|
- **向量化工作流**:定时扫描未向量化项目,调用 OpenAI API 生成向量
|
||||||
|
- **AI 搜索工作流**:接收查询 → 生成向量 → 相似度搜索 → 返回结果
|
||||||
|
|
||||||
|
**前端层(Next.js)**
|
||||||
|
- 搜索框增加 AI 模式切换按钮
|
||||||
|
- 显示相似度评分和匹配原因
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据库设计
|
||||||
|
|
||||||
|
### Schema 修改
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model Project {
|
||||||
|
// ... 现有字段
|
||||||
|
|
||||||
|
// 新增:向量嵌入字段
|
||||||
|
embedding vector(1536)? // pgvector 类型
|
||||||
|
embeddingUpdatedAt DateTime? // 向量化更新时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 迁移 SQL
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 启用 pgvector 扩展
|
||||||
|
CREATE EXTENSION IF NOT EXISTS vector;
|
||||||
|
|
||||||
|
-- 添加向量列
|
||||||
|
ALTER TABLE "Project" ADD COLUMN "embedding" vector(1536);
|
||||||
|
ALTER TABLE "Project" ADD COLUMN "embeddingUpdatedAt" TIMESTAMP;
|
||||||
|
|
||||||
|
-- 创建 HNSW 索引(余弦距离)
|
||||||
|
CREATE INDEX idx_project_embedding_cosine
|
||||||
|
ON "Project" USING hnsw ("embedding" vector_cosine_ops)
|
||||||
|
WITH (m = 16, ef_construction = 64);
|
||||||
|
|
||||||
|
-- 复合索引(查找未向量化项目)
|
||||||
|
CREATE INDEX idx_project_embedding_null
|
||||||
|
ON "Project" ("id")
|
||||||
|
WHERE "embedding" IS NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 向量化内容策略
|
||||||
|
|
||||||
|
**字段权重分配:**
|
||||||
|
- 名称(40%):`name` + `nameEn`
|
||||||
|
- 描述(40%):`description` + `descriptionEn`
|
||||||
|
- 标签(15%):`tags`(逗号连接)
|
||||||
|
- 详细内容(5%):`content` + `contentEn`(截取前 500 字)
|
||||||
|
|
||||||
|
**示例输入文本:**
|
||||||
|
```
|
||||||
|
AI Video Generator
|
||||||
|
一个基于人工智能的视频生成工具,可以自动从文本生成高质量视频...
|
||||||
|
人工智能, 视频生成, AIGC
|
||||||
|
详细功能介绍...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## n8n 工作流设计
|
||||||
|
|
||||||
|
### 工作流 1:定时向量化
|
||||||
|
|
||||||
|
**触发器**:Cron 表达式(每 5 分钟)
|
||||||
|
|
||||||
|
```
|
||||||
|
*/5 * * * *
|
||||||
|
```
|
||||||
|
|
||||||
|
**流程**:
|
||||||
|
```
|
||||||
|
触发器
|
||||||
|
↓
|
||||||
|
查询未向量化项目(LIMIT 20)
|
||||||
|
↓
|
||||||
|
批量处理(每批 5 个)
|
||||||
|
↓
|
||||||
|
构造文本内容(合并字段)
|
||||||
|
↓
|
||||||
|
调用 OpenAI Embeddings API
|
||||||
|
↓
|
||||||
|
更新数据库 embedding 字段
|
||||||
|
↓
|
||||||
|
等待 1 秒(控制速率)
|
||||||
|
↓
|
||||||
|
下一批
|
||||||
|
```
|
||||||
|
|
||||||
|
**PostgreSQL 查询**:
|
||||||
|
```sql
|
||||||
|
SELECT id, name, "nameEn", description, "descriptionEn",
|
||||||
|
content, "contentEn"
|
||||||
|
FROM "Project"
|
||||||
|
WHERE "embedding" IS NULL
|
||||||
|
AND "status" = 'ACTIVE'
|
||||||
|
LIMIT 20
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误处理**:
|
||||||
|
- API 限流:指数退避重试(1s → 2s → 4s)
|
||||||
|
- 最多重试 3 次
|
||||||
|
- 失败记录日志
|
||||||
|
|
||||||
|
### 工作流 2:AI 搜索
|
||||||
|
|
||||||
|
**触发器**:Webhook(`/webhook/ai-search`)
|
||||||
|
|
||||||
|
**流程**:
|
||||||
|
```
|
||||||
|
Webhook 接收查询
|
||||||
|
↓
|
||||||
|
接收参数:{ query, locale, limit, filters }
|
||||||
|
↓
|
||||||
|
调用 OpenAI Embeddings API(生成查询向量)
|
||||||
|
↓
|
||||||
|
PostgreSQL 向量相似度搜索
|
||||||
|
↓
|
||||||
|
应用过滤条件(tags, status)
|
||||||
|
↓
|
||||||
|
格式化结果(添加相似度评分)
|
||||||
|
↓
|
||||||
|
返回 JSON 响应
|
||||||
|
```
|
||||||
|
|
||||||
|
**PostgreSQL 查询**:
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
id, name, "nameEn", slug, description, "descriptionEn",
|
||||||
|
1 - (embedding <=> '{{ query_vector }}'::vector) as similarity
|
||||||
|
FROM "Project"
|
||||||
|
WHERE "embedding" IS NOT NULL
|
||||||
|
AND "status" = 'ACTIVE'
|
||||||
|
ORDER BY embedding <=> '{{ query_vector }}'::vector
|
||||||
|
LIMIT {{ limit || 20 }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应格式**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"project": { /* 项目数据 */ },
|
||||||
|
"similarity": 0.89,
|
||||||
|
"matchReason": "项目名称和描述与图像生成高度相关"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 42,
|
||||||
|
"searchTime": 156
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next.js API 设计
|
||||||
|
|
||||||
|
### 路由配置
|
||||||
|
|
||||||
|
**文件**:`src/app/api/search/ai/route.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { NextResponse } from 'next/server'
|
||||||
|
|
||||||
|
const N8N_WEBHOOK_URL = process.env.N8N_AI_SEARCH_WEBHOOK
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json()
|
||||||
|
|
||||||
|
// 转发到 n8n 工作流
|
||||||
|
const n8nResponse = await fetch(N8N_WEBHOOK_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
|
||||||
|
const results = await n8nResponse.json()
|
||||||
|
|
||||||
|
return NextResponse.json(results)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Search failed' },
|
||||||
|
{ status: 500 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求格式**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"query": "帮我找做图像生成的AI工具",
|
||||||
|
"locale": "zh",
|
||||||
|
"limit": 20,
|
||||||
|
"filters": {
|
||||||
|
"tags": ["AIGC"],
|
||||||
|
"status": "ACTIVE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端交互设计
|
||||||
|
|
||||||
|
### UI 组件修改
|
||||||
|
|
||||||
|
**文件**:`src/components/search/SearchBar.tsx`
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 添加 AI 模式切换按钮(Sparkles 图标)
|
||||||
|
- AI 模式时按钮高亮(金色背景)
|
||||||
|
- 不同模式的占位符提示
|
||||||
|
- 加载状态优化
|
||||||
|
|
||||||
|
**关键代码**:
|
||||||
|
```tsx
|
||||||
|
<button
|
||||||
|
onClick={() => setAiMode(!aiMode)}
|
||||||
|
className={`
|
||||||
|
px-3 py-2 border-2 transition-all
|
||||||
|
${aiMode
|
||||||
|
? 'bg-yellow-400 border-yellow-500 text-black'
|
||||||
|
: 'bg-white border-gray-300 text-gray-600'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
title="AI 语义搜索"
|
||||||
|
>
|
||||||
|
<Sparkles className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### AI 搜索结果展示
|
||||||
|
|
||||||
|
**文件**:`src/components/search/AISearchResults.tsx`
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 显示相似度指示条(左侧彩色条)
|
||||||
|
- 相似度评分(0-100%)
|
||||||
|
- 匹配原因说明(可选)
|
||||||
|
- 颜色编码:绿色(>0.8)、黄色(>0.6)、红色(<0.6)
|
||||||
|
|
||||||
|
### 搜索模式对比
|
||||||
|
|
||||||
|
| 特性 | 传统搜索 | AI 搜索 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| 占位符 | "搜索项目名称..." | "描述你想要的项目..." |
|
||||||
|
| 匹配方式 | 关键词模糊匹配 | 向量语义相似度 |
|
||||||
|
| 返回速度 | 极快(<100ms) | 较快(1-3s) |
|
||||||
|
| 结果增强 | 无 | 相似度评分 + 匹配原因 |
|
||||||
|
| 提示信息 | 无 | "💡 试试:'帮我找能生成视频的 AI 工具'" |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 环境变量配置
|
||||||
|
|
||||||
|
### Next.js 应用(`.env.local`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Neon 数据库(已有)
|
||||||
|
DATABASE_URL=postgres://...
|
||||||
|
|
||||||
|
# n8n Webhook
|
||||||
|
N8N_AI_SEARCH_WEBHOOK=https://your-n8n-instance.com/webhook/ai-search
|
||||||
|
N8N_WEBHOOK_API_KEY=your-webhook-key # 可选,用于安全验证
|
||||||
|
```
|
||||||
|
|
||||||
|
### n8n 工作流
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OpenAI API(用于 Embeddings)
|
||||||
|
OPENAI_API_KEY=sk-...
|
||||||
|
|
||||||
|
# Neon 数据库(与 Next.js 共享)
|
||||||
|
NEON_DATABASE_URL=postgres://...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理与优化
|
||||||
|
|
||||||
|
### 错误处理策略
|
||||||
|
|
||||||
|
**n8n 工作流**:
|
||||||
|
- API 限流:指数退避重试
|
||||||
|
- API Key 无效:发送告警
|
||||||
|
- 数据库查询失败:返回友好错误信息
|
||||||
|
- 向量未就绪:提示用户稍后重试
|
||||||
|
|
||||||
|
**前端**:
|
||||||
|
- 10 秒超时限制
|
||||||
|
- 超时或错误时自动降级到传统搜索
|
||||||
|
- Toast 消息提示用户
|
||||||
|
|
||||||
|
### 性能优化
|
||||||
|
|
||||||
|
**数据库查询**:
|
||||||
|
- 只返回必要字段(不返回 content)
|
||||||
|
- 使用 HNSW 索引
|
||||||
|
- 设置查询超时(5s)
|
||||||
|
|
||||||
|
**缓存策略(可选)**:
|
||||||
|
- Redis 缓存常见查询结果(5 分钟 TTL)
|
||||||
|
- 内存缓存热门查询
|
||||||
|
|
||||||
|
### 监控指标
|
||||||
|
|
||||||
|
- 平均搜索响应时间
|
||||||
|
- API 调用次数/成本
|
||||||
|
- 向量化完成率
|
||||||
|
- 错误率
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 成本估算
|
||||||
|
|
||||||
|
### OpenAI Embeddings API
|
||||||
|
|
||||||
|
**定价**:
|
||||||
|
- text-embedding-3-small:$0.00002 / 1K tokens
|
||||||
|
|
||||||
|
**估算**:
|
||||||
|
- 单个项目(500 tokens):$0.00001
|
||||||
|
- 1000 个项目:$0.01
|
||||||
|
- 单次搜索(10 tokens):$0.0000002
|
||||||
|
- 1000 次搜索:$0.0002
|
||||||
|
|
||||||
|
**月度预算**:$5 可处理 50 万个项目或 2500 万次搜索
|
||||||
|
|
||||||
|
### Neon 免费套餐
|
||||||
|
|
||||||
|
**限制**:
|
||||||
|
- 存储:0.5GB
|
||||||
|
- 计算:300 小时/月
|
||||||
|
|
||||||
|
**向量存储**:
|
||||||
|
- 单个项目(1536 维):3KB(半精度)
|
||||||
|
- 1000 个项目:3MB
|
||||||
|
- 10,000 个项目:30MB ✅
|
||||||
|
|
||||||
|
**结论**:免费套餐完全够用(可支持 5,000-10,000 个项目)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试计划
|
||||||
|
|
||||||
|
### 单元测试
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/__tests__/search.test.ts
|
||||||
|
describe('AI Search', () => {
|
||||||
|
it('should handle empty query', async () => {
|
||||||
|
const response = await fetch('/api/search/ai', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ query: '' })
|
||||||
|
})
|
||||||
|
expect(response.status).toBe(400)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fallback to traditional search on error', async () => {
|
||||||
|
// 测试错误降级逻辑
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E 测试
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/e2e/ai-search.spec.ts
|
||||||
|
test('AI 搜索功能', async ({ page }) => {
|
||||||
|
await page.goto('/zh/projects')
|
||||||
|
await page.click('[data-testid="ai-mode-toggle"]')
|
||||||
|
await page.fill('input[name="search"]', '视频生成工具')
|
||||||
|
await page.press('input[name="search"]', 'Enter')
|
||||||
|
await expect(page.locator('.ai-search-results')).toBeVisible()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 集成测试
|
||||||
|
|
||||||
|
- 测试 n8n 工作流端到端
|
||||||
|
- 验证向量搜索结果准确性
|
||||||
|
- 测试错误场景(API 限流、数据库连接失败)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 部署清单
|
||||||
|
|
||||||
|
### 数据库准备
|
||||||
|
|
||||||
|
- [x] 运行数据库迁移
|
||||||
|
- [x] 验证 pgvector 扩展已启用
|
||||||
|
- [x] 检查 HNSW 索引创建成功
|
||||||
|
|
||||||
|
### n8n 配置
|
||||||
|
|
||||||
|
- [ ] 创建向量化工作流
|
||||||
|
- [ ] 配置 Cron 触发器(每 5 分钟)
|
||||||
|
- [ ] 配置 PostgreSQL 节点
|
||||||
|
- [ ] 配置 OpenAI Embeddings 节点
|
||||||
|
- [ ] 添加错误处理和重试逻辑
|
||||||
|
|
||||||
|
- [ ] 创建 AI 搜索工作流
|
||||||
|
- [ ] 配置 Webhook 触发器
|
||||||
|
- [ ] 配置 OpenAI Embeddings 节点
|
||||||
|
- [ ] 配置 PostgreSQL 向量查询
|
||||||
|
- [ ] 配置结果格式化
|
||||||
|
|
||||||
|
- [ ] 测试工作流
|
||||||
|
- [ ] 手动触发向量化流程
|
||||||
|
- [ ] 测试 Webhook 搜索
|
||||||
|
- [ ] 验证错误处理
|
||||||
|
|
||||||
|
### Next.js 部署
|
||||||
|
|
||||||
|
- [ ] 添加环境变量(N8N_WEBHOOK_URL)
|
||||||
|
- [ ] 创建 API 路由(`/api/search/ai`)
|
||||||
|
- [ ] 更新 SearchBar 组件
|
||||||
|
- [ ] 创建 AISearchResults 组件
|
||||||
|
- [ ] 本地测试完整流程
|
||||||
|
- [ ] 部署到 Vercel
|
||||||
|
|
||||||
|
### 验证步骤
|
||||||
|
|
||||||
|
1. **向量化测试**:
|
||||||
|
```sql
|
||||||
|
-- 检查已向量化项目数
|
||||||
|
SELECT COUNT(*) FROM "Project" WHERE embedding IS NOT NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **搜索测试**:
|
||||||
|
- 输入自然语言查询
|
||||||
|
- 验证返回结果相关性
|
||||||
|
- 检查相似度评分
|
||||||
|
|
||||||
|
3. **性能测试**:
|
||||||
|
- 测量平均响应时间
|
||||||
|
- 验证并发处理能力
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后续优化方向
|
||||||
|
|
||||||
|
1. **混合搜索**:结合关键词搜索和向量搜索,提升准确率
|
||||||
|
2. **查询缓存**:Redis 缓存热门查询结果
|
||||||
|
3. **A/B 测试**:对比传统搜索和 AI 搜索的用户体验
|
||||||
|
4. **多模态搜索**:支持图片、语音输入
|
||||||
|
5. **个性化排序**:基于用户历史行为优化结果
|
||||||
|
6. **自动标签建议**:AI 分析项目内容推荐标签
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
- [Neon pgvector 文档](https://neon.com/docs/extensions/pgvector)
|
||||||
|
- [OpenAI Embeddings API](https://platform.openai.com/docs/guides/embeddings)
|
||||||
|
- [pgvector GitHub](https://github.com/pgvector/pgvector)
|
||||||
|
- [HNSW 算法论文](https://arxiv.org/abs/1603.09320)
|
||||||
Reference in New Issue
Block a user