返回笔记首页

完整代码实现文档(Part 1)

主题配置

项目完整文件结构

plain
ai-content-studio/
├── 📄 package.json                          # 项目依赖配置
├── 📄 tsconfig.json                         # TypeScript 配置
├── 📄 next.config.js                        # Next.js 配置
├── 📄 .env.example                          # 环境变量示例
├── 📄 .gitignore                            # Git 忽略文件
├── 📄 README.md                             # 项目说明文档
│
├── 📁 public/                               # 静态资源
│   ├── logo.svg                            # 网站图标
│   └── images/                             # 图片资源
│
├── 📁 src/                                  # 源代码目录
│   ├── 📁 app/                             # Next.js 14 App Router
│   │   ├── layout.tsx                      # 根布局(Ant Design 配置)
│   │   ├── page.tsx                        # 首页
│   │   ├── globals.css                     # 全局样式
│   │   │
│   │   ├── 📁 (dashboard)/                # 主应用路由组
│   │   │   ├── layout.tsx                 # 仪表盘布局
│   │   │   ├── projects/                  # 项目列表页
│   │   │   │   └── page.tsx
│   │   │   ├── create/                    # 创作页面
│   │   │   │   └── page.tsx
│   │   │   ├── settings/                  # 设置页面
│   │   │   │   └── api/
│   │   │   │       └── page.tsx          # API 配置页
│   │   │   └── [id]/                     # 项目详情页
│   │   │       └── page.tsx
│   │   │
│   │   └── 📁 api/                        # API 路由
│   │       └── ai/                        # AI 相关接口
│   │           ├── plan/                  # 规划 Agent
│   │           │   └── route.ts
│   │           ├── write/                 # 写作 Agent
│   │           │   └── route.ts
│   │           ├── optimize/              # 优化 Agent
│   │           │   └── route.ts
│   │           └── test/                  # API 测试
│   │               └── route.ts
│   │
│   ├── 📁 components/                      # React 组件
│   │   ├── 📁 agents/                     # Agent 相关组件
│   │   │   ├── AgentCard.tsx             # Agent 状态卡片
│   │   │   └── AgentPipeline.tsx         # Agent 流水线
│   │   ├── 📁 editor/                     # 编辑器组件
│   │   │   └── ContentEditor.tsx         # 内容编辑器
│   │   └── 📁 layout/                     # 布局组件
│   │       ├── Header.tsx                # 头部导航
│   │       ├── Sidebar.tsx               # 侧边栏
│   │       └── Footer.tsx                # 页脚
│   │
│   ├── 📁 lib/                            # 核心逻辑库
│   │   ├── 📁 ai/                        # AI 相关
│   │   │   ├── client.ts                 # AI 客户端工厂
│   │   │   └── monitor.ts                # 使用监控
│   │   ├── 📁 agents/                    # Agent 实现
│   │   │   ├── planner.ts                # 规划 Agent
│   │   │   ├── writer.ts                 # 写作 Agent
│   │   │   ├── optimizer.ts              # 优化 Agent
│   │   │   └── orchestrator.ts           # Agent 协调器
│   │   ├── 📁 db/                        # 数据库
│   │   │   ├── supabase.ts               # Supabase 客户端
│   │   │   ├── queries.ts                # 数据库查询
│   │   │   └── types.ts                  # 类型定义
│   │   ├── 📁 prompts/                   # Prompt 模板
│   │   │   ├── planner.ts                # 规划 Prompt
│   │   │   ├── writer.ts                 # 写作 Prompt
│   │   │   └── optimizer.ts              # 优化 Prompt
│   │   └── 📁 utils/                     # 工具函数
│   │       ├── text.ts                   # 文本处理
│   │       └── format.ts                 # 格式化
│   │
│   ├── 📁 stores/                         # 状态管理
│   │   ├── projectStore.ts               # 项目状态
│   │   └── agentStore.ts                 # Agent 状态
│   │
│   ├── 📁 hooks/                          # 自定义 Hooks
│   │   ├── useProject.ts                 # 项目管理
│   │   └── useAgentStream.ts             # 流式响应
│   │
│   ├── 📁 types/                          # TypeScript 类型
│   │   ├── agent.ts                      # Agent 类型
│   │   └── project.ts                    # 项目类型
│   │
│   └── 📁 theme/                          # 主题配置
│       └── themeConfig.ts                # Ant Design 主题
│
└── 📁 database/                            # 数据库脚本
    └── schema.sql                         # 数据库表结构

