☰
转岗AI应用开发三个月:技术栈竟都是课堂老本
2026/9/26 6:13:57 网站建设 项目流程

转岗做 AI 应用开发三个月,最大的感受不是"技术好难"或者"新东西好多",反而是个让我自己都有点意外的结论:日常吃的这碗饭,技术栈的根子,课上真的基本都讲过。我科班出身,做过几年传统后端,年初转岗到一家中小自研公司专职搞 AI 应用开发。三个月里天天打交道的,无非就是HTTP协议、JSON序列化、流式输出、状态管理、异步任务调度,再加一点Prompt工程和Agent编排。把这些词摆出来一看,确实,哪一样不是当年计算机网络、操作系统、软件工程课上敲过黑板的?只不过当年听课的时候,真没想过它们会以这种方式组合起来,变成一套"AI应用开发技术栈"。

这篇文章不聊"三个月从小白到大神"那种鸡汤,我就实打实盘点一下,这三个月我每天用的技术栈长什么样,跟课程内容对照起来到底重合在哪、脱节在哪,以及如果你也想转AI应用开发,哪些课内基本功最值得翻出来炒冷饭。全文偏向实操体会,想到哪写到哪,刚转岗或者准备转岗的朋友应该能找到点共鳴。

1. 转岗三个月,我日常用的技术栈全景盘点

先说结论:AI应用开发岗位的技术栈,并没有想象中那么"AI"。大模型确实是核心,但岗位的日常重心反而落在工程侧——怎么把模型能力稳定、高效、可控地封装成产品功能,这才是大多数时间在忙的事。

1.1 技术栈清单:从协议层到业务层挨个过

我按每天打开代码后实际接触到的层次,把技术栈盘一遍:

  • 协议与传输层:HTTP/HTTPS、SSE(Server-Sent Events)、WebSocket。其中SSE是重头戏,大模型回答的实时渲染基本靠它。
  • 数据与序列化层:JSON几乎统治一切,无论是请求体、响应体还是流式传输的chunk,基本都是JSON片段。偶尔会用MessagePack或者protobuf做内部高吞吐传输,但频率不高。
  • 服务端框架层:公司主栈是Python系的FastAPI,也有部分Java服务用Spring Boot。AI应用普遍是Python写算法服务、Java/Go写业务网关的混合架构。
  • 模型交互层:OpenAI/国内各家大模型的API SDK、OpenAI兼容协议封装、Prompt模板管理、上下文窗口管理、Function Calling/Tool Use。
  • 数据存储层:关系型数据库存业务数据,向量数据库(Milvus、pgvector、Chroma等)存文档切片和Embedding,Redis做缓存和临时会话状态。
  • 任务与编排层:Celery/Redis Queue处理异步任务,LangGraph或自研的状态机做Agent流程编排,定时任务负责数据同步和索引刷新。
  • 可观测性:结构化日志、链路追踪、Token用量统计、流式输出延迟监控。

这张清单摊开来看,真正的"AI特有"部分,其实只有模型交互那一层。剩下的大半壁江山,都是传统后端开发的看家本事。

1.2 为什么技术栈"看起来不AI"?

中小自研公司不像大厂有专门的算法团队和平台组,AI应用开发岗位往往是"全栈+半算法"的混合体。产品要的是能落地的功能:一个能回答私有知识库问题的客服助手,一个能根据用户意图调用多个内部工具的Agent,一个能自动生成报表草稿的Copilot。这些功能落地,70%的工作量在工程化——把模型输出变成可靠的产品体验,把不可控的延迟和错误消化在系统设计里。

也正因为这样,面试时被问"AI应用开发的技术栈是什么",千万别张口就只背模型名字。面试官真正想听的是:你知不知道SSE怎么实现流式渲染?你怎么控制上下文长度?工具调用失败怎么回退?用户中断生成了怎么处理?这些问题全都在课内知识框架里能找到答案。

2. 课堂知识变现现场:SSE流式输出与abort控制

