Agent&Skill 打造个人专属口语语料库

0 阅读15分钟

AI 口语导师:QMD RAG + 7 步蒸馏 + 双模式 Skill 架构详解

摘要:本文分享我开源的 AI 雅思口语个性化语料库+训练项目 PersonaLingo。它把"通用 AI 助手"改造成"领域专属 AI 导师"——通过 QMD 三层 RAG 引擎实现零 Embedding 依赖的语义检索,通过 7 步蒸馏流水线把用户画像炼成专属语料库,让每个人讲述自己的故事。再通过 双模式 Skill 架构支持"一行命令安装"与"Docker 长期部署"两种使用场景。本文将系统拆解整体设计、核心算法、关键代码与工程实践,全文约 5000 字,欢迎一同探讨。

PersonaLingo Banner


一、引言:雅思口语学习的三大困境

作为一个曾经被雅思口语反复折磨的开发者,我深刻体会到传统口语备考的三大痛点:

1. 素材通用化——"背模板"陷阱 市面上的口语题库千篇一律,"My favourite teacher is Mr. Smith…"——考官一年听上千遍,模板答案早就成了减分项。可真的让自己写,又总卡在"我没有故事"。

2. 个性化缺失——"AI 不懂我" 直接问 ChatGPT "请帮我准备 Describe a person who inspired you",得到的回答几乎是泛化版的维基百科:内容很对,但和"我"毫无关系。AI 不知道我是 INTJ,不知道我做过 ACM,不知道我去过哪些城市。

3. 练习低效——"开口即忘" 大部分练习只停留在"读一遍 sample answer",没有锚定故事(Anchor)、桥接表达(Bridge)、地道词汇(Vocabulary)、句型模板(Pattern)的结构化沉淀,背了等于没背。

我开始思考:能不能像 NotebookLM 那样,把"我自己"喂给 AI,再让 AI 反向产出一个完全个性化的雅思语料库? 这就是 PersonaLingo 的起点——它不是又一个聊天机器人,而是一个"会研究你、再为你定制内容"的领域 AI 导师。

NotebookLM 给了我很大的启发:当 AI 拥有"用户私有上下文"时,它的输出会从"教科书"变成"私人教练"。但 NotebookLM 是通用工具,缺少口语备考的领域知识——比如 Anchor-Bridge-Vocabulary-Pattern 这种结构化沉淀,比如 P1/P2/P3 的题型差异,比如 CEFR 词汇分级。所以 PersonaLingo 的差异化定位是:在 NotebookLM 式个性化的基础上,叠加雅思口语领域 know-how


二、项目定位:从通用助手到专属导师

一句话定义:PersonaLingo 是一个 AI 驱动的个性化雅思口语语料库生成器——输入你的 MBTI、专业背景、兴趣经历,输出可直接背诵的、属于你一个人的题库 + 故事 + 词汇 + 句型四件套。

核心价值:让 AI 从"通用助手"进化为"领域专属导师",把每一个雅思考生都视作独立的语料生产对象。

  • GitHub 地址github.com/orzcls/Pers…
  • 技术栈:FastAPI + Vue 3 + LLM (OpenAI/Anthropic 双适配) + 自研 QMD RAG
  • License:MIT,欢迎 Fork、PR、魔改

下文我会按 功能 → 架构 → 核心创新 → 部署 的顺序,把整套设计讲清楚。这个项目从立项到首版开源大约花了我 3 个月业余时间,期间踩过不少坑——比如最早版本想用 OpenAI Embedding 做 RAG,结果发现"个人开发者预算 + 题库频繁变更"两个约束下,向量索引方案根本不划算,于是才有了后来的 QMD 引擎。


三、功能全景:以用户旅程为线索

PersonaLingo 的 9 个前端视图被设计成一条完整的"备考链路",下面按用户使用顺序逐一展示。

3.1 用户画像问卷:信息采集的起点

进入首页后,用户首先完成一份个性化问卷:从基础信息(专业、目标分数)到兴趣经历(旅行、项目、爱好),所有字段都会作为下游 LLM 蒸馏的"原料"。

在这里插入图片描述

3.2 MBTI 16 宫格:性格维度建模

性格是口语风格的关键变量。INTJ 倾向逻辑论证、ENFP 偏好情绪共鸣,PersonaLingo 会根据 MBTI 调整生成内容的语气、举例方式、句式偏好。

在这里插入图片描述

3.3 AI 对话引擎:多轮深度访谈

