前端+AI实战:用React搭建资料AI问答助手的完整记录
2026/9/15 8:16:46 网站建设 项目流程

说一个略微扎心但很真实的事:前两天和一个做了多年后端的哥们儿吃饭,他问我,你们前端现在是不是很慌?我说慌什么,他说AI都能自己写前端了。我没接话,因为我心里清楚,AI不仅能写前端,也能写后端,甚至写得比很多人都快。真正该慌的不是某条技术栈,而是“只会照着原型切页面”这种工作方式本身。我决定认真把“前端+AI”啃一遍,今天是第四天。这几天踩了不少坑,也想通了不少事,顺手把刚跑通的一个小项目写出来,给同样在观望的前端同学一个参考。

这个项目背景很简单:我手上有一批零散的内部资料和培训文档,想快速做一个“资料AI问答助手”的网页版。前端负责对话界面和历史记录,AI能力直接走大模型API,再加一层轻量的本地检索逻辑。听起来不难,但真正做完,涉及的东西比想象中多:提示词设计、流式渲染、上下文管理、错误降级,以及一堆只有在真实联调里才会遇到的坑。这篇笔记就按我这几天实际经历的节奏来写,不绕弯子。

1. 为什么前端要啃AI?我的动机和行业观察

1.1 前端正在经历从“画页面”到“接大脑”的转型

前几天我刷到一个招聘帖,岗位名称直接叫“AI应用前端工程师”。要求写得很直白:除了React和TypeScript,还得熟悉大模型API的调用方式、会处理流式响应、理解RAG的基本流程、能把对话式交互做得跟ChatGPT一样顺滑。这个岗位给到的薪资,比同级别的普通前端岗位高出不少。

这件事给了我挺大触动。过去两年,前端圈流行一个说法:业务复杂度都往后端塞,前端就是“调接口的皮肤”。但现在情况变了,大模型把“接口”这个词的含义彻底扩大了。以前的接口返回结构化JSON,你拿到数据渲染就好;现在的接口返回的是一段还在往外蹦的文字流,你不仅要在页面上展示,还得管理它的状态、中断、重试、上下文长度,甚至要在本地做缓存和索引。这些能力,传统前端开发里根本不会遇到。

所以我的判断是,前端学AI,不是去卷算法,也不是非要把Transformer原理啃得底朝天。更现实的路是:去掌握那些“让AI能力真正落地到业务里”的技能。模型是别人的,数据是企业的,而把两者粘起来、做成好用的产品界面,恰恰是前端最擅长的事情。这是机会,比焦虑实在得多。

1.2 AI编程没有那么玄,先搞清楚它改变的是什么

另一个让我下决心学AI的原因,是AI编程工具已经深度融入了我的日常工作。最开始我用AI辅助写代码,也就是让它补全函数、解释报错,后来慢慢开始让它写整个页面、改样式、做数据联调。实话说,前半个月我踩过不少坑,比如它生成的东西看起来能用,一上线就崩;再比如它特别容易“一本正经地胡说八道”,尤其在组件版本和接口字段这种细节上容易出错。

但用得多了之后,我总结出一个规律:AI编程工具的产出质量,90%取决于你怎么描述需求。同样一个对话框,有人能几句话让它写出可用的组件,有人来回扯皮半小时还拿不到能跑的结果。这其实就是“提示词”的能力。在AI编程场景里,提示词远远不只是一句话,它包含了角色设定、约束条件、输入输出格式、错误处理逻辑,甚至要给它看完整的报错信息和相关代码。

今天我练手时,特意给AI编程助手定义了一份简单的“skills”——也就是一套固定格式的角色与行为说明,让它在帮我写前端代码时自动遵循。效果还挺明显,至少它不再默认用哪些老掉牙的写法,也能主动帮我考虑边界情况。这个方向我想后面继续深入,因为AI Agent时代,前端开发者可能要经常跟“技能定义”这种东西打交道。

2. 今日实战:给本地知识库加一个AI问答界面

2.1 项目目标与选型思路

既然要学,光看教程肯定不行。我的做法是,拿一个真实要用的场景练手:给手头一沓内部培训资料做一个网页版问答助手。用户输入问题,系统从资料里检索相关内容,连同问题一起发给大模型,生成答案并展示在页面上,同时保留会话历史。

