Files
doodle-mini/docs/AI生成题目方案.md
T
2026-06-25 12:46:43 +08:00

8.8 KiB
Raw Blame History

AI 生成题目方案(对话式出题)

文档状态:方案讨论 / 待评审 创建日期:2026-06-16 背景:当前小程序与同类应用差异不明显,用户画像不够精准。探索引入 AI 能力——用户用自然语言描述需求,AI 将其翻译为对预置「技能(绘制方法)」的调用,并完成出题与绘制。


一、战略层面:先想清楚「为什么要做」

本方案在技术上完全可行,但要避免 AI 沦为「为差异化而加的功能」,而非「解决真实痛点的功能」。立项前需先回答:

  • 用户的真实痛点是什么? 是「找不到合适的题目」,还是「孩子不爱练」「不知道该练什么」?如果痛点不在出题,AI 出题器再酷也救不了留存。
  • 家长真的会打字描述需求吗? 「我想要培养数感的题目」这类话,家长未必说得出(多数人不知道「数感」是什么)。更真实的诉求往往是「我家娃 5 岁,给我今天该练的」。这意味着 AI 的价值可能不在「自然语言理解」,而在 「帮不懂教育的家长做决策」
  • 不用 AI 能否满足 80% 如果几个下拉框 + 模板就能满足,AI 的边际价值仅是「输入方式更自然」,通常撑不起差异化。

结论 / 定位建议: 把 AI 定位成 「懂教育的助教」(帮家长判断该练什么、循序渐进地推荐),而不是 「自然语言转绘制指令的翻译器」。前者是真差异,后者只是花哨的表单。


二、技术层面:经典的 Function Calling / Tool Use 架构

「把需求变成可调用的技能」在工程上的成熟范式叫 工具调用(tool use / function calling:已有的「绘制方法」即工具,LLM 负责把人话翻译成「调用哪个工具 + 什么参数」。

2.1 整体数据流(小程序云开发)

用户输入(自然语言)
  → 小程序前端 (聊天式 UI)
  → 云函数 ai-orchestrator
  → 调用 LLM(带 tools 定义)
       ├─ 缺参数 → 返回追问("孩子几岁?")  ← 多轮
       └─ 参数齐全 → 返回结构化调用意图
  → 确认环节(用户点"确认生成")
  → 云函数 / 前端调用内置绘制方法
  → 生成题目数据 + 渲染(canvas/图片)
  → 返回前端展示 / 保存 / 下载

2.2 关键模块

1)技能注册表(最核心) 每个绘制方法描述成一个 tool schema,让 LLM 知道有哪些能力、各需要什么参数:

{
  "name": "generate_number_sense",
  "description": "生成培养数感的练习题(比大小、数的分解、数数等)",
  "parameters": {
    "type": "object",
    "properties": {
      "age":        { "type": "integer", "description": "孩子年龄 3-8" },
      "sub_type":   { "type": "string", "enum": ["比大小", "数的分解", "按数取物"] },
      "count":      { "type": "integer", "description": "题目数量" },
      "number_range": { "type": "string", "enum": ["1-10", "1-20", "1-100"] }
    },
    "required": ["age", "sub_type", "count"]
  }
}

2LLM 选型(国内合规很重要) 云函数可发 HTTP 请求,能接任意 LLM。国内备案合规、且支持 OpenAI 兼容 function calling 的可选:通义千问、豆包(火山方舟)、DeepSeek、文心、混元。微信云开发本身也有「AI 能力 / 微信对话开放平台」。建议挑一个支持 tools 参数的,编排逻辑几乎无需自写。

3)多轮对话 + 槽位填充(slot filling 「缺年龄就追问年龄」的逻辑交给 LLM 判断,比手写 if-else 更强。需要做的只是:

  • 每轮带上对话历史(存云数据库或前端回传);
  • 在 system prompt 里要求:「参数不全时先友好追问,不要瞎猜」。

4)「决策」与「生成」必须分离 ⚠️(最重要的工程原则) 让 LLM 决定「生成什么」(结构化参数),但题目实际内容由确定性代码生成:

  • 不要让 LLM 直接吐出 20 道算术题——会算错、会重复、不可控。
  • 让 LLM 输出 {age:5, sub_type:"比大小", count:20, range:"1-10"},再由绘制方法按规则生成 + 渲染。