蒸馏链路 Stage 1 会启动一段"深度调研对话",AI 主动追问用户的真实经历,逐步从"模糊画像"逼近"可写作的具体故事"。

在这里插入图片描述

3.4 动态题库:86 道高频题分级管理

题库支持 P1/P2/P3 分级筛选,并支持"季度动态更新"——通过远程拉取最新题库快照,避免静态题库过时。

在这里插入图片描述

3.5 智能笔记:四件套结构化沉淀

每道题的输出都会被切分成 Anchor(锚定故事)+ Bridge(桥接句)+ Vocabulary(地道词)+ Pattern(句型),用结构化卡片呈现,便于复习。

在这里插入图片描述

3.6 Skill 导出:一键打包成可复用资产

最后一步,用户可以把整套个性化语料导出为 skill.zip + site/index.html + corpus.json + profile.md 四件套,作为长期复习资产,或继续二次蒸馏。

在这里插入图片描述


四、技术架构:四层分层与模块拆分

PersonaLingo 整体采用经典的 前端 → API → 服务 → 数据 四层架构,但在"服务层"做了非常细的领域拆分——18 个后端服务模块各司其职。整体分层如下图所示:

PersonaLingo 系统架构全景图

🏗️ 架构亮点:服务层 18 个模块按职责单一原则拆分,QMD RAG 与 Distill Pipeline 完全解耦,任一模块均可独立替换或升级。

4.1 核心数据模型

整个系统的数据流转都围绕 5 个核心 Schema 展开(位于 backend/app/models/corpus.py):

模型字段含义作用
CorpusData顶层容器一个用户的完整语料快照
AnchorStory锚定故事用户真实经历的故事素材
BridgeItem桥接句把抽象题目"拐到"自己故事的过渡句
VocabItem地道词汇带例句、近义词、CEFR 等级
PatternItem句型模板可复用的高级句式骨架

这 5 个模型同时也是前端展示卡片、Skill 导出、QMD 检索的统一接口——Schema 即合约,是整个项目能跑通的关键。

4.2 18 个后端服务模块(节选)

backend/app/services/ 下按职责拆成 18 个模块,避免单文件膨胀。其中最关键的几个:

  • qmd_engine.py:QMD 三层检索引擎
  • distill_pipeline.py:7 步蒸馏编排器
  • llm_adapter.py:OpenAI/Anthropic 统一适配
  • skill_builder.py:Skill 包构建器
  • topic_sync.py:远程题库季度同步

4.3 9 个前端视图

前端 Vue 3 SPA 按功能切成 9 个视图:Home / Questionnaire / MBTI / Topics / Chat / Notes / Settings / Export / Skill,状态由 Pinia 统一管理。视觉上采用了我自己提炼的 Soft Scholar 柔雅学院风——米白底色 + 墨绿主色 + 衬线标题,避免常见 AI 产品的"赛博蓝紫"审美疲劳,更贴合"学术 + 备考"语境。

4.4 关键依赖与版本策略

后端:fastapi >= 0.110pydantic v2sqlalchemy 2.xhttpx(异步 LLM 调用);前端:vue 3.4 + vite 5 + pinia + vue-router 4 + tailwindcss 3。所有依赖都钉到 minor version,避免"周一好好的、周三突然报错"——这对一个想被 Fork 的开源项目尤为重要。


五、核心创新:三大引擎拆解(重点)

如果说前面是"项目长什么样",那么这一节就是"它为什么值得开源"。我把三项最值得分享的设计单独拎出来——QMD RAG、7 步蒸馏、双模式 Skill 共同构成 PersonaLingo 的创新三角。

PersonaLingo 三大核心创新关系图

💡 三角联动:QMD 提供检索底座、蒸馏流水线生产语料、双模式 Skill 决定交付形态——三者通过同一份 Schema 强对齐,任何一环升级都会自动反哺其余两环。


5.1 QMD RAG 引擎:零 Embedding 依赖的三层检索

设计理念:传统 RAG 需要预先训练或调用 Embedding 模型(OpenAI text-embedding-3、bge-large 等),这意味着额外的 API Key、向量数据库、维度对齐成本。对一个 轻量化、可离线运行、面向个人开发者 的项目,我希望——

  • ✅ 零外部 Embedding 依赖
  • ✅ 渐进降级(LLM 不可用时仍能跑)
  • ✅ 单机性能友好(SQLite 即可)

于是有了 QMD 三层架构:Query Expansion → Multi-signal Match → Distill Rerank。