前端技术栈选择了React + Vite + TailwindCSS。之所以这么选,一是现在很多中后台项目都是这套组合,二是Vite的冷启动快,适合我这种每天只能挤出一两个小时的学习节奏。组件库直接用了Ant Design,没有重新造轮子。AI交互部分没有用第三方现成的对话组件,因为这类组件的抽象程度普遍不够,在流式渲染和消息状态上,自己写反而更可控。

后端我简单搭了一个Node.js服务,主要负责把前端传来的问题拼接成完整提示词,调大模型API,再把流式返回转发给前端。按理说应该用Java写,这也是我之前计划补的方向,但今天为了快速验证前端链路,先用自己最熟的环境跑通再说。这里我自己的体会是,学习阶段千万不要在技术选型上过度纠结,能最快验证想法的组合就是好组合。

2.2 前端聊天界面的核心设计

一个AI问答界面,表面看起来就是一个聊天窗,但真做起来有几个环节不能省。我把今天实现的界面拆成三个核心区域:左侧是会话列表,中间是消息流,底部是输入区。会话列表支持新建会话、切换历史和删除,消息流区分用户消息和AI消息,输入区带发送按钮和“停止生成”按钮。

消息的数据结构我设计得比较朴素:

interface Message { id: string; role: 'user' | 'assistant'; content: string; status: 'pending' | 'streaming' | 'done' | 'error'; createdAt: number; }

这个status字段很关键,尤其对AI对话场景。一开始我没设计这个字段,导致AI在流式返回时UI要么不断重渲染,要么干脆不更新。后来加上status,渲染逻辑才清晰起来:pending表示请求刚发出,在等待首个token;streaming表示正在流式输出;done表示输出完成;error则是请求失败或超时。组件里只需要根据status切换不同的展示形态就可以了。

页面的整体布局我用了一个简单的flex结构,外层容器高度撑满视口,左侧会话栏固定宽度220px,右侧主区域flex: 1。消息区用了overflow-y: auto,配合滚动到底部的逻辑,保证新消息能自动出现在视野里。这个滚动逻辑看着简单,但如果不做,长对话时体验会非常差。

2.3 对接模型的请求封装与数据流

AI问答和普通接口请求最大的不同,在于响应是持续到达的。我封装了一个请求方法,用fetch的ReadableStream逐段读取服务端转发回来的数据,再用TextDecoder按UTF-8解码。