核心文件详细代码和注释

1. package.json - 项目依赖配置

json
{
  "name": "ai-content-studio",
  "version": "1.1.0",
  "description": "AI 驱动的内容创作工作台 - 基于多 Agent 协作",

  // 项目脚本命令
  "scripts": {
    // 开发模式:启动热重载开发服务器
    "dev": "next dev",

    // 生产构建:编译 TypeScript 和优化代码
    "build": "next build",

    // 生产启动:运行编译后的应用
    "start": "next start",

    // 代码检查:ESLint 检查代码规范
    "lint": "next lint",

    // 自动修复:自动修复可修复的代码问题
    "lint:fix": "next lint --fix",

    // 类型检查:检查 TypeScript 类型错误(不生成文件)
    "type-check": "tsc --noEmit",

    // 代码格式化:Prettier 格式化所有代码
    "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,md}\"",

    // 生成数据库类型:从 Supabase 自动生成 TypeScript 类型
    "db:generate-types": "npx supabase gen types typescript --project-id YOUR_PROJECT_ID > src/lib/db/types.ts"
  },

  // 生产依赖
  "dependencies": {
    // ===== 核心框架 =====
    "next": "14.0.4",              // Next.js 框架
    "react": "^18.2.0",            // React 库
    "react-dom": "^18.2.0",        // React DOM 渲染

    // ===== UI 组件库 =====
    "antd": "^5.12.0",             // Ant Design 组件库
    "@ant-design/icons": "^5.2.6", // Ant Design 图标
    "@ant-design/nextjs-registry": "^1.0.0", // Next.js 集成

    // ===== AI 相关 =====
    "ai": "^3.0.0",                // Vercel AI SDK
    "@ai-sdk/openai": "^0.0.42",   // OpenAI 适配器(DeepSeek 兼容)
    "zod": "^3.22.4",              // Schema 验证

    // ===== 状态管理 =====
    "zustand": "^4.4.7",           // 轻量级状态管理

    // ===== 数据库 =====
    "@supabase/supabase-js": "^2.39.0", // Supabase 客户端

    // ===== 工具库 =====
    "dayjs": "^1.11.10",           // 日期处理(Ant Design 依赖)
    "nanoid": "^5.0.4"             // ID 生成
  },

  // 开发依赖
  "devDependencies": {
    "@types/node": "^20",          // Node.js 类型定义
    "@types/react": "^18",         // React 类型定义
    "@types/react-dom": "^18",     // React DOM 类型定义
    "eslint": "^8",                // 代码检查工具
    "eslint-config-next": "14.0.4",// Next.js ESLint 配置
    "prettier": "^3.1.1",          // 代码格式化工具
    "typescript": "^5"             // TypeScript 编译器
  },

  // 引擎要求
  "engines": {
    "node": ">=18.17.0",           // 最低 Node.js 版本
    "npm": ">=9.0.0"               // 最低 npm 版本
  }
}

关键依赖说明

  1. Ant Design
    • 企业级 UI 组件库
    • 60+ 组件开箱即用
    • 完善的中文文档
  2. Vercel AI SDK
    • 统一的 AI 接口
    • 支持流式响应
    • 兼容多个 AI 提供商
  3. Zustand
    • 比 Redux 简单 10 倍
    • 性能好、体积小
    • TypeScript 友好

3. src/theme/themeConfig.ts - 主题配置

typescript
/**
 * Ant Design 主题配置
 *
 * 定制品牌色、圆角、字体等
 * 所有组件都会应用这些配置
 */

import type { ThemeConfig } from 'antd';