QMD RAG 三层检索引擎架构

核心代码骨架如下(节选自 backend/app/services/qmd_engine.py):

# QMD 三层检索引擎
class QMDEngine:
    async def search(self, query: str, corpus: CorpusData) -> List[SearchResult]:
        # Q 层:Query Expansion(LLM 语义扩展 + 同义词规则)
        expanded = await self.query_expand(query)
        # M 层:Multi-signal Match(BM25 词频 + TF-IDF 语义 + RRF 融合)
        candidates = self.multi_signal_match(expanded, corpus)
        # D 层:Distill Rerank(LLM 智能重排序)
        return await self.distill_rerank(candidates, query)

    async def query_expand(self, query: str) -> List[str]:
        """Q 层:用 LLM 把'describe a teacher'扩展为多个语义变体。
        当 LLM 不可用时,回退到本地同义词词典,保证最低可用性。"""
        try:
            return await self.llm.expand(query, n=5)
        except LLMError:
            return self.synonym_fallback(query)

    def multi_signal_match(self, queries: List[str], corpus) -> List[Candidate]:
        """M 层:BM25 与 TF-IDF 双信号召回,用 RRF 融合避免单一指标偏差。"""
        bm25_hits = self.bm25.search(queries, corpus, top_k=50)
        tfidf_hits = self.tfidf.search(queries, corpus, top_k=50)
        return self.rrf_fuse(bm25_hits, tfidf_hits, k=60)

    async def distill_rerank(self, candidates, query):
        """D 层:把 Top-50 候选交给 LLM 做 Pointwise/Listwise 重排。"""
        return await self.llm.rerank(query, candidates, top_k=10)

关键工程取舍

  • RRF(Reciprocal Rank Fusion) 替代加权求和:避免 BM25 与 TF-IDF 分数尺度不一致
  • 降级链:LLM Rerank 不可用 → 回退到 RRF 排名;LLM Expand 不可用 → 回退到同义词词典
  • 冷启动:corpus 入库时一次性建好 BM25 倒排索引,查询 P95 < 200ms

为什么不用向量数据库? 我做过一组对比实验:在 86 道题、平均每道 12 条 Anchor/Bridge 的语料规模下(约 1000 条文档),BM25 + TF-IDF + RRF 的召回 Top10 命中率达到 91%,而 OpenAI Embedding + 余弦相似度只有 88%。在如此小规模、强领域、高同义词的语料里,词频信号比向量距离更靠谱。同时省下了:维度对齐、Embedding 调用费用、向量库部署成本。这是典型的"小数据集向量未必赢"案例。

5.2 7 步蒸馏流水线:三段式架构

graph TB
    subgraph Frontend["Frontend (Vue 3 + Vite + Tailwind)"]
        UI[User Interface]
        Router[Vue Router]
        Store[Pinia Store]
        I18N[i18n Internationalization]
    end

    subgraph Backend["Backend (FastAPI + Python)"]
        API[REST API Layer]

        subgraph CoreServices["Core Services"]
            CG["Corpus Generator<br/>5-step Pipeline"]
            CE["Conversation Engine<br/>NotebookLM-style"]
            TM["Topic Manager<br/>P1+P2+P3"]
            NE["Note Generator<br/>Mermaid Mindmap"]
            SE["Skill Exporter<br/>MD+JSON"]
        end

        subgraph RAGLayer["QMD RAG Engine"]
            QE["Query Expansion<br/>Semantic Expansion"]
            MS["Multi-signal Match<br/>BM25 + TF-IDF + RRF"]
            RR["Reranker<br/>LLM Reranking"]
        end

        subgraph LLMLayer["LLM Adapter Layer"]
            OA[OpenAI Compatible]
            AN[Anthropic Claude]
        end

        TK["Token Manager<br/>Threshold Alert + Auto Compression"]
        SL["Style Learner<br/>Style Learning"]
    end

    subgraph Data["Data Layer"]
        DB[(SQLite)]
        Topics["Topic Bank JSON<br/>86 topics"]
        Vocab["Idiomatic Vocab<br/>132 words"]
        QTypes["Question Types<br/>7 categories"]
    end

    UI --> Router --> API
    Store --> API
    API --> CoreServices
    CG --> RAGLayer
    CE --> RAGLayer
    CE --> TK
    CE --> SL
    RAGLayer --> LLMLayer
    CG --> LLMLayer
    CoreServices --> DB
    TM --> Topics
    CG --> Vocab
    CG --> QTypes

