Skip to content

Repository files navigation

英客 EToker:AI 英语口语陪练

视频链接:https://www.bilibili.com/video/BV1b5Et6GEVm/?vd_source=556ccddc7ab547193d4f85a944dcbb44 英客 EToker 是一款本地可运行的 AI 英语口语陪练工具,面向中国英语学习者在面试、考试、职场、留学、旅游和兴趣交流等真实场景中进行多轮英语对话训练。项目覆盖从“开始说”到“复盘再练”的完整学习闭环:目标诊断、场景选择、实时语音对话、低压力开口辅助、纠错反馈、课后报告、错题库、表达库和成长追踪。

当前版本定位为比赛和产品验证阶段的 MVP:主分支可在本地直接启动;没有真实模型密钥时仍可使用规则教练和本地兜底能力完成基础演示;配置 Qwen Omni Realtime 和 OpenAI-compatible 大模型后,可以体验更接近真实 AI 语音教练的完整链路。

目录

设计理念

1. 不是聊天机器人,而是口语训练产品

项目没有把“能聊天”作为终点,而是围绕口语学习的真实行为设计产品闭环:

  1. 用户先设置学习目标和当前水平。
  2. 系统推荐今日任务。
  3. 用户进入具体场景,用英语和 AI 角色对话。
  4. 练习中可以请求提示、简化表达、中文思路或重说本轮。
  5. 结束后生成结构化报告。
  6. 报告中的错误和好表达沉淀到学习库。
  7. 成长页把历史练习聚合成趋势和常错点。

2. 语音优先,但保留可解释状态

AI 口语陪练的核心体验是低延迟语音对话,但语音链路容易受模型、网络、麦克风和浏览器策略影响。因此练习房间会显式展示连接状态、麦克风状态、字幕、AI 回复状态、报告生成进度和 fallback 原因,让用户知道系统正在做什么。

3. Provider 可替换,避免绑定单一厂商

后端把教练、语音、发音评测、翻译和存储都拆成可替换 Provider。当前实现支持:

  • 规则教练兜底
  • OpenAI-compatible 大模型教练
  • Qwen Omni WebSocket Realtime 语音 Provider
  • 浏览器语音证据发音评估
  • 腾讯翻译优先、本地 quick-local 翻译兜底
  • SQLite / JSON 会话存储

这样后续替换模型、迁移云服务或增加新能力时,不需要重写页面流程。

功能总览

模块 当前能力 设计目的
今日工作台 目标诊断、当前路径、今日任务、推荐场景 把用户从“选哪个场景”引导到“今天练什么”
场景库 60 个课程化场景,覆盖 6 类目标和不同难度 给练习提供明确角色、目标和成功标准
自定义场景 用户输入场景,可选 AI 角色和用户角色 支持更贴近个人需求的训练任务
练习房间 WebSocket 实时对话、Qwen Omni 语音、字幕、反馈 承载真实口语陪练体验
低压力辅助 提示、简化表达、中文思路、重说本轮 降低开口压力,减少卡住和沉默
纠错策略 可选择逐轮点评或课后统一评分 平衡交流流畅度和反馈密度
翻译 对话、反馈、报告、能力详情可按需翻译 支持中国学习者理解反馈
课后报告 综合分、等级、雷达图、优势、缺陷、典型错误、训练计划 让学习结果可解释、可量化
学习库 错题库、表达库、来源报告跳转 把一次练习沉淀为长期资产
成长页 开口分钟、连续练习、平均分、趋势、能力快照、常错点 给用户持续进步感
历史记录 会话列表、删除、报告回看 支持复盘和演示数据管理

技术栈

前端

技术 用途
React 18 页面和交互状态管理
TypeScript 前端类型约束,与共享契约保持一致
Vite 本地开发服务和生产构建
React Router 页面路由
lucide-react 图标系统
CSS 全局设计系统、响应式布局、动效
WebSocket API 练习房间实时对话和语音通道
Web Audio API 麦克风采集、PCM 音频处理、远端音频播放
localStorage 语言偏好、目标诊断、逐轮点评开关、今日任务状态

后端