这三个月我写得最多的代码,就是跟大模型流式输出有关的逻辑。这块也是"课上讲过"最典型的地方——计算机网络课讲HTTP的时候提过SSE,前端课讲fetch和事件流,操作系统课讲中断和资源清理。听着都是零散的,结果在实际项目里全串起来了。

2.1 SSE流式输出,到底是怎么跑起来的

大模型接口基本都支持流式返回,服务端通过HTTP长连接,把一段段增量内容不断推给客户端。前端拿到后逐段渲染,用户才能看到"打字机"效果。如果没有SSE,一个复杂问题可能要等十几秒才能看到第一个字,产品体验会非常糟糕。

服务端核心思路是利用FastAPI的StreamingResponse,把大模型SDK返回的异步迭代器直接透传给HTTP响应:

from fastapi import FastAPI from fastapi.responses import StreamingResponse import json app = FastAPI() async def sse_generator(prompt: str): # 假设 llm_stream 是异步生成器,逐chunk产出文本 async for chunk in llm_stream(prompt): # SSE 格式要求:每个消息以 data: 开头,空行结尾 yield f"data: {json.dumps({'delta': chunk}, ensure_ascii=False)}\n\n" yield "data: [DONE]\n\n" @app.post("/chat") async def chat_endpoint(payload: dict): prompt = payload["prompt"] return StreamingResponse( sse_generator(prompt), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" } )

这里有几个坑值得记一下:

  • SSE格式的data前缀和空行分隔是约定,不能漏。
  • X-Accel-Buffering: no这个头是给Nginx看的,不然Nginx可能缓冲整个响应,前端等半天一个字都出不来,我在测试环境就撞过这个。
  • media_type必须是text/event-stream,否则前端EventSource不认。

2.2 前端流式渲染:fetch + ReadableStream才是主流

前端实现方案,我建议直接上用fetch配合ReadableStream,别用EventSource。因为EventSource只支持GET请求,没法带自定义header和请求体,AI对话场景几乎必带用户token和复杂参数,用EventSource会非常别扭。