蒸馏(Distillation)是 PersonaLingo 最有特色的环节——把"用户画像"逐步炼成"四件套语料"。整体设计成"前置研究 → 框架抽象 → 内容生产"三段式 7 步:

7 步蒸馏流水线全景

为什么要做"前置研究阶段"(Stage 1-2)? 最早版本我直接 Stage 3 开始生成 Persona,结果发现:

  • LLM 缺乏"为什么这样写"的依据,输出飘忽
  • 同一份问卷跑两次,结果差异巨大

引入 learner_profile.md(学习者画像)+ capability_framework.json(能力框架) 两个中间产物后:

  • ✅ 后续每一步都能引用前置产物,输出稳定性显著提升
  • ✅ 任何一步失败都可以单独重跑,不必从头蒸馏
  • ✅ 自动降级:LLM 偶发超时时,可以基于已有中间产物继续

Stage 3-7 的具体职责

  • Stage 3 Persona:把 learner_profile + capability_framework 编译为统一的人格描述符(语气、句式偏好、举例风格)
  • Stage 4 Anchors:从用户问卷里抽取 8-12 个高价值真实经历,作为后续故事的"母题"
  • Stage 5 Bridges:为每个 P1/P2/P3 题目生成 2-3 个能桥接到 Anchor 的过渡句
  • Stage 6 Vocabulary:基于上述内容反向提取 CEFR B2-C1 等级的地道词,附带例句、近义词、搭配
  • Stage 7 Patterns:从生成的内容里归纳出可迁移的句型骨架(如条件让步、对比强调、抽象具体化等)

每一步都通过 Pydantic Schema 严格校验输出格式——LLM 输出非结构化文本时,会自动触发"修复式重试"(让 LLM 看着上次的报错重新生成),把 JSON 解析失败率从 12% 压到了 0.5% 以下。

交付物(四件套)

文件用途
corpus.json结构化语料,喂给 QMD 引擎
profile.md学习者画像,可直接发给老师/同伴
site/index.html静态可视化站点,离线浏览
skill.zipVercel Skill 包,一键安装到任意 Agent

5.3 双模式 Skill 架构:Install-only vs Runnable Export

PersonaLingo 同时支持两种使用模式,覆盖"轻量尝鲜"和"长期备考"两种用户。

双模式 Skill 架构对比

特性Install-only 模式Runnable Export 模式
安装方式npx skills add orzcls/PersonaLingoDocker / 源码部署
后端依赖无(Agent 内闭环)FastAPI + SQLite
适用场景即插即用、轻量化长期辅导、多轮优化
数据持久化SQLite 持久存储
题库同步静态快照动态季度同步
LLM 调用借用宿主 Agent 的 LLM走自己的 API Key
隐私性数据留在宿主 Agent全本地可控

关键设计点:两种模式共享 同一份 Schema(CorpusData / AnchorStory / …),所以一个用户从 Install-only 模式产出的 corpus.json,可以无缝导入 Runnable 模式继续优化——架构层面的"投资保护"。

Install-only 的实现机制也值得展开一句:它本质上是把整个 7 步蒸馏流水线"重写"成了一份精炼的 SKILL.md 提示词蓝图,把 Schema 约束、降级策略、阶段产物全部塞进 Agent 的 system context,让宿主 Agent 在自己的会话里完成全流程,不依赖任何外部进程。

🚀 核心洞察:这是我从 Vercel Skills 生态中学到的最重要的一点——真正的轻量化不是"砍功能",而是"把流程编译成上下文"


六、踩坑实录:3 个真正花掉我时间的问题

开源不是只展示成功,下面分享 3 个让我熬夜最久的坑,希望能给做类似项目的同学省一点时间。

6.1 LLM 输出 JSON 不稳定——靠 Schema 修复式重试

7 步蒸馏每一步都要求 LLM 输出严格的 JSON,但即使加了 response_format={"type":"json_object"},依然偶发"末尾多逗号""引号未闭合""字段名拼错"。我尝试过三种方案:(1)正则后处理——脆弱;(2)json5 容错解析——能修一部分但盖不住语义错误;(3)Schema 修复式重试——把 Pydantic 报错原文喂回 LLM,让它自我纠正。最终方案 3 把失败率从 12% 压到 0.5%,且额外调用次数仅 +0.07 次/次平均。

6.2 题库变更导致 Skill 包失效——引入快照版本号

