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

25 KiB
Raw Blame History

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):

# 使用 psql 连接
psql $DATABASE_URL

# 或使用 Neon SQL Editor: https://console.neon.tech

执行 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,在字段列表末尾添加:

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: 创建迁移文件

# 生成迁移(会创建 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),确保包含:

-- 启用 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: 应用迁移

# 如果在上一步中已经自动应用,跳过此步骤
# 否则手动应用:
pnpm prisma migrate deploy

Step 6: 验证迁移

-- 检查表结构
\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

pnpm prisma generate

Step 8: 提交

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. 导航到 CredentialsNew
  3. 选择 OpenAI API
  4. 输入名称:OpenAI Embeddings
  5. 粘贴 API Key
  6. 保存

Step 3: 添加 Neon 数据库凭证

  1. 在 n8n 中导航到 CredentialsNew
  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. 输入查询:
SELECT id, name, "nameEn", description, "descriptionEn", content, "contentEn"
FROM "Project"
WHERE "embedding" IS NULL
  AND "status" = 'ACTIVE'
LIMIT 20
  1. 测试节点(应该返回现有项目列表)
  2. 保存节点

Step 4: 添加 Function 节点(构造文本内容)

  1. 添加节点:FunctionJavaScript
  2. 命名:Construct Text Content
  3. 输入代码:
// 为每个项目构造用于向量化的文本内容
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
    }
  };
});
  1. 测试节点(应该添加 textContent 字段)
  2. 保存节点

Step 5: 添加 Split in Batches 节点

  1. 添加节点:Split In Batches
  2. 配置:
    • Batch Size: 5(每批 5 个项目,避免 API 限流)
  3. 保存节点

Step 6: 添加 OpenAI Embeddings 节点

  1. 添加节点:OpenAIEmbeddings
  2. 使用 OpenAI Embeddings 凭证
  3. 配置:
    • Model: text-embedding-3-small
    • Input: {{ $json.textContent }}
  4. 测试节点(应该返回向量数组)
  5. 保存节点

Step 7: 添加 PostgreSQL 节点(更新 embedding

  1. 添加节点:Postgres
  2. 操作:Execute Query
  3. 输入查询:
UPDATE "Project"
SET
  "embedding" = '{{ $json.data[0].embedding }}'::vector,
  "embeddingUpdatedAt" = NOW()
WHERE "id" = {{ $json.id }}
  1. 测试节点
  2. 保存节点

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: 验证向量化结果

-- 检查已向量化项目数
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):
curl -X POST https://your-n8n.com/webhook/ai-search \
  -H "Content-Type: application/json" \
  -d '{"query":"test","locale":"zh","limit":5}'
  1. 保存节点

Step 3: 添加 OpenAI Embeddings 节点(生成查询向量)

  1. 添加节点:OpenAIEmbeddings
  2. 使用 OpenAI Embeddings 凭证
  3. 配置:
    • Model: text-embedding-3-small
    • Input: {{ $json.query }}
  4. 测试节点
  5. 保存节点

Step 4: 添加 PostgreSQL 节点(向量相似度搜索)

  1. 添加节点:Postgres
  2. 操作:Execute Query
  3. 输入查询:
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 }}
  1. 测试节点(应该返回相似度排序的项目)
  2. 保存节点

Step 5: 添加 Function 节点(查询项目标签)

  1. 添加节点:FunctionJavaScript
  2. 输入代码:
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
    }
  };
});
  1. 保存节点

Step 6: 添加 PostgreSQL 节点(查询标签)

  1. 添加节点:Postgres
  2. 操作:Execute Query
  3. 输入查询:
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)
  1. 保存节点

Step 7: 添加 Function 节点(合并标签)

  1. 添加节点:FunctionJavaScript
  2. 输入代码:
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;
  1. 测试节点
  2. 保存节点

Step 8: 添加 Function 节点(格式化响应)

  1. 添加节点:FunctionJavaScript
  2. 输入代码:
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
  }
};
  1. 保存节点

Step 9: 连接节点

Webhook → OpenAI Embeddings → Postgres (向量搜索) → Function (查询标签) → Postgres (标签) → Function (合并) → Function (格式化) → Response

Step 10: 测试完整工作流

  1. 点击 Test Workflow
  2. 在 Webhook 节点中点击 Listen for Test Event
  3. 发送测试请求:
curl -X POST https://your-n8n.com/webhook/ai-search \
  -H "Content-Type: application/json" \
  -d '{"query":"视频生成工具","locale":"zh","limit":5}'
  1. 验证响应格式:
{
  "results": [
    {
      "project": { /* 项目数据 */ },
      "similarity": 0.89,
      "matchReason": "相似度: 89%"
    }
  ],
  "total": 5,
  "searchTime": 1234
}

Step 11: 激活工作流

  1. 点击 Active 开关
  2. 保存生产环境的 Webhook URL

Step 12: 配置环境变量

.env.local 中添加:

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

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 路由

启动开发服务器:

pnpm dev

测试 API

curl -X POST http://localhost:3000/api/search/ai \
  -H "Content-Type: application/json" \
  -d '{"search":"视频生成工具","limit":5}'

预期响应:

{
  "results": [...],
  "total": 5,
  "searchTime": 1234
}

Step 3: 提交

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: 读取现有组件

# 查看现有组件结构
cat src/components/search/SearchBar.tsx

Step 2: 添加 AI 模式状态

在组件中添加:

'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 函数:

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 切换按钮

在搜索输入框旁边添加:

<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: 提交

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

'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

// 在页面中添加 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: 提交

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: 测试向量化流程

-- 检查向量化进度
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 搜索

# 测试自然语言查询
curl -X POST http://localhost:3000/api/search/ai \
  -H "Content-Type: application/json" \
  -d '{"search":"帮我找能生成视频的 AI 工具","limit":10}'

验证:

  • 返回结果与"视频生成"相关
  • 相似度分数合理(>0.6
  • 响应时间 < 3 秒

Step 3: 测试边界情况

# 空查询
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: 测试搜索响应时间

# 使用 time 命令测量
time curl -X POST http://localhost:3000/api/search/ai \
  -H "Content-Type: application/json" \
  -d '{"search":"视频生成","limit":20}'

目标:< 3 秒

Step 2: 测试并发搜索

# 使用 apachebench 或类似工具
ab -n 100 -c 10 -p search.json -T application/json \
  http://localhost:3000/api/search/ai

目标:能处理 10 并发无错误

Step 3: 检查数据库查询性能

在 n8n PostgreSQL 节点中查看执行时间:

EXPLAIN ANALYZE
SELECT ... (向量搜索查询)

目标:< 500ms


Task 10: 文档更新

Files:

  • Modify: README.md(可选)
  • Modify: CLAUDE.md

Step 1: 更新 CLAUDE.md

CLAUDE.md 中添加 AI 搜索部分:

## 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 中添加:

N8N_AI_SEARCH_WEBHOOK=https://your-production-n8n.com/webhook/ai-search

Step 3: 部署 Next.js 应用

# 推送到 main 分支触发部署
git push origin main

# 或手动部署
vercel --prod

Step 4: 验证生产环境

# 测试生产环境 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 小时 难度等级: 中等