第41期 | 项目1:AI知识库产品
第41期 | 项目1:AI知识库产品
🎯 今天你将学会
- 从产品视角设计一个 AI 知识库产品(不只是技术实现)
- 产品级开发的项目规划方法(需求→设计→实现→测试→部署)
- 实现完整的 AI 知识库:文档管理 + RAG问答 + 对话历史 + 用户认证
- 理解产品级代码跟教程代码的区别——可维护、可扩展、可部署
📖 核心知识
从「教程代码」到「产品代码」
模块一到四你写了大量代码,但那些是教程代码——为了让你理解概念,代码尽可能简单。
产品代码不同:
| 维度 | 教程代码 | 产品代码 |
|---|---|---|
| 错误处理 | try-catch + console.log | 统一错误边界 + 用户友好提示 + 自动恢复 |
| 状态管理 | 简单 useState | Zustand + persist + optimistic updates |
| 数据验证 | 假设数据格式正确 | Zod schema 验证 + 边界情况处理 |
| 性能 | 不考虑 | 虚拟列表 + 懒加载 + 缓存 + debounce |
| 安全 | 不考虑 | API Key 保护 + 输入验证 + 内容过滤 |
| 部署 | localhost | CI/CD + 环境变量 + 监控 + 日志 |
| 可维护性 | 写完就扔 | 代码规范 + 测试覆盖 + 文档同步 |
本期目标:把教程代码升级为产品代码。
产品定义:KnowBase AI
产品名: KnowBase — AI 驱动的知识库
核心功能:
| 功能 | 描述 | 优先级 |
|---|---|---|
| 文档管理 | 上传、查看、删除文档 | P0 |
| RAG 问答 | 基于文档的智能问答 + 引用来源 | P0 |
| 对话历史 | 多轮对话 + 历史记录 | P0 |
| 用户认证 | 注册/登录 + 个人知识库 | P1 |
| 文档分享 | 生成分享链接,让其他人也能用你的知识库 | P2 |
| API 接口 | 提供 REST API,让其他应用也能调用知识库 | P2 |
技术栈:
| 层级 | 技术 | 原因 |
|---|---|---|
| 前端 | Next.js 14 (App Router) + React 18 + TS | SSR + API Routes 一体 |
| UI | shadcn/ui + Tailwind CSS | 轻量可定制 |
| 状态管理 | Zustand + React Query | Zustand 管 UI 状态,RQ 管 API 状态 |
| 后端 | Next.js API Routes | 前后端一体,减少运维 |
| LLM | OpenAI GPT-4o-mini + DALL-E | 成本可控 |
| 向量存储 | Supabase pgvector | 免费 + SQL兼容 |
| 数据库 | Supabase PostgreSQL | 用户数据 + 文档元数据 |
| 认证 | Supabase Auth | 免费 + 简单 |
| 部署 | Vercel | 免费 + 自动 CI/CD |
项目规划:5 天开发计划
Day 1:项目搭建 + 认证
上午:
- 创建 Next.js 项目 + 安装依赖
- 配置 Tailwind + shadcn/ui
- 配置 Supabase(数据库 + Auth)
- 实现注册/登录页面
下午:
- 实现认证中间件(保护 API 路由)
- 实现布局组件(Sidebar + Header + AuthGuard)
- 基础 CRUD API(文档管理接口)
Day 2:文档管理
上午:
- DocumentUpload 组件(拖拽上传 + 进度显示)
- 文档上传 API(接收文件 → 分片 → embedding → 存入 pgvector)
- DocumentList 组件(文档列表 + 搜索 + 删除)
下午:
- ChunkPreview 组件(分片预览 + 编辑)
- 文档详情页面
- 知识库统计面板(文档数、分片数、搜索次数)
Day 3:RAG 问答核心
上午:
- ChatInterface 组件(结合第33期的聊天界面)
- RAG Chat API(向量搜索 + LLM + 流式返回)
- SourceReference 组件(引用来源展示)
下午:
- 对话历史管理(多轮对话 + 持久化)
- 对话标题自动生成
- 模式切换(纯对话 / RAG问答)
Day 4:产品级打磨
上午:
- 错误边界组件(统一错误处理)
- 数据验证(Zod schema)
- 性能优化(虚拟列表 + 缓存)
- Token 使用量追踪 + 成本估算面板
下午:
- 暗黑模式完善
- 响应式布局优化(移动端适配)
- 加载状态优化(Skeleton + Suspense)
- 空状态设计
Day 5:部署 + 文档
上午:
- Vercel 部署配置
- 环境变量管理
- CI/CD 配置(自动测试 + 自动部署)
- 性能监控(Vercel Analytics)
下午:
- README 完善
- API 文档生成
- 用户指南编写
- 项目复盘 + 记录经验
核心代码实现
1. 项目结构(产品级)
knowbase/
├── app/
│ ├── (auth)/ — 认证相关页面(登录/注册)
│ │ ├── login/page.tsx
│ │ └── register/page.tsx
│ ├── (main)/ — 主功能页面(需要认证)
│ │ ├── layout.tsx — AuthGuard + Sidebar + Header
│ │ ├── page.tsx — 首页(对话界面)
│ │ ├── knowledge/
│ │ │ ├── page.tsx — 知识库管理
│ │ │ └── [id]/page.tsx — 文档详情
│ │ └── settings/page.tsx — 设置(模型切换/Token追踪)
│ └── api/
│ │ ├── auth/ — 认证接口
│ │ ├── ai/
│ │ │ ├── chat/route.ts — 基础对话
│ │ │ └── rag/route.ts — RAG问答
│ │ ├── documents/
│ │ │ ├── upload/route.ts
│ │ │ ├── list/route.ts
│ │ │ └── delete/[id]/route.ts
│ │ └── search/route.ts — 向量搜索
│ └── layout.tsx — 全局布局
├── features/
│ ├── auth/ — 认证模块
│ ├── chat/ — 聊天模块
│ ├── knowledge/ — 知识库模块
│ └── settings/ — 设置模块
├── lib/
│ ├── ai-client.ts — AI客户端
│ ├── supabase.ts — Supabase客户端
│ ├── zod-schemas.ts — Zod验证schema
│ └── error-handler.ts — 统一错误处理
├── components/
│ ├── ui/ — shadcn/ui
│ └── layout/ — 布局组件
├── types/
└── middleware.ts — 认证中间件
2. 认证中间件(Next.js App Router)
// middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs';
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const supabase = createMiddlewareClient({ req, res });
const { data: { session } } = await supabase.auth.getSession();
// 未登录 → 重定向到登录页
if (!session && req.nextUrl.pathname.startsWith('/(main)')) {
return NextResponse.redirect(new URL('/login', req.url));
}
// 已登录 → 不允许访问登录页
if (session && req.nextUrl.pathname.startsWith('/(auth)')) {
return NextResponse.redirect(new URL('/', req.url));
}
return res;
}
export const config = {
matcher: ['/(main):path*', '/(auth):path*'],
};
3. Zod 数据验证
// lib/zod-schemas.ts
import { z } from 'zod';
// 文档上传验证
export const documentUploadSchema = z.object({
file: z.instanceof(File)
.refine(f => f.size <= 20 * 1024 * 1024, '文件大小不能超过 20MB')
.refine(f => ['application/pdf', 'text/markdown', 'text/plain'].includes(f.type) || f.name.endsWith('.md'),
'请上传 PDF、Markdown 或纯文本文件'),
title: z.string().min(1).max(100),
});
// 聊天消息验证
export const chatMessageSchema = z.object({
message: z.string().min(1, '消息不能为空').max(4000, '消息太长'),
conversationId: z.string().uuid(),
mode: z.enum(['chat', 'rag']),
});
// API 响应验证
export const apiResponseSchema = z.object({
content: z.string(),
sources: z.array(z.object({
id: z.string(),
title: z.string(),
content: z.string(),
source: z.string(),
relevance: z.number().min(0).max(1),
})).optional(),
usage: z.object({
prompt_tokens: z.number(),
completion_tokens: z.number(),
total_tokens: z.number(),
}).optional(),
});
4. 统一错误处理
// components/ErrorBoundary.tsx
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="p-8 text-center">
<h2 className="text-xl font-semibold mb-2">出了点问题</h2>
<p className="text-gray-500 mb-4">{this.state.error?.message}</p>
<button
onClick={() => this.setState({ hasError: false, error: null })}
className="px-4 py-2 rounded-lg bg-blue-500 text-white"
>
重试
</button>
</div>
);
}
return this.props.children;
}
}
5. Supabase 数据库 Schema
-- 文档表
CREATE TABLE documents (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users NOT NULL,
title TEXT NOT NULL,
file_type TEXT NOT NULL,
file_size INTEGER NOT NULL,
chunk_count INTEGER DEFAULT 0,
status TEXT DEFAULT 'processing' CHECK (status IN ('processing', 'completed', 'error')),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 文档分片表(带向量)
CREATE TABLE document_chunks (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
document_id UUID REFERENCES documents ON DELETE CASCADE NOT NULL,
content TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
embedding VECTOR(1536), -- OpenAI embedding 维度
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 向量搜索函数
CREATE FUNCTION match_documents(
query_embedding VECTOR(1536),
match_count INT DEFAULT 5,
match_threshold FLOAT DEFAULT 0.5
) RETURNS TABLE (
id UUID,
document_id UUID,
content TEXT,
similarity FLOAT
) AS $
SELECT
id,
document_id,
content,
1 - (embedding <=> query_embedding) AS similarity
FROM document_chunks
WHERE 1 - (embedding <=> query_embedding) > match_threshold
ORDER BY embedding <=> query_embedding
LIMIT match_count;
$ LANGUAGE SQL;
产品级 vs 教程级的关键差异
| 环节 | 教程做法 | 产品做法 | 为什么 |
|---|---|---|---|
| 认证 | 不做 | Supabase Auth + middleware | 每个用户有自己的知识库 |
| 数据验证 | 假设格式正确 | Zod schema 验证 | 防止无效数据进入系统 |
| 错误处理 | console.log | ErrorBoundary + 用户友好提示 | 用户不看 console |
| 向量存储 | 内存 mock | Supabase pgvector | 生产环境需要持久化和高性能 |
| 文档分片 | 简单切分 | 段落级分片 + 元数据 | 搜索精度取决于分片质量 |
| 部署 | localhost | Vercel + CI/CD | 用户需要在线访问 |
常见误区
误区1:产品级 = 更多功能
产品级不是功能更多,而是每个功能更可靠。错误处理、数据验证、性能优化——这些看不见的东西才是产品级的核心。
误区2:先写完所有功能再部署
应该 Day 3 结束时就做第一次部署——尽早暴露生产环境的问题(CORS、SSL、环境变量等)。
误区3:认证不重要
没有认证 = 所有用户共享一个知识库 = 数据混乱。认证是产品级应用的基础设施。
🤖 AI协作实战
实战场景:5 天用 AI 协作开发 KnowBase
我给 Cursor Composer 的任务拆分:
Day 1 任务清单:
1. 创建 Next.js 项目 + 安装所有依赖(openai, @supabase/supabase-js, zod, zustand, react-markdown, lucide-react)
2. 配置 shadcn/ui + Tailwind CSS
3. 配置 Supabase 客户端(lib/supabase.ts)
4. 实现注册/登录页面(用 Supabase Auth)
5. 实现认证中间件(middleware.ts)
6. 实现主布局(Sidebar + Header + AuthGuard)
Day 2 任务清单:
1. DocumentUpload 组件(拖拽上传 + 进度)
2. 文档上传 API(/api/documents/upload)
3. DocumentList 组件
4. ChunkPreview 组件
Day 3 任务清单:
1. ChatInterface 组件
2. RAG Chat API(/api/ai/rag)
3. SourceReference 组件
4. 对话历史管理
Day 4 任务清单:
1. ErrorBoundary + zod-schemas
2. 性能优化(虚拟列表 + 缓存)
3. 暗黑模式 + 响应式
4. Token 追踪面板
Day 5 任务清单:
1. Vercel 部署
2. README + API 文档
AI 协作效率统计:
| 天数 | 我做决策 | AI 写代码 | 审查时间 | 总耗时 |
|---|---|---|---|---|
| Day 1 | 架构 + 技术选型 | 项目搭建 + 认证代码 | 30min | 3h |
| Day 2 | 文档管理逻辑 | 组件代码 + API | 20min | 3h |
| Day 3 | RAG 流程设计 | 聊天 + RAG 代码 | 25min | 4h |
| Day 4 | 优化策略 | 优化 + 打磨代码 | 30min | 4h |
| Day 5 | 部署 + 文档策略 | 配置 + 文档 | 15min | 3h |
| 总计 | 策略决策 | 95%代码量 | 2h | 17h |
对比纯手写预估:50-60 小时。效率提升 3.5x。
学到了什么: 5 天开发一个产品级项目是可行的——前提是 AI 写 95% 的代码,你做 100% 的决策和审查。你的角色不是「写代码的人」,而是「做产品决策的人」。
💻 动手练习
练习1(简单):搭建项目骨架 + 认证
创建 Next.js 项目 + 安装依赖 + 配置 Supabase Auth + 实现登录/注册页面 + 认证中间件。确保骨架能运行,登录流程正常。
练习2(中等):实现文档管理 + RAG 问答
在项目骨架上添加:
- 文档上传 + 列表 + 删除
- RAG 问答界面 + 引用来源展示
- 对话历史管理
练习3(挑战):完整 5 天开发计划
按本期的 5 天计划完整开发 KnowBase AI:
- Day 1-5 严格执行
- 每天记录 AI 参与度和效率
- 最终部署到 Vercel
- 写一份项目复盘报告
📌 本期要点
- 产品级 vs 教程级: 不是功能更多,而是每个功能更可靠(错误处理/数据验证/性能优化/安全防护)
- 5 天开发计划: Day1(认证) → Day2(文档管理) → Day3(RAG核心) → Day4(打磨) → Day5(部署)
- 认证是基础设施: Supabase Auth + middleware,每个用户有自己的知识库
- Zod 数据验证是必需的: 防止无效数据进入系统——教程代码假设格式正确,产品代码不假设
- 你的角色变了: 从「写代码的人」变成「做产品决策的人」——AI 写 95% 代码,你做 100% 决策
🔗 下期预告
下一期是项目1的续篇——部署与优化。你将学会把 KnowBase 部署到 Vercel、做性能优化、SEO 配置,以及产品级监控。
如果你没有苹果电脑,需要上传ios到APPStore可以访问以下网站
iPA上传工具 - IPA解析与AppStore提交
更多推荐



所有评论(0)