Files
agent-park/docs/plans/2026-01-25-ai-search-implementation.md
T

1146 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 智能搜索系统实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 为项目列表添加 AI 语义搜索功能,支持自然语言查询并返回语义相关的项目,所有 AI 逻辑在 n8n 中实现,Next.js 应用保持纯净。
**Architecture:** 使用 Neon PostgreSQL 的 pgvector 扩展存储项目向量,n8n 工作流处理向量化(定时任务)和 AI 搜索(Webhook),Next.js API 路由作为代理转发请求到 n8n,前端添加切换按钮和相似度展示。
**Tech Stack:** Neon PostgreSQL + pgvector, n8n, OpenAI Embeddings API (text-embedding-3-small), Next.js 15 App Router, Prisma ORM, TypeScript, Tailwind CSS
---
## 前置准备
### Task 0: 验证环境和依赖
**Step 1: 验证 Neon 数据库 pgvector 支持**
连接到 Neon 数据库(使用 Prisma Studio 或 psql):
```bash
# 使用 psql 连接
psql $DATABASE_URL
# 或使用 Neon SQL Editor: https://console.neon.tech
```
执行 SQL
```sql
-- 检查 pgvector 扩展是否已安装
SELECT * FROM pg_extension WHERE extname = 'vector';
-- 如果未安装,安装它
CREATE EXTENSION IF NOT EXISTS vector;
-- 验证安装
SELECT vector_dims('[1,2,3]'::vector);
```
预期输出:`3`
---
## Phase 1: 数据库迁移
### Task 1: 创建 Prisma Schema 迁移
**Files:**
- Modify: `prisma/schema.prisma`
**Step 1: 添加向量字段到 Project 模型**
`prisma/schema.prisma` 中找到 `model Project`,在字段列表末尾添加:
```prisma
model Project {
// ... 现有字段(id, name, nameEn, slug, description, descriptionEn, content, contentEn, status, source, createdAt, updatedAt
// 新增:向量嵌入字段
embedding vector(1536)? // pgvector 类型,1536维(OpenAI text-embedding-3-small
embeddingUpdatedAt DateTime? // 记录向量化更新时间
}
```
**Step 2: 创建迁移文件**
```bash
# 生成迁移(会创建 SQL 文件)
pnpm prisma migrate dev --name add_project_embedding
# 预期输出:
# ✓ The following migration has been created and applied from new schema changes:
# migrations/TIMESTAMP_add_project_embedding/migration.sql
```
**Step 3: 验证生成的 SQL**
打开生成的迁移文件(例如 `prisma/migrations/20260125000000_add_project_embedding/migration.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;
```
**Step 4: 手动优化迁移(可选)**
如果生成的 SQL 不包含索引,手动修改迁移文件添加上述索引 SQL。
**Step 5: 应用迁移**
```bash
# 如果在上一步中已经自动应用,跳过此步骤
# 否则手动应用:
pnpm prisma migrate deploy
```
**Step 6: 验证迁移**
```sql
-- 检查表结构
\d "Project"
-- 应该看到:
-- embedding | vector(1536) |
-- embeddingUpdatedAt | timestamp |
-- 检查索引
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'Project';
-- 应该看到:
-- idx_project_embedding_cosine
-- idx_project_embedding_null
```
**Step 7: 更新 Prisma Client**
```bash
pnpm prisma generate
```
**Step 8: 提交**
```bash
git add prisma/schema.prisma prisma/migrations
git commit -m "feat: 添加项目向量嵌入字段和 pgvector 索引"
```
---
## Phase 2: n8n 工作流配置
### Task 2: 配置 n8n 凭证
**Step 1: 获取 OpenAI API Key**
访问 https://platform.openai.com/api-keys 创建新的 API Key。
**Step 2: 在 n8n 中添加 OpenAI 凭证**
1. 打开 n8n 实例
2. 导航到 **Credentials****New**
3. 选择 **OpenAI API**
4. 输入名称:`OpenAI Embeddings`
5. 粘贴 API Key
6. 保存
**Step 3: 添加 Neon 数据库凭证**
1. 在 n8n 中导航到 **Credentials****New**
2. 选择 **PostgreSQL**
3. 输入名称:`Neon Database`
4. 配置连接:
- Host: 从 `DATABASE_URL` 提取(例如 `xxx.neon.tech`
- Database: 数据库名(例如 `neondb`
- User: 用户名
- Password: 密码
- SSL: 启用
5. 点击 **Test Connection** 验证
6. 保存
**注意**:不提交凭证到 git。
---
### Task 3: 创建向量化工作流
**Step 1: 创建新工作流**
在 n8n 中:
1. 点击 **New Workflow**
2. 命名为:`Project Vectorization`
**Step 2: 添加 Cron 触发器**
1. 添加节点:**Cron**
2. 配置:
- Trigger Interval: `Every 5 minutes`
- Cron Expression: `*/5 * * * *`
3. 保存节点
**Step 3: 添加 PostgreSQL 节点(查询未向量化项目)**
1. 添加节点:**Postgres**(使用 `Neon Database` 凭证)
2. 操作:**Execute Query**
3. 输入查询:
```sql
SELECT id, name, "nameEn", description, "descriptionEn", content, "contentEn"
FROM "Project"
WHERE "embedding" IS NULL
AND "status" = 'ACTIVE'
LIMIT 20
```
4. 测试节点(应该返回现有项目列表)
5. 保存节点
**Step 4: 添加 Function 节点(构造文本内容)**
1. 添加节点:**Function** → **JavaScript**
2. 命名:`Construct Text Content`
3. 输入代码:
```javascript
// 为每个项目构造用于向量化的文本内容
const projects = $input.all();
return projects.map(item => {
const project = item.json;
// 合并字段,按权重构造
const parts = [
project.name || '',
project.nameEn || '',
project.description || '',
project.descriptionEn || '',
// tags 稍后处理
(project.content || '').substring(0, 500),
(project.contentEn || '').substring(0, 500)
].filter(Boolean);
const textContent = parts.join('\n\n');
return {
json: {
...project,
textContent: textContent
}
};
});
```
4. 测试节点(应该添加 `textContent` 字段)
5. 保存节点
**Step 5: 添加 Split in Batches 节点**
1. 添加节点:**Split In Batches**
2. 配置:
- Batch Size: `5`(每批 5 个项目,避免 API 限流)
3. 保存节点
**Step 6: 添加 OpenAI Embeddings 节点**
1. 添加节点:**OpenAI** → **Embeddings**
2. 使用 `OpenAI Embeddings` 凭证
3. 配置:
- Model: `text-embedding-3-small`
- Input: `{{ $json.textContent }}`
4. 测试节点(应该返回向量数组)
5. 保存节点
**Step 7: 添加 PostgreSQL 节点(更新 embedding**
1. 添加节点:**Postgres**
2. 操作:**Execute Query**
3. 输入查询:
```sql
UPDATE "Project"
SET
"embedding" = '{{ $json.data[0].embedding }}'::vector,
"embeddingUpdatedAt" = NOW()
WHERE "id" = {{ $json.id }}
```
4. 测试节点
5. 保存节点
**Step 8: 添加 Wait 节点(控制速率)**
1. 添加节点:**Wait**
2. 配置:
- Amount: `1`
3. 保存节点(在 Split in Batches 和 OpenAI 之间)
**Step 9: 连接节点**
按顺序连接:
```
Cron → Postgres (查询) → Function (构造) → Split in Batches → Wait → OpenAI Embeddings → Postgres (更新) → Loop back to Split in Batches
```
**Step 10: 测试完整工作流**
1. 点击 **Test Workflow**
2. 手动触发 Cron 节点
3. 验证:
- 查询返回未向量化项目
- 文本内容正确构造
- OpenAI API 成功调用
- 数据库成功更新
**Step 11: 激活工作流**
1. 点击 **Active** 开关
2. 验证 Cron 调度正常运行
**Step 12: 验证向量化结果**
```sql
-- 检查已向量化项目数
SELECT COUNT(*) FROM "Project" WHERE "embedding" IS NOT NULL;
-- 查看某个项目的向量
SELECT id, name, "embeddingUpdatedAt"
FROM "Project"
WHERE "embedding" IS NOT NULL
LIMIT 5;
```
---
### Task 4: 创建 AI 搜索工作流
**Step 1: 创建新工作流**
在 n8n 中:
1. 点击 **New Workflow**
2. 命名为:`AI Semantic Search`
**Step 2: 添加 Webhook 触发器**
1. 添加节点:**Webhook**
2. 配置:
- HTTP Method: `POST`
- Path: `ai-search`
- Response Mode: **When Last Node Finishes**
3. 记录 Webhook URL(例如 `https://your-n8n.com/webhook/ai-search`
4. 测试 Webhookcurl):
```bash
curl -X POST https://your-n8n.com/webhook/ai-search \
-H "Content-Type: application/json" \
-d '{"query":"test","locale":"zh","limit":5}'
```
5. 保存节点
**Step 3: 添加 OpenAI Embeddings 节点(生成查询向量)**
1. 添加节点:**OpenAI** → **Embeddings**
2. 使用 `OpenAI Embeddings` 凭证
3. 配置:
- Model: `text-embedding-3-small`
- Input: `{{ $json.query }}`
4. 测试节点
5. 保存节点
**Step 4: 添加 PostgreSQL 节点(向量相似度搜索)**
1. 添加节点:**Postgres**
2. 操作:**Execute Query**
3. 输入查询:
```sql
SELECT
p.id,
p.name,
p."nameEn",
p.slug,
p.description,
p."descriptionEn",
p.status,
p."createdAt",
1 - (p."embedding" <=> '{{ $json.data[0].embedding }}'::vector) as similarity
FROM "Project" p
WHERE p."embedding" IS NOT NULL
AND p.status = 'ACTIVE'
ORDER BY p."embedding" <=> '{{ $json.data[0].embedding }}'::vector
LIMIT {{ $json.limit || 20 }}
```
4. 测试节点(应该返回相似度排序的项目)
5. 保存节点
**Step 5: 添加 Function 节点(查询项目标签)**
1. 添加节点:**Function** → **JavaScript**
2. 输入代码:
```javascript
const results = $input.all();
const projectIds = results.map(r => r.json.id).join(',');
// 为每个项目查询标签
return results.map(item => {
return {
json: {
...item.json,
projectId: item.json.id
}
};
});
```
3. 保存节点
**Step 6: 添加 PostgreSQL 节点(查询标签)**
1. 添加节点:**Postgres**
2. 操作:**Execute Query**
3. 输入查询:
```sql
SELECT
t.id,
t.name,
t."nameEn",
t.slug,
pt."projectId"
FROM "Tag" t
INNER JOIN "ProjectTag" pt ON t.id = pt."tagId"
WHERE pt."projectId" IN (SELECT UNNEST(STRING_TO_ARRAY('{{ $json.projectIds }}', ','))::INTEGER)
```
4. 保存节点
**Step 7: 添加 Function 节点(合并标签)**
1. 添加节点:**Function** → **JavaScript**
2. 输入代码:
```javascript
const projects = $('Postgres').all();
const tags = $('Postgres1').all();
// 合并标签到项目
const results = projects.map(project => {
const projectTags = tags
.filter(t => t.json.projectId === project.json.id)
.map(t => ({
id: t.json.id,
name: t.json.name,
nameEn: t.json.nameEn,
slug: t.json.slug
}));
return {
json: {
project: {
...project.json,
tags: projectTags
},
similarity: project.json.similarity,
matchReason: `相似度: ${(project.json.similarity * 100).toFixed(0)}%`
}
};
});
return results;
```
3. 测试节点
4. 保存节点
**Step 8: 添加 Function 节点(格式化响应)**
1. 添加节点:**Function** → **JavaScript**
2. 输入代码:
```javascript
const results = $input.all();
const searchTime = $now.toMillis() - $('Webhook').item.json.startTime;
return {
json: {
results: results.map(r => r.json),
total: results.length,
searchTime: searchTime
}
};
```
3. 保存节点
**Step 9: 连接节点**
```
Webhook → OpenAI Embeddings → Postgres (向量搜索) → Function (查询标签) → Postgres (标签) → Function (合并) → Function (格式化) → Response
```
**Step 10: 测试完整工作流**
1. 点击 **Test Workflow**
2. 在 Webhook 节点中点击 **Listen for Test Event**
3. 发送测试请求:
```bash
curl -X POST https://your-n8n.com/webhook/ai-search \
-H "Content-Type: application/json" \
-d '{"query":"视频生成工具","locale":"zh","limit":5}'
```
4. 验证响应格式:
```json
{
"results": [
{
"project": { /* 项目数据 */ },
"similarity": 0.89,
"matchReason": "相似度: 89%"
}
],
"total": 5,
"searchTime": 1234
}
```
**Step 11: 激活工作流**
1. 点击 **Active** 开关
2. 保存生产环境的 Webhook URL
**Step 12: 配置环境变量**
`.env.local` 中添加:
```bash
N8N_AI_SEARCH_WEBHOOK=https://your-n8n.com/webhook/ai-search
```
---
## Phase 3: Next.js API 实现
### Task 5: 创建 AI 搜索 API 路由
**Files:**
- Create: `src/app/api/search/ai/route.ts`
**Step 1: 创建 API 路由文件**
创建 `src/app/api/search/ai/route.ts`
```typescript
import { NextResponse } from 'next/server'
import { ProjectQuerySchema } from '@/lib/validations'
const N8N_WEBHOOK_URL = process.env.N8N_AI_SEARCH_WEBHOOK
if (!N8N_WEBHOOK_URL) {
throw new Error('N8N_AI_SEARCH_WEBHOOK environment variable is not set')
}
export async function POST(request: Request) {
try {
const body = await request.json()
// 验证查询参数
const validatedQuery = ProjectQuerySchema.parse(body)
// 转发到 n8n 工作流
const n8nResponse = await fetch(N8N_AI_SEARCH_WEBHOOK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: validatedQuery.search,
locale: body.locale || 'zh',
limit: validatedQuery.limit,
filters: {
tags: validatedQuery.tags,
status: validatedQuery.status
}
})
})
if (!n8nResponse.ok) {
throw new Error(`n8n webhook failed: ${n8nResponse.statusText}`)
}
const results = await n8nResponse.json()
return NextResponse.json(results)
} catch (error) {
console.error('AI search error:', error)
if (error instanceof Error && error.name === 'ZodError') {
return NextResponse.json(
{ error: 'Invalid query parameters', details: error },
{ status: 400 }
)
}
return NextResponse.json(
{ error: 'AI search failed', message: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
)
}
}
```
**Step 2: 测试 API 路由**
启动开发服务器:
```bash
pnpm dev
```
测试 API
```bash
curl -X POST http://localhost:3000/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"视频生成工具","limit":5}'
```
预期响应:
```json
{
"results": [...],
"total": 5,
"searchTime": 1234
}
```
**Step 3: 提交**
```bash
git add src/app/api/search/ai/route.ts
git commit -m "feat: 添加 AI 搜索 API 路由"
```
---
## Phase 4: 前端组件实现
### Task 6: 修改 SearchBar 组件
**Files:**
- Modify: `src/components/search/SearchBar.tsx`
**Step 1: 读取现有组件**
```bash
# 查看现有组件结构
cat src/components/search/SearchBar.tsx
```
**Step 2: 添加 AI 模式状态**
在组件中添加:
```tsx
'use client'
import { useState } from 'react'
import { Sparkles, Search } from 'lucide-react'
// ... 保留现有 imports
export function SearchBar() {
// ... 保留现有状态
const [aiMode, setAiMode] = useState(false)
const [loading, setLoading] = useState(false)
// ... 保留现有逻辑
}
```
**Step 3: 修改搜索处理函数**
更新 `handleSearch` 函数:
```tsx
const handleSearch = async () => {
if (!query.trim()) return
setLoading(true)
try {
const endpoint = aiMode ? '/api/search/ai' : '/api/projects'
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
search: query,
locale: 'zh',
limit: 20
})
})
if (!response.ok) {
throw new Error('Search failed')
}
const data = await response.json()
// 处理结果...
// if (aiMode) {
// setAIResults(data.results)
// } else {
// setProjects(data.results)
// }
} catch (error) {
console.error('Search error:', error)
// 显示错误提示
} finally {
setLoading(false)
}
}
```
**Step 4: 添加 AI 切换按钮**
在搜索输入框旁边添加:
```tsx
<div className="flex gap-2">
{/* 搜索输入框 */}
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
placeholder={
aiMode
? "描述你想要的项目..."
: "搜索项目名称..."
}
className="flex-1 px-4 py-2 border-2 border-gray-300"
/>
{/* AI 模式切换按钮 */}
<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>
{/* 搜索按钮 */}
<button
onClick={handleSearch}
disabled={loading}
className="px-6 py-2 bg-blue-600 text-white"
>
{loading ? '搜索中...' : <Search className="w-5 h-5" />}
</button>
</div>
{/* AI 模式提示 */}
{aiMode && (
<div className="mt-2 text-sm text-gray-600">
💡 试试:"帮我找能生成视频的 AI 工具"
</div>
)}
```
**Step 5: 测试组件**
1. 访问 `http://localhost:3000/zh/projects`
2. 点击 AI 切换按钮(应该变金色)
3. 输入查询并搜索
4. 验证加载状态显示
**Step 6: 提交**
```bash
git add src/components/search/SearchBar.tsx
git commit -m "feat: 添加 AI 搜索模式切换按钮"
```
---
### Task 7: 创建 AI 搜索结果组件
**Files:**
- Create: `src/components/search/AISearchResults.tsx`
**Step 1: 创建组件文件**
创建 `src/components/search/AISearchResults.tsx`
```tsx
'use client'
import { ProjectCard } from '@/components/project/ProjectCard'
import type { Project } from '@/hooks/useProjects'
interface AISearchResult {
project: Project
similarity: number
matchReason?: string
}
interface AISearchResultsProps {
results: AISearchResult[]
}
export function AISearchResults({ results }: AISearchResultsProps) {
if (results.length === 0) {
return (
<div className="text-center py-12 text-gray-600">
未找到相关项目,试试其他描述吧
</div>
)
}
return (
<div className="space-y-6">
{/* 相似度说明 */}
<div className="flex items-center gap-2 text-sm text-gray-600">
<span>匹配度:</span>
<div className="flex items-center gap-1">
<span className="text-green-600 font-semibold"></span>
<span></span>
<span className="text-red-600 font-semibold"></span>
</div>
</div>
{/* 结果列表 */}
{results.map(({ project, similarity, matchReason }) => (
<div key={project.id} className="relative">
{/* 相似度指示条 */}
<div
className="absolute left-0 top-0 bottom-0 w-1 rounded-l"
style={{
backgroundColor: getSimilarityColor(similarity)
}}
/>
{/* 项目卡片 */}
<div className="ml-3">
<ProjectCard project={project} />
{/* AI 匹配信息 */}
<div className="mt-3 px-4 py-3 bg-yellow-50 border-l-4 border-yellow-400 rounded-r">
<div className="flex justify-between items-center text-sm">
<span className="font-semibold text-gray-700">
匹配度: {(similarity * 100).toFixed(0)}%
</span>
{matchReason && (
<span className="text-gray-600">{matchReason}</span>
)}
</div>
</div>
</div>
</div>
))}
</div>
)
}
function getSimilarityColor(score: number): string {
if (score > 0.8) return '#22c55e' // green-500
if (score > 0.6) return '#eab308' // yellow-500
return '#ef4444' // red-500
}
```
**Step 2: 在项目列表页面中使用**
修改 `src/app/[locale]/projects/page.tsx`
```tsx
// 在页面中添加 AI 结果状态
const [aiResults, setAIResults] = useState<AISearchResult[]>([])
const [isAIResult, setIsAIResult] = useState(false)
// 在搜索处理中设置结果
if (aiMode) {
setAIResults(data.results)
setIsAIResult(true)
} else {
setProjects(data.results)
setIsAIResult(false)
}
// 在渲染中切换
{isAIResult ? (
<AISearchResults results={aiResults} />
) : (
<ProjectList projects={projects} />
)}
```
**Step 3: 测试组件**
1. 访问项目列表页面
2. 启用 AI 模式
3. 搜索查询
4. 验证:
- 相似度指示条显示正确颜色
- 匹配度百分比正确
- 项目卡片正常渲染
**Step 4: 提交**
```bash
git add src/components/search/AISearchResults.tsx
git add src/app/[locale]/projects/page.tsx
git commit -m "feat: 添加 AI 搜索结果展示组件"
```
---
## Phase 5: 测试与验证
### Task 8: 端到端测试
**Step 1: 测试向量化流程**
```sql
-- 检查向量化进度
SELECT
COUNT(*) FILTER (WHERE "embedding" IS NOT NULL) as vectorized,
COUNT(*) FILTER (WHERE "embedding" IS NULL) as not_vectorized,
COUNT(*) as total
FROM "Project";
```
预期:`not_vectorized` 应该逐渐减少(每 5 分钟更新一批)。
**Step 2: 测试 AI 搜索**
```bash
# 测试自然语言查询
curl -X POST http://localhost:3000/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"帮我找能生成视频的 AI 工具","limit":10}'
```
验证:
- 返回结果与"视频生成"相关
- 相似度分数合理(>0.6
- 响应时间 < 3 秒
**Step 3: 测试边界情况**
```bash
# 空查询
curl -X POST http://localhost:3000/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"","limit":10}'
# 预期:400 错误
# 无结果查询
curl -X POST http://localhost:3000/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"xyzabc123不存在的项目","limit":10}'
# 预期:空 results 数组
```
**Step 4: 测试降级逻辑**
在前端测试:
1. 停止 n8n 工作流
2. 执行 AI 搜索
3. 验证错误提示和降级到传统搜索
---
### Task 9: 性能验证
**Step 1: 测试搜索响应时间**
```bash
# 使用 time 命令测量
time curl -X POST http://localhost:3000/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"视频生成","limit":20}'
```
目标:< 3 秒
**Step 2: 测试并发搜索**
```bash
# 使用 apachebench 或类似工具
ab -n 100 -c 10 -p search.json -T application/json \
http://localhost:3000/api/search/ai
```
目标:能处理 10 并发无错误
**Step 3: 检查数据库查询性能**
在 n8n PostgreSQL 节点中查看执行时间:
```sql
EXPLAIN ANALYZE
SELECT ... (向量搜索查询)
```
目标:< 500ms
---
### Task 10: 文档更新
**Files:**
- Modify: `README.md`(可选)
- Modify: `CLAUDE.md`
**Step 1: 更新 CLAUDE.md**
`CLAUDE.md` 中添加 AI 搜索部分:
```markdown
## AI Search System
项目支持 AI 语义搜索功能,基于 pgvector 和 n8n 工作流实现。
### Architecture
- **Vector Database**: Neon PostgreSQL + pgvector extension
- **AI Services**: n8n workflows (vectorization + search)
- **Frontend**: Toggle button + similarity scores
### Environment Variables
```bash
# n8n Webhook
N8N_AI_SEARCH_WEBHOOK=https://your-n8n.com/webhook/ai-search
```
### n8n Workflows
1. **Project Vectorization**: Cron job (every 5 min) to generate embeddings
2. **AI Semantic Search**: Webhook for natural language queries
### Usage
用户在项目列表页面点击 AI 搜索按钮(✨ 图标),输入自然语言描述,系统返回语义相关的项目。
```
**Step 2: 提交**
```bash
git add CLAUDE.md
git commit -m "docs: 添加 AI 搜索系统文档"
```
---
## Phase 6: 部署
### Task 11: 生产环境部署
**Step 1: 验证 n8n 工作流**
1. 确保 n8n 实例在生产环境运行
2. 激活两个工作流
3. 验证 Webhook URL 可访问
**Step 2: 配置 Vercel 环境变量**
在 Vercel Dashboard 中添加:
```bash
N8N_AI_SEARCH_WEBHOOK=https://your-production-n8n.com/webhook/ai-search
```
**Step 3: 部署 Next.js 应用**
```bash
# 推送到 main 分支触发部署
git push origin main
# 或手动部署
vercel --prod
```
**Step 4: 验证生产环境**
```bash
# 测试生产环境 API
curl -X POST https://your-domain.com/api/search/ai \
-H "Content-Type: application/json" \
-d '{"search":"视频生成","limit":5}'
```
**Step 5: 监控 n8n 执行**
在 n8n Dashboard 中:
- 查看工作流执行历史
- 检查错误日志
- 监控 API 使用量
**Step 6: 设置告警(可选)**
在 n8n 中配置错误告警:
- OpenAI API 失败
- 数据库连接错误
- Webhook 超时
---
## 总结
### 完成检查清单
- [ ] Phase 1: 数据库迁移完成
- [ ] Phase 2: n8n 工作流配置完成
- [ ] Phase 3: Next.js API 路由实现
- [ ] Phase 4: 前端组件实现
- [ ] Phase 5: 测试与验证通过
- [ ] Phase 6: 生产环境部署成功
### 关键指标
- 向量化完成率: > 95%
- 搜索响应时间: < 3 秒
- 相似度准确率: 用户满意 > 80%
- API 成本: < $5/月(假设 1000 次搜索/天)
### 后续优化方向
1. 添加查询缓存(Redis
2. 实现混合搜索(向量 + 关键词)
3. A/B 测试用户体验
4. 多模态搜索(图片、语音)
5. 个性化排序
---
**计划创建日期**: 2026-01-25
**预计完成时间**: 4-6 小时
**难度等级**: 中等