德语老师项目计划 - 升级版


✅ 建议加一个 Phase 0:项目骨架 & 设计规范

在你动手写双栏 UI 之前,先把“地基”打好,会让后面所有阶段舒服很多。

Phase 0 目标

  • 搭好基础项目结构和设计系统(组件 + 配色 + 字体)。
  • 配好环境变量、API 封装、代码质量工具。

具体补充

  1. 项目初始化
  • npx create-next-app@latest german-tutor --ts --tailwind --eslint
  • App Router 开启,src 目录结构大致可以一开始就定好:
src/
  app/
    page.tsx
    api/
      chat/route.ts       # 统一 LLM 网关
  components/
    layout/
    chat/
    correction/
  lib/
    llm.ts                # 封装调用 Gemini/DeepSeek
    types.ts              # 公共类型
    utils.ts
  1. 设计系统 (Design System Lite)
  • 定一个简单但统一的视觉语言:
    • 主色:如 #2563eb(蓝)
    • 错误高亮:#ef4444(红)
    • 正确高亮:#22c55e(绿)
  • Tailwind 里自定义:
// tailwind.config.ts 里 extend theme
colors: {
  primary: '#2563eb',
  error: '#ef4444',
  success: '#22c55e',
}
  • 封装几个通用组件:
    • <Button />
    • <TextArea />
    • <Card />(右侧纠错卡复用)
  1. 类型定义(从第一天就 strongly-typed)
// src/lib/types.ts
export type Role = 'user' | 'assistant';

export interface Message {
  id: string;
  role: Role;
  content: string;
  createdAt: string;
}

export interface Correction {
  id: string;
  messageId: string;
  originalText: string;
  correctedText: string;
  explanation: string;
}

export interface TutorResponse {
  correction: string;
  revised_sentence: string;
  reply: string;
}

这些类型从 Phase 1 用到 Phase 5,都能复用。


🏁 Phase 1 再细化:前端 & 模型接入

你写得已经很清楚,我补三个点:交互细节、错误处理、JSON 校验

1. UI 细节补完

  • 响应式设计
    • Desktop:左右两栏。
    • Mobile:上下两栏(Tab 切换 “对话 / 纠错”)。
  • 输入体验
    • Shift + Enter 换行,Enter 发送。
    • 发送中按钮 Loading 状态;右上角可以加一个 “Model: Gemini Pro / DeepSeek” 标签。
  • 气泡的语义区分
    • 用户气泡:右对齐,浅背景。
    • AI 气泡:左对齐,带小头像(🇩🇪)。

2. API Route & Vercel AI SDK 封装

目标:不让前端直接碰第三方 API,统一走 /api/chat

// src/app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { callTutorModel } from '@/lib/llm';

const BodySchema = z.object({
  message: z.string().min(1),
});

export async function POST(req: NextRequest) {
  const json = await req.json();
  const parsed = BodySchema.safeParse(json);
  if (!parsed.success) {
    return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
  }

  try {
    const result = await callTutorModel(parsed.data.message);
    return NextResponse.json(result);
  } catch (e) {
    console.error(e);
    return NextResponse.json(
      { error: 'Model error, please try again later.' },
      { status: 500 }
    );
  }
}

callTutorModel 里根据环境变量选择 Gemini 或 DeepSeek。

3. 严格的 JSON 约束(防止模型乱返回)

配合你的 system prompt,把“只能返回 JSON”写死,并在服务器端再校验一遍:

// src/lib/llm.ts (示例伪代码)
import { z } from 'zod';

const TutorResponseSchema = z.object({
  correction: z.string(),
  revised_sentence: z.string(),
  reply: z.string(),
});

export async function callTutorModel(userText: string) {
  const prompt = `
你是一个德语私教。用户发德语句子,你只返回 JSON,不要多余文字:

{
  "correction": "中文解释错误,如果无错则留空",
  "revised_sentence": "修正后的德语句子,如果无需修改就等于原句",
  "reply": "你的德语回复,引导对话继续"
}
用户输入:${userText}
`;

  const raw = await callGeminiOrDeepSeek(prompt); // 具体实现略

  const parsedJson = safeParseJson(raw); // 自己写一个 try/catch
  return TutorResponseSchema.parse(parsedJson); // 若schema不匹配直接抛错
}

前端就可以放心地按 TutorResponse 类型渲染两栏。


🚧 Phase 2:后端与持久化再补几笔

你已经列出了四张表,我补充:

1. 推荐表结构(更贴合“多会话 + 匿名体验”)

-- profiles
id uuid primary key
auth_user_id uuid unique  -- Supabase auth.user.id
display_name text
created_at timestamptz

-- sessions (一个会话=一个“学习场景”)
id uuid primary key
user_id uuid references profiles(id)
title text
created_at timestamptz
updated_at timestamptz

-- messages
id uuid primary key
session_id uuid references sessions(id)
role text check (role in ('user', 'assistant'))
content text
created_at timestamptz
language text default 'de' -- 将来可扩展其他语言

-- corrections
id uuid primary key
message_id uuid references messages(id)
original_text text
corrected_text text
explanation text
error_type text       -- 如 'grammar', 'word_order', 'vocab'
created_at timestamptz

2. 匿名用户 → 注册用户的过渡

  • Phase 2 还可以加一个小目标:
    未登录也可以用,但一旦登录,就把当前 session“绑定”到用户
  • 逻辑示意:
    • 客户端生成一个 local_session_id(UUID)存 localStorage。
    • 未登录状态下:messages/corrections 依然可以存 Supabase,以匿名 user_id 或 null 标记(RLS 控制可见性)。
    • 用户登录后:调用一次后端迁移 API,把 local_session_id 下的所有 messages/corrections 绑定到该用户的 profile。