export const theme: ThemeConfig = {
  // ===== Token 级别配置 =====
  // 这些是设计系统的基础变量
  token: {
    // ----- 品牌色 -----
    colorPrimary: '#1890ff',      // 主色:蓝色(按钮、链接等)
    colorSuccess: '#52c41a',      // 成功色:绿色
    colorWarning: '#faad14',      // 警告色:橙色
    colorError: '#ff4d4f',        // 错误色:红色
    colorInfo: '#1890ff',         // 信息色:蓝色

    // ----- 圆角 -----
    borderRadius: 8,              // 基础圆角:8px
    borderRadiusLG: 12,           // 大圆角:12px(卡片等)
    borderRadiusSM: 4,            // 小圆角:4px(按钮等)

    // ----- 间距 -----
    padding: 16,                  // 基础内边距
    paddingLG: 24,                // 大内边距
    paddingSM: 12,                // 小内边距

    // ----- 字体 -----
    fontSize: 14,                 // 基础字号
    fontSizeHeading1: 32,         // h1 字号
    fontSizeHeading2: 28,         // h2 字号
    fontSizeHeading3: 24,         // h3 字号

    // 字体家族(优先使用系统字体,性能好)
    fontFamily: `-apple-system, BlinkMacSystemFont,
                 "Segoe UI", Roboto, "Helvetica Neue",
                 Arial, "PingFang SC", "Hiragino Sans GB",
                 "Microsoft YaHei", sans-serif`,

    // ----- 阴影 -----
    boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
    boxShadowSecondary: '0 4px 16px rgba(0, 0, 0, 0.08)',
  },

  // ===== 组件级别配置 =====
  // 针对特定组件的定制
  components: {
    // ----- 按钮组件 -----
    Button: {
      controlHeight: 40,          // 默认高度:40px
      controlHeightLG: 48,        // 大按钮高度:48px
      controlHeightSM: 32,        // 小按钮高度:32px
      fontSize: 14,               // 字号
      fontSizeLG: 16,             // 大按钮字号
      borderRadius: 8,            // 圆角
      paddingContentHorizontal: 16, // 水平内边距
    },

    // ----- 输入框组件 -----
    Input: {
      controlHeight: 40,          // 默认高度
      fontSize: 14,               // 字号
      borderRadius: 8,            // 圆角
      paddingBlock: 8,            // 上下内边距
      paddingInline: 12,          // 左右内边距
    },

    // ----- 卡片组件 -----
    Card: {
      borderRadiusLG: 12,         // 圆角
      paddingLG: 24,              // 内边距
      boxShadowTertiary: '0 1px 4px rgba(0, 0, 0, 0.08)',
    },

    // ----- 表单组件 -----
    Form: {
      labelFontSize: 14,          // 标签字号
      verticalLabelPadding: '0 0 8px', // 垂直布局标签内边距
    },

    // ----- 消息提示 -----
    Message: {
      contentPadding: '12px 16px',
      fontSize: 14,
    },
  },
};

主题配置的好处

  1. 统一视觉:所有组件自动应用相同的设计规范
  2. 易于维护:修改一处,全局生效
  3. 品牌定制:轻松更换品牌色

使用示例

typescript
// 所有按钮都会自动应用这些样式
<Button type="primary">提交</Button> // 高度 40px,圆角 8px
<Button size="large">大按钮</Button> // 高度 48px

4. src/lib/ai/client.ts - AI 客户端工厂

typescript
/**
 * AI 客户端工厂
 *
 * 职责:
 * 1. 统一管理多个 AI 提供商(DeepSeek、OpenAI、Claude)
 * 2. 根据配置自动选择 AI 服务
 * 3. 提供一致的调用接口
 *
 * 设计模式:工厂模式
 */

import { createOpenAI } from '@ai-sdk/openai';
import { createAnthropic } from '@ai-sdk/anthropic';

/**
 * AI 提供商类型
 */
export type AIProvider = 'deepseek' | 'openai' | 'claude';

/**
 * AI 配置接口
 */
export interface AIConfig {
  provider: AIProvider;      // 提供商
  deepseekKey?: string;      // DeepSeek API Key
  openaiKey?: string;        // OpenAI API Key
  claudeKey?: string;        // Claude API Key
}

/**
 * 获取当前 AI 配置
 *
 * 优先级:
 * 1. 环境变量(服务端)
 * 2. localStorage(客户端)
 * 3. 默认值(DeepSeek)
 *
 * @returns AI 配置对象
 */
export function getAIConfig(): AIConfig {
  // 服务端:从环境变量读取
  if (typeof window === 'undefined') {
    return {
      provider: (process.env.AI_PROVIDER as AIProvider) || 'deepseek',
      deepseekKey: process.env.DEEPSEEK_API_KEY,
      openaiKey: process.env.OPENAI_API_KEY,
      claudeKey: process.env.ANTHROPIC_API_KEY,
    };
  }

  // 客户端:从 localStorage 读取
  const stored = localStorage.getItem('ai_config');
  if (stored) {
    try {
      return JSON.parse(stored);
    } catch {
      // JSON 解析失败,返回默认值
    }
  }

  // 默认配置
  return {
    provider: 'deepseek',
    deepseekKey: process.env.NEXT_PUBLIC_DEEPSEEK_API_KEY,
  };
}