技术 用途
Go 后端主语言
net/http HTTP API 和轻量路由
自定义 CORS middleware 支持 Vite 前端跨端口访问 Go API
github.com/coder/websocket WebSocket 实时对话和语音代理
SQLite 默认本地会话持久化
modernc.org/sqlite SQLite Go 驱动
OpenAI-compatible Chat Completions 大模型教练、总结、翻译、辅助
Qwen Omni WebSocket Realtime 真实语音输入、语音输出、字幕和远端事件
Tencent Cloud TMT API 中文翻译 Provider
Go test 后端单元和集成测试

共享契约

packages/shared 定义前端使用的 TypeScript 数据结构。Go 后端保持相同 JSON 字段,保证前后端传输稳定。

核心类型包括:

  • Scenario
  • PracticeSession
  • DialogueTurn
  • SessionSummary
  • LearningAsset
  • ClientRealtimeMessage
  • ServerRealtimeEvent

系统架构

总体架构

flowchart LR
  User[用户] --> Web[React Web App]

  Web -->|HTTP JSON| API[Go HTTP API]
  Web -->|WebSocket| RT[Realtime Session WS]
  Web -->|WebSocket + PCM Audio| VoiceWS[Local Voice WS Proxy]

  API --> Scenario[Scenario Repository]
  API --> Store[(SQLite / JSON Store)]
  API --> Coach[Coach Provider]
  API --> Speech[Speech Provider]
  API --> Translate[Translation Provider]

  RT --> Hub[In-memory Realtime Hub]
  Hub --> Coach
  Hub --> Store

  VoiceWS --> VoiceProvider[Qwen Omni Realtime Provider]
  VoiceProvider --> DashScope[DashScope Qwen Omni WS]
  VoiceWS --> Store
  VoiceWS --> Speech

  Scenario --> ScenarioData[(data/scenarios/*.json)]
  Store --> SessionDB[(data/sessions/sessions.db)]
  Coach --> Rule[Rule-based Fallback]
  Coach --> LLM[OpenAI-compatible LLM]
  Translate --> Tencent[Tencent TMT]
  Translate --> QuickLocal[quick-local fallback]
Loading

运行拓扑

flowchart TB
  subgraph Browser[Browser]
    React[React App :5173]
    Audio[Mic / AudioContext]
    LocalStorage[localStorage]
  end

  subgraph Local[Local Machine]
    Go[Go Server :8080]
    SQLite[(SQLite DB)]
    ScenarioJSON[(Scenario JSON)]
  end

  subgraph Remote[Optional Remote Providers]
    LLM[OpenAI-compatible LLM]
    Omni[Qwen Omni Realtime]
    TMT[Tencent Translate]
  end

  React --> Go
  Audio --> React
  React --> LocalStorage
  Go --> SQLite
  Go --> ScenarioJSON
  Go -.with keys in .env.-> LLM
  Go -.with keys in .env.-> Omni
  Go -.with keys in .env.-> TMT
Loading

学习闭环

flowchart LR
  Profile[目标诊断] --> Plan[今日计划]
  Plan --> Scenario[场景练习]
  Scenario --> Dialogue[实时对话]
  Dialogue --> Feedback[逐轮反馈或课后反馈]
  Feedback --> Report[结构化报告]
  Report --> Assets[错题库 / 表达库]
  Assets --> Plan
  Report --> Progress[成长页]
  Progress --> Profile
Loading

核心链路

1. 创建场景并进入练习

sequenceDiagram
  participant U as 用户
  participant Web as React 前端
  participant API as Go API
  participant Store as SQLite
  participant Scenario as 场景仓库

  U->>Web: 选择场景 / 自定义场景
  Web->>API: POST /api/sessions
  API->>Scenario: 读取场景配置
  API->>Store: 创建 PracticeSession
  Store-->>API: 保存成功
  API-->>Web: 返回 session
  Web->>Web: 跳转 /practice/:sessionId
Loading

2. WebSocket 文本实时对话

sequenceDiagram
  participant Web as React PracticeRoom
  participant WS as Go Realtime WS
  participant Coach as Coach Provider
  participant Store as Session Store

  Web->>WS: 建立 /api/sessions/{id}/realtime
  WS-->>Web: session.snapshot
  Web->>WS: turn.submit(clientMessageId, text)
  WS-->>Web: turn.processing
  WS-->>Web: turn.user_echo
  WS->>Coach: Respond / StreamRespond
  Coach-->>WS: thinkingText
  WS-->>Web: turn.thinking
  Coach-->>WS: assistant delta
  WS-->>Web: turn.delta
  WS->>Store: 保存 DialogueTurn
  WS-->>Web: turn.completed
Loading

3. Qwen Omni WebSocket Realtime 语音链路

sequenceDiagram
  participant Browser as Browser Mic
  participant Web as useOmniRealtimeVoiceSession
  participant Go as Go Voice Proxy
  participant Omni as Qwen Omni Realtime
  participant Store as Session Store

  Browser->>Web: getUserMedia + AudioContext
  Web->>Go: GET /api/sessions/{id}/voice
  Go->>Omni: Connect with server-side API key
  Web->>Go: input_audio_buffer.append
  Go->>Omni: forward audio
  Web->>Go: input_audio_buffer.commit + response.create
  Omni-->>Go: transcript / audio.delta / status
  Go-->>Web: transcript / remote audio / status
  Web->>Go: POST /voice/turns
  Go->>Store: 保存语音 turn 和 metadata
Loading

4. 结束练习并生成报告

sequenceDiagram
  participant Web as PracticeRoom
  participant WS as Realtime WS
  participant API as Server Logic
  participant Coach as Summary Provider
  participant Store as SQLite

  Web->>WS: session.complete(clientMessageId)
  Web->>Web: 显示报告生成进度条
  WS->>API: completeSession(sessionId)
  API->>Coach: Summarize(scenario, session)
  Coach-->>API: SessionSummary
  API->>API: 补齐指标、等级、训练计划
  API->>Store: 保存 completed session
  WS-->>Web: session.completed
  Web->>Web: 进度到 100%
  Web->>Web: 跳转 /summary/:sessionId
Loading

前端设计

目录结构

apps/web/
  src/
    app/                 应用入口、路由、全局样式
    components/          通用状态、指标、布局组件
    features/
      i18n/              中英文文案、场景本地化
      planning/          目标诊断、今日计划、成长统计
      realtime/          文本 WebSocket 实时会话
      settings/          全局设置弹窗
      speech/            浏览器语音相关能力
      voice/             Qwen Omni Realtime 语音 Hook
    pages/
      Home/              今日练习工作台
      ScenarioSelect/    场景库
      CustomScenario/    自定义场景
      PracticeRoom/      练习房间
      Summary/           课后报告
      Library/           错题库和表达库
      History/           历史记录
      Progress/          成长页
    services/            API Client

页面路由

路径 页面 作用
/ 今日工作台 目标诊断、今日计划、推荐场景
/scenarios 场景库 分类、难度、搜索、课程卡
/custom-scenario 自定义场景 输入场景和角色,生成专属练习
/practice/:sessionId 练习房间 语音对话、辅助、反馈、结束练习
/summary/:sessionId 课后报告 能力报告、翻译、收藏错题和表达
/history 历史记录 查看和删除历史会话
/library 学习库 错题库、表达库
/progress 成长页 趋势、能力快照、常错点

前端状态设计

flowchart TD
  UI[Page UI] --> Hooks[Feature Hooks]
  Hooks --> API[services/api.ts]
  Hooks --> WS[Realtime / Voice WebSocket]
  Hooks --> Storage[localStorage]

  Storage --> Locale[语言偏好]
  Storage --> Profile[目标诊断]
  Storage --> ReviewMode[逐轮点评开关]
  Storage --> TaskStatus[今日任务状态]

  API --> Server[Go API]
  WS --> Server
Loading

体验设计要点

  • 首页直接呈现今日任务,而不是营销页。
  • 练习房间把场景、角色、字幕、语音、辅助和反馈放在同一屏。
  • 单句模式适合逐句训练,连续模式适合更自然的对话。
  • 反馈时机可配置:逐轮点评或课后统一评分。
  • 结束练习时不弹突兀弹窗,而是在页面内展示报告生成进度。
  • 翻译按钮按需展开,避免中文解释打断英语训练环境。
  • 移动端和桌面端都使用响应式布局,核心操作始终可触达。

后端设计

目录结构

apps/server/
  cmd/server/main.go       服务启动、Provider 组装
  internal/api/            HTTP API、WebSocket API、业务入口
  internal/config/         .env 和环境变量读取
  internal/scenario/       场景 JSON 加载
  internal/session/        会话、turn、summary、learning asset 模型
  internal/coach/          规则教练和 OpenAI-compatible 教练
  internal/speech/         发音评估 Provider
  internal/voice/          Qwen Omni Realtime Provider
  internal/realtime/       WebSocket 消息协议和内存 Hub
  internal/storage/        SQLite / JSON 存储

后端模块关系

flowchart TD
  Main[cmd/server/main.go] --> Config[internal/config]
  Main --> API[internal/api.Server]
  Main --> Scenario[internal/scenario.Repository]
  Main --> Storage[internal/storage.SessionStore]
  Main --> Coach[internal/coach.Provider]
  Main --> Speech[internal/speech.Provider]
  Main --> Voice[internal/voice.Provider]

  API --> Scenario
  API --> Storage
  API --> Coach
  API --> Speech
  API --> Voice
  API --> Realtime[internal/realtime.Hub]

  Coach --> Rule[RuleBasedCoach]
  Coach --> LLM[OpenAICompatibleCoach]
  Voice --> Disabled[DisabledProvider]
  Voice --> Omni[QwenOmniRealtimeProvider]
Loading

Provider 设计

Provider 默认实现 可选实现 作用
Coach rule openai / llm / openai-compatible 生成回复、纠错、总结、辅助
Speech browser 预留 基于转写、时长、置信度生成发音证据
Voice disabled qwen-omni-ws 实时语音输入输出
Storage sqlite json 会话和学习资产持久化
Translation quick-local tencent-tmt 对话、反馈、报告翻译

中间件

后端没有引入 Gin、Echo 等 Web 框架,而是使用 Go 标准库 net/http。当前中间件采用 http.Handler 包装方式实现:

func withCORS(next http.Handler) http.Handler

作用:

  • 设置 Access-Control-Allow-Origin
  • 支持 GET, POST, DELETE, OPTIONS
  • 处理 Vite 开发服务跨端口访问
  • OPTIONS 预检请求直接返回 204

数据模型

核心实体

erDiagram
  SCENARIO ||--o{ PRACTICE_SESSION : creates
  PRACTICE_SESSION ||--o{ DIALOGUE_TURN : contains
  PRACTICE_SESSION ||--o| SESSION_SUMMARY : generates
  PRACTICE_SESSION ||--o{ LEARNING_ASSET : saves
  DIALOGUE_TURN ||--o{ LEARNING_ASSET : source

  SCENARIO {
    string id
    string category
    string title
    string difficulty
    string coachRole
    string userRole
    string openingLine
  }

  PRACTICE_SESSION {
    string id
    string scenarioId
    string status
    string startedAt
    string completedAt
  }

  DIALOGUE_TURN {
    string id
    string userText
    string assistantText
    int responseLatencyMs
    string coachProvider
  }

  SESSION_SUMMARY {
    int overallScore
    string level
    string abilityProfile
    string coachProvider
  }

  LEARNING_ASSET {
    string id
    string kind
    string sourceText
    string targetText
    string category
  }
Loading

Scenario

场景是课程化训练的入口,包含:

  • 场景标题和描述
  • 训练目标
  • AI 角色和用户角色
  • 开场白
  • 关键词
  • 成功标准
  • 建议表达
  • 评分维度
  • 可选音色

PracticeSession

会话记录完整练习过程:

  • 场景 ID 和标题
  • 会话状态
  • 多轮 DialogueTurn
  • 聚合指标
  • 课后报告
  • 错题和表达资产

DialogueTurn

一轮对话包含:

  • 用户文本或语音转写
  • AI 回复
  • 纠错
  • 表达建议
  • 发音评估
  • 反馈时机
  • 语音 metadata
  • Provider 和 fallback 原因

API 契约

HTTP API

方法 路径 作用
GET /api/health 健康检查
GET /api/scenarios 获取场景列表
POST /api/sessions 创建普通或自定义会话
GET /api/sessions 获取历史会话
DELETE /api/sessions 批量删除会话
GET /api/sessions/{id} 获取会话详情
DELETE /api/sessions/{id} 删除单个会话
POST /api/sessions/{id}/turns HTTP 兼容方式提交一轮回答
POST /api/sessions/{id}/summary 结束练习并生成报告
POST /api/sessions/{id}/voice/turns 保存语音 turn 和 metadata
POST /api/sessions/{id}/translate 翻译对话、反馈或报告文本
POST /api/sessions/{id}/assist 获取低压力开口辅助
POST /api/sessions/{id}/learning-assets 收藏错题或表达
DELETE /api/sessions/{id}/learning-assets/{assetId} 删除收藏资产
GET /api/learning-assets 获取学习库

WebSocket API

路径 作用
/api/sessions/{id}/realtime 文本实时对话、流式回复、结束练习
/api/sessions/{id}/voice 本地语音代理,连接 Qwen Omni Realtime

Realtime 事件

client -> server
  turn.submit
  session.complete
  ping

server -> client
  session.snapshot
  turn.processing
  turn.user_echo
  turn.thinking
  turn.delta
  turn.completed
  session.completed
  error

配置说明

项目默认读取根目录 .env。没有 .env 时,也可以用默认配置启动基础功能。

服务配置

变量 默认值 说明
APP_HOST 127.0.0.1 后端监听地址
APP_PORT 8080 后端端口
APP_DATA_DIR data 数据目录
APP_STORAGE_PROVIDER sqlite sqlitejson
APP_DATABASE_PATH data/sessions/sessions.db SQLite 数据库路径
VITE_API_BASE /api 前端 API 基础路径

大模型教练配置

变量 默认值 说明
APP_COACH_PROVIDER rule rule / openai / llm / openai-compatible
OPENAI_API_KEY / LLM_API_KEY / DASHSCOPE_API_KEY 大模型 API Key
OPENAI_BASE_URL / LLM_BASE_URL https://api.openai.com/v1 OpenAI-compatible Base URL
OPENAI_MODEL / LLM_MODEL gpt-4o-mini 模型名称
LLM_TIMEOUT_SECONDS 60 请求超时时间

Qwen Omni 实时语音配置

变量 默认值 说明
APP_VOICE_PROVIDER disabled disabledqwen-omni-ws
DASHSCOPE_API_KEY / QWEN_OMNI_API_KEY Qwen Omni API Key
QWEN_OMNI_MODEL / VOICE_MODEL qwen3.5-omni-flash-realtime 实时语音模型
QWEN_OMNI_REALTIME_URL / VOICE_REALTIME_URL wss://dashscope.aliyuncs.com/api-ws/v1/realtime Realtime endpoint
QWEN_OMNI_VOICE / VOICE_NAME Tina 默认音色
VOICE_TIMEOUT_SECONDS 20 连接超时

腾讯翻译配置

变量 默认值 说明
TENCENT_TRANSLATE_SECRET_ID 腾讯云 SecretId
TENCENT_TRANSLATE_SECRET_KEY 腾讯云 SecretKey
TENCENT_TRANSLATE_REGION ap-guangzhou 区域
TENCENT_TRANSLATE_SOURCE en 源语言
TENCENT_TRANSLATE_TARGET zh 目标语言
TENCENT_TRANSLATE_PROJECT_ID 0 项目 ID
TENCENT_TRANSLATE_TIMEOUT_SECONDS 6 超时时间

不要把真实密钥写进前端 VITE_* 变量,也不要提交到公开仓库。评审演示可以用本地 .env 配置。

启动与验证

环境要求

依赖 建议版本
Node.js 20+
npm 10+
Go 1.22+

安装依赖

npm install

启动前后端

npm run dev

默认地址:

Web:    http://127.0.0.1:5173
Server: http://127.0.0.1:8080

单独启动

npm run dev:web
npm run dev:server

检查和测试

npm run check
npm run build
npm run test:server

PowerShell 端口占用清理

$ports = 8080,5173
$connections = Get-NetTCPConnection -LocalPort $ports -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Listen' }
$processIds = $connections | Select-Object -ExpandProperty OwningProcess -Unique
foreach ($processId in $processIds) {
  taskkill /PID $processId /T /F
}

故障排查

1. server port 8080 is already in use

说明后端端口已被占用。先结束旧进程,再重新运行:

Get-NetTCPConnection -LocalPort 8080 | Select-Object OwningProcess
taskkill /PID <pid> /T /F

2. web port 5173 is already in use

说明 Vite 前端端口已被占用:

Get-NetTCPConnection -LocalPort 5173 | Select-Object OwningProcess
taskkill /PID <pid> /T /F

3. qwen omni websocket realtime voice provider is not configured

检查:

  • APP_VOICE_PROVIDER=qwen-omni-ws
  • DASHSCOPE_API_KEYQWEN_OMNI_API_KEY 已配置
  • QWEN_OMNI_REALTIME_URL 是 WebSocket 地址,不是普通 HTTPS 域名
  • 后端日志出现 voice provider: qwen-omni-ws

4. Realtime WebSocket 连接失败

检查:

  • Go 服务是否启动
  • 前端 VITE_API_BASE 是否指向 /api
  • 浏览器是否允许 WebSocket
  • 代理或防火墙是否拦截本地端口

5. 麦克风没有声音

检查:

  • 浏览器是否授权麦克风
  • 页面是否运行在 localhost / 127.0.0.1 或 HTTPS
  • 系统麦克风输入设备是否正确
  • 浏览器是否支持 getUserMediaAudioContext

6. AI 回复慢或 fallback

检查后端日志:

coach provider: ... request failed ... fallback=rule

可能原因:

  • API Key 缺失或无效
  • Base URL 配置错误
  • 模型名不支持
  • 网络超时
  • Provider 返回格式不符合结构化 JSON

7. 翻译没有使用腾讯翻译

检查:

  • TENCENT_TRANSLATE_SECRET_ID
  • TENCENT_TRANSLATE_SECRET_KEY
  • TENCENT_TRANSLATE_REGION

未配置时会自动使用 quick-local 兜底,并在日志中打印:

translation provider: tencent-tmt not configured fallback=quick-local

安全与隐私

当前项目是本地优先 MVP,默认数据存储在本机:

  • 场景数据:data/scenarios
  • 会话数据:data/sessions/sessions.db
  • 旧 JSON 数据:data/sessions/sessions.json

设计原则:

  1. 长期 API Key 只在 Go 后端读取。
  2. 前端不使用 VITE_* 暴露模型或语音密钥。
  3. 练习报告、错题和表达库保存在本地 SQLite。
  4. 语音 Provider 连接远端模型时,只转发实现语音对话所需的数据。
  5. 分享报告和云端账号体系不在当前 MVP 范围内。

路线图

阶段 状态 说明
场景选择和基础练习 已完成 场景库、练习房间、历史记录
WebSocket 实时通道 已完成 实时提交、流式回复、断线重连
SQLite 存储 已完成 本地会话持久化
大模型教练 已完成 OpenAI-compatible Provider,规则 fallback
Qwen Omni 语音 已完成 WebSocket Realtime 语音输入输出
开口辅助和纠错时机 已完成 hint、simplify、idea、retry、逐轮/课后反馈
结构化课后报告 已完成 雷达图、能力详情、翻译、报告生成进度
错题库和表达库 已完成 收藏、去重、学习库页面
目标诊断和成长页 已完成 今日计划、趋势、常错点
MVP 演示收口 进行中 本地化脚本、质量观测、额度模拟、隐私说明

暂缓范围:

  • 真实支付、订单和会员系统
  • 复杂运营后台
  • 社区、排行榜、同伴练习
  • 真人教师体系
  • 儿童版本
  • 官方考试分数认证

项目价值总结

英客 EToker 的核心不是单点 AI 能力,而是一条可解释、可复盘、可持续的口语学习链路:

flowchart LR
  Speak[开口说英语] --> Coach[AI 角色对话]
  Coach --> Correct[纠错与表达升级]
  Correct --> Report[课后能力报告]
  Report --> Library[错题和表达沉淀]
  Library --> Plan[下一次训练计划]
  Plan --> Speak
Loading

这套架构让产品既能在没有云端模型时完成基础演示,也能在接入真实模型和语音服务后升级为更自然、更低延迟、更个性化的 AI 英语口语陪练。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages