ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

前端转型AI应用开发:用Next.js+LangChain.js从0搭建

前端转型AI应用开发:用Next.js+LangChain.js从0搭建 最近一年多隔三差五就有做前端的同学跑过来问我现在到处都在说AI应用开发自己每天还在写表格、表单、弹窗还能不能转型我的回答一直都很明确能而且前端转AI应用开发大概率比你想象中顺畅得多。我在一线写了七八年React最近大半年基本都在用Next.js和LangChain.js做AI项目从最开始的聊天机器人到现在落地生产的Agent和文档问答系统越做越觉得之前天天写CRUD练出来的那套工程能力在AI赛道上反而成了很多人没有的护城河。这篇文章我想把这大半年走过的路完整拆开讲一遍为什么选Next.js和LangChain.js、项目骨架怎么搭、流式输出怎么做、RAG和Agent怎么接以及我实际踩过的那些坑。适合写过React、愿意碰一下Node服务端的前端同学跟着文章走一遍你就能从0搭出一个能上线演示的AI应用而不是停留在“AI好厉害”的围观状态。1. 先把方向想明白前端转型AI赛道的底层逻辑1.1 天天写CRUD练出来的都是AI应用需要的底层能力很多前端一听到“AI应用开发”下意识觉得要懂机器学习、懂Transformer、懂微调然后就开始自我否定。我一开始也这么想直到实际动手才发现市面上绝大多数AI应用岗位要的并不是训练模型的人而是把大模型能力集成进业务系统的人这类岗位通常叫AI应用工程师。这个岗位要求的核心技能是对接模型API、设计Prompt、管理上下文、做检索增强、处理流式输出、设计Agent工具、评估效果。你仔细品一下这些能力跟CRUD背后的数据库设计、接口设计、状态管理、性能优化、异常处理本质上是同一套工程思维。唯一的区别是CRUD的输入输出是确定性的AI应用的输入输出是概率性的。你过去加班改需求时积累的边界情况处理能力、对异步流程的掌控力恰恰是AI项目里最稀缺的地基。1.2 AI应用开发和传统业务开发到底差在哪我先把这两类项目的差异列个表看完你就能理解为什么前端转身并不突兀维度传统CRUD业务AI应用核心输入表单、列表、操作按钮用户的自然语言核心逻辑确定性的增删改查不确定的生成、推理、工具调用输出形态固定字段和状态码流式文本、结构化数据、富文本难点工程高并发、缓存、事务Token成本、上下文窗口、幻觉控制调试方式断点、日志提示词追踪、链路可观测性上线关注稳定性、容灾延迟、效果分、按量计费当然AI应用最后还是要落在具体业务上所以两者不是替代关系而是叠加关系。前端真正要补的不是重新学一套算法而是理解大模型的行为边界以及怎么用工程手段把不可控的生成结果驯化到可控。1.3 选型Next.js LangChain.js 为什么值得既然目标是低成本冲进AI赛道选型只考虑一件事什么组合能让你用最少的新知识最快跑通一个完整应用。Python加LangChain当然成熟但前端转型的人引入Python等于同时学一门新语言加一套服务端基建成本翻倍。Next.js能在一个项目里同时搞定前端页面、API路由、服务端渲染、环境变量管理、部署前端完全不用重学另一套部署方式。LangChain.js则是AI工程链路的乐高积木把模型调用、Prompt模板、工具调用、检索流程都封装成JS模块写起来的感觉和写异步代码差不多不会有“这是另一个世界的东西”的割裂感。我自己带过几个前端同事跑这套组合有一定Node基础的人一周左右就能完成“页面接口大模型”的最小闭环。提示LangChain.js虽然是LangChain官方推出的JavaScript版本但它在API设计上针对JS生态做了很多调整不能简单把Python代码翻译成JS来用。建议直接看JS官方文档而不是拿Python经验硬套。2. 从零搭项目一个最小可运行的AI对话应用2.1 初始化Next.js项目并安装依赖动手第一步把项目先拉起来。我写作本文时Next.js的稳定版本已经到15.xApp Router是默认范式直接按下面命令初始化npx create-next-applatest ai-frontend-demo # 按提示选择TypeScript、Tailwind CSS、ESLint等建议全选Yes初始化和安装依赖其实是两个容易出问题的环节这里补一句经验npx如果卡住大概率是网络问题可以先把npm registry切到国内镜像。项目创建好之后进入目录安装LangChain相关依赖npm install langchain langchain/openai langchain/core注意新版LangChain.js把很多模型实现拆到了独立包所以langchain/openai是必须装的不能只装langchain。我自己就遇到过只装langchain结果怎么import都报错的情况后来才发现子包没装。2.2 选择模型服务先让请求能通接下来要选一个模型服务。我的建议是国内开发者直接用可直连的、兼容OpenAI协议的服务商比如DeepSeek、智谱、Kimi这些都是不错的选择。这里我用DeepSeek举例因为它API便宜、响应快、兼容性做得也稳。注册之后拿到API Key放到项目根目录的.env.local里DEEPSEEK_API_KEYsk-你的key关于.env.local有个最容易踩的坑这个文件名不能拼错写成.env或者.local.env都不会被Next.js自动加载。而且改完环境变量必须重启开发服务器才能生效不然会一直报key不存在。2.3 写第一个对话接口在App Router模式下创建一个API路由路径是src/app/api/chat/route.ts。这个文件导出POST函数前端只要向/api/chat发请求就能拿到模型回复import { NextRequest } from next/server; import { ChatOpenAI } from langchain/openai; const model new ChatOpenAI({ model: deepseek-chat, apiKey: process.env.DEEPSEEK_API_KEY, configuration: { baseURL: https://api.deepseek.com/v1, }, }); export async function POST(req: NextRequest) { const { messages } await req.json(); const history messages.map((m: { role: string; content: string }) m.role user ? [human, m.content] : [ai, m.content] ); const res await model.invoke(history); return Response.json({ content: res.content }); }这段代码做的事很直白把前端传来的对话记录转成LangChain的消息格式调用模型把结果返回前端。注意我用的是model.invoke它返回一个完整结果适合验证链路是否通。等需求变成打字机效果时再换成流式接口。2.4 前端聊天框让对话转起来前端在页面组件里维护一个messages数组用useState管理调用接口后把AI回复推入数组请求期间加loading状态避免重复提交。如果你之前写过axios请求这里的理解成本基本为零。我建议第一版前端先不要加花里胡哨的样式把核心交互跑通就够。等整个链路稳定后再补消息气泡、Markdown渲染、复制按钮这些体验细节。一个最简对话页面从初始化到跑通快的人半小时就够慢的人最多半天。注意model.invoke(history)里传的是一个二维数组。LangChain.js会把这种“human/ai”交替数组自动转成内部消息对象。直接使用new HumanMessage(...)也可以但前端传过来的JSON转二维数组更省事少引两个类。3. 关键升级从“等十秒”到流式输出3.1 为什么必须做流式如果模型回答要等十秒钟才整段弹出来产品基本没法用。现在主流AI应用全部是打字机效果原理上用到了服务端事件流SSE服务端把模型生成的文本一块一块写入响应前端通过ReadableStream逐块读取。Next.js的Route Handler原生支持ReadableStream所以这事在Next.js里特别顺。SSE不是WebSocket它是单向的从服务端推给客户端基于普通HTTP连接实现成本低很多。做AI对话场景完全够用不需要为了聊天去引入WebSocket那套复杂度。3.2 服务端改造把invoke换成stream服务端改动非常小把invoke换成stream即可同时引入Vercel的ai包来包装流import { StreamingTextResponse } from ai; const stream await model.stream(history); return new StreamingTextResponse(stream);这里用到的ai包是Vercel的AI SDK它能把LangChain的流对象转成浏览器友好的SSE格式。在Next.js的Route Handler里直接返回StreamingTextResponse就行剩余的事情框架都处理好了。3.3 前端接收流式数据前端如果用原生fetch可以配合ReadableStream手动解析const res await fetch(/api/chat, { method: POST, body: JSON.stringify({ messages }), }); const reader res.body!.getReader(); const decoder new TextDecoder(); while (true) { const { done, value } await reader.read(); if (done) break; const text decoder.decode(value); // 把text累积追加到当前消息中 }踩过的坑这里提醒一句res.body在部分浏览器或中间网关下可能不是标准流生产环境如果接了CDN或自建网关要确认网关支持SSE别把响应缓冲掉了。我之前一个项目就是Nginx没关缓冲结果流式数据全部积压到一次性返回前端看到的“打字机效果”变成“等待10秒后整段刷出”排查了整整一下午。3.4 流式输出时的错误处理流式输出的错误处理跟普通请求不一样因为响应头已经发出去了不能再返回一个错误状态码。处理思路是服务端把异常信息作为一段特殊标记写进流里前端读取时识别并提示用户。我的做法是包一层try { const stream await model.stream(history); return new StreamingTextResponse(stream); } catch (e) { return new StreamingTextResponse( new ReadableStream({ start(controller) { controller.enqueue([ERROR] (e as Error).message); controller.close(); }, }) ); }前端在累积文本时发现[ERROR]前缀就停止追加并展示错误提示。这样比让前端猜HTTP状态码可靠得多因为流式请求一旦开始HTTP状态码基本都是200。4. 接入工具和Agent让AI真的能办事4.1 “只会聊”不叫落地对话接口跑通之后很多前端会卡在下一步我希望AI能查数据库、能调公司内部接口、能直接改数据怎么办这就是工具调用Tool Calling发挥作用的场景。工具调用的核心思想值得反复咀嚼模型在回答你的问题之前可以先判断自己需要哪个工具然后返回一个结构化的tool_calls指令包含工具名称和参数但模型自己不执行。真正执行的是你的代码执行结果再次回传给模型模型看到结果后继续生成最终回答。整个循环大概是这样的模型想调工具你执行结果回填模型继续输出。先不用管Agent的底层原理你只需要理解它本质上就是一个“判断、执行、回填”的循环。4.2 在LangChain.js里封装一个工具举例给AI加一个“查用户订单”的工具。先用zod定义入参结构再写执行函数import { z } from zod; import { tool } from langchain/core/tools; const getOrdersTool tool(async ({ userId }) { const orders await db.orders.findMany({ where: { userId }, take: 5, orderBy: { createdAt: desc }, }); return JSON.stringify(orders); }, { name: get_user_orders, description: 根据用户ID查询最近的订单列表, schema: z.object({ userId: z.string().describe(用户的ID), }), });这里有一个非常容易被忽视的细节工具的description写得越具体模型判断“什么时候用这个工具”就越准。很多人随手写一句“查订单”实际效果会时灵时不灵。我建议描述里写清楚触发场景、参数含义、以及返回数据形态比如“当用户询问订单状态、物流、最近购买记录时使用”。4.3 通过AgentExecutor让模型自己决定调用在LangChain.js里可以通过createToolCallingAgent绑定工具然后用AgentExecutor来执行import { AgentExecutor, createToolCallingAgent } from langchain/agents; import { ChatPromptTemplate } from langchain/core/prompts; const prompt ChatPromptTemplate.fromMessages([ [system, 你是一个订单助手需要时使用工具获取真实数据], [human, {input}], ]); const agent createToolCallingAgent({ llm: model, tools: [getOrdersTool], prompt, }); const executor new AgentExecutor({ agent, tools: [getOrdersTool], }); const res await executor.invoke({ input: 查一下用户abc123的最近订单, });AgentExecutor会替你完成“模型判断需要工具调用工具把结果回填给模型模型生成最终回答”的循环。前端同学看这段一定亲切本质上它就是一组异步调用加上一个循环并不需要理解什么深奥算法。4.4 工具调用在Next.js里的位置工具执行逻辑必须放在服务端不能放浏览器。原因有两个工具里大概率要访问密钥或数据库连接放前端等于裸奔放着让用户直接触发安全边界就没了。用Next.js实现时就是把这些逻辑整体写进route.ts前端依然只发一个POST请求。工具调用会给接口增加几次模型往返如果工具本身执行慢整体延迟能拉到十几秒。这时候有两个优化方向一是给工具查询加缓存比如相同参数的查询结果缓存5分钟二是利用流式输出先把“正在调用工具”的阶段提示推给前端让用户知道系统在工作而不是傻等。经验第一次做Agent项目不要一上来绑十几个工具。先绑2-3个跑通后再逐步加。工具多了以后模型选错工具的概率会指数上升调试成本也跟着陡增。真实项目里工具数量从5个升到10个效果稳定性明显下降需要额外做很多约束才能拉回来。5. 接入外部知识RAG实战让AI回答你的文档5.1 RAG是什么用前端理解的方式RAG检索增强生成说起来挺唬人拆开就是“先搜后答”。拿前端概念打比方传统LLM就像一个只带默认数据的组件所有信息都压在模型参数里RAG则是在问答之前先去公司的文档表里做一次检索把相关的几段内容取出来连同问题一起塞给模型让模型“看着资料回答”。它的核心价值不是让模型变聪明而是让模型能回答“不在训练数据里”的内容比如你公司的内部规章制度、最近上线的产品说明、私有技术文档。这也是目前AI应用落地最广泛的方向之一企业内部知识库问答、客服辅助、运维排查本质都是RAG。5.2 RAG链路需要哪几步标准链路是五步文档加载读取PDF、Markdown、Word等格式把内容提取成纯文本文本切分把长文档切成固定大小的块块与块之间保留少量重叠向量化用Embedding模型把每段文本变成一串数字向量存储把向量和原文本存进向量数据库检索与问答根据用户问题做向量相似度检索找到最相关的几个块拼进Prompt用前端思维理解向量化相当于给每个文档块打一个“语义指纹”搜索引擎就是在海量指纹里做最近邻匹配找到语义上跟问题最贴近的几个文本块。跟传统关键词搜索的区别在于它不需要关键词精准命中“iPhone怎么修”也能匹配到“苹果手机故障处理”的文档。5.3 在Next.js工程里落地一个文档问答如果是本地演示或中小型项目可以先用MemoryVectorStore顶一阵子不需要一上来就引入Pinecone或pgvector。处理上传文档的接口大概长这样import { TextLoader } from langchain/document_loaders/fs/text; import { RecursiveCharacterTextSplitter } from langchain/text_splitter; import { MemoryVectorStore } from langchain/vectorstores/memory; import { ChatOpenAI, OpenAIEmbeddings } from langchain/openai; const embeddings new OpenAIEmbeddings({ apiKey: process.env.DEEPSEEK_API_KEY, configuration: { baseURL: https://api.deepseek.com/v1 }, }); export async function POST(req: NextRequest) { const file await req.formData().then((fd) fd.get(file) as File); const text await file.text(); const splitter new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 50, }); const docs await splitter.createDocuments([text]); const vectorStore await MemoryVectorStore.fromDocuments(docs, embeddings); // 实际项目需要把store持久化演示时先放在内存 return Response.json({ docCount: docs.length }); }回答问题的接口则先检索再作答const relevant await vectorStore.similaritySearch(question, 4); const context relevant.map((doc) doc.pageContent).join(\n\n); const prompt 请基于以下资料回答问题\n\n${context}\n\n问题${question}; const res await model.invoke(prompt);整体逻辑很直白但有两件事要注意。一是切分大小会影响检索效果默认500字对大多数说明书、接口文档够用但如果内容偏向合同或技术标准需要调小到200-300字因为这类文档的语义粒度更细。二是在生产环境中MemoryVectorStore服务重启就会丢数据如果要做长期可用的产品建议换成真实的向量数据库。5.4 前端如何把RAG体验做好RAG产品的体验瓶颈通常在“用户不知道AI答得对不对”。这一块前端其实大有可为把检索到的引用来源展示在回答下方上传文档时给进度提示允许用户点“重新生成”在回答里高亮引用片段。这些能力恰好是前端的主场。我做过的知识库问答项目用户满意度最高的小功能不是模型调参而是点击引用直接跳转到原文档位置。这提醒我一个很重要的点AI产品里的体验设计最终拼的还是前端功底。模型能力再强用户感知不到也是白搭。6. 第三方依赖与版本踩坑真实项目的现状6.1 LangChain.js版本更新太快API天天变这应该是现阶段做LangChain.js最让人头疼的部分。从0.x升到1.x/2.x期间很多API被移到独立子包类名和导入路径都有调整。比如旧版本的import { OpenAI } from langchain/llms/openai新版本通常要改成import { ChatOpenAI } from langchain/openai。我的建议是项目里所有langchain相关依赖一定要锁定精确版本号不要用^或者latest。出现API不存在的情况不要凭记忆乱改直接去对应版本的官方文档查用法。不要随便升级大版本除非你对API变化做了完整评估。6.2 Next.js和LangChain的兼容性Next.js App Router的route.ts跑在Serverless环境默认执行时间有限。如果你用自托管方式部署必须注意Node.js版本太老的Node版本跑不了最新的Next.js。建议在next.config.js里配置module.exports { output: standalone, experimental: { serverComponentsExternalPackages: [langchain, langchain/openai], }, };output: standalone是给Docker部署用的能让镜像体积小很多。serverComponentsExternalPackages把LangChain相关包标记为外部依赖否则打包时经常报一些奇怪的动态导入错误。另外写AI接口时要考虑是否要加export const maxDuration 60放宽执行限制不然Agent场景几十秒的耗时很容易在部署后报504。6.3 环境变量和密钥管理API Key一定放在服务端只用NEXT_PUBLIC_开头的变量会暴露到浏览器这点怎么强调都不为过。我建议所有模型密钥统一放在.env.local并且坚持不在任何日志里打印请求地址和Key。一旦泄露模型服务商一般都有盗刷风险轻则损失几十块重则账号被封。前端页面如果需要展示某个模型名称或开关配置可以在服务端代理读取环境变量再传给组件而不是直接把Key暴露出去。6.4 Token成本监控AI项目的成本大头在Token。上线前一定做好三件事给对话长度设上限给单次请求加maxTokens对Agent工具的返回结果做裁剪。我自己就遇到过一次线上知识库一天烧掉几十块的案例原因是用户反复问同一类问题时上下文越积越长每轮请求的Token数都在膨胀。一个简单有效的策略是给会话设置滑动窗口只保留最近几轮消息。另一个策略是设置每日预算告警大多数模型服务商都提供用量统计和阈值告警上线前一定要配置好。7. 前端转型AI的学习路径建议和面试要点7.1 别一上来啃算法先把应用串起来前端转AI最大的误区是一上来刷Transformer论文、看机器学习课程。那套东西对AI应用开发来说性价比极低而且容易挫败信心。更好的路径是先用现成API做一个聊天应用再逐步加流式、加工具、加RAG每一步都做一个可演示的小项目。当你能够独立把一个“能回答公司文档内容、会调用订单接口、还能流式输出”的Agent做出来你在这个赛道上的竞争力已经超过大多数只停留在“调API”层面的应聘者。关键不是你知道多少概念而是你亲手跑通了多少个链路。7.2 AI应用岗面试常见考察点结合近两年看到的岗位要求前端转AI应用开发面试中常被问到的有几类问题怎么设计Prompt让模型稳定输出JSON流式输出怎么处理断线重连和错误恢复上下文管理怎么做才能避免对话越长效果越差RAG里chunk大小怎么选检索到的内容不相关怎么办Agent工具选择错误怎么兜底超时了怎么办怎么评估一个AI应用的效果怎么主动收集badcase这些问题大部分都可以在实际项目里找到答案。如果你在项目简历里写清楚“为了解决工具选择错误我用约束字段和结果校验兜底”面试官大概率会眼前一亮。7.3 给还在犹豫的人一个最低成本的实验建议如果你现在还在公司写业务代码又没有完整的AI项目机会可以找一个内部场景做“缝合怪”比如在内部后台里加一个“AI解析报错日志”的入口用Next.js写一个独立小服务接入模型API每天晚上把当天报错拉出来让AI总结归类。这类项目不需要很长时间不涉及敏感数据的话审批也容易通过但能让你把完整链路走一遍还能直接在团队里产生实际价值。很多公司对新技术试点其实是欢迎的前提是你给的是“解决具体问题”的方案而不是“我想学AI”的理由。最后说点掏心窝的话。我翻自己一年前刚接触LangChain.js时的笔记最大的感慨是前端转AI最难的不是技术而是心态。总觉得自己算法不熟、数学不好、模型原理说不清楚就不敢往这个方向投简历。但实际上AI应用开发目前最稀缺的是能把模型能力和真实业务接起来的人而把复杂状态、异步流、边界情况理清楚恰好是前端每天都在做的事情。如果你现在还在写CRUD别急着否定自己先花一个周末把文章里的最小对话项目搭起来再花两周把流式、工具、RAG都串上去。等你亲手做出一个能跑的东西你会发现所谓AI高薪赛道远没有想象中那么高不可攀。最后再分享一个小技巧做AI应用时永远不要问模型“你能做什么”而是先问自己“你希望它做什么”。把需求拆成输入、输出、边界条件然后再开始写代码。这个习惯比任何框架都管用。
返回列表