最近在做 DocFlow,一个融合 Agent、RAG 和知识图谱的全栈实战项目,实测支持百人实时协同,支持 Word(DOCX)、PDF 导入,Agent 能检索资料、改写文档、操作表格,背后实现了 主 Agent 统筹、子 Agent 并行查证与结果复核,以及上下文自动压缩、长短期记忆和任务恢复,RAG 则涵盖 父子分块、BM25+向量混合检索、RRF 融合、重排与原文引用溯源,适合想深入源码、动手做 AI 应用的同学
同时整理了 80 篇 Agent 开发、72 篇 RAG、70 篇知识图谱文章,共 222 篇学习资料,从基础原理到工程实践,可以结合源码边学边做,感兴趣可以加我微信 yunmz777,了解项目与配套资料,一起交流 AI 开发。
最近在网上看到一些关于前端的讨论。有人说,部门已经没有纯前端了,页面、接口、部署都得一起做。有人感叹,明明投的是前端岗位,招聘要求里却写着全栈、AI 应用和 Agent 开发。
再看看 Agent 生成页面、修改组件、对接接口的表现,确实很容易焦虑:前端是不是快没了?以前学的 React、CSS、状态管理和工程化,还有必要继续研究吗?
我不觉得前端已经死了,但我们的学习方式确实可以调整。
我们不必把每个包的调用方式、每个 API 的参数和每段代码的实现都记在脑子里。更值得掌握的,是这些技术能解决什么问题、适合什么场景,以及怎样判断 Agent 有没有用对。
比如,我们不需要闭眼写出 Zustand 的初始化代码,但应该知道它适合管理哪些状态。也不必背下 Redux Toolkit 的每个 API,却需要理解项目为什么采用它,新增功能是否应该沿用现有方案。
具体代码可以让 Agent 查文档、参考项目实现。我们理解技术,才能给它更合适的需求和约束,也能在它提出方案时看懂其中的取舍。
所以,值得讨论的不是以后还要不要学前端,而是:当实现工作更多地交给 Agent,我们应该把时间花在哪些知识上,才能更好地完成一个功能?
岗位在扩大交付范围,前端能力仍然用得上
从整理过的公开招聘要求来看,前端基础并没有被 AI 技能整体替换。
组件开发岗位仍然关注 TypeScript、React、组件库、持续集成和页面响应速度。有的 AI 原生产品岗位,则把 React 渲染性能与模型交互放在同一组要求中。这些公开职位描述说明,开发 AI 产品同样需要处理前端工程问题。
部分岗位确实在扩大职责范围。Agent 产品相关岗位开始涉及流式输出、动态组件渲染和前后端协作。桌面与 AI 前端岗位中,也能看到 Electron 架构、多任务、工具调用和跨端交互。
这些样本不能代表整个市场,更不能把所有要求合并成每位前端都必须掌握的清单。但它们提醒我们,学习不能只停留在组件怎么写、接口怎么调,还要理解一个功能怎样从需求走到交付。
这里需要分清两条方向。
使用 Agent 开发产品,主要解决开发效率和交付质量的问题。开发 Agent 产品,则需要进一步处理任务执行、工具调用、人工确认和结果展示。
我们可以同时发展这两类能力,但不必因为开始使用 Coding Agent,就立即把模型训练、底层推理和所有 Agent 框架都加入学习计划。
先让已有知识能够支持技术选择,再沿着项目需要扩展,比不断重学一整套技术栈更有价值。
学一个技术,先弄清楚它能帮我们做什么
过去学习一个库,我们很容易从安装、初始化、API 列表一路看下去,最后用能不能独立默写示例判断自己是否掌握。
现在可以换一种顺序。
我会先看它解决哪类问题、项目里有没有等价方案、引入以后可以省掉哪些工作,以及需要接受什么限制。遇到具体需求时,再让 Agent 根据文档和项目版本完成实现。
例如,面对一个越来越卡的长列表,我们不必提前背熟某个虚拟列表组件的全部参数,但应该知道列表虚拟化是一种可考虑的方案,也知道动态高度、滚动定位和内容搜索需要一起验证。
面对频繁重复请求,也不必马上手写一套缓存。我们应该想到,项目可能已经有请求缓存工具,先让 Agent 检查现有能力,再判断是调整配置还是补充实现。
技术知识的作用,是让我们在遇到问题时知道有哪些办法可选,而不是要求所有办法都能脱离文档手写出来。
具体语法用得多了自然会熟悉,但无需把背下来当作学习目标。涉及不熟悉的细节,也可以让 Agent 协助查证。我们需要逐渐建立的是对用途、场景和限制的理解。
状态管理先分清数据,再决定使用什么工具
状态管理最能说明这种学习方式的区别。
我们让 Agent 开发一个工作台,它可能很快就创建出全局 Store,把当前会话、弹窗开关、接口结果、任务进度和表单输入全部放进去。
代码能够运行,却不代表这些数据就应该由同一个地方维护。
React 的状态结构建议强调避免矛盾、冗余和重复状态。落实到项目里,我会先判断数据由谁维护、哪些区域需要读取、生命周期有多长,再决定使用什么方案。
| 状态类型 | 常见例子 | 我会优先考虑的管理方式 |
|---|---|---|
| 组件内部状态 | 弹窗开关、输入内容、当前展开项 | 留在组件或功能内部,使用 useState 或 useReducer |
| 跨组件的客户端状态 | 工作台布局、多个区域共享的选择状态 | 根据项目架构考虑 Zustand、Redux Toolkit 等方案 |
| 需要分享和恢复的页面状态 | 搜索条件、分页、当前资源标识 | 适合表达在 URL 中的部分,以 URL 为准 |
| 服务端数据的本地缓存 | 用户资料、文档列表、任务查询结果 | 考虑 TanStack Query 或 RTK Query 等方案 |
| 持久化的任务事实 | 工具执行结果、审批是否有效、任务是否结束 | 由服务端或独立任务运行时维护,前端同步和展示 |
这些工具并不是互相替代的,也不意味着每个项目都应该全部安装。
Zustand提供 Store、状态更新和选择性订阅等能力。工作台需要在多个区域共享客户端状态,又希望保持较少的约定时,可以考虑它。
项目已经围绕 action、reducer、middleware 建立了协作方式,多个模块也依赖这些约定时,我通常会优先沿用 Redux。讨论现在的 Redux 实践,也应该以官方推荐的 Redux Toolkit为基础,而不是只记得早期大量手写样板代码的印象。
这不是小项目用 Zustand、大项目用 Redux 的固定公式。现有代码、团队约定、状态关系和维护成本,都应该进入选择过程。
接口数据还有另一组问题:结果什么时候过期、什么时候重新获取、修改后怎样更新缓存。TanStack Query和 RTK Query提供了相应能力,使用以后,就没必要不加区分地把同一份结果再复制到另一个 Store。
本地编辑草稿可以单独保存,但要明确什么时候初始化、什么时候提交、什么时候丢弃,避免两份数据都被当作最新结果。
我们不需要记住这些库的每一种写法,但应该能告诉 Agent:弹窗状态留在组件内部,工作台布局沿用现有 Store,文档列表交给查询缓存,任务是否完成以服务端为准。
懂得这些区别,才能让 Agent 按项目需要组织状态,而不是只得到一套看起来完整的代码。
JavaScript 和 TypeScript,重点是理解行为与约束
JavaScript 仍然值得学,但没有必要把大量时间花在冷门语法题和实现背诵上。
更有用的是理解异步、引用、作用域和错误传播怎样影响功能。
例如,用户先搜索 A,随后搜索 B。B 的请求先返回,页面展示了正确结果,之后 A 的请求才结束,又把页面覆盖成旧内容。
我们不一定要现场默写解决代码,但应该识别出这是异步结果与当前操作不匹配的问题。接下来就可以要求 Agent 检查请求取消、过期响应过滤和结果写入条件,而不是继续调整防抖时间。
AbortController可以用于取消请求及相关读取操作,但它不能替代应用对结果归属的判断。我们至少需要知道,减少请求、取消读取和阻止旧结果覆盖,是几个不同的问题。
闭包也是一样。理解闭包保留对词法环境的访问,是为了在回调、定时器和异步逻辑出现旧数据时,知道应该往哪个方向检查,不是为了反复背诵定义。
TypeScript 则可以帮助我们表达业务约束。
等待审批的任务应该携带审批信息,成功任务才允许读取最终结果,失败任务需要提供可处理的错误。我们知道可辨识联合、类型收窄和穷尽检查能表达这些差异,就可以让 Agent 按这些约束建模。
外部数据还需要另一层检查。写下 as TaskResult 不会自动验证接口响应或模型输出,因为类型断言不提供运行时检查。
因此,看到 Agent 用类型断言消除报错时,我们应该继续确认:这里是真的已经验证过数据,还是只是让编译器不再提示?
我们需要理解这些机制能保证什么、不能保证什么。具体类型怎么写、取消逻辑怎么实现,可以借助 Agent 和文档完成。
React 要理解状态、渲染和副作用的用途
学习 React,也可以把重点从记忆 Hook 写法,转向理解它们分别解决什么问题。
useState 和 useReducer 怎样帮助组织组件状态,Effect 适合处理哪类外部同步,组件身份怎样影响状态保留,这些知识会直接影响设计。
例如,切换页面后表单内容为什么还保留着?修改 key 后,输入内容为什么突然消失?这些行为与组件在树中的位置和身份有关。React 对状态保留与重置的说明,可以帮助我们判断应该保留、重置还是提升状态。
遇到问题后,让 Agent 随意加一个 key,有时能让现象消失,却也可能把需要保留的状态一起清掉。
Effect 也经常被当作通用补丁。
一个值能够从现有数据直接计算出来,就未必需要额外保存,再通过 Effect 同步。React 官方关于不必要 Effect 的说明,讨论的正是这类问题。
订阅事件、连接外部系统和维护定时器,则需要明确建立与清理的时机。页面离开后仍然收到旧事件,或者反复进入后出现重复消息,就应该检查副作用与清理逻辑。
我们可以让 Agent 完成依赖分析和代码修改,但应该知道,删除依赖项或关闭检查规则,并不自动等于解决问题。
使用 Next.js 时,还需要理解服务端与客户端组件的职责。浏览器交互放在哪里,数据读取由谁承担,哪些内容不应该进入客户端,这些都属于方案判断。
不必背下框架所有限制,但要知道这些区别存在。这样,Agent 提出把整个页面改成客户端组件时,我们才会追问:究竟哪些区域需要交互,有没有必要扩大到整个页面?
HTML、CSS 和交互,决定界面是否真的好用
HTML 和 CSS 同样可以少背写法,多理解它们提供的能力。
布局、滚动、语义结构、焦点和响应式,仍然是前端交互的重要基础。我们不需要记住每个属性的全部取值,却应该知道哪些机制可以解决当前问题。
截图只能说明某个时刻的视觉效果,不能证明连续操作时的行为正确。
一个聊天工作台里,用户正在向上阅读历史内容,新消息到来时是否应该自动滚到底部?侧栏缩窄以后,代码块会不会撑破布局?移动端弹出键盘后,输入框和发送按钮还能不能使用?
这些行为需要提前定义,再让 Agent 按照要求实现。
弹窗也不只是遮罩和居中容器。模态对话框的交互要求还涉及焦点进入、键盘操作和关闭后的焦点恢复。
知道这些要求,我们就会优先让 Agent 复用项目中已经处理过相关行为的组件,而不是每次重新拼出一个只有视觉效果的弹窗。
组件抽象也需要判断。两处界面看起来相似,不一定意味着应该立即合成一个参数很多的公共组件。我们需要看它们的行为是否相同,未来变化是否会一起发生。
每新增一个场景都要添加多个布尔参数时,就值得重新检查抽象方式,而不是继续往公共组件里叠条件。
这些能力不依赖我们记住多少 CSS,却影响我们能否把交互要求说清楚,并发现生成结果中的问题。
文件上传断点续传如何实现的?
这类问题可以检验我们是否理解通信过程,不一定要靠默写一份完整实现来回答。
采用分片上传方案时,可以先创建上传会话,再为分片确定标识、预期长度和必要的校验信息。服务端验证并保存后,才将它记录为已接收。
连接中断以后,客户端查询服务端确认的进度,再上传缺失部分。不能只相信本地进度条,因为请求已经发出,不等于服务端已经完整保存。
最后一个分片可以小于标准分片大小,它是否完整,应依据自身的预期长度和校验结果判断。重复上传同一分片,也需要明确的处理规则,避免重试产生重复结果。
具体用什么 API 切片、怎样调度并发、请求函数怎么封装,都可以让 Agent 实现。我们需要明确的是,什么才算上传成功,失败后根据什么恢复。
这里的区别同样适用于 Agent 的流式交互。
连接断开时,任务可能还在执行。页面重新连接,也不应该默认重新创建任务。原生 EventSource 支持断线重连,SSE 事件格式包含 id 和 retry 等字段,但这些机制并不等于应用已经具备完整的任务恢复能力。
对于需要恢复的界面,我会要求任务具有稳定标识,能够查询当前状态,并补齐指定位置之后的事件。重复事件需要去重,无法补齐历史时重新获取完整状态。
用户点击停止,也要说明是停止接收内容,还是请求取消实际任务。界面不能仅仅因为连接关闭,就把任务显示为已经取消。
知道网络 API 能做什么,还要知道它与业务完成之间隔着哪些步骤。这样才能给 Agent 正确的实现目标。
性能优化先知道哪里慢,再选择工具
性能优化也不需要从背诵优化手段开始。
我们更应该知道有哪些工具可以帮助定位问题,以及不同问题应该交给哪类方案处理。
Chrome DevTools 的 Performance 面板可以记录运行过程,帮助分析主线程活动和渲染开销。遇到页面卡顿,先获取记录,再判断时间主要花在计算、更新还是布局上,比直接让 Agent 加缓存更有针对性。
例如,一个持续输出内容的工作台,可能每次更新都重新解析全部文本,也可能让整个历史列表跟着刷新。
主要开销在文本处理上,就应该检查解析和更新范围。主要开销在列表渲染上,再考虑减少不必要更新或使用虚拟化。不能因为记得某个性能优化 API,就把它放到所有组件里。
Agent 可以帮助分析记录、提出假设和修改实现。我们需要给它可复现的数据、操作路径和评价标准,再检查修改前后的差异。
高频更新采用缓冲和合并以后,要验证最后一段内容有没有丢失。列表虚拟化以后,要验证滚动定位、搜索和键盘操作。减少计算以后,也要确认结果没有过期。
性能能力不在于记住多少优化写法,而在于能把问题定位到合适的方向,并用相同条件比较修改结果。
多个 Agent 同时返回内容,如何展示给用户?
这类需求需要的首先是信息组织和状态设计,而不是知道某个聊天组件的全部属性。
假设一个任务同时在检索资料、分析文件和生成修改建议。所有输出按到达顺序混进同一个文本区域,虽然容易实现,但用户很难判断每段内容属于哪个步骤,也不知道局部失败会不会影响整体结果。
因此,任务、步骤、消息和工具调用需要有明确的归属关系。
默认界面可以展示整体进度、关键动作、待处理事项和最终结果。具体参数、执行日志和错误详情放到可展开区域,优先让用户理解当前正在发生什么。
并行任务还需要明确完成规则。一个步骤完成,不等于整个任务完成。某个步骤等待审批,也不一定要求其他无关步骤全部暂停。这些规则应该由任务设计确定,前端据此展示。
人工确认要落到具体操作上。
用户应该知道系统准备修改哪些文件、覆盖哪些内容,以及确认后会发生什么。批准记录要绑定对应操作,避免用户看到的是旧建议,实际执行的却是另一份内容。
取消也需要区分请求和结果。用户点击取消后,可以先展示正在取消,等执行端确认,再更新最终状态。
对于动态结果,我会优先让模型返回受约束的组件类型和数据,再由前端使用已注册组件渲染,而不是让模型生成的任意代码直接进入执行环境。
这些展示和交互都可以让 Agent 编写,但我们需要先明确含义。否则,界面可能做得很丰富,用户却仍然不知道系统做到了哪一步,自己现在应该做什么。
你是怎么约束 AI 生成代码的规范和风格的?
我们不必提前替 Agent 决定每一处实现,但应该提供足够的项目背景和交付要求。
项目规则适合记录目录结构、模块职责、编码约定、测试命令和特殊注意事项。AGENTS.md提供了一种面向编码 Agent 组织这些信息的方式,具体读取范围和规则优先级需要结合使用的工具确认。
规则不需要写成一份要求 Agent 背诵的百科。当前项目真正有效的约定,比大量泛泛的最佳实践更有用。
例如,项目已经采用 Redux Toolkit,新功能就应该先检查现有状态组织方式。Agent 准备引入另一个 Store 时,可以要求它说明现有方案为什么不够、增加依赖有什么收益,以及后续怎样维护。
我们不必因为熟悉旧方案就拒绝所有变化,但也不能只因为新方案代码更短,就接受额外的维护成本。
任务说明也要具体。
修复会话切换后旧结果覆盖的问题,可以明确允许修改的模块、必须保留的接口、是否允许新增依赖,以及需要通过的验收场景。Agent 可以先检查调用关系,提出方案,再分阶段实现。
与当前任务相关的类型、调用位置、测试和约束需要能够被找到。已经失效的规则、重复说明和互相矛盾的要求,则应该及时清理。
规则表达要求,工具和检查负责落实要求。
不得修改某个目录,可以写在说明里。必须强制执行的限制,还应该通过工具权限、工作区隔离或变更检查落实。不能把一句提醒当作系统已经具备的控制能力。
我们对这些机制的用途有认识,才能决定什么时候补充上下文、什么时候增加检查,以及什么时候需要限制 Agent 的操作范围。
测试和工程化,要能确认交付结果
代码交给 Agent 编写以后,我们需要有办法确认功能是否满足要求。
这并不意味着测试必须全部手写。测试同样可以由 Agent 生成,但测试目标不能只由当前实现反向决定。
状态转换和数据处理可以用较小范围的测试检查,接口与事件结构需要验证契约,用户操作过程则适合端到端测试。知道这些方法分别覆盖什么范围,就能让 Agent 选择合适的验证方式。
Playwright 官方建议围绕用户可见行为编写测试,并保持测试隔离。我们可以据此把验收重点放在业务结果上。
例如,实现人工审批,需要验证拒绝后执行端没有继续调用对应工具,而不是只检查按钮变成了灰色。实现任务恢复,需要模拟中断和重新进入,确认结果没有丢失,也没有重复创建任务。
Agent 编写的测试也可能只是在重复当前代码的行为。错误实现与错误断言互相配合,测试照样可以通过。因此,我们要检查断言是否来自需求,是否覆盖了真正不能出错的条件。
修复失败时,也需要留意它有没有通过删除断言、放宽类型或绕过错误处理,让检查表面通过。
构建与发布则需要一套可重复执行的流程。依赖能否复现安装,类型检查和关键测试是否通过,生产构建是否成功,错误能否对应到具体版本,都应该有确定的检查方式。
这些命令和配置不必全部记住,但我们应该知道项目需要哪些检查,以及哪些检查缺失会让问题进入交付环节。
涉及长任务时,还应该让错误能够关联到任务和步骤记录。已经发生的文件覆盖、消息发送等外部操作,也要单独考虑后续处理,不能认为回滚代码就能恢复一切。
我们需要掌握的是这些检查和处理方式的用途,再让 Agent 帮助把它们落实到项目中。
全栈能力沿着功能延伸,不必重新背一套后端
岗位要求延伸到全栈,并不意味着我们需要同时熟练默写几门后端语言。
已经熟悉 JavaScript 和 TypeScript,可以先沿着 Node.js 理解接口、身份认证、数据库、文件处理和异步任务怎样协作。具体框架 API 可以需要时查,先把一个功能涉及的职责弄清楚。
以文档 Agent 为例,前端提交修改请求以后,系统需要知道请求属于谁、操作哪份文档、用户是否有权限,以及任务当前处于什么状态。
生成结果怎样保存,用户确认后怎样应用,发生冲突后怎样处理,这些问题需要有明确安排。我们不一定亲自编写每一层,但应该知道它们分别由哪里负责。
权限尤其不能只停留在界面上。
隐藏按钮可以改善体验,却不能作为最终授权依据。OWASP 的授权建议强调,不能依赖客户端检查决定资源访问权限,授权必须在可信的执行环境中落实。
Agent 工具调用也一样。用户在界面上点击批准,执行端仍然需要校验身份、操作对象、授权范围和批准是否有效。模型认为可以执行,不能替代系统授权。
学习 RAG 时,也应该知道它怎样利用检索结果支持回答、数据怎样进入系统、错误怎样被发现,而不是只记住某个框架的初始化方式。
模型训练、底层推理优化和 GPU 开发,可以等目标岗位或项目明确需要时再深入。对以应用交付为主的前端,先理解各层能提供什么能力,已经能帮助我们更有效地指导 Agent。
选一个复杂领域,把技术知识用起来
只知道越来越多工具的名字,也很难形成具体能力。更好的练习方式,是选择一个熟悉的领域,让这些知识真正参与设计和验证。
例如文档编辑。
Tiptap 使用 Schema 定义文档允许存在的节点、标记和结构。知道这一点,我们就不会把所有文档修改都当作普通字符串替换,也会要求 Agent 优先使用编辑器提供的文档操作能力。
可以围绕一个具体场景做深:用户选中内容让 Agent 修改,生成期间另一位协作者改变了目标段落。系统需要识别变化,让用户审阅建议,再决定应用、重新生成或放弃。
这里会用到文档结构、选区定位、差异展示、协同和撤销恢复。我们不必背下编辑器全部命令,但需要知道有哪些能力可以利用,以及哪些操作必须检查冲突。
桌面 Agent 则会涉及进程职责、工具权限和应用生命周期。Electron 官方安全指南强调上下文隔离、沙箱、IPC 消息来源校验,以及限制向渲染进程暴露的能力。
知道这些机制的用途,才会在 Agent 实现桌面功能时提出合适的要求,而不是为了调用方便,就把系统能力直接开放给整个页面。
复杂表格、可视化、画布和多端应用,也都可以成为深入方向。没必要同时学习全部领域,先把一个功能的正常行为、失败情况和验收方式弄清楚。
项目里的技术深度,可以体现在我们知道怎样组合现有能力解决问题,而不必用从零手写了多少底层功能来证明。
学习投入可以重新分配
对我来说,需要减少的主要是机械记忆和重复实现,需要增加的是对技术用途、方案取舍和验证方式的理解。
| 不必作为主要学习目标的内容 | 更值得投入的内容 |
|---|---|
| 背熟每个 API 的参数和调用格式 | 知道它提供什么能力,需要时找到准确用法 |
| 默写状态管理库的初始化代码 | 分清状态归属,判断是否需要引入或复用某个方案 |
| 记忆大量 CSS 属性和布局模板 | 理解布局、滚动、响应式和焦点行为 |
| 重复编写请求、缓存和表单样板代码 | 了解成熟工具能处理什么,明确项目需要的行为 |
| 收集大量框架、Agent 和工具名称 | 在具体需求里理解适用场景、限制和组合方式 |
| 只检查代码能否运行 | 用测试、日志和实际操作确认结果是否符合需求 |
这些调整并不要求我们放弃代码阅读。看不懂某段实现时,可以让 Agent 解释、拆解或补充测试,再回到需求判断结果。
也没必要为了某个暂时不用的技术,先完成一整套课程。项目遇到问题,先理解问题属于哪一类,再了解合适的工具,通常更容易学进去。
有明确现场编码要求的岗位,可以针对性准备。但日常工程能力的积累,不必一直围绕脱离文档默写答案展开。
比起再做一个只展示成功路径的新 Demo,我更愿意把已有功能的取消、恢复、冲突和异常处理补完整。我们会在这个过程中真正知道,各种包和 API 分别能帮到哪里,又有哪些事情需要额外设计。
总结
我不认为前端已经死了,也不认为我们需要继续靠记住多少代码写法来证明能力。
React、TypeScript、CSS 和浏览器仍然值得学,但学习重点可以放在它们提供什么能力、适合解决什么问题,以及使用时需要注意哪些限制。具体语法、参数和样板实现,需要时可以让 Agent 结合文档完成。
知道 Zustand、Redux Toolkit 和查询缓存各自处理什么问题,我们才能给出合适的状态方案。理解请求取消、任务恢复和权限校验的职责,才能避免把一个 API 的能力误当作整个功能已经完成。
懂技术,是为了知道什么时候该让 Agent 用什么办法,以及拿什么结果确认它做对了,而不是为了把它能生成的代码再背一遍。
我们可以把更多实现工作交出去,同时保留对需求、技术选择和交付结果的判断。这样学到的前端知识,才能真正帮助我们用 Agent 写出更合适的代码。