为什么你要学 AI Agent?—— 从"切图仔"到全栈的进化之路
先讲个故事归公众号:糖墨夕 最新发布
假设你叫小陈,一个写了三年 Vue3 的前端。你的日常是:接需求、排页面、调接口、改 Bug。偶尔写写 Composable,用 ref 和 reactive 管理状态,用 watch 监听变化,日子过得挺稳。
直到有一天,老板在周会上突然说: "咱们要做个智能客服,能自动回答用户问题的那种。小陈,你研究一下,两周后给我方案。"
你当时脑子里大概是这样的:
好了,理清了这个认知,我们来看看,为什么前端工程师在"药剂师"这个角色上,反而比别人更有优势。
为什么前端在 AI 时代反而有优势?
很多人觉得 AI 开发是算法工程师的专利,得会 Python、懂深度学习、能训练模型。这个印象也不能说错——如果你要做模型层的工作,比如训练一个大模型、做微调、搞 RLHF,那确实需要深厚的 ML 背景。但问题是,99% 的 AI 应用开发者根本不需要碰模型层。
打个比方:你开一家餐厅,不需要会种地,只需要会用食材做菜。AI Agent 开发也一样:你不需要自己训练模型,你只需要"用好"现成的模型。种地是农民的事(OpenAI、Anthropic、DeepSeek 这些公司),做菜是厨师的事(你,应用开发者)。
搞 AI Agent,本质上是在做应用层开发,而不是模型层开发。 而说到应用层开发,前端工程师有几个看家本领,在 AI 领域简直是降维打击。
1. 流式渲染:你每天都在写的东西,AI 聊天刚好需要
你有没有用过 ChatGPT?打字的时候,回复是一个字一个字蹦出来的,不是一下子全出来。这种"流式输出"的效果,在 AI 应用里几乎无处不在——因为 LLM 生成文本本身就是一个 token 一个 token 往外蹦的过程,逐字输出是最自然的交互方式。
而实现这种效果,技术上叫什么?叫 Server-Sent Events(SSE) 或者 Streaming HTTP。前端同学应该不陌生——你写大文件分片上传、WebSocket 实时消息、进度条轮询,本质上都是在处理"数据流"。数据不是一次性返回的,而是一段一段来的,来了你就更新 UI。
举个例子,下面这段代码,前端同学看着应该很眼熟:
// 调用 AI 接口,流式读取响应
// 这跟处理大文件上传的进度回调,本质上是同一件事
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: '你是谁?' })
})
const reader = response.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
// 每收到一段数据,就拼到消息上,UI 自动更新
const chunk = decoder.decode(value, { stream: true })
// Vue3 响应式:改了 messages,DOM 自动更新
messages.value[messages.value.length - 1].content += chunk
}
这不就是前端最熟悉的"数据来了,更新 DOM"吗?只不过以前是 API 返回 JSON,现在是 AI 返回文字流,本质上没区别。你甚至会发现,AI 对话的流式渲染,比很多前端项目里的复杂交互还要简单。
在 Vue3 里,我们用 ref 或者 reactive 就能轻松接住这些流式数据。Vue 的响应式系统天生就是做这个的——数据变了,UI 自动跟着变,你不用手动操作 DOM。
而且,你还可以把这段流式读取逻辑封装成一个 Composable,以后任何 AI 项目都能复用:
// composables/useStreamChat.ts
// 封装了流式聊天逻辑,以后任何 AI 项目都能直接用
import { ref } from 'vue'
export function useStreamChat() {
const messages = ref<Message[]>([])
const isStreaming = ref(false)
async function sendMessage(content: string) {
messages.value.push({ role: 'user', content, id: crypto.randomUUID() })
isStreaming.value = true
const aiMessage = { role: 'assistant' as const, content: '', id: crypto.randomUUID() }
messages.value.push(aiMessage)
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: content })
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const reader = response.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
aiMessage.content += decoder.decode(value, { stream: true })
}
} catch (err) {
aiMessage.content = '抱歉,AI 响应出错了,请稍后重试。'
console.error('流式聊天错误:', err)
} finally {
isStreaming.value = false
}
}
return { messages, isStreaming, sendMessage }
}
封装成 Composable 之后,你在任何 Vue3 组件里都能用 const { messages, sendMessage } = useStreamChat() 一行代码搞定流式聊天。这就是前端工程化的力量——把复杂的逻辑封装起来,暴露简单的接口。这个能力,在后端 AI 开发场景里同样重要——你封装得越好,后续维护成本越低。
而且,Vue3 的 watch 和 computed 还能帮你做更多事情。比如:
// 用 watch 监听消息变化,自动滚动到底部
watch(
() => messages.value.length,
() => { nextTick(() => { chatContainer.value?.scrollTo({ top: 999999 }) }) }
)
// 用 computed 计算当前对话的 Token 用量
const tokenUsage = computed(() => {
return messages.value.reduce((sum, msg) => sum + msg.content.length, 0)
})
这些操作,你写 Vue3 的时候天天都在用,搬到 AI 应用里完全复用。
2. 状态管理:你的 Pinia 经验可以直接复用
一个 AI Agent 对话界面,背后牵涉的状态其实不少,而且比普通的 CRUD 页面要复杂一些:
这些状态管理问题,用 Pinia 一个 Store 就搞定了。你每天都在做的事情,换个场景而已。
// 一个典型的 Agent 对话 Store —— 看看跟你平时的 Pinia Store 有啥区别?
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useChatStore = defineStore('chat', () => {
// 当前会话的消息列表
const messages = ref<Message[]>([])
// Agent 当前的思考状态
const agentStatus = ref<'idle' | 'thinking' | 'calling_tool' | 'responding' | 'error'>('idle')
// 当前正在执行的工具调用
const currentToolCalls = ref<ToolCall[]>([])
// 用户偏好设置
const userPreferences = ref<Record<string, any>>({})
// 多会话列表
const conversations = ref<Conversation[]>([])
// 计算属性:当前会话的 Token 用量估算
const estimatedTokens = computed(() => {
return messages.value.reduce((sum, msg) => sum + Math.ceil(msg.content.length / 4), 0)
})
async function sendMessage(content: string) {
messages.value.push({ role: 'user', content, id: crypto.randomUUID() })
agentStatus.value = 'thinking'
try {
const response = await fetch('/api/agent/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
messages: messages.value,
preferences: userPreferences.value
})
})
// 流式读取响应,逐字更新到 UI
const reader = response.body!.getReader()
const decoder = new TextDecoder()
const aiMsg = { role: 'assistant' as const, content: '', id: crypto.randomUUID() }
messages.value.push(aiMsg)
agentStatus.value = 'responding'
while (true) {
const { done, value } = await reader.read()
if (done) break
aiMsg.content += decoder.decode(value, { stream: true })
}
agentStatus.value = 'idle'
} catch (err) {
agentStatus.value = 'error'
console.error('Agent 调用失败:', err)
}
}
return {
messages, agentStatus, currentToolCalls,
userPreferences, conversations, estimatedTokens,
sendMessage
}
})
你看,这不就是 Pinia 的常规操作吗?唯一的区别是,以前你调的是 CRUD 的 REST API,现在你调的是 AI Agent API。状态管理的内核没变,只是数据来源变了。 该用 ref 用 ref,该用 computed 用 computed,该用 watch 用 watch——都是你写了三年的东西。
而且,Pinia 的 $subscribe 和 $onAction 在 Agent 场景下特别好用。比如:
// 监听 Agent 状态变化,自动记录日志(方便调试)
const chatStore = useChatStore()
// 每当 agentStatus 变化时,打印日志
chatStore.$subscribe((mutation, state) => {
if (mutation.storeId === 'chat') {
console.log(`[Agent] 状态变更: ${state.agentStatus}`, {
messageCount: state.messages.length,
toolCalls: state.currentToolCalls
})
}
})
// 拦截 sendMessage action,记录每次请求的耗时
chatStore.$onAction(({ name, after }) => {
if (name === 'sendMessage') {
const start = Date.now()
after(() => {
console.log(`[Agent] sendMessage 耗时: ${Date.now() - start}ms`)
})
}
})
这些 Pinia 的高级特性,在 Agent 应用的调试和监控中非常实用。你不需要额外引入什么监控工具,Pinia 自带的 API 就能搞定。这就是"用你熟悉的工具做新事情"的好处——你不用去学新框架,只需要把旧工具用在新场景。
3. 用户体验:前端最懂用户想要什么
AI 应用最怕什么?最怕用户不知道 AI 在干嘛。
你见过那种点了按钮之后啥反应都没有的 APP 吗?是不是很想摔手机?AI 对话也一样。如果用户发了一条消息,等了 5 秒钟啥都没看到,他的第一反应不是"AI 在处理",而是"是不是我网断了?是不是系统挂了?"
前端工程师天天跟用户体验打交道,这些东西你闭着眼睛都知道怎么处理:
- Loading 状态:AI 思考的时候,显示一个"正在思考..."或者跳跃的三个点动画。简单的骨架屏、动态的思考步骤展示,都能大幅降低用户的焦虑感。这些动画效果,你写 CSS 或者用 Vue 的
<Transition>组件就能搞定。 - 错误处理:AI 接口超时了怎么办?返回了乱码怎么办?Token 用完了怎么办?调用频率被限制了怎么办?每一种错误,你都得给用户一个友好的提示,而不是一个红色的
500 Internal Server Error。这跟普通表单提交的错误处理逻辑一模一样,只是错误类型多了几种。 - 思考过程可视化:这是 AI Agent 特有的需求。Agent 在"思考"的过程中,可能会调用工具、搜索知识库、做推理。把这些步骤展示出来——比如"正在搜索知识库..." → "找到 3 条相关文档" → "正在生成回答..."——用户会觉得"哇,这个 AI 好聪明,它真的在做事",而不是"怎么还没好?"
- 引导式交互:用户第一次用 AI 应用的时候,往往不知道能问什么。一个好的引导面板(比如"试试问这些问题"),或者一些预设的快捷指令,能极大降低使用门槛。
- 断点续传:用户在对话过程中刷新了页面,回来之后对话还在吗?如果对话进行到一半,Agent 正在思考,刷新之后能恢复吗?这些细节,前端都需要考虑。
这些细节,纯后端工程师可能觉得"能用就行",但前端知道——体验好,用户才愿意用;用户愿意用,产品才有价值。 而 AI 应用的用户体验,目前整个行业都还在摸索阶段。你现在进来,正好是"开荒"的时候,你的每一个体验优化,都可能成为行业标准。
举个例子,现在很多 AI 对话产品的"思考过程可视化"做得很粗糙——要么只显示一个"思考中...",要么干脆什么都不显示。但如果你能做成像下面这样:
用户:帮我分析一下上个月的销售数据 Agent: [1/4] 正在理解你的需求... ✓ [2/4] 正在查询数据库... 找到 847 条记录 ✓ [3/4] 正在分析数据趋势... 发现 3 个异常点 ✓ [4/4] 正在生成分析报告... ▊
用户看到这个,心里就有底了——"Agent 在做事,不是卡住了"。而且每步完成之后打勾,给人一种"进度在推进"的感觉。这种细节,前端天生就比后端敏感。因为前端天天跟用户打交道,知道什么样的反馈能降低用户焦虑。
再比如,Agent 的"思考步骤"其实可以做成可交互的。用户点击某个步骤,可以展开看详细信息——比如"查询数据库"这一步,展开后能看到执行的 SQL 语句、返回的记录数、耗时。这些信息对普通用户可能没用,但对开发者调试非常有用。而且,把"调试信息"自然地融入"用户界面",这正是前端组件化设计的长处。
4. 组件化思维:Agent 前端就是一堆组件的组合
Vue3 的组件化思维,和 AI Agent 应用的前端架构简直是天作之合。你想想,一个 Agent 应用的前端,大概需要哪些组件?拆开来看:
这跟写一个后台管理系统有什么区别?都是组件拆分、数据流转、状态管理,套路一模一样。 每个组件接收 props,发出 emits,通过 Pinia 共享全局状态。你完全可以把你之前写后台管理系统的经验,平移到 AI Agent 应用的 UI 开发中。
而且,因为 AI 对话的 UI 模式相对固定(消息列表 + 输入框),你甚至可以写一套通用的 Agent UI 组件库,以后做任何 AI 项目都能复用。一套组件,所有 AI 项目通吃,这不比每次都从零写 CRUD 页面爽?
说到组件库,其实 Vue3 生态里已经有一些可以直接用的 AI 对话组件了,比如 Vue Chat UI、Naive UI 的 Chat 组件等。但我建议你前期还是自己手写一遍——因为只有自己写过,才知道流式渲染的细节、Markdown 渲染的坑、消息滚动定位的边界情况。等你自己写过一遍之后,再用现成的组件库,就能理解它们内部做了什么,出了问题也能自己排查。
还有一个细节点,前端可能没意识到:Markdown 渲染在 AI 对话里是刚需。 LLM 返回的内容经常包含代码块、表格、列表、加粗、链接。你写的消息气泡组件,需要支持 Markdown 渲染。而前端对 Markdown 渲染太熟了——marked + highlight.js,或者直接用 markdown-it,三行代码搞定。你在技术博客、文档站里早就用过无数次了。
// AI 消息气泡组件 —— 支持 Markdown 渲染
// MessageBubble.vue
<script setup lang="ts">
import { marked } from 'marked'
import { computed } from 'vue'
const props = defineProps<{ message: ChatMessage }>()
// 用 marked 把 Markdown 转成 HTML
const htmlContent = computed(() => {
if (props.message.role === 'assistant') {
return marked(props.message.content, { breaks: true })
}
return props.message.content
})
</script>
<template>
<div :class="['message-bubble', message.role]">
<div v-if="message.role === 'assistant'" v-html="htmlContent" />
<div v-else>{{ message.content }}</div>
</div>
</template>
你看,Markdown 渲染、代码高亮、深色模式适配——这些前端做了无数遍的事情,在 AI 对话里全部需要。而且前端做这些,比后端快得多、好得多。因为前端是离用户最近的人,最知道什么样的交互体验是好的。
小结
AI Agent 应用的前端开发,本质上就是"流式数据渲染 + 状态管理 + 用户体验优化 + 组件化拆分"这四件事。而在这四件事上,前端工程师已经积累了多年的经验。所以,不要觉得自己是"写页面的"就搞不了 AI——恰恰相反,AI 应用最缺的就是能把用户体验做好的前端。你的技能树,在 AI 应用开发里是非常稀缺的。
AI Agent 不是魔法,就是一个"能自己做事的程序"
聊到 AI Agent,很多人脑子里会浮现出科幻电影里的画面:一个无所不能的 AI,能跟你聊天、帮你订机票、替你写代码、甚至还能跟你谈恋爱。坦白说,这种想象也不算错,但离我们现在的技术现实还有点距离。
现在能落地的 AI Agent,本质上就是一个 "能自己做事的程序" 。它和普通程序的区别在于:普通程序只会按你写的 if-else 执行,而 Agent 能自己"思考"下一步该做什么。
看到区别了吗?普通程序是"死"的,你写什么它就做什么;Agent 是"活"的,它能理解用户意图,自己决定该做什么,然后调用工具去执行。
那 Agent 是怎么做到"自己思考"的呢?其实拆开来看,就四个核心组件,缺一不可:
Agent 的四个核心组件
把这四个组件拼起来,就是一个能用的 Agent 了。听起来是不是没那么玄乎?
而且,实现一个最简版的 Agent,代码量比你想象的要少得多。我们直接写一个:
// 一个最简单的 AI Agent,不到 50 行代码
// 功能:用户问天气,Agent 自动调用天气 API 回答
// 运行前需要:npm install openai
import OpenAI from 'openai'
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
// 第一步:定义 Agent 可以用的工具
// 每个工具包含:名称、描述、参数定义(JSON Schema 格式)
const tools = [
{
type: 'function' as const,
function: {
name: 'get_weather',
description: '获取指定城市的天气信息',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名称,如"北京"' }
},
required: ['city']
}
}
}
]
// 第二步:实现工具的实际逻辑
// 实际项目中这里会调用真实的天气 API
async function getWeather(city: string): Promise<string> {
// 模拟返回天气数据
// 生产环境替换为:fetch(`https://api.weather.com?city=${city}`)
return `${city}今天晴,25°C,湿度 60%,适合出门`
}
// 第三步:Agent 主循环 —— 这是 Agent 的核心逻辑
async function agentRun(userMessage: string) {
const messages: any[] = [{ role: 'user', content: userMessage }]
// 把用户消息发给 LLM,让它决定要不要调工具
const response1 = await client.chat.completions.create({
model: 'gpt-4',
messages,
tools // 告诉 LLM 有哪些工具可用
})
const toolCall = response1.choices[0].message.tool_calls?.[0]
// 如果 LLM 决定要调工具
if (toolCall && toolCall.function.name === 'get_weather') {
// 解析 LLM 提取的参数(比如 city: "北京")
const args = JSON.parse(toolCall.function.arguments)
const weatherResult = await getWeather(args.city)
// 把工具执行结果发回给 LLM,让它生成最终回复
messages.push(response1.choices[0].message) // LLM 的"我要调工具"决策
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: weatherResult // 工具返回的结果
})
const response2 = await client.chat.completions.create({
model: 'gpt-4',
messages
})
return response2.choices[0].message.content
}
// LLM 觉得不需要调工具,直接返回它的回复
return response1.choices[0].message.content
}
// 试试效果
const answer = await agentRun('北京今天天气怎么样?')
console.log(answer)
// 输出:"北京今天天气不错!晴,25°C,湿度 60%,很适合出门哦~"
对了,看到上面那个 Agent 代码里 get_weather 这个工具的定义了吗?注意它的参数格式:
{
type: 'function',
function: {
name: 'get_weather',
description: '获取指定城市的天气信息',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名称,如"北京"' }
},
required: ['city']
}
}
}
这个格式叫 JSON Schema,是 OpenAI 定义的 Function Calling 规范。你不需要手写这个 JSON,但你需要理解它的含义——它告诉 LLM:"嘿,你有一个叫 get_weather 的工具可以用,它需要一个 city 参数,是字符串类型。"LLM 看到这个描述,就知道当用户问天气的时候,它应该调用这个工具,并且把城市名作为参数传进去。
这个机制,就是 AI Agent 能够"做事"的底层原理。它不是什么神奇的黑箱,就是一个定义清晰、格式规范的接口约定。你作为开发者,要做的就是定义好工具、实现好工具,然后把工具列表发给 LLM,LLM 自己会决定什么时候该用哪个工具。
现在,让我们开始吧
好了,写了这么多,总结一下这一章的核心观点。记住这五句话就行:
其实这五句话,每一句都可以展开成一章。但你现在不需要记住所有细节——你只需要记住一个感觉: "AI Agent 开发,我好像也能搞。" 带着这个感觉进入下一章,学习效率会高很多。因为最难的从来不是技术本身,而是"我能不能学会"这个心理障碍。一旦你跨过了这个障碍,剩下的就是按部就班地学。
- AI Agent 开发是应用层开发,不是模型层开发。 你不需要会训练模型,只需要会用模型。就像你不需要会种地,只需要会用食材做菜。
- 前端在 AI Agent 开发中有天然优势。 流式渲染、状态管理、用户体验、组件化思维——这些都是你每天都在做的事情,搬到 AI 应用里完全复用。
- AI Agent 不是魔法,就是一个"能自己做事的程序"。 核心就四个组件:大脑(LLM)、工具(Tools)、记忆(Memory)、规划(Planning)。把它们拼起来,就是一个能用的 Agent。
- Vue3 + NestJS + TypeScript 是前端学 Agent 的最优路线。 全栈 TypeScript,前后端类型安全,学习成本最低,80% 的精力都能花在 Agent 开发本身。
- 15 章,四个阶段,边学边做,每章都有产出。 从第 5 章开始就能写出能跑的 Demo,学完就能写在简历上。
这五个观点,其实也是这本小册的"核心设计理念"。我写这本小册的目的,不是为了让你成为 AI 研究员,而是让你成为一个能用 AI 解决实际问题的全栈工程师。所以这本书里不会有复杂的数学公式,不会有晦涩的学术论文引用,只有"能跑通的代码"和"看得懂的比喻"。如果你觉得哪里看不懂,那一定是我没讲清楚,不是你的问题。
如果你看完这一章,心里的想法从"我搞不了 AI"变成了"好像也没那么难",那这一章的目的就达到了。AI Agent 开发的门槛,比你想象的低得多。你缺的不是能力,而是正确的入门路径和一套好的教程——而这两样东西,这本小册都给你准备好了。
最后,我想用一句话收尾:
"不要等到 AI 成熟了再学,那时候别人已经跑了很远了。
现在就是最好的时机,因为你已经具备了一切必要的基础。
剩下的,只是往前走一步。"
下一章,我们来聊聊"AI Agent 到底是个啥?"——用送外卖、炒菜、订机票这些生活场景,把 Agent 的四个核心概念掰开揉碎了讲。保证你看完就能跟朋友吹牛:"AI Agent?我懂,其实就是……"
归公众号:糖墨夕 最新发布