兼得 AI 的「听得懂人话」与传统代码的「100% 正确可控」。

5)确认环节 真正调用绘制方法前,把解析出的参数回显给用户确认(「将生成 20 道 1-10 比大小,确认?」)。既防误解,也是好体验。

2.3 小程序云开发的几个坑

说明 / 对策
云函数超时 默认 20sLLM 调用可能慢。先做非流式(一次返回);想要打字机效果,后期再用 WebSocket 或 HTTP 触发器 + SSE,复杂度高,别一开始就做。
内容安全(必须) 微信强制要求。用户输入和 AI 输出都要过 security.msgSecCheck,否则可能被封。合规硬要求,非可选。
API Key 保护 LLM 的 key 只能放云函数环境变量,绝不能进前端
成本与频率 每次对话烧 token。加缓存(相同需求复用)、限频,防刷。
冷启动延迟 云函数冷启动 + LLM 延迟叠加,首次可能 3-5sUI 要有 loading 反馈。

三、要不要把绘制方法搬到服务端?

短答:不需要把「绘制」搬到服务端。但要先把现有方法拆成两层——「出题数据」和「画图」——只有前者可能值得上服务端,后者留在小程序里。

3.1 把「绘制方法」拆成三层

① 意图理解层   用户人话 → 结构化参数      ← 必须在服务端(云函数 + LLM)
② 出题数据层   参数 → 题目数据(JSON)       ← 可服务端、可客户端
③ 渲染绘制层   题目数据 → canvas 画出来     ← 留在小程序前端

现有「绘制方法」大概率是 ②③ 混在一起(一个函数既算题目又调 wx.canvas 画图)。做 AI 真正要做的不是「搬到服务端」,而是「把 ② 和 ③ 解耦」

3.2 为什么渲染层(③)不该搬服务端

  • 小程序 canvas 是客户端 API,云函数(Node 环境)里没有 DOM、没有原生 canvas;要画图得引 node-canvas 之类,又重又易踩坑,得不偿失。
  • 渲染留前端:性能好、可交互(手写、橡皮擦等)、省服务器成本。

→ ③ 保持现状,一行都不用动

3.3 ② 出题数据层:搬不搬都行

方案 A — 什么都不搬(最快上线,推荐起步) 云函数只做 ①,返回结构化参数给前端:

// 云函数返回
{ skill: "number_sense", age: 5, sub_type: "比大小", count: 20, range: "1-10" }

前端拿到参数,调已有的出题 + 绘制方法,照常跑。AI 是「加在前面的一层翻译」,老代码完全复用。

方案 B — 把出题逻辑(②)搬服务端 云函数直接算好题目数据返回:

{ problems: [ {left:7, right:3, op:">"}, {left:2, right:8, op:"<"}, ... ] }

前端只负责画。

什么时候才值得选 B

  • 出题逻辑要保密/防作弊(题库、难度算法不想暴露前端);
  • 多端复用(以后有 H5、APP,出题逻辑只维护一份);
  • 出题需要服务端资源(查数据库题库、调别的接口)。

以上都没有,先选 A

3.4 关键动作

唯一的关键动作:确保出题逻辑是个「纯数据函数」(输入参数、输出 JSON、不碰 canvas。做到这点,搬不搬服务端都是后话,随时可切。


四、最小可行起步(MVP

别一上来做全套对话系统,先做最小闭环验证:

  1. 单个技能:只挑「数感题目」一个绘制方法接进来。
  2. 单轮或两轮对话:用户说需求 → AI 解析参数(缺了追问一次)→ 确认 → 生成。
  3. 跑通链路:「人话 → 结构化参数 → 已有绘制方法」。

跑通后回答关键问题:家长真的会用自然语言输入吗?还是更想点几下就出题? 用真实数据决定是否继续往「对话式」投入,而非凭感觉。


五、待办 / 下一步

  • 盘点现有「绘制方法」清单及调用方式,判断 ②③ 缠绕程度。
  • 确定 LLM 供应商(合规 + 支持 function calling)。
  • 抽离一个出题逻辑为纯数据函数(以「数感题目」为试点)。
  • 搭建 ai-orchestrator 云函数骨架 + 技能注册表。
  • 接入内容安全 msgSecCheck
  • MVP 灰度,观察家长真实输入行为与转化数据。