/**
 * 创建 AI 客户端
 *
 * 使用工厂模式,根据配置创建对应的客户端
 *
 * @param config - AI 配置(可选,不传则使用默认配置)
 * @returns AI 客户端实例
 *
 * @example
 * // 使用默认配置(DeepSeek)
 * const client = createAIClient();
 *
 * // 使用自定义配置
 * const client = createAIClient({
 *   provider: 'openai',
 *   openaiKey: 'sk-...',
 * });
 */
export function createAIClient(config?: AIConfig) {
  // 获取配置
  const aiConfig = config || getAIConfig();

  // 根据提供商创建对应的客户端
  switch (aiConfig.provider) {
    case 'deepseek':
      // DeepSeek 使用 OpenAI 兼容格式
      // 只需修改 baseURL 和 API Key
      return createOpenAI({
        apiKey: aiConfig.deepseekKey || process.env.DEEPSEEK_API_KEY,
        baseURL: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',
      });

    case 'openai':
      // OpenAI 官方
      return createOpenAI({
        apiKey: aiConfig.openaiKey || process.env.OPENAI_API_KEY,
      });

    case 'claude':
      // Anthropic Claude
      return createAnthropic({
        apiKey: aiConfig.claudeKey || process.env.ANTHROPIC_API_KEY,
      });

    default:
      // 不支持的提供商,抛出错误
      throw new Error(`不支持的 AI 提供商: ${aiConfig.provider}`);
  }
}

/**
 * 获取模型名称
 *
 * 不同提供商使用不同的模型名称
 *
 * @param config - AI 配置(可选)
 * @returns 模型名称字符串
 *
 * @example
 * const modelName = getModelName(); // 'deepseek-chat'
 */
export function getModelName(config?: AIConfig): string {
  const aiConfig = config || getAIConfig();

  // 模型名称映射
  const modelMap: Record<AIProvider, string> = {
    deepseek: 'deepseek-chat',           // DeepSeek 通用模型
    openai: 'gpt-4-turbo-preview',       // GPT-4 Turbo
    claude: 'claude-3-sonnet-20240229',  // Claude 3 Sonnet
  };

  return modelMap[aiConfig.provider] || 'deepseek-chat';
}

/**
 * 保存 AI 配置
 *
 * 保存到 localStorage(仅客户端)
 *
 * @param config - 要保存的配置
 *
 * @example
 * saveAIConfig({
 *   provider: 'deepseek',
 *   deepseekKey: 'sk-...',
 * });
 */
export function saveAIConfig(config: AIConfig): void {
  if (typeof window !== 'undefined') {
    localStorage.setItem('ai_config', JSON.stringify(config));
  }
}

设计亮点

  1. 工厂模式
    • 统一接口,隐藏实现细节
    • 易于扩展新的 AI 提供商
  2. 配置优先级
    • 环境变量 > localStorage > 默认值
    • 灵活适配不同场景
  3. 类型安全
    • 完整的 TypeScript 类型定义
    • 编译时检查,减少运行时错误

使用示例

typescript
// 在 Agent 中使用
import { createAIClient, getModelName } from '@/lib/ai/client';

const client = createAIClient();
const model = getModelName();

const result = await generateText({
  model: client(model),
  prompt: '你好',
});

5. src/lib/agents/planner.ts - 规划 Agent

typescript
/**
 * 规划 Agent
 *
 * 职责:
 * 1. 接收用户输入的主题和要求
 * 2. 调用 AI 生成文章大纲
 * 3. 返回结构化的大纲数据
 *
 * 这是多 Agent 系统的第一个 Agent
 */

import { generateObject } from 'ai';
import { createAIClient, getModelName } from '@/lib/ai/client';
import { z } from 'zod';
import { buildPlannerPrompt } from '@/lib/prompts/planner';

/**
 * 大纲 Schema 定义
 *
 * 使用 Zod 定义数据结构:
 * 1. 自动验证 AI 返回的数据
 * 2. 自动生成 TypeScript 类型
 * 3. 确保数据格式正确
 */