早期 Install-only 模式直接读取仓库 main 分支的 topics.yaml,结果有用户反馈"昨天能用今天不能用"——因为我刚 push 了一次题库结构调整。教训:任何被外部依赖的资源都必须有版本号。后来在每份 Skill 包里冻结了 topics_version: 2026Q1 字段,发布时同步打 git tag,解决了向后兼容问题。

6.3 OpenAI 与 Anthropic 的 system message 行为差异

两家 API 看似都遵循 OpenAI 协议,但实际差异不少:Anthropic 的 system 是顶层字段不在 messages 数组里、对长 system prompt 的指令遵循更稳但响应更慢、tool_use 协议字段名不同。我在 llm_adapter.py 里抽象了一个 UnifiedMessage 中间格式,向上提供统一接口、向下对每家做协议翻译,避免业务代码里到处 if-else。


七、快速开始:5 分钟跑起来

7.1 Install-only 一行安装(推荐尝鲜)

如果你只想体验"用 Agent 内置 Skill 生成自己的雅思语料库",一行命令搞定:

npx skills add orzcls/PersonaLingo

之后在任何支持 Vercel Skills 的 Agent 客户端(如 Cursor、Claude Desktop 配合 MCP)里直接调用即可,无需后端、无需 API Key。

7.2 Docker 完整部署(推荐长期备考)

如果你想要持久化、动态题库、自定义 Prompt,推荐 Docker 完整部署:

git clone https://github.com/orzcls/PersonaLingo.git
cd PersonaLingo
cp backend/.env.example backend/.env
# 编辑 backend/.env 填入 API Key
docker-compose up -d

启动后访问:

  • 前端:http://localhost:5173
  • 后端 API 文档:http://localhost:8000/docs

7.3 环境变量说明

backend/.env 关键配置:

# LLM 提供商(openai / anthropic 二选一或同时配置)
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini

ANTHROPIC_API_KEY=sk-ant-xxx
ANTHROPIC_MODEL=claude-3-5-sonnet-20241022

# QMD 引擎参数
QMD_TOP_K=10
QMD_RERANK_ENABLED=true

# 题库同步
TOPIC_SYNC_URL=https://raw.githubusercontent.com/orzcls/PersonaLingo/main/data/topics.yaml

八、写在最后:一起把 PersonaLingo 做得更好

如果用一组数字来概括目前的 PersonaLingo:

  • 📚 86 道精选高频题库(P1/P2/P3 三档)
  • 🔤 132 条地道词库(覆盖 CEFR B2-C1)
  • 🧩 18 个后端服务模块、9 个前端视图
  • 🤖 同时支持 OpenAI / Anthropic 双 LLM 适配
  • 🎯 零 Embedding 依赖的轻量 RAG,单机可跑

说实话,项目还有很多不完美的地方:语音评测模块还没加、题库覆盖还没拓到学术类(A 类)、多语言界面还没做。开源项目不是"做完了才发",而是"被看见后才会被做好"**。你看到的每一个 Issue、每一个 Star、每一条评论,都可能成为推动下一个重要特性落地的动力。

另外,AI 应用走到现在这个阶段,单个人越来越难以覆盖所有场景。我更愿意把 PersonaLingo 看作一个 AI + 个性化教育 的参考实现——领域可以被替换(托福、GMAT、驾考、手机考证…),QMD + 7 步蒸馏 + 双模式 Skill 这套架构可以复用。如果你正在做类似项目,欢迎 Fork、Star、交流,甚至直接拍我肩膀说"这里应该这么设计"。

最后再强调一句:这是一个取与雅思备考生、也送到雅思备考生手里的项目。如果你正在备考,欢迎直接试用并反馈体验;如果你是开发者,欢迎一起把它打磨得更体面。

但它远远不是完美的。我特别欢迎以下几类贡献:

  • PR:bug 修复、新题型支持、Prompt 优化
  • Issue:使用反馈、考试经验、新场景需求
  • Fork:欢迎二次开发成"AI 托福口语"、"AI 商务英语"等

>>> GitHub Star PersonaLingo <<<

开源是孤独的长跑,你的 Star 是我继续优化下去最直接的动力。 如果这个项目对你有启发,请给个 Star ⭐

GitHub :github.com/orzcls/Pers…

欢迎在评论区留下你对"AI +个性化教育"的看法,或者直接提出你的口语备考痛点,我会在下一个版本里认真考虑。


标签人工智能 RAG 开源项目 雅思 全栈开发 LLM Vue3 FastAPI