聊聊技术探索与实践:一个深圳炼丹师的前端“翻车”与重生记
大家好,我是小陈,坐标深圳南山科技园,日常在一家和腾讯系业务打得火热的中型互联网公司做 AI 算法工程师。没错,就是那个天天调参、炼模型、被 GPU 显存支配的男人。不过今天这篇文章不聊 loss 收敛或者 attention 机制,而是想聊聊去年我“被迫”卷入的一个前端项目——以及它如何让我重新理解了“全栈”的真正含义。
事情得从去年 Q3 说起。我们团队负责一个智能客服系统的算法模块,原本我的工作止步于 API 输出结构化意图识别结果。但产品经理某天在周会上轻飘飘一句:“能不能让前端直接渲染对话流程图?用户拖拽调整逻辑,实时生效。” 我当时心里咯噔一下:这不就是把后端逻辑甩锅给前端嘛!但 PM 补了一句:“你们不是说要‘端到端体验’吗?” 好吧,我闭嘴了。
被逼上梁山:从 PyTorch 到 React 的奇幻漂流
说实话,我上一次写前端还是大学用 jQuery 给社团做个活动报名页。这几年虽然偶尔用 Flask 写个 demo,但 React/Vue 这些现代框架对我来说基本等于“听说过”。但 deadline 不等人——双 11 大促前必须上线新交互功能,而前端组人手紧缺(据说隔壁大厂又挖走了两个)。领导拍板:“小陈,你不是老用 ChatGPT 写脚本吗?试试看,搞不定再叫外援。”
于是,我开始了白天调 BERT,晚上啃 React 的双重生活。第一天就踩了个经典坑:
// 初版代码,自信满满
const FlowEditor = () => {
const [nodes, setNodes] = useState([]);
useEffect(() => {
fetch('/api/intent-graph')
.then(res => res.json())
.then(data => setNodes(data)); // 直接赋值!
}, []);
return <Canvas nodes={nodes} />;
};
结果一加载就报错:Cannot add property x, object is not extensible。查了半天才发现,后端返回的 JSON 对象被 freeze 了(出于安全考虑),而我用的可视化库 D3 需要动态加属性。当时真的想砸键盘——这哪是前端开发,这是在解谜!
还好有 Claude 救我狗命。我把报错贴进去,它秒回:
“建议深拷贝后再操作:
setNodes(JSON.parse(JSON.stringify(data))),或者用structuredClone(如果你的浏览器支持)”
虽然有点 dirty,但先跑起来再说。这让我意识到:前端的状态管理,远比我想象的脆弱又敏感。
技术选型:在“炫技”和“能跑”之间反复横跳
接下来是核心问题:用什么方案画这个流程图?选项很多:
- 自己用 SVG + D3 手搓
- 用现成的 React Flow
- 上 AntV/X6(阿里系,文档中文友好)
- 甚至考虑过用 Three.js 做 3D 版(别笑,PM 真提过)
我和前端同事(唯一留守的那个)开了个小会,列了张对比表:
| 方案 | 学习曲线 | 社区支持 | 与现有 Ant Design 兼容 | 可维护性 |
|---|---|---|---|---|
| 手搓 SVG | ⭐⭐⭐⭐⭐ | ❌ | ✅ | ❌(我写的没人敢改) |
| React Flow | ⭐⭐ | ✅✅✅ | ✅ | ✅✅ |
| AntV/X6 | ⭐⭐⭐ | ✅✅ | ✅✅✅ | ✅✅ |
| Three.js | ⭐⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
最终我们选了 React Flow —— 虽然它是国外库,但 TypeScript 支持好,API 清晰,而且我们的 UI 框架是 Ant Design Pro,社区有成熟集成方案。更重要的是:我一个算法工程师,没时间造轮子。
但选型只是开始。真正的地狱是“交互逻辑”。比如用户拖动一个节点到另一个节点上方,要自动连线。听起来简单?实际要考虑:
- 节点是否可连接(有些意图不能作为起点)
- 连线方向限制(只能从 A->B,不能反向)
- 实时校验环路(不能形成死循环)
我一度想放弃,直接扔给后端校验。但想到“用户体验”,咬牙硬上。用 React Flow 的 onConnect 回调 + 自定义校验函数,总算搞定。代码丑得我自己都不忍直视,但能跑就行:
const isValidConnection = (connection: Connection) => {
const source = nodes.find(n => n.id === connection.source);
const target = nodes.find(n => n.id === connection.target);
// 业务规则校验
if (source?.type === 'end') return false;
if (target?.type === 'start') return false;
// 检测环路(简化版)
const visited = new Set<string>();
const hasCycle = (id: string): boolean => {
if (visited.has(id)) return true;
visited.add(id);
const outs = edges.filter(e => e.source === id).map(e => e.target);
return outs.some(hasCycle);
};
return !hasCycle(target!.id);
};
开发心得:代码可读性比“聪明”更重要
在这个项目里,我最大的转变是:不再追求“一行搞定”。
以前写 Python 脚本,喜欢用 list comprehension 嵌套三层,觉得特 hacker。但在前端协作场景下,这种“炫技”等于埋雷。有一次我写了个 reducer 函数,用了三元运算符嵌套,自测没问题。结果第二天测试同学提了个 bug:删除节点时,相邻连线没清除。
我 debug 半小时才发现,某个边界条件没覆盖。后来重构成清晰的 if-else 分支,还加了注释:
// 删除节点时,同步清理关联的边
case 'DELETE_NODE':
return {
...state,
nodes: state.nodes.filter(n => n.id !== action.nodeId),
edges: state.edges.filter(
e => e.source !== action.nodeId && e.target !== action.nodeId
),
};
同事 review 时直接夸:“这代码我看得懂!” —— 对程序员来说,这是最高赞誉了。
另外,TypeScript 真香。虽然一开始嫌麻烦要写 interface,但当项目复杂度上来后,编辑器自动提示 + 编译时检查,救了我无数次。特别是和后端联调时,定义好 IntentNode 和 FlowEdge 的类型,前后端契约清晰,再也不用猜字段名了。
求职视角:为什么我劝算法工程师也学点前端?
最近在帮朋友内推,聊到技能栈,我发现一个趋势:大模型时代,纯算法岗越来越难找,但“AI+工程”复合人才吃香。
比如我们这次做的智能客服流程编排,表面是前端功能,底层依赖 NLU 模型输出的结构化数据。如果我不懂前端怎么消费数据,就很难设计出合理的 API 契约。反过来,如果前端不懂一点 NLP 概念(比如什么是 slot filling、intent confidence),也很难做出合理的交互反馈。
上周面试一个候选人,简历写“熟悉 Transformer”,但问到“如果前端需要实时显示模型推理进度,你怎么设计接口”,直接懵了。而另一个候选人,虽然论文不多,但做过一个用 Web Worker 跑 ONNX 模型的小工具,当场给了二面。
所以我的建议是:不要把自己锁死在“算法”标签里。学点前端(哪怕是基础 React + TS),至少能:
- 快速验证 idea(不用等前端排期)
- 更好地和产品/前端沟通(说人话)
- 在求职时多一张底牌(尤其创业公司/中小厂)
尾声:炼丹之外,也要学会砌砖
现在回头看,这个“半路出家”的前端项目虽然让我掉了不少头发( literal,发际线又后移了),但收获远超预期。不仅交付了功能,还在团队里建立了“小陈不仅能调参还能写界面”的人设(笑)。
更重要的是,它让我明白:技术探索不是为了成为全栈,而是为了打通认知闭环。当你知道数据从模型出来后,要经过怎样的旅程才能变成用户看到的界面,你设计的系统才会更健壮、更人性化。
对了,上周五晚上加班修复最后一个样式 bug 时,运维同事路过吐槽:“你们前端是不是又把 z-index 设成 999999 了?” 我笑着回他:“这次没,我用了 CSS-in-JS。”
——毕竟,在深圳这片热土上,能跑的代码才是好代码,管它用什么写的呢。
(完)

评论 0