码道 —— 专属学习答疑对话AI:从 0 到 1 打造一个纯前端智能学习助手
码道 —— 专属学习答疑对话AI:从 0 到 1 打造一个纯前端智能学习助手
用 HTML5 + CSS3 + JavaScript 三件套,把一个普通的大模型流式接口,变成一个真正"为学习而生"的对话产品。本文完整记录了这个项目的构思、实现、踩坑与交付全过程。
在这里插入图片描述
运行效果:
一、前言:为什么会想做一个学习答疑 AI
我们都有过这样的经历:课堂上老师讲到一个概念,当时听懂了,课后做题却发现完全不会;翻教材抓不住重点,网上一搜,得到的是一大堆良莠不齐的内容,筛选信息比学习本身还耗时间。今天的大模型能力已经足够强大,可以胜任"答疑",但一个通用的聊天窗口并不是为"学习"设计的:
常见的问题有三个。 第一,回答是"大段文字墙",没有重点、没有结构,读起来像阅读理解;第二,回答缺少"学习场景"的要素——没有关键要点提炼、没有生活化例子、没有下一步可以追问的方向;第三,对话是"一次性"的,昨天问的问题和今天问的问题完全割裂,无法形成知识体系。
所以我产生了做一个"学习原生"的 AI 对话产品的想法:它不止于"回答正确",更要"教得明白"。于是有了本项目——专属学习答疑对话 AI。
它希望做到三件事:
- 结构化:每次回答都像一份"学习卡片",有标题、有概述、有要点、有例子,一眼抓住重点;
- 可追问:回答末尾给出"值得继续深入的问题",点击即可接着学,形成追问闭环;
- 零门槛:纯前端实现,浏览器打开即用,不需要安装任何东西、不需要后端服务。
这个项目本质上回答了一个问题:如何把一个通用的大模型 API,通过提示词工程 + 前端工程,变成一个"学习体验"远优于裸聊天窗口的产品?
二、项目目标与设计理念
2.1 产品定位
项目名称定为"专属学习答疑对话 AI",面向学生和自学者,覆盖数学、物理、英语、编程等学科,支持概念解释、原理推导、解题思路、代码示例、学习方法等多类问题。
2.2 三个设计原则
原则一:结构化优先。 这是整个项目的灵魂。AI 的回答不应是一坨连续的文本,而应当被组织成「回答类型标签 → 精炼标题 → 一句话概述 → 详细正文 → 关键要点 → 生活例子 → 追问建议」的完整信息架构。结构化的好处是:大脑更容易记忆、回顾时更容易定位、知识点之间的关系更清晰。
原则二:学习路径驱动。 好的老师从不只给答案,而是给你"继续学习的入口"。所以每条回答都设计了 related_questions(追问建议),把一次答疑变成一条不断延伸的学习路径。
原则三:渐进式体验。 流式输出时给用户即时的"打字机"反馈;拿到完整结构后再升级为精美的卡片视图。整个过程平滑无感,不打断思考节奏。
2.3 功能清单
- 流式对话:SSE 逐字输出,可随时停止生成;
- 深度思考可视化:展示模型的推理过程,点击可展开;
- 结构化学习卡片:六种回答类型自动加徽章,正文支持 Markdown;
- 多轮上下文:自动携带历史,追问无缝衔接,最多保留 20 轮;
- 零依赖 Markdown 渲染器:标题、列表、代码块、表格、公式美化;
- 现代 UI:深浅主题一键切换、移动端自适应、欢迎屏快捷提问;
- 容错降级:模型偶尔不守规矩输出非 JSON 时,自动降级为普通文本,绝不白屏。
三、技术选型:为什么坚持用纯前端
3.1 原生三件套 vs 现代框架
在做技术选型时,我认真地比较了 React/Vue 与原生 HTML5+CSS3+JavaScript 的取舍。结论是:这个项目完全不需要框架。
原因有三。其一,项目规模可控:核心就是"一个聊天界面 + 一个流式请求 + 一个卡片渲染",状态管理并不复杂,引入框架反而带来构建链路的复杂度;其二,交付物极简:原生项目意味着一个 index.html 拷到任何静态托管(GitHub Pages、AtomGit Pages、OSS、COS)就能跑,无需 npm install、无需构建;其三,学习价值高:写原生能让你真正理解 DOM、事件、异步流这些前端根基,而不是被框架的抽象遮蔽。
JavaScript 采用 ES6+,模块之间用全局命名空间(window.API_MODULE、window.MARKDOWN_MODULE)沟通,同时在文件内部封装 module.exports 以便 Node 环境做单元测试——一套代码,两个环境复用。
3.2 大模型 API 选型
模型调用采用 GitCode 大模型平台提供的 OpenAI 兼容接口(https://api-ai.gitcode.com/v1/chat/completions),模型为 deepseek-ai/DeepSeek-V4-Flash。选择它的理由:
- 接口标准:OpenAI 兼容格式,生态成熟、文档丰富,前端实现有大量可参考的经验;
- 原生支持流式:SSE 输出,天然适合聊天场景的逐字渲染;
- 支持推理预算:
thinking_budget参数允许模型先"深度思考"再作答,配合reasoning_content字段,我们可以把思考过程也变成产品体验的一部分; - 参数灵活:
temperature/top_p/frequency_penalty均可调,方便针对"教学场景"收敛更稳定、更克制的语气。
3.3 为什么不做 Python 后端
任务给了一份 Python 参考代码(requests 流式请求)。为什么不直接照抄成 FastAPI 后端?因为:这个场景下,后端是多余的。模型接口的公网可及性、CORS 的宽松配置,使得浏览器直接调用成为可能;砍掉后端意味着:部署成本为零、维护成本为零、任何人都可以 fork 后改一行配置就跑起来。当然,这样做的代价是 API Key 会暴露在浏览器中,关于这一点我在后文的"部署与安全"章节会坦诚讨论。
四、核心任务:把 Python 流式调用翻译成 JavaScript
4.1 Python 参考代码解析
任务提供了一份简洁的 Python 参考实现,核心逻辑是:
def query(payload):
response = requests.post(API_URL, headers=headers, json=payload, stream=True)
for line in response.iter_lines():
if not line.startswith(b"data:"):
continue
if line.strip() == b"data:[DONE]" or line.strip() == b"data: [DONE]":
return
yield json.loads(line.decode("utf-8").lstrip("data:").rstrip("/n"))
chunks = query({...model..., "stream": True, ...})
for chunk in chunks:
print(chunk["choices"])
这段代码有三个关键动作:
- 以
stream=True发起请求,拿到一个永不结束的字节流; - 逐行扫描,只认
data:前缀的行,遇到[DONE]结束; - 把每一行
data:后的 JSON 取出来解析,供上层消费。
在 Python 里这很简单——iter_lines() 帮你做好了按行切分、编码解码的所有脏活。但在浏览器里,我们要用手动方式完成同样的工作,这就是本项目的第一个技术难点。
4.2 浏览器端的对应实现
我在 js/api.js 中实现了与 Python 参考代码一一对应的功能:
| Python | JavaScript |
|---|---|
requests.post(..., stream=True) | fetch(url, {method:'POST', body}) + resp.body.getReader() |
resp.iter_lines() | 读取 Uint8Array → TextDecoder 解码 → 手动按 \n 切分 |
line.startswith(b"data:") | line.trim().startsWith('data:') |
json.loads(...) | JSON.parse(...) + 容错清理 |
data:[DONE] 判断 | payload === '[DONE]' → 触发 onEnd |
其中最关键也最容易被忽略的是按行切分的缓冲处理。TCP/HTTP 流并不保证"一行"刚好落在一个 reader.read() 的返回里,很可能一次 read() 返回半行、下一次返回剩余部分,甚至一次返回多行。因此我维护了一个 buffer 字符串:每次把新解码的文本追加进 buffer,然后循环 indexOf('\n') 取出完整行,处理完再清掉;最后还要处理"流结束时没有换行符结尾的残余内容"。这部分是纯手工实现的 iter_lines()。
4.3 让接口更"现代"的设计
在翻译之外,我对 API 层做了三个面向浏览器的增强:
第一,AbortController 原生支持"停止生成"。 用户在界面上点击"停止"时,controller.abort() 会中断 fetch,我们捕获 AbortError 后正常收尾。这是 Python 参考代码没有的能力,但对聊天产品至关重要——如果回答开始胡说八道,用户应该有权立刻终止。
第二,解析函数做成纯函数。 parseSSEChunk(line) 不依赖任何全局状态,输入一行原始文本,输出 {content, reasoning, done} 或 null。纯函数的好处是:可以在 Node 里直接做单元测试,api.js 通过 UMD 风格同时兼容浏览器和 Node 两种环境。
第三,显式分离正文与思考。 GitCode 接口的 delta 里同时有 content(正式回答)和 reasoning_content(思考过程)两个字段。我在解析层就把它们剥离开,分别回调 onContent 和 onReasoning,让上层 UI 可以有两种完全不同的呈现方式。
有了这个 API 层,Python 参考代码的能力就 100% 迁移到了浏览器,并且得到了增强。但此时我们还只是"能拿到文本流",离"学习产品"还有很远——接下来是最核心的一步:提示词工程。
五、系统提示词工程:让模型输出结构化 JSON
5.1 为什么需要 JSON
如果只是把流式文本显示出来,那和用一个裸的 ChatGPT 没有任何区别。我要的是:模型按我的信息架构来组织回答。最直接、最可控的办法就是在系统提示词里"约法三章",要求模型每一次回答都只输出一个合法 JSON 对象,然后由前端解析并分区域渲染。
结构化的回报是巨大的:
- 前端渲染更精美:标题是标题、要点是列表、示例是卡片,不需要用正则从散文里猜结构;
- 学习效率更高:学生先看到"一句话概述"判断是否命中自己的疑问,再决定要不要深入正文;
- 交互更丰富:
related_questions可以直接变成可点击的追问按钮,形成交互闭环。
5.2 JSON 字段的信息架构设计
我设计的 JSON 结构包含 7 个字段,每个字段都对应一个产品决策,而不是拍脑袋:
{
"answer_type": "概念解释 | 原理推导 | 解题思路 | 代码示例 | 学习方法 | 综合答疑",
"title": "不超过15字的精炼标题",
"summary": "一句话总结核心结论(≤40字)",
"content": "Markdown 格式的详细解答正文",
"key_points": ["3~5条关键知识点,每条≤25字"],
"example": "一个具体的生活例子或通俗类比",
"related_questions": ["2~3个值得追问的问题"]
}
逐字段说明设计动机:
answer_type(枚举):用一个受控枚举而不是自由文本。六个固定取值好处有二:前端可以用固定的颜色徽章体系,视觉上区分不同回答类型——"代码示例"回答和"概念解释"回答一眼可分;模型也不用纠结措辞,选一个最贴切的即可,避免"类型"这种 metadata 的任意漂移。title(≤15字):像新闻标题一样精炼。这个约束很反直觉但很有效——如果不在提示词里强制,模型会输出一长串"关于xxx现象的一些深入探讨与分析"。限制了长度,标题才真正起到"扫描"的作用。summary(≤40字):一句话说清核心结论。它是回答的"电梯演讲",让学生在花 3 秒读完后判断"这个问题是不是我要的"。content(Markdown):详细解答正文。这里是真正的知识输送区,逻辑推演、分步讲解、示例代码都放这里,允许使用小标题、列表、代码块等 Markdown 语法。key_points(3~5条):面向"记忆"的浓缩。多项认知科学研究都表明,分层级、要点化的信息比线性文本更利于记忆提取。这里是学生复习时的快速索引。example(生活化):面向"理解"的类比。抽象概念落地的最好方式是一个具体的例子——向小朋友解释"浮力"最好的方式是"把空塑料瓶按进水池,手会感觉到一股向上的力"。related_questions(2~3个):面向"延伸"的路径。这既是学习设计的体现,也天然成为前端交互的素材(可点击追问)。
5.3 提示词的工程细节
提示词里我埋了几个容易被忽视、但对"模型守规矩"至关重要的细节:
- 明确禁止 JSON 之外的输出:明确要求"不要输出 Markdown 代码块标记(如
```json包裹)、不要输出注释"。很多模型在输出 JSON 时习惯用代码块包裹,前端还得费劲剥壳;直接在提示词层消灭它。 - 标注转义要求:明确说明"字符串内的换行必须使用
\n转义,不要使用真实换行"。这是血泪教训——JSON 规范不允许字符串值中出现未转义的真实换行,一旦模型在content里放了真实换行,JSON.parse直接失败。把这条写进提示词,模型的遵从率显著上升。 - 给出字段级约束:每个字段后面都写了"不超过多少字 / 从哪些类型中选 / 可以省略"等边界条件。大模型对明确、量化的约束比模糊的形容词(如"简洁一点")遵从度高得多。
- 兜底条款:提示词最后一条写了"如果用户明确要求你输出作文、诗歌等,你仍然要把内容放到
content字段中,并遵守整体 JSON 结构"。这确保了用户问"写一首关于宇宙的诗"时,系统不会被"必须输出 JSON"的硬约束卡死,而是把诗放进正文区,整体照常工作。
这段提示词是整个项目"产品化"的点睛之笔——同样的模型权重,不同的提示词,产出完全不同等级的产品体验。
六、前端架构设计
项目代码量约 1900 行,我按"职责单一"拆成了清晰的模块:
├── index.html # 页面骨架:侧边栏 + 聊天区 + 输入区
├── css/
│ └── style.css # 全部样式:CSS 变量主题 + 组件 + 响应式
├── js/
│ ├── config.js # 配置:API 地址、密钥、模型参数、系统提示词
│ ├── api.js # 网络层:请求体构建 + SSE 解析(纯函数,可单测)
│ ├── markdown.js # 渲染层:零依赖 Markdown 渲染器(安全转义)
│ └── app.js # 应用层:交互、状态、流式渲染、卡片组装
└── test/ # 三层测试
各层依赖关系是单向的:app.js → api.js → config.js,app.js → markdown.js。网络层与渲染层完全独立、可被 Node 直接测试,这也为后面的测试体系打好了基础。
config.js 把系统提示词独立成一个大字符串,而不是散落在请求代码里——提示词是"产品配置"而不是"代码",单独管理意味着后续调优(比如换一个 JSON 结构、加一门学科)不需要动业务代码。
七、SSE 流式解析与增量渲染的细节
7.1 解析器实现
parseSSEChunk 是全项目单测覆盖最高、也最"较真"的函数。它的输入输出约定是:
- 空行、非
data:前缀行 → 返回null(忽略); data: [DONE]→ 返回{ done: true };- 合法 JSON 且含
choices→ 返回{ content, reasoning, done: false }; - 残缺 JSON → 返回
null(不抛异常,因为流式过程中"半行"是常态)。
每一条都对应真实会遇到的边界情况,比如流中途断掉、服务端发心跳空行、finish_reason 所在的空 delta 消息等。
7.2 增量 JSON 解析:边收边渲染
拿到文本流之后,最朴素的做法是"全文收完再解析",但那样用户在几秒内只能看到光秃秃的"生成中……",体验很差。
我的策略是边收边试:
- 每收到一段
content增量,就追加进累积 buffer; - 立刻用"宽容清理 +
JSON.parse"尝试解析一次; - 解析失败(大多数时候,因为 JSON 还没闭合)→ 用打字机效果把原始文本显示出来,保证用户有即时反馈;
- 一旦解析成功 → 用结构化视图渲染标题、概述、要点等字段(此时
content可能已完整),并把正文区升级为 Markdown 渲染; - 后续增量继续追加——如果模型输出稳定,后续多次解析会拿到更完整的字段;如果偶发失败,则沿用最后一次成功的结构,不覆盖。
这个策略的体验效果是:回答一开始像"打字机"在写作文本;当 JSON 闭合的那一瞬间,界面"唰"地升级成带徽章、要点的卡片;之后正文部分继续像打字机一样渲染 Markdown。用户全程有反馈,且最终呈现是精致的结构化卡片——两全其美。
7.3 思考过程的处理
模型在正式回答前会先产生大段 reasoning_content。这段思考内部语言不适合直接糊在正文里,我把它放进一个默认折叠的"深度思考"区域,点击可展开查看模型的推理路径。这对学习者其实是额外的价值——"看 AI 怎么想"本身就是在学习一种解题思路。系统提示词没有禁用思考(thinking_budget: 2048),因为我判断在这个产品里,展示思考过程是加分项。
八、结构化学习卡片的渲染
8.1 DOM 结构
一条助手回答的卡片结构如下:
.msg.assistant
├── .msg-avatar(机器人头像)
└── .msg-body
├── .thinking(可折叠思考区)
└── .ai-card
├── .type-badge(回答类型徽章,六色体系)
├── .card-title
├── .card-summary
├── .card-content(Markdown 正文)
├── .card-keys(关键要点清单)
├── .card-example(例子卡片)
└── .card-related(追问按钮组)
8.2 渲染策略的两个细节
详情一:插入锚点的动态查找。 卡片在"流式态"和"完成态"之间切换时,底部的"生成中…"提示条(.stream-hint)会被移除。要点/示例/追问区块的插入位置依赖这个锚点,如果锚点已不存在还硬用 insertBefore(node, anchor),就会抛出 NotFoundError。修复方案是:插入前先 querySelector 找锚点,找不到就 appendChild。这个 bug 是浏览器自动化测试抓出来的,后面会细说。
详情二:追问按钮的交互闭环。 related_questions 渲染为可点击的 “chips”,点击后直接作为新问题发送,且历史上下文会一并带上。于是"追问"不再是用户手打,而是"点一下"——交互成本降到最低,学习路径可以一路延伸下去。我在浏览器测试里专门验证了这条链路:点击追问 → 第二轮用户消息出现 → 第二轮回答正常返回。
8.3 容错降级
再强的提示词也不能保证模型 100% 服从。我做了三级降级:
- 模型返回合法 JSON → 精致卡片;
- 模型返回了文本但非 JSON → 降级为普通文本消息完整展示(绝不丢内容);
- 模型什么都没返回 → 展示一条温和的提示。
另外,前端还做了"清理 JSON 包裹"的容错:万一模型还是用 ` ```json … ````包裹了输出,解析前会先剥掉围栏。三层防线保证用户永远不会面对一个白屏或一个报错弹窗。
九、零依赖 Markdown 渲染器
9.1 为什么自己写
Markdown 渲染的"工业标准"是 marked.js,但我决定自己写一个约 120 行的轻量渲染器。动机:零依赖是硬需求(项目目标是"拷到静态托管就能跑"),CDN 引入会让项目在离线/内网环境下立即失效;而我们的正文场景相对收敛——标题、粗斜体、行内代码、代码块、有序/无序列表、引用、链接,这些语法手写实现并不困难,还能顺便做定制(比如公式美化、代码块换主题色)。
9.2 安全:先转义,再渲染
所有面向用户的 HTML 注入风险都必须在渲染层堵死。 我的实现顺序是:先对全文做 HTML 转义(& < > " ' 全部编码),再按语法规则生成结构标签,<script>、<img onerror> 这类注入 payload 在转义阶段就失效了。单元测试里专门有一条 XSS 转义 用例守住这条底线。
9.3 公式美化
学习场景绕不开数学公式。完整方案是接入 KaTeX,但同样受"零依赖"约束。折中方案:识别 \(...\)(行内)和 $$...$$(块级)包裹的公式,用强调色 + 衬线字体 + 居中块级样式渲染成"公式感"的视觉样式,同时保留原始 LaTeX 文本。效果上聊胜于无,而且为未来无缝升级 KaTeX 预留了同一套标记。我在 README 的展望里明确写了"引入 KaTeX 是真公式渲染的下一步"。
十、UI/UX 设计
10.1 视觉与主题系统
整体走"学习工具"气质:干净的卡片式布局、indigo 渐变主色、白色与浅紫灰的背景基调。全部颜色抽成 CSS 自定义属性(变量),深浅两套主题通过 body.dark 切换变量取值即可,主题切换按钮一行 classList.toggle 搞定,且用 localStorage 记住用户偏好。
10.2 移动端与细节打磨
- 宽度不足 860px 时,侧边栏收起为抽屉,配合半透明遮罩(
mask)弹出; - 消息区自动滚动跟随(流式输出期间持续
scrollTop = scrollHeight); - 输入框支持 Enter 发送、Shift+Enter 换行、Esc 在生成中停止;
- 打字状态下"生成中"提示条有三点动画,营造呼吸感;
- 回答类型徽章用六种低饱和度配色区分学科气质(蓝=概念、绿=解题、橙=代码……)。
十一、测试:三层验证体系
一个纯前端项目最容易出现"在自己电脑上能跑,换个环境就废"的问题。所以我给项目配了三层测试:
11.1 单元测试(离线)
test/parser.test.js(Node 直接运行,21 个断言)覆盖:SSE 解析器的各种边界(空行、[DONE]、残缺 JSON、空 choices、带空格前缀)、请求体构建、Markdown 渲染的每一项语法、XSS 转义、公式美化。重点:js/api.js 和 js/markdown.js 都要能被 Node require,这要求网络层与渲染层不依赖 DOM——这个约束反推了架构的干净。
11.2 端到端冒烟(联网)
test/e2e-smoke.js 真实调用模型接口:「发问 → 流式累积 → JSON 解析 → 校验 7 个字段」全链路。它验证的是外部世界的约定没有变——API 格式、模型行为、JSON 结构,任何一个变化都会在这里暴露。
11.3 浏览器级验证(可选)
test/browser-check.js 用 puppeteer 驱动真实 Chromium:打开页面 → 点快捷提问 → 等流式完成 → 断言卡片结构 → 点追问按钮 → 收集控制台错误。这层测试抓到了上一节提到的 insertBefore 崩溃,也验证了"全程无控制台报错"。
三次测试跑完,才叫"一个合格的前端交付"。
十二、踩坑记录
把一个通用接口变成产品的过程中,值得记录的坑有五个:
insertBefore锚点失效:流式提示条被移除后再被当作插入锚点,报NotFoundError。修复:动态查找锚点,找不到就append。教训:DOM 引用不要长期持有,临时再查。- JSON 字符串内的真实换行:模型在
content字段里输出真实换行导致JSON.parse崩溃。修复分两层:提示词层明确转义要求 + 前端层降级兜底。 reasoning_content与content混淆:思考过程和正文混在一起显示会惨不忍睹。修复:解析层分离,UI 层折叠展示。- 半行/多行缓冲:字节流切分不能用"整块直接 parse"。修复:手工实现按行缓存队列。
- CORS 与本地 file:// 协议:
file://下部分浏览器 fetch 受限。修复:README 明确建议用本地静态服务器(python3 -m http.server),生产推荐静态托管。
十三、部署与安全
13.1 部署
原生三件套的部署是最轻松的部分:index.html + css/ + js/ 一整个目录传到任意静态托管即可。开发期用 python3 -m http.server 8000 或 npx serve。
13.2 一句必须说的实话:API Key 会暴露
纯前端方案下,config.js 里的 apiKey 对任何打开开发者工具的用户都是透明的。这是纯前端 + 直连模型的固有代价,我在 README 里明确写了,并给出两条建议:个人学习项目可以接受;若要做正式商用服务,必须加一个服务端代理——密钥只存在服务端,浏览器请求服务端,服务端做鉴权、限流、转发,这同时也解决了调用计费的归属问题。这是诚实的技术边界,不藏着掖着。
十四、项目结构与文件职责一览
├── index.html # 零依赖入口,承载侧边栏/聊天区/输入区三块骨架
├── css/style.css # CSS 变量深浅主题、六色徽章、卡片/气泡/输入区、响应式
├── js/
│ ├── config.js # API_URL / apiKey / 模型参数 / systemPrompt(产品配置集中地)
│ ├── api.js # buildRequestBody / parseSSEChunk(纯函数) / streamChat
│ ├── markdown.js # renderMarkdown / escapeHtml / 公式美化
│ └── app.js # 消息渲染、增量JSON解析、结构化卡片、主题/清空/追问交互
├── test/
│ ├── parser.test.js # 21 项离线单元测试
│ ├── e2e-smoke.js # 真实 API 链路冒烟
│ └── browser-check.js# puppeteer 浏览器级验证
└── README.md # 完整说明:快速开始 / 配置 / 提示词JSON / 测试 / 部署
十五、未来规划
项目已经跑通,但"学习助手"的想象空间远不止于此。我在 README 的展望里列了这些方向:
- 接入 KaTeX 实现真正的数学公式渲染,覆盖理工科解题场景;
- 学科知识库检索:给模型挂上 RAG(检索增强),让回答基于课本/讲义而非纯记忆;
- 错题本与记忆曲线:把追问过的知识点记入用户画像,用间隔重复提醒复习;
- 多端同步:目前数据在
localStorage,后期可接轻量后端做真正的多端同步; - 语音交互:结合 Web Speech API,让"答疑"在通勤路上也能发生;
- 服务端代理化:解决密钥暴露与计费归属,同时支撑多用户并发。
十六、总结
回顾整个开发,这个项目最有价值的产出其实不是代码,而是一个清晰的认知:大模型只是"原料",产品体验取决于你如何设计它的输出契约。
同样是 deepseek-ai/DeepSeek-V4-Flash,裸的聊天窗口给出的是信息瀑布,而这个项目通过一份系统提示词 + 一个增量渲染管线,让它给出了"标题 → 概述 → 正文 → 要点 → 例子 → 追问"的学习卡片。模型的能力被格式约定和前端呈现放大成了一个完整的教学产品。这正是"码道"所倡导的:用工程能力,把 AI 的能力翻译成真正的用户体验。
如果你也想做一个自己的 AI 产品,我的建议是:先别急着上框架、上后端,从"你希望 AI 以什么结构回答问题"开始设计——把输出契约想清楚了,前端、提示词、测试一切都顺理成章。这套思路,对任何"AI + 场景"的产品都适用。
项目地址:https://atomgit.com/gcw_XKgDEJsW/zhuanshuxuexidayiduihuaAI
感谢阅读,欢迎 star、fork,也欢迎在评论区交流你在构建 AI 产品时踩过的坑。
更多推荐




所有评论(0)