const OutlineSchema = z.object({
  // 文章标题
  title: z.string().describe('吸引人的文章标题'),

  // 简介部分
  introduction: z.object({
    hook: z.string().describe('开场白,吸引读者'),
    thesis: z.string().describe('文章核心论点'),
    wordCount: z.number().describe('建议字数'),
  }),

  // 主体章节(数组)
  sections: z.array(
    z.object({
      heading: z.string().describe('章节标题'),
      points: z.array(z.string()).describe('核心要点列表'),
      wordCount: z.number().describe('建议字数'),
      tips: z.string().optional().describe('写作建议'),
    })
  ).min(3).max(10), // 限制章节数量:3-10个

  // 结论部分
  conclusion: z.object({
    summary: z.string().describe('总结要点'),
    callToAction: z.string().describe('行动号召'),
    wordCount: z.number().describe('建议字数'),
  }),

  // SEO 相关
  keywords: z.array(z.string()).min(5).max(10).describe('关键词列表'),
  seoTitle: z.string().describe('SEO 优化标题'),
  metaDescription: z.string().max(160).describe('元描述(最多160字)'),
});

/**
 * 从 Schema 自动推导 TypeScript 类型
 *
 * 这样我们就不需要手动定义类型了
 */
export type Outline = z.infer<typeof OutlineSchema>;

/**
 * 规划参数接口
 */
export interface PlanContentParams {
  topic: string;                                    // 文章主题
  type: 'blog' | 'xiaohongshu' | 'article' | 'weixin'; // 文章类型
  audience: string;                                 // 目标受众
  wordCount: number;                                // 总字数要求
  tone: 'professional' | 'casual' | 'humorous';     // 语气风格
}

/**
 * 规划内容(主函数)
 *
 * 这是规划 Agent 的核心函数
 *
 * @param params - 规划参数
 * @returns 结构化的文章大纲
 *
 * @throws {Error} 当 AI 调用失败或返回数据格式错误时
 *
 * @example
 * const outline = await planContent({
 *   topic: 'AI 在前端开发中的应用',
 *   type: 'blog',
 *   audience: '前端开发者',
 *   wordCount: 2000,
 *   tone: 'professional',
 * });
 *
 * console.log(outline.title);      // "AI 赋能前端:开发效率提升指南"
 * console.log(outline.sections);   // [{ heading: '...', points: [...] }]
 */
export async function planContent(
  params: PlanContentParams
): Promise<Outline> {
  // 步骤 1: 创建 AI 客户端
  const client = createAIClient();
  const modelName = getModelName();

  // 步骤 2: 构建 Prompt
  // buildPlannerPrompt 会根据参数生成详细的提示词
  const prompt = buildPlannerPrompt(params);

  // 步骤 3: 调用 AI 生成结构化数据
  // generateObject 是 Vercel AI SDK 的函数
  // 它会:
  // 1. 调用 AI 模型
  // 2. 解析返回的 JSON
  // 3. 使用 Zod Schema 验证数据
  // 4. 如果验证失败,会自动重试
  const result = await generateObject({
    model: client(modelName),     // AI 模型
    schema: OutlineSchema,        // 数据结构定义
    prompt: prompt,               // 提示词
    temperature: 0.7,             // 创造性参数(0-1,越高越有创意)
  });

  // 步骤 4: 返回验证后的数据
  // result.object 已经是符合 OutlineSchema 的数据
  return result.object;
}

/**
 * 规划内容(带重试)
 *
 * 在网络不稳定的情况下,提供自动重试功能
 *
 * @param params - 规划参数
 * @param maxRetries - 最大重试次数(默认 3 次)
 * @returns 结构化的文章大纲
 */
export async function planContentWithRetry(
  params: PlanContentParams,
  maxRetries: number = 3
): Promise<Outline> {
  let lastError: Error | null = null;

  // 重试循环
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await planContent(params);
    } catch (error) {
      lastError = error as Error;

      // 如果不是最后一次尝试,等待后重试
      if (i < maxRetries - 1) {
        // 指数退避:第 1 次等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒
        await new Promise(resolve =>
          setTimeout(resolve, Math.pow(2, i) * 1000)
        );
      }
    }
  }

  // 所有重试都失败,抛出最后一个错误
  throw lastError || new Error('规划失败');
}

核心技术点

  1. Zod Schema 验证
    • 定义数据结构
    • 自动验证 AI 返回
    • 生成 TypeScript 类型
  2. generateObject
    • Vercel AI SDK 的核心功能
    • 比普通的 generateText 更可靠
    • 自动处理 JSON 解析和验证
  3. 重试机制
    • 指数退避算法
    • 提高成功率

面试要点

  • "我实现了一个规划 Agent,使用 Zod 进行 Schema 验证,确保 AI 返回的数据格式正确"
  • "采用了指数退避的重试策略,在网络不稳定时提高成功率"