这样第一天你就可以有“无感 onboarding”:用得爽了再注册保留记录。

3. Supabase RLS / 安全

  • messagescorrections 必须开 RLS:
    • 规则:只能看到 user_id = auth.uid()user_id IS NULL AND session_id IN (客户端提供、且验证所有权)
  • 后端改造:
    • 原来的 /api/chat 在 Phase 2 变成:
      1. 解析请求中的 sessionId
      2. 插入 user 消息。
      3. 调 LLM。
      4. 插入 assistant 消息 & corrections。
      5. 返回最新状态给前端。

🧠 Phase 3:Dify / 知识库层的“架构升级”

你写的“混合架构”方向很好,我帮你明确一下**“切换条件”接口设计**。

1. 什么时候走 Dify?

  • 用户选择了某个“场景模式”:
    • 点餐 Deutsch im Restaurant
    • 电话咨询 Termin beim Arzt
    • 学术写作 E-Mail an Professor
  • 或者某一类练习:
    • “基于某本教材第 5 章做练习”。

这些场景需要知识库检索 / 多 step 的 Agent 逻辑,你就走 Dify:

// 伪逻辑
if (mode === 'free_chat') {
  return await callGemini(userText);
} else {
  return await callDifyFlow({ userText, mode });
}

2. Dify 接入时的“防缠绕”原则

  • 保持现在的 /api/chat 接口不变,只在内部决定走哪条路。
  • 保持 TutorResponse 类型不变:
    无论是 Dify 还是 Gemini,最后统一适配成相同的 JSON 输出格式
    -> 这样前端完全不用改,Phase 1 的 UI 可以一路用到 Phase 5。

💰 Phase 4:商业化时再多考虑 3 件事

你提到了配额 + VIP + 语音,非常好。我帮你再加:

1. 计费 & 配额设计

  • 免费层:
    • 无登录试用:每天 5 条。
    • 注册免费:每天 20 条。
  • 付费层:
    • 月订阅:不限对话数 + 高级纠错分析 + 专项训练 + 语音。
  • 技术上:
    • 在 Supabase 加个 plans 表和 usage 表。
    • 每次请求 /api/chat 时:
      • 查当天使用次数。
      • 超限则返回 “配额用完” 的统一错误 JSON。

2. 语音细节

  • 录音 → 文字:
    • 先用浏览器 Web Speech API(轻量版),之后可以接 OpenAI Whisper API 做高质量版。
  • 文字 → 语音:
    • 封装一个 /api/tts,内部调用 ElevenLabs 或 OpenAI TTS。
    • 聊天气泡下方加一个小喇叭按钮,点击就播放语音版回复。

🚀 Phase 5:数据飞轮的“产品化”

你提到错题生成 & 周报推送,我帮你把它拆成可开发任务。

1. “错题本” → “练习生成器”

  • 新增一个 API:/api/generate-exercises
    • 入参:correction_id or corrected_text + explanation
    • 出参:一个数组,比如:
[
  {
    "type": "fill_in_blank",
    "question": "Ich ___ müde.",
    "options": ["bin", "bist", "seid"],
    "answer": "bin",
    "explanation": "动词 sein 的第一人称单数变位是 bin。"
  },
  ...
]
  • 前端在纠错卡片上加一个按钮:“基于此生成练习”。

2. 周报(或月报)自动化

  • Supabase + 定时任务(cron):
    • 每周一跑一个 Edge Function:
      1. 统计每个用户上一周的错误类型分布(error_type)。
      2. 生成一个摘要 prompt 给 LLM,总结本周重点 + 推荐练习。
      3. 通过邮件(如 Resend / Supabase 内置 email)发送。

📅 本周执行计划:升级版

你原来的 3 天计划很好,我帮你再精细化一下(仍然只做 Phase 1):

Day 1 – 只做 UI,不碰 API

  • 搭 Next.js + Tailwind 项目结构。
  • 写出:
    • <ChatLayout />:左右分栏 + 响应式布局。
    • <ChatMessageList /> + <ChatInput />
    • <CorrectionPanel /> + <CorrectionCard />
  • 先用假数据填充:
    • 左侧几条机器 / 用户对话。
    • 右侧几条纠错卡片(红/绿高亮)。
完成标准:不用任何后端,页面一打开就能看到“像产品”的 UI。

Day 2 – API Key + 后端接口壳子

  • 申请 Gemini / DeepSeek API Key。
  • .env.local 配好:GEMINI_API_KEYDEEPSEEK_API_KEY
  • /api/chat route(先用 mock 返回固定 JSON)。
  • callTutorModel 函数(先不真正调第三方,只返回 hard-coded JSON)。
完成标准:前端输入一句话,能收到“看起来合理的”假响应,并完成左 / 右栏联动渲染。

Day 3 – 真正接入 LLM & JSON 校验

  • callTutorModel 里改成真正调用 Gemini/DeepSeek。
  • 加上:
    • System prompt。
    • JSON 解析 + zod 校验。
  • 改善一点 UX:
    • “发送中...” 状态。
    • 模型错误时,在右侧显示一张红色错误卡片(而不是无声失败)。
完成标准:
你可以自然地用德语和它聊几句,左侧对话 + 右侧纠错都正常、无崩溃。

如果你愿意,下一步我可以直接帮你:

  • 出一个 Phase 1 的完整文件结构 + 关键组件代码草稿
  • 或者出一版 Supabase 数据库建表 SQL + RLS 策略模板,你只要复制粘贴就能用。