async function streamChat(messages, onDelta, signal) { const response = await fetch('/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }), signal // AbortController 的 signal }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // 按换行拆出完整SSE事件 const events = buffer.split('\n\n'); buffer = events.pop(); // 最后一段可能不完整,留在buffer里 for (const event of events) { const line = event.trim(); if (!line.startsWith('data:')) continue; const data = line.slice(5).trim(); if (data === '[DONE]') return; try { const parsed = JSON.parse(data); onDelta(parsed.delta); } catch (e) { console.warn('parse chunk error', data, e); } } } }

2.3 abort控制:一个被很多人忽略的课程考点

"停止生成"这个按钮,看起来是个小功能,内部藏的细节一点都不少。用户点击停止,前端需要做两件事:

  1. 调用AbortController的abort方法,断开fetch请求,停止接收后续chunk。
  2. 通过独立接口通知服务端,告诉大模型SDK那边的流式生成也停掉,把GPU资源和token额度省下来。

只做前端abort不做后端通知,是最常见的半吊子实现。前端虽然不再等流了,但服务端还在等模型输出,token照跑不误。

# 独立的停止接口 @app.post("/chat/abort") async def abort_chat(request_id: str): # 从全局任务表里找到对应的生成任务,取消异步任务 task = active_tasks.get(request_id) if task: task.cancel() return {"ok": True}

这块说白了就是操作系统课上讲过的"协作式取消"——一个任务要响应外部的中断信号,主动释放资源,而不是傻等完成。当年学的时候觉得抽象,写了几回abort逻辑后,是真真切切懂了。

2.4 上下文窗口管理:把课内的"有限资源调度"概念搬过来

大模型的上下文窗口是硬约束,不是你想传多少传多少。最常见的做法是维护一个滑动窗口,保留最近N轮对话,加上系统提示词和检索回来的参考资料,控制在模型支持的token上限以内。

实操中我习惯分四块拼上下文:

组成优先级说明
系统提示词最高角色设定、输出格式约束,必须保留
检索结果高当前问题相关的知识库片段,实时注入
最近对话中滑动窗口保留最近8~10轮
历史摘要低更早的内容压缩成摘要,防止遗忘太久远的信息

这个策略跟操作系统里的内存换页思路几乎一样:热点数据留在"内存"里,冷数据换到"磁盘"(也就是压缩摘要)。课堂知识换个马甲就出现在生产环境里,这种体验这三个月里反复出现。

3. 从零封装AI交互逻辑:这周我踩过的工程化台阶

如果说流式输出是AI应用的门面,那把大模型交互逻辑封装成稳定、可测试、可扩展的服务模块,就是里子。标题里说"技术栈课上基本都讲过",在这个环节体现得最明显——依赖注入、接口抽象、缓存策略、重试机制,全是软件工程课的老朋友。

3.1 模型接入层:适配器模式的实战

公司不会只接一家大模型,今天可能用A厂的接口跑主线,明天为了成本和效果就会想换B厂。这时候如果模型调用逻辑跟业务逻辑焊死在一起,换模型等于重写业务代码。

我的做法是定义一个统一的模型网关接口,再用适配器包装各家SDK:

class LLMProvider(Protocol): async def chat_stream(self, messages: list[dict], tools: list | None = None) -> AsyncIterator[str]: ... class OpenAIClient: def __init__(self, api_key: str, base_url: str, model: str): self.client = OpenAI(api_key=api_key, base_url=base_url) self.model = model async def chat_stream(self, messages, tools=None): stream = self.client.chat.completions.create( model=self.model, messages=messages, tools=tools, stream=True, ) for chunk in stream: delta = chunk.choices[0].delta.content if delta: yield delta

这样业务代码只管面向LLMProvider编程,底层到底接的是哪家、是不是自研底座,都不影响上层。软件工程课讲"开闭原则"的时候举的插件化例子,就是这玩意儿。

3.2 超时、重试与熔断:网络课和分布式课的双料考点

大模型接口的稳定性是个玄学——偶尔因上游负载高而变慢,偶尔直接超时。所以封装时我强制要求三个参数:连接超时、读超时、总超时。

超时类型建议值说明
连接超时5s到网关建立连接的时间上限
读超时60s两次chunk之间的最大间隔
总超时300s整个流式请求的上限,防止任务悬挂

读超时这个参数很关键。大模型生成有"思维链"的时候,思考过程可能持续几十秒,第一段token迟迟不出,如果读超时设太短会误杀正常任务。我一开始设了10秒,被坑了两次后才调到60秒。

重试策略也有讲究:像429限流、503上游繁忙这类错误,退避重试是有效的;但400参数错误、401鉴权失败这类问题,重试一万次也没用。我总结的规则是——幂等请求且错误码属于5xx/429,才做指数退避重试,最多3次。

3.3 Prompt管理:从"写字符串"到"模板即代码"

刚转岗的时候,我也觉得Prompt就是写一段话丢给模型,简单。三个月后我的看法变了:生产环境里的Prompt必须当成代码资产来管理,要有版本、要有测试、要有降级方案。

我现在的做法是:

  • 模板与代码分离:Prompt模板放在独立目录,用Jinja2渲染变量,不在Python代码里拼字符串。
  • 版本管理:Prompt文件跟代码一起进Git,每次改动必须写变更说明。
  • 评测集回归:维护一组标准问答对,每次改Prompt后自动跑一遍,对比输出质量,防止优化A场景的时候把B场景搞坏。

这一步跟软件工程课讲的配置管理、持续集成理念完全一致。很多AI应用开发面试题会问"你怎么评估Prompt改得好不好",答不上来评测和回归,基本就是说只靠人肉试。

3.4 把AI交互流程写进SOP文档

标题对应的热词里有个"ai应用开发的sop文档",我确实觉得这项工作很有必要。AI应用跟传统后端最大的不同,是不可控因素太多、排查链路太长,没有SOP,出了问题全靠拍脑袋。

我们的SOP核心就四张表:

  1. 模型接入检查单:密钥管理、限流配额、支持的参数、计费模式。
  2. 流式输出联调清单:SSE格式验证、断线重连逻辑、超时参数、abort链路。
  3. 上线前评估表:响应延迟、首token延迟、错误率、Token成本预估。
  4. 线上故障排查树:用户报告"回答卡住"时,按前端AbortController、网关超时、模型接口、上游故障逐层排查。

这套SOP的底子,就是以前做传统后端写的故障排查手册。方法论没变,变的只是排查对象里多了个大模型。

4. 三个月踩坑实录:那些课上没教但必须会的细节

讲课归讲课,真打实战还是有一堆"课外题"。这些细节单拎出来都不难,难的是你要知道它们存在。我把踩过的坑挑几个有代表性的写出来,给后来人当个参考。

4.1 首token延迟和总耗时的区别,直接影响产品体验

大模型接口的响应时间,必须拆成"首token延迟"和"总耗时"两个指标来看。首token延迟决定用户多久看到第一个字,总耗时决定整个回答的完整性体验。

很多AI应用的体验卡顿,并不是模型速度慢,而是链路里多了好几跳:客户端到业务网关、业务网关到模型网关、模型网关到模型API,每一跳都可能增加0.5到1秒的固定开销。我做过一次优化,把模型网关直接部署到跟模型API同区域,首token延迟从3.8秒降到了1.9秒,效果立竿见影。

排查问题时,先分清延迟加在哪一层,再动手优化,比盲目换模型强得多。

4.2 Token统计不能只靠猜,计费要按字符实算

大模型的计费模式是按token数算的。跟用户展示用量、做成本控制,都需要精确统计。问题在于,同一个字符串在不同模型的分词器下,token数可能不一样。

我的做法是:如果用的是OpenAI兼容接口,就用它的tiktoken离线库做统计;如果自研模型,就调用服务端暴露的tokenize接口。总之别用"汉字数乘1.5"这种粗略估算来对账,那只能糊弄自己,糊弄不了财务。

另外,Prompt里的系统提示词、工具定义也要计入成本。如果这些内容很长,那每一轮对话都在背着这个固定成本跑,我见过有人系统提示词写了3000个token,一问三不知的模型还每天烧着钱,这属于成本意识的问题。

4.3 流式输出的断线续传,产品层要想清楚

浏览器跟服务器的长连接不是百分之百可靠,用户可能切换网络、锁屏、或者点开一个新页面导致连接中断。这里有个产品决策:中断后怎么处理?

  • 方案A:不续传,用户重新提问。实现最简单,适合短回答场景。
  • 方案B:服务端缓存完整回答,前端断线重连时直接补全。实现复杂,适合长文生成场景。
  • 方案C:采用"先落库后推送"模式,回答完整写入存储后再逐步推送。多做一步持久化,抗风险能力最强。

我们上线初期用的方案A,被用户吐槽"答案白生成了一半"之后,改成了方案B。改造成本主要在服务端维护一个requestId到完整响应的映射,定期清理防止内存泄漏。

4.4 Agent开发:工具调用远比想象中需要工程约束

最近面试题里"agent开发需要哪些技术栈"问得很多。我三个月的体会是,Agent的核心难点不在"让模型决定调用哪些工具",而在"工具异常了系统怎么办"。

比如模型决定调用一个内部API查库存,结果API超时了。这时候Agent应该:

  1. 捕获工具异常,把错误信息回传给模型。
  2. 模型根据错误信息,决定是重试、换工具、还是直接向用户道歉。
  3. 整个过程要有次数限制,防止模型陷入无限自愈循环。

这个链路需要一整套工具注册、参数校验、超时控制、异常回传机制。比起模型层的花样,工程侧的规约才是Agent能不能稳定落地的基础。

5. 面试与进阶:三个月的积累能沉淀成什么

转岗三个月,除了干活,还得对付面试官。毕竟谁也不能保证在一家公司待到退休。把日常用的技术栈梳理清楚,对面试和简历都有实打实的帮助。

5.1 AI应用开发面试题的高频考点,其实都在技术栈里

我把最近刷到和实际被问到的AI应用开发面试题归了个类,发现考来考去就是这五个板块:

板块高频题目对应日常技术栈
流式交互SSE和WebSocket的区别是什么?怎么实现打字机效果?协议层、StreamingResponse
上下文管理超过上下文窗口怎么处理?多轮对话怎么做压缩?滑动窗口、摘要策略
函数调用Function Calling的执行流程是什么?参数校验失败怎么办?Tool Use、适配器封装
Agent编排多步骤Agent怎么保证状态一致性?子任务失败如何回滚?状态机、重试/熔断
工程治理线上回答质量差怎么排查?Token成本怎么控制?可观测性、评测回归

每一道题,都能对应回我前面写的那张技术栈清单。所以转岗AI开发,制胜点不在追新,而在把工程基本功跟模型能力结合起来。课内知识是骨架,AI是新长出来的肌肉。

5.2 学习路线建议:先别急着啃大模型原理

如果你问我要一份AI应用开发学习路线,我的建议跟市面上很多教程反着来:

  1. 先把HTTP、SSE、异步编程玩熟(两周)。
  2. 用FastAPI写一个调用大模型的流式接口,把前端渲染打通(一周)。
  3. 做一轮完整的Prompt工程和上下文管理设计(两周)。
  4. 实现一个带工具调用的Agent,比如能查天气、能查库存的助手(三周)。
  5. 深入学习向量检索和RAG,搭一个私有知识库问答系统(三周)。
  6. 最后有余力再看Transformer原理、模型微调这类偏算法的内容。

这个顺序的核心逻辑是:AI应用开发的岗位价值在"应用"二字,先在工程层面跑通,再往算法方向延伸,正反馈会强得多。

5.3 中小自研公司的AI应用开发岗,值不值得去?

热词里有人问"中小自研公司的ai应用开发岗位多吗"。我的体感是:数量在涨,但对人的要求很杂。中小公司没有大厂的冗余人力,一个人往往要覆盖前端交互、后端接口、提示词调优、模型接入甚至一些数据分析的活。

如果你跟我一样是转岗过来的,这种环境反而是好事:接触面广、决策链路短、能快速建立起对AI应用开发的整体认知。缺点是没人带你,所有坑都得自己趟一遍,而且文档少、代码review不严,需要更强的自律性。

我个人认为,第一段AI开发经历去中小自研公司是划算的。只要注意别把自己变成"只会调API的胶水工",每做完一个功能都沉淀点工程总结,半年后再看,能力密度会比在大厂拧螺丝高不少。

6. 转岗三个月的体会:课堂是地基,实战是装修

三个月前转岗那天,我心里也没底,总觉得"AI应用开发"这六个字高深莫测,得从头学一堆闻所未闻的东西。真正上手以后才发现,日常用的技术栈,从协议到框架到设计模式,课程里都埋过伏笔。课堂知识像地基,看不见但托着一切;实战经验像装修,决定住得舒不舒服。两者缺一不可。

最后分享一个这三个月里养成的习惯:每天下班前,把当天解决的问题记一条到自己的wiki里,哪怕只有两三行。问题是什么、根因在哪、怎么解决的、下次怎么预防。三个月攒下来,这本wiki已经成了我面试和写复盘时最值钱的素材。它不是课程笔记,也不是代码注释,而是你从一个转岗者变成一个有体系工程师的证据。

如果你也正在考虑转岗AI应用开发,别被新名词唬住,回去翻翻当年的课程笔记,再搭一个最小可跑的流式对话Demo,你会发现自己比想象中准备得充分得多。

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

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

立即咨询