十四个月、三个大版本迭代,团队从”会写代码”变成”能设计可演进的复杂系统”。回头看这段经历,最值钱的不只是学会了某个框架,而是被真实业务逼着补上了工程化的每一块短板。这个平台要同时处理多家 LLM 的接入、异步调度、流式推送、成本计费,任何一块偷懒都会在线上的某个深夜还回来。下面按技术栈、核心能力、领域知识、踩坑、软技能五个维度,把这段成长的来龙去脉记下来,对正在做同类产品的团队也算一份参考。
一、技术栈的深度积累
技术栈的成长不是换了个流行框架那么简单,而是每个版本都对应一个真实问题的倒逼。没有踩坑就没有版本号,下面是后端和前端两条演进线。
后端:从 CRUD 到分布式系统
| 阶段 | 技能 |
|---|---|
| v0.1 | Spring Boot + MyBatis CRUD |
| v0.5 | 多模块拆分 + 统一异常 |
| v1.0 | Redis 分布式锁 + 滑动窗口限流 |
| v1.5 | RabbitMQ 异步任务编排 |
| v2.0 | WebSocket 跨实例推送 + SSE 流式 |
| v2.5 | 多 LLM 抽象 + Flux.merge 并行 |
最大收获:从”单机能跑”到”分布式协同”的工程能力。
这条演进线里,v1.0 到 v1.5 之间是最痛苦的。当时批量评测任务量涨起来,单机跑完一批要几个小时,用户等得没耐心,我们只能把任务拆碎丢进 RabbitMQ,才有了 v1.5。每个阶段的技能都不是我们提前规划的,全是流量和故障教出来的,回头看这张表,每行背后都有一到两个线上事故。
前端:从 Vue 2 思维到现代工程化
| 阶段 | 技能 |
|---|---|
| v0.1 | Vue 2 Options API + Webpack |
| v0.5 | Vue 3 Composition API + Vite |
| v1.0 | TypeScript 全量 + Pinia |
| v1.5 | 组件库封装 + 路由级代码分割 |
| v2.0 | ECharts 复杂可视化 + WebSocket 集成 |
| v2.5 | 性能优化 + 可观测性 |
最大收获:现代前端工程化的全栈视野。
前端这条线最明显的转折是 v2.0 的 WebSocket 集成。批量任务实时进度、LLM 流式输出都靠它,前端从”展示页”变成了”实时交互终端”,对连接管理、断线重连、渲染节流的理解全是这段时间补上的。之前我们以为前端就是画页面,做完 WebSocket 集成才明白,实时交互场景下前端的复杂度不比后端低。
二、核心能力的跃迁
技术栈是工具,核心能力才是带得走的资产。这一节讲五件事:抽象、系统设计、性能调优、故障排查、可观测性意识,每一项都是从具体问题里长出来的,不是看书看来的。
1. 抽象能力
为多家 LLM 写适配器时,我们反复被一个问题折磨:为什么每家模型的接口都差一点?有的用流式,有的返回格式还不一样。后来想明白了,问题出在抽象由被调用方定义了。正确做法是由平台定义业务无关的接口,模型实现去适配它:
// 不变:聊天接口
public interface LlmProvider {
ChatResult chat(ChatRequest req);
}
// 可变:每家协议、字段、限流
class OpenAIProvider implements LlmProvider { ... }
class ClaudeProvider implements LlmProvider { ... }
这种抽象能力可以迁移到任何”多供应商”场景。
抽象能力不是看架构书学会的,是被第三家模型接入时的重复劳动逼出来的。前两家我们各写一套对接代码,到第三家终于受不了,回头重构出这套接口。现在再接新模型,只需要实现一个接口,剩下的全部复用。抽象的价值只有在你重复三次之后才真正体会到,纸上谈兵是体会不到的。
2. 系统设计能力
从”功能怎么实现”到”系统怎么演化”,视角完全不同。做功能时只需要回答”怎么跑通”,做系统时要回答三个问题:
- 哪些是核心域(必须独立);
- 哪些是支撑域(可以用第三方);
- 哪些是未来需要重写的(保留扩展点)。
// 抽象成 "评分服务" 接口,保留未来替换实现的能力
public interface JudgeService {
JudgeResult score(ScoringRequest req);
}
// 现在是 LLM-as-Judge
// 未来可以替换为 Reward Model
// 业务代码不用改
系统设计能力在评测平台上的体现是:我们始终没把评分逻辑写死在业务代码里。这样 LLM-as-Judge 后面换掉时,改动面只在一个实现类。当初写这个接口时觉得”想太远了”,现在回头看正是这个”多余”的抽象,让平台在模型快速迭代期没有反复返工。
3. 性能调优能力
性能优化做过很多次,真正留下印象的是这组数据,每次优化都有明确的指标前后对比:
| 调优项 | 前后对比 |
|---|---|
| 5 模型并行调用 | 25s → 5s |
| 子任务分页查询 | 5s → 50ms |
| 限流精度 | 60 次/分钟 → 60 次/60 秒 |
| 缓存命中率 | 60% → 99% |
不只是”会调优”,是”知道为什么这么调优”。
“5 模型并行 25 秒到 5 秒”那次,本质是把串行等待改成并行聚合;”限流精度”那项,是把独立的 60 个请求窗口改成 60 秒滑动窗口,精度误差从分钟级降到秒级。调优的每一笔收益背后都是一个被验证过的原理,这才是调优能力和背答案的区别,也是面试时能讲出深度的底气。
4. 故障排查能力
平台踩过的典型故障,现在看是宝贵的教案,当时每一个都让人熬到凌晨:
- OOM:大列表渲染没分页,浏览器崩溃。学到”前端也需要分页”。
- WebSocket 推送风暴:5 万子任务同时完成把 WS 打爆。学到”批量聚合推送”。
- LLM 评分漂移:同一答案隔天分数差 1-2 分。学到”温度设 0 + 多评委”。
- 缓存击穿:热点 key 过期瞬间 1 万请求回源 DB。学到”singleflight + 后台刷新”。
- MySQL 死锁:批量更新没排序。学到”按主键顺序更新”。
每条故障背后都有一个”早该想到”的懊悔,也有一个从此长期有效的规约。比如 WebSocket 推送风暴之后,我们所有批量进度推送都改成了 500ms 聚合,这个规约再也没被打破过。故障排查能力就是这样堆出来的:踩一个坑,固化成一条规则,下次同类问题不再犯。
5. 可观测性意识
以前只关心”功能跑没跑通”,现在关心:
- 这个接口 P99 多少?
- 这条 SQL 走没走索引?
- 这个错误影响多少用户?
- 这个用户今天用了多少钱?
可观测性是生产系统的”体检表”。
可观测性意识的转变来自一次事故:某个接口变慢,我们盯着日志猜了三个小时,后来才发现是索引没建。从那以后,每个接口上线前都先想清楚”上线后我怎么知道它好不好”,指标先行成了习惯。没有指标的系统就像没体检的人,看着健康,一查全是隐患。
三、领域知识的积累
技术之外,做评测平台还顺带攒了一整套 LLM 领域的知识,这些知识的密度是普通学习路径给不了的,因为全是真实业务验证过的。
LLM 行业认知
- 主流模型的优劣对比(GPT-4o 强在综合、Claude 强在逻辑、DeepSeek 强在中文);
- API 限流/计费规则(OpenAI 按 1k token、Claude 分 input/output);
- Prompt 工程的实战技巧(few-shot、CoT、role prompting);
- 评测方法论(人工评分、LLM-as-Judge、ELO 排名)。
这些认知不是看文档看出来的,是每天真实调用的经验积累。比如”Claude 逻辑强”,具体体现在代码评测场景它的得分稳定性更好;”DeepSeek 中文强”,体现在中文长文评测上。纸上谈兵的知识很难到这个颗粒度,这些判断直接影响我们给用户推荐哪个模型做评分员。
用户洞察
- 评测的核心痛点是”我写了一个 prompt,不知道哪个更合适”;
- 用户最常卡在”token 太多超出预算”;
- 评分员的选择比”评分 prompt”本身更重要;
- 报告可定制比”图表好看”重要。
用户洞察里印象最深的一条是”评分员比 prompt 重要”。我们一直以为把评分 prompt 调优就能解决一致性问题,用户数据却显示,换个稳定的评分模型带来的提升比调 prompt 明显得多。技术和需求的错位,往往要经过真实用户才能校正,这也是做 B 端产品最值钱的认知积累。
四、踩过的最深坑
挑五个最深的坑出来,每个都改写了我们的一条设计约定,写在这里算是给后来的团队探路。
坑 1:LlmProvider 抽象泄漏
// 错:抽象里直接暴露了 OpenAI 字段
public interface LlmProvider {
ChatResult chat(OpenAIRequest req); // ❌ 泄漏实现
}
// 对:业务无关的抽象
public interface LlmProvider {
ChatResult chat(ChatRequest req); // ✅ 平台定义
}
学到的:抽象接口必须由调用方定义,不是被调用方。
这个坑的根源是图省事:第一个接入的模型是 OpenAI,就把它的请求类型直接放进了接口,等接入 Anthropic 时接口就崩了。抽象一旦泄漏实现,后续所有适配器都要被迫兼容第一个模型,这是最隐蔽的债务,改起来牵一发动全身,只能重构。
坑 2:分布式锁误删
-- 错:直接删锁
del KEYS[1]
-- 对:校验 token 后再删
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
end
学到的:分布式锁必须用 Lua 脚本保证原子性。
直接删锁的后果是锁过期后另一线程拿到锁,第一个线程还把它删了,导致两个线程同时进入临界区。我们当时是业务上出现重复扣费才发现这个问题。校验 token 再删,把”删除”和”校验”合进一个原子操作,问题才根除。这个坑在网上讨论很多,但只有自己撞上才会真正记住。
坑 3:SSE 流被 Nginx 缓冲
流式响应延迟 30s → 排查发现 Nginx proxy_buffering on
学到的:所有反向代理都要 proxy_buffering off。
这个问题排查过程很折磨人:本地直连后端是流式的,走代理就变成 30 秒攒一批,用户体验完全不对。最后抓包发现 Nginx 默认开了响应缓冲。类似问题还有个兄弟:WebSocket 的 proxy_read_timeout 也要调大,否则连接会被静默断开。反向代理的默认配置几乎都是为普通 HTTP 设计的,流式场景全得手动改。
坑 4:评分一致性差
同一答案 GPT-4 打 8 分、Claude 打 6 分,差异过大
学到的:多评委交叉 + 中位数 + 方差检测是稳定性的关键。
单个评委的主观偏好无法消除,但多个不同家族的评委一起打分,取中位数,再用方差检测异常,就能把偏激评分稀释掉。后来我们把方差超过阈值的评测标记为”存疑”,提示用户重新评测或人工复核。单评委方案在 demo 里够用,一上生产就不行了。
坑 5:批量任务进度丢失
WebSocket 断线后用户重连,进度从 0 重新开始
学到的:重连必须发 SNAPSHOT 同步当前状态。
断线重连只恢复连接、不恢复状态,等于白连。我们在服务端维护每个批次的进度快照,客户端重连后第一件事就是拉 SNAPSHOT,把 UI 拉到最新,再续上增量推送。这个方案上线后再也没有用户反馈”进度清零”,问题小,影响却很大。
五、软技能的成长
技术能力之外,做平台这一年最明显的变化是软技能。代码解决单点问题,平台涉及的角色多了,沟通本身就是生产环节。
1. 跨团队协作
平台涉及:算法、后端、前端、产品、运营、客服。
- 算法:评分 prompt 调优;
- 运营:排行榜需求;
- 客服:用户反馈处理。
跨团队协作的核心是“把技术语言翻译成业务语言”。
有一次算法同学要求”评分相关性到 0.9″,产品听不懂,双方僵住。我们把它翻译成”评出来的分数跟人工评的差别在一个档次以内的比例要过 90%”,沟通立刻通畅。翻译能力比代码能力更稀缺,也更容易被忽略,但它是多角色项目里最影响效率的一环。
2. 文档化能力
文档不是”写完就忘”,而是”未来的自己/同事”。
- 代码注释解释”为什么”而不是”是什么”;
- README 给新人 5 分钟上手;
- ADR(架构决策记录)记录”为什么选 A 不选 B”。
文档化的转折点是一次”代码能跑但没人敢改”的经历。后来我们每做一个关键技术决策都写 ADR,三个月后回看,很多当时的犹豫和取舍都清晰了,新人接手也快。注释写”是什么”的代码一年后照样看不懂,写”为什么”的代码半年后还能直接改,这个差别是真实体会到的。
3. 时间管理
平台 14 个月开发,团队 4 人,每人每天 1-2 个任务:
- 早上处理消息、review PR;
- 上午写核心功能;
- 下午开会 + 协调;
- 晚上学习新东西。
关键:“专注时间”是稀缺资源。
我们试着把会议集中到下午,上午尽量不打断。执行后发现,上午两个小时的连续编码产出,比下午断断续续四个小时还多。时间管理不是排满日程,是保护大块时间,这个经验对做复杂系统的团队特别适用,因为深度任务一旦被打断,重新进入状态的成本很高。
六、对未来项目的启发
这段经历沉淀下来几条方法论,现在做新项目都会直接套用,算是一套可复用的启动清单。
1. 先设计后写代码
平台早期有一个”先写后改”的痛苦期。现在做新项目:
- 第一周:写 ADR,列架构;
- 第二周:搭骨架,跑通端到端;
- 第三周起:迭代功能。
“先写后改”不是完全不行,但适用场景是需求完全清楚、体量很小的小功能。平台这种多系统协作的项目,两周设计换来的返工减少,远超设计的投入。现在我们的习惯是:设计文档写不满一页纸的项目,多半是没想清楚。
2. 可观测性是 Day 1 的需求
不要等”上线后”才加监控。从第一个接口开始就有:日志、指标、链路追踪。
这句话我们是在一次复盘后写进团队约定的。那之前监控是后补的,结果每次事故都要靠现场加日志再复现,耗时翻倍。Day 1 就上可观测性,等于给系统配了出生证明,后面排障直接看指标,不用从头猜。
3. 用户反馈比技术正确重要
平台 80% 的功能是用户投票选出来的。技术上”应该这样做”和用户”实际想要”经常不同。
最典型的是报告定制。我们内部觉得现有报告已经很完善,用户投票却压倒性要求自定义模板。跟着用户走,功能上线率明显提高。技术洁癖要服务于用户价值,而不是反过来,这个道理我们在报告功能上反复验证过。
4. 成本意识
LLM 调用按 token 计费。一个并发失控能把月度预算烧光。所有”调用外部服务”的代码都要加预算检查。
我们在线上的确遇到过”一个死循环把预算烧掉大半”的事故。从那以后,预算检查从业务代码下沉到网关,任何未经预算检查的 LLM 调用直接拒绝,成本这层防护变成了强制约束。普通项目的成本是服务器,LLM 项目的成本是每分钟都在燃烧的 token,预算意识必须前置。
七、给类似项目的建议
如果你也在做”AI 中间件”类产品(评测、Agent、Workflow):
- 先做减法:MVP 不要 20 个功能,3 个就够;
- 早期付费用户:找 5 个愿意付费的客户,比 100 个观望者有用;
- 可观测性 Day 1:日志/指标/追踪比功能多;
- LLM 抽象先想清楚:再写第一行业务代码;
- 保留人工兜底:AI 不可靠时,人工通道要有。
这五条里,最容易忽略的是最后一条”人工兜底”。AI 的不可靠是概率性的,总会有漏网之鱼,评测结果有争议时如果没有人工复核通道,用户对平台的信任会直接崩塌。我们吃过这个亏:有一次评分争议没有人工介入通道,用户直接流失,后来才补上人工复核。
八、个人成长里的三件事
把十四个月的成长压缩成三句话,每句都是从一个具体经历里抽出来的:
- 从”实现功能”到”设计系统”;
- 从”能跑就行”到”生产级质量”;
- 从”写代码”到”权衡决策”。
第三件事最难。以前看到问题就想着手写代码,现在会先问一句”值不值得现在做”,这背后是预算、人力、风险的综合判断。能做出这种权衡,才算真正从执行者变成了决策者,也是这十四个月最宝贵的个人收获。
常见问题(FAQ)
Q1:学习路径怎么走?
从”会用框架”到”理解原理”到”能设计系统”。
Q2:需要哪些前置知识?
Java/TS 基础 + 分布式系统基础 + LLM API 基础。
Q3:最难的部分是什么?
不是技术,是”识别真问题”和”持续投入”。