async function fetchAIResponse(messages: Message[]) { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }), }); if (!res.ok || !res.body) { throw new Error(`请求失败:${res.status}`); } const reader = res.body.getReader(); const decoder = new TextDecoder('utf-8'); let content = ''; while (true) { const { done, value } = await reader.read(); if (done) break; content += decoder.decode(value, { stream: true }); // 每拿到一段数据就回调更新UI onChunk(content); } return content; }

这段代码是今天整个项目的核心。最开始我用的是axios,但axios对流式响应支持不够友好,后来换成原生fetch配合ReadableStream,问题迎刃而解。解码时那个{ stream: true }参数也别漏,如果漏了,遇到中文多字节字符跨段传输时会出现乱码。这个问题我踩了一下午,后面在排查实录里会细说。

3. 关键细节:提示词、流式输出与skills的落地

3.1 提示词工程:先给角色再给约束

提示词这块,我一开始的理解很简单:把用户问题丢给大模型就行。跑了几个例子之后发现,这种直接问法在纯知识问答场景还凑合,但要结合具体的资料内容提问,效果就一言难尽了。模型会自由发挥,引用不存在的资料,甚至编造根本不存在的文档内容。

后来我改成了一套比较稳定的提示词模板:先给模型定义角色和任务边界,再说明回答要求,最后把检索到的相关资料作为上下文拼进去。

你是企业内部文档助手。请基于“参考资料”回答用户问题。 要求:

  1. 只依据参考资料回答,参考资料中没有的内容,明确说明“资料中未找到相关信息”;
  2. 回答内容控制在300字以内,使用简洁的中文;
  3. 如用户问题与文档无关,请引导用户询问文档相关内容。

参考资料: {retrievedContext}

用户问题: {question}

这样处理后,模型胡编乱造的情况明显减少。核心原因也好理解:大模型本质上是一个“接龙游戏”,你给它的上下文越明确、边界越清晰,它自由发挥的空间就越小。提示词不是写作文,是在划定模型的思考范围。这个理解对我后面写各种AI功能帮助很大。

3.2 流式输出的体验优化技巧

流式输出做完之后,页面能实时显示文字了,但体验还是有点问题。我遇到的第一个问题是,文字更新频率太快,页面肉眼可见地发卡。查了一下才知道,每读到一小段数据就setState,React会频繁调度更新,尤其当内容越来越长,渲染成本会持续升高。

我把更新策略改成了“节流更新”:用一个定时器,每100到150毫秒才把最新内容批量写入state。这样既不影响文字展示的流畅度,也大幅减少了渲染次数。另一个小技巧是让已渲染内容尽量少变化,比如AI消息的内容用一个稳定的ref来存累计文本,只在定时器触发时更新一次UI中的文本。

还有一个细节是“停止生成”功能。用户点击停止后,需要真正中断请求,而不是只在UI上把按钮变个颜色。我这里用AbortController实现了中断逻辑:

const controller = new AbortController(); // 发起请求时传入 signal fetch('/api/chat', { signal: controller.signal }); // 点击“停止生成”时调用 controller.abort();

中断之后,要把当前消息的status置为done,并把已经生成的部分内容保留下来。很多AI产品都有这个逻辑,看起来是个小功能,但对用户体感影响非常大。不加的话,用户在等待过程中只能干瞪眼,想停又停不下来,体验会打折扣。

3.3 AI Agent与skills:前端可以提前布局的方向

今天研究AI Agent相关概念时,我越发觉得,前端在这个领域能做的事情一点都不少。AI Agent说白了,就是让大模型不仅能“回答问题”,还能“调用工具、完成动作”。比如用户说“帮我查一下本周的排期并整理成表格”,Agent会拆解任务、查询数据、生成表格甚至直接更新页面。

这里跟前端最贴近的一个概念就是skills。AI编程工具里的skills,简单理解就是预先定义好的角色能力和工作规范。我在项目里试着给AI编程助手定义了一个前端开发skill,内容包括:优先使用最新稳定版的React、组件样式统一用TailwindCSS、接口调用统一走封装好的请求方法。定义之后,AI生成的代码风格稳定多了,至少不会再出现一个项目里混着三种组件写法的场面。

我给自己的计划是,后面几天继续研究怎么把AI Agent接入到前端项目里。对前端来说,最现实的应用方向有两个:一是把对话式交互做得更智能,让AI能操作页面元素;二是做“AI助手面板”,让它在网页上帮用户完成填写、查询、总结这类操作。这些场景里,前端不只是UI层,而是整个AI能力的交互入口。前端同学如果现在开始积累这些能力,后面会很有竞争力。

4. 学习路线与工具清单:给想转AI的前端一份可执行清单

4.1 我整理的前端+AI最小学习路径

这几天我查了不少资料,也问了几位已经在做AI应用开发的朋友,结合自己的实践,整理出一条对前端比较友好的学习路径,今天写出来供参考。这条路径我分成四个阶段,每个阶段都有明确的目标和产出,不会让人越学越迷茫。

第一阶段,先把大模型API用起来,建立最小闭环。目标不是理解模型原理,而是能写一个网页,成功调用大模型接口并得到回复。这个阶段只需要会发HTTP请求、会处理JSON就够了,一般一两天能跑通。

第二阶段,掌握流式响应和对话上下文管理。目标是做一个能连续对话的聊天界面,把输入框、消息列表、加载状态、发送/停止按钮这些交互细节都打磨好。这一步是前端区别于纯后端同学的核心优势所在,也是后面做AI应用的基本功。

第三阶段,学习RAG的基本思路,也就是检索增强生成。理解如何把本地资料切成片段、存进向量库或做关键词索引,然后在用户提问时把相关资料检索出来拼进提示词。这个阶段不用深入算法细节,重点是理解数据从哪里来、怎么跟提示词结合。

第四阶段,再研究AI Agent和工具调用。试着让模型根据用户意图调用不同的函数或接口,比如查询天气、创建任务、生成报表。这一步触及到“AI替用户干活”的本质,也是目前行业需求增长最快的方向。

4.2 工具与资源清单

工具方面,我目前用的几样东西都是经过实际验证的:前端框架用React + Vite,样式层用TailwindCSS配合组件库,AI编程辅助用Cursor和GitHub Copilot,大模型API先用的在线服务,后面有需要再接本地模型。调试方面,Chrome DevTools还是主力,特别是Network面板看请求耗时和流式数据的返回情况。

AI编程插件的选择上,我个人偏爱能用自然语言描述需求、并且能同时读取项目上下文的那种。用的时候建议养成一个习惯:先让AI阅读项目里的关键文件,比如package.json、路由配置、组件目录结构,再让它改代码或写代码。这样生成的代码往往更贴合工程现状,而不是凭空造一套。

资料这块,我推荐几个方向:官方文档优先,大模型厂商的API文档和示例代码是最准确的;其次是开源项目,GitHub上有很多“chatgpt clone”或“ai-chat-ui”之类的项目,把前端、后端、部署全链路都跑一遍,比自己闭门造车快得多。此外,日常开发时多留意那些用了AI能力的网站或产品,拆解它们的交互设计,再对照自己实现一遍,收获很大。

5. 今日踩坑记录与排查实录

5.1 三个典型问题与解决过程

第一个坑就是前面提到的axios流式返回乱码。我最初通过axios的onDownloadProgress去读取数据,结果发现拿到的响应体是乱码,尤其是中文内容,几乎没法用。查了半天,发现axios在处理流式响应时,对分段数据的解码逻辑不太透明,很容易遇到编码问题。后来换用fetch的ReadableStream手动解码,问题才解决。这个经验放在这里,给后面做类似需求的人提个醒:流式场景优先用fetch,不要一上来就套axios。

第二个坑是Node服务端在转发流式数据时,默认的响应头少了一项。前端一直拿不到数据,排查了很久才发现是Content-Type设置成了application/json导致的。改成text/event-stream之后,数据就能正常到达了。其实这里的本质问题是,流式响应的Content-Type需要按照流式协议来设置,不然浏览器端拿到的响应体不会按预期作为可读取的流来处理。

第三个坑是长内容场景下的串字问题。到聊天记录超过二十轮的时候,我发现AI偶尔会把上一轮的内容和这一轮的混在一起,排查后发现是服务端拼接历史消息时,没有把每一轮的完整消息体都带上,而是只带了部分内容。这种问题在调试阶段很难发现,因为短对话时上下文短,模型不会出问题;一旦上下文变长,拼接不完整的消息就会被模型当成“正常输入”去理解,答案就会跑偏。

5.2 问题排查速查表与避坑技巧

今天的问题不少,但好在都定位到了原因。我把几个常见问题整理成一张速查表,方便以后遇到类似情况快速对照:

现象可能原因解决思路
页面一直不显示AI回复响应头Content-Type错误确认流式接口使用text/event-stream
中文内容乱码解码未按流式处理fetch + TextDecoder,并带上{stream: true}
文字滚动卡顿React频繁setState使用节流,控制UI更新频率
停止按钮点了没用请求未真正中断使用AbortController并传入fetch signal
上下文长了回答跑偏历史消息拼接不完整审查服务端拼接逻辑,带上完整消息体

处理AI接口时还有几个小技巧,我也总结一下:第一,所有AI请求都要设超时。大模型接口的响应时间波动很大,没有超时机制的话,页面可能一直转圈。第二,要区分“网络失败”和“模型返回异常”,最好把错误类型分开处理,至少用户看到的提示信息要友好,不能直接把一堆技术报错拍在用户脸上。第三,接口返回的数据在做类型定义时,不要只写一个any,尤其是流式数据,要针对状态字段做联合类型,后面维护起来会省很多事。

最后再分享一条这几天最深的感受:学前端+AI,最重要的是先跑通一个端到端的小项目,再回过头补理论,效率和动力都会高很多。今天这个项目虽然还比较粗糙,但当我第一次看到页面上一个字一个字蹦出AI回答的时候,还是有点兴奋的,因为我知道这条路走对了。明天打算继续把AI Agent的编排逻辑再啃一啃,到时候有新发现再写一篇笔记出来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询