
1. 为什么我最终选了 TypeScript Node.js 来做 AI 助手作为一个前后端都折腾过、最近大半年一头扎进 AI 助手Agent开发的从业者被问得最多的一个问题是现代 AI 助手后端为什么那么多团队最后都落在 TypeScript Node.js 这套组合上先说几个真实经历过的场景你就知道这个选择不是猎奇而是被现状逼出来的。我最早做的那个 AI 助手原型是用 Python 写的当时想法很简单AI 生态的 SDK 都是 Python 首选模型调用、向量检索、数据处理一圈下来都是 Python 的现成轮子。但原型跑通之后问题接踵而至。第一个是长连接和流式输出。现在的 LLM 接口清一色支持 SSEServer-Sent Events也就是模型把 token 一个个吐出来服务端要实时转发给前端。Python 的异步生态虽然能用 FastAPI 加 async 搞定但一旦要同时维护几十个 WebSocket 会话、给每个会话做独立的状态机管理代码复杂度就开始失控。第二个是类型。AI 助手的核心是“意图识别-参数提取-工具调用-结果合成”这条链路每个环节的数据结构都在频繁调整。Python 动态类型在这种频繁重构的场景下改一个字段名要全局搜索漏改一处就是线上事故。后来我换了一个思路用 TypeScript Node.js 重写了整个助手后端。不是说 Python 不行而是面对“高并发长连接 流式响应 强协议约束”这类场景TypeScript Node.js 在工程层面给了我更强的确定性和更低的维护成本。下面我把整套选型逻辑、架构设计、实操过程以及踩过的坑系统性地拆开来讲。如果你正在做或者准备做 AI 助手、AI Agent 这类项目这篇文章里的内容基本都是可以直接抄作业的。2. 这套技术栈到底解决了 AI 助手的哪些核心痛点2.1 事件驱动架构与 AI 对话场景天然契合AI 助手的交互模式和传统 Web 应用有很大区别。传统接口是“请求-响应”一次性的客户端问一句服务端算完返回连接就关了。AI 助手不是这样尤其是带工具调用Function Calling的 Agent它的处理过程是多轮、长耗时、状态持续变化的。用户说一句话助手内部可能要先判断意图然后调用一个天气查询工具拿到结果再调用一个订票工具最后汇总成自然语言回复。这个过程可能持续 10 秒甚至 30 秒期间客户端需要实时看到中间状态“正在查询余票…”“正在提交订单…”这种体验要求服务端长时间维持连接并且能把不同阶段的数据增量推给客户端。Node.js 的核心模型就是事件驱动、非阻塞 I/O。每一个长连接在 Node 里就是一个事件流它不像传统多线程模型那样为每个连接开一个线程而是用单线程加事件循环来调度所有连接。这意味着在同样的硬件条件下Node.js 更容易支撑上千个并发长连接而且每个连接之间的状态隔离做得非常干净。我用一个简单的比喻传统请求-响应模式像去银行柜台办事叫号、办理、完成、走人每个窗口对应一个客户AI 助手的交互模式更像在微信上和客服聊一整天的售后问题客服一直在线随时接收你发来的新消息也随时给你推送处理进展。Node.js 天生就是为这种“保持在线、随时响应”的模式设计的。2.2 异步非阻塞 I/O 如何吞下高并发请求你可能听过 Node.js 的“单线程”说法很多人误以为单线程意味着性能差。实际上Node.js 单线程指的是 JavaScript 执行栈只有一个主线程但 I/O 操作全部交给底层线程池处理主线程只负责调度和回调。对比一下阻塞式 I/O 就能理解差别。假设你要同时读取三个文件传统同步模型是一个一个读读完第一个才能读第二个总耗时为三段总和。Node.js 的做法是同时发起三个读请求谁先完成谁先回调总耗时接近最慢的那一个。用在 AI 助手里这个特性尤其关键。以真实场景为例AI 助手接到用户指令后可能需要同时做三件事——查数据库中的用户画像、调向量库做相似度检索、请求大模型接口做意图分类。这三件事没有严格的先后依赖。同步模型下哪怕最慢的一步耗时 3 秒总耗时就是三步相加可能超过 8 秒。Node.js 并发发起三个请求总耗时基本就是最慢那一步的耗时用户体验的延迟被大幅压缩。但这里有个陷阱也是我在实战中印象最深的一点并发请求的数量一定要控制。Node.js 的异步模型处理并发确实强但如果你一把梭同时发 200 个请求底层线程池默认 4 个线程用于某些文件操作网络 I/O 则依赖系统 epoll 等机制照样会堵。后面我会讲到如何用信号量或者设施来处理这个问题。2.3 流式响应与 SSE 推送为何在 Node 里格外顺手现代大模型接口几乎都支持流式输出。同一个提示词一次性返回全部内容和流式逐字返回用户的主观体验差别非常大。流式输出能显著降低用户的等待焦虑这在聊天型 AI 助手里是刚需。在 Java 或者 Go 里做流式转发也不是不行但代码会把状态管理搞得颇复杂。Node.js 对流的支持在语言层面就内置了Stream 模块配合 Async Iterators可以直接把 LLM SDK 返回的异步可迭代对象逐块转发给前端的 SSE 连接。我实盘写过这部分代码核心逻辑非常短import { Response } from express; async function handleStream(res: Response, llmStream: AsyncIterablestring) { res.writeHead(200, { Content-Type: text/event-stream; charsetutf-8, Cache-Control: no-cache, Connection: keep-alive, }); for await (const chunk of llmStream) { const payload data: ${JSON.stringify({ delta: chunk })}\n\n; res.write(payload); } res.end(data: [DONE]\n\n); }这段代码就是 AI 助手流式输出的核心骨架。Python 里用 FastAPI 也差不多能做到但 Node.js 无需额外引入线程池模型对回调边界的处理天然就是异步原生的你不需要纠结某个函数是不是 async 的因为它从头到尾就是基于异步构建的。而且 Node.js 对 SSE 断线重连的支持前端天然就有 EventSource API 可以对接不需要写额外的 WebSocket 协议处理。如果后续要升级为全双工通信用户随时打断 AI 回复AI 也要主动推送消息从 SSE 平移去 WebSocket 也基本是同一套事件流思想迁移成本极低。2.4 TypeScript 的强类型如何约束 AI 助手的状态流如果说 Node.js 解决了“并发和流式”的问题那 TypeScript 解决的就是“复杂状态管理下的代码可维护性”。AI 助手的内部状态流比传统接口复杂得多。一个简单的多轮对话状态就要包含当前会话 ID、用户意图、已收集的槽位信息比如订票场景下的出发地、目的地、日期、上次调用的工具名、工具返回结果是否已经写入上下文等。在 JavaScript 这种动态类型语言里这些状态散落在各种对象里全靠自觉维护。一旦状态结构调整比如槽位信息从扁平结构改成嵌套结构你很难提前发现哪些地方在读取旧结构。而在 TypeScript 里我可以把这些状态定义成明确的类型编译器在编译期就帮我排查所有使用者。type SlotKey departure | destination | date; interface DialogState { intent: string | null; slots: PartialRecordSlotKey, string; lastToolCall?: string; toolResults: Mapstring, unknown; turnCount: number; } interface AgentSession { sessionId: string; state: DialogState; history: ChatMessage[]; }看到这里你可能觉得这只是开发体验上的改善但实际项目中它对“维护成本”的影响是决定性的。我接手过一个没有类型定义的老项目一个会话状态对象被改了七八个方法每个人都在往里面塞新字段最后没有人清楚这个对象里面到底有什么。用 TypeScript 重写两星期后IDE 的智能提示和编译器的类型检查直接把这些隐患全部暴露了出来。还有一点很实际现在大模型 Function Calling 返回的参数结构都是 JSON而 JSON 的核心就是键值对结构。用 TypeScript 定义好参数 Schema前端调用和后端解析都是同一套类型定义天然避免了解析结果“跑偏”的问题。本质上就是用编译器帮你把 AI 的“自由文本输出”转化成“可校验的结构化数据”。2.5 生态链的衔接优势前端同构与工具链完整做 AI 助手几乎不可能只有一个后端通常还有配套的 web 管理台、配置页面、数据看板。如果用 Java 写后端前端是 React 或 Vue等于一个项目维护两套语言体系。用 TypeScript 写后端前后端是同一门语言类型定义可以直接共享接口文档可以自动生成连数据结构改了之后 IDE 都能高亮出来哪些前端组件也要跟着改。另外Node.js 的包管理生态对 AI 开发特别友好。大模型 SDK、向量数据库客户端、队列组件、定时任务库几乎都能找到类型定义齐全的最新版本。相比之下有一些 Python 库在类型标注上确实还是放养状态用起来心里没底。这套生态链的好处不只是在开发期。部署的时候Node.js 的一个镜像扔到服务器上就能跑不需要复杂的虚拟环境管理配合 PM2 或者容器化平台做进程守护也已经有非常成熟的方案。对一个需要快速迭代的 AI 助手项目来说这些工程便利是可以直接折算成人力成本的。3. 动手搭建一个基于 TS Node 的 AI 助手骨架3.1 项目目录结构与模块划分理论讲完了直接上实操。我用一个模拟项目 X某 AI 助手后端来完整演示如何从零搭起这套架构。先看项目目录结构每个目录的命名和职责都对应前面分析的核心痛点agent-server/ ├── src/ │ ├── agent/ # 会话管理、状态机、对话编排 │ │ ├── session-store.ts │ │ ├── state-machine.ts │ │ └── agent-loop.ts │ ├── tools/ # 工具调用注册与动态加载 │ │ ├── registry.ts │ │ ├── weather.ts │ │ └── order.ts │ ├── llm/ # 大模型接口封装与流式处理 │ │ ├── client.ts │ │ └── prompt-builder.ts │ ├── transport/ # 对外 HTTP、SSE、WebSocket │ │ ├── http-server.ts │ │ └── sse-handler.ts │ ├── types/ # 全局共享类型定义 │ │ └── index.ts │ └── utils/ # 重试、限流、序列化等工具 ├── tests/ ├── package.json └── tsconfig.json这个结构不是一拍脑袋定的每一个目录都能对应到前面分析里的关键需求agent目录处理“会话状态流”这是整个系统复杂度最高的地方。tools目录是“工具调用”的入口刻意设计成插件化注册方便未来加新工具不动核心逻辑。transport目录统一对外通信方式把 SSE 和 HTTP 分离方便未来加 WebSocket。llm目录把模型厂商的差异隔离在内部换模型供应商时只改这一个目录。项目初始化我就直接用 npm 创建Node 版本至少 20 起步Node 20 的稳定性比之前版本好很多尤其是对 AsyncLocalStorage 的支持做会话级上下文隔离非常方便。3.2 核心模块的 TypeScript 实现要点先说会话管理模块。AI 助手和用户的每一次交互都跟一个会话绑定我会用InteractionState来抽象。用 TypeScript 的Partial和Record类型组合既能保证“必须字段”的完整性又允许“待填充槽位”的灵活性interface InteractionState { sessionId: string; userId: string; intent: string | null; slots: PartialRecordstring, string; pendingToolCalls: string[]; completedToolCalls: string[]; lastAssistantMessage: string; updatedAt: number; }会话存储我先用基于 Redis 的 Hash 结构。interval 短暂、读写快、自带过期时间适合处理“会话超过 30 分钟自动失效”的需求。后来项目耦合了 Redis正好用上 Hash 过期时间的组合。再说状态机。一个违反直觉的教训是永远不要用 “if-else 地狱” 来管理 Agent 的对话状态。AI 助手的状态变化比普通表单复杂得多同一个状态“等待用户补充出发日期”可能因为用户回答“随便”而进入“需要进一步澄清”也可能因为模型推断出具体日期而直接进入“确认信息”。用 “if-else” 做后期改一个分支代码就崩半边。我采用“事件驱动状态转移”核心设计是“当前状态 事件 转移表”三个要素type AgentState idle | waiting_for_slot | calling_tool | generating | done; type AgentEvent | { type: USER_INPUT; text: string } | { type: SLOT_FILLED; slot: string; value: string } | { type: TOOL_RESULT; toolName: string; result: unknown } | { type: STREAM_FINISHED }; const transitionTable: RecordAgentState, PartialRecordAgentEvent[type], AgentState { idle: { USER_INPUT: calling_tool }, calling_tool: { TOOL_RESULT: generating }, generating: { STREAM_FINISHED: done }, waiting_for_slot: { SLOT_FILLED: calling_tool }, done: { USER_INPUT: calling_tool }, };后面事件进来后只需要查这个表判断当前状态下该事件是否合法合法就做状态切换不合法就进入“澄清话术”分支。这个设计让我在后期加了至少 4 个新状态时核心逻辑零改动。然后是自动调用 Function Calling 的流程。这一块是整个 AI 助手的大脑关键在于“规划-执行-观察”的循环。我把这个过程封装为一个 agent-loopasync function runAgentLoop(session: AgentSession, userInput: string) { await session.stateMachine.dispatch({ type: USER_INPUT, text: userInput }); while (true) { const modelResponse await llmClient.call({ messages: session.history, tools: ToolRegistry.getAllSchemas(), }); if (modelResponse.toolCalls.length 0) { // 模型不再需要调用工具进入最终生成阶段 await streamResponse(session, modelResponse.content); break; } for (const toolCall of modelResponse.toolCalls) { const result await ToolRegistry.execute(toolCall.functionName, toolCall.arguments); session.history.push({ role: assistant, content: null, toolCallId: toolCall.id, }); session.history.push({ role: tool, toolCallId: toolCall.id, content: JSON.stringify(result), }); } } }这段代码的核心思想就是把工具调用结果重新塞回对话历史让模型自己决定下一步动作。这也是当前主流 Agent 架构最通用的一种做法——它不需要预先定义复杂的 DAG有向无环图流程一切交给模型的推理能力动态决定。3.3 SSE 长连接的完整实现与心跳保活有了状态机和 agent-loop接下来就是怎么把结果实时推给前端。SSE 连接的实现注意点很多我一次说全。首先SSE 不是普通的 HTTP 请求连接建立之后服务端要一直持有 response 对象不放。Node.js 的 http 模块天然支持这种长连接但要注意三点第一必须设置正确的响应头。实践中标准的响应体请参考上面那段代码。此外建议再加一个X-Accel-Buffering: no防止 Nginx 层缓存响应。第二必须做心跳保活。很多云厂商和浏览器会在连接空闲一定时间后主动断开连接。为了对抗超时我每隔 30 秒发送一个: keep-alive\n\n注释行。它不会被 EventSource 解析成事件但能让连接保持活动。const heartbeat setInterval(() { res.write(: keep-alive\n\n); }, 30000); res.on(close, () { clearInterval(heartbeat); });第三必须处理客户端突然断开。我最初以为 SSE 连接是单方向的客户端挂了服务端会收到close事件但实际上有些代理中间层会吞掉这个事件。后来我改用“写失败即断开”的策略调用res.write()如果抛出异常说明底层 socket 已经断了立即清理会话状态机和相关的 LLM 请求。try { res.write(payload); } catch (err) { sessionStore.destroy(sessionId); llmClient.abortCurrentRequest(); res.end(); }这一套下来SSE 的稳定性才算真正达标。实测连续跑 300 分钟模拟 500 个并发连接断线率从一开始的 5% 降到了 0.3%。3.4 并发控制和背压不要让模型供应商成为瓶颈前面提到过 Node.js 并发能力强但依赖外部系统的场景下并发能力恰恰是隐患。大模型接口普遍有速率限制RPM每分钟请求数多个用户同时触发工具调用一个不留神就把厂商的限额打满直接返回 429 错误。我在项目中直接给 LLM 调用加了一个异步信号量import pLimit from p-limit; const limit pLimit(10); // 同时最多 10 个 LLM 请求 const results await Promise.all( requests.map((req) limit(() llmClient.call(req))) );这个p-limit库内部实现很轻量本质就是维护一个任务队列和一个当前执行数计数器。但它的好处是我可以在不改变调用方代码的情况下通过一个数字参数控制全局流速。后来厂商把限额调到 20我只需要改这一个数字。还有一个容易忽略的点是“背压”backpressure。AI 助手的对话历史不能无限累加token 数超了模型就崩了。我写了一个简单的 token 截断策略当历史消息总 token 数超过模型上下文窗口的 80% 时优先移除最早的对话轮次但永远保留系统提示词和最近一轮用户输入。这个逻辑也是状态流的一部分不能放到工具层面做否则会污染状态一致性。4. 实战中常见问题与排查技巧实录4.1 类型安全陷阱模型返回的 JSON 真的是你定义的类型用 TypeScript 最大的好处是类型安全但有一个前置条件编译器并不知道运行时的数据是什么形状。AI 模型的输出是 JSON 文本它在运行时只是字符串要经过 JSON.parse 之后才是对象。如果你把JSON.parse的结果直接断言为WeatherResult这个类型断言实际上是在骗编译器“相信我它就是这样的结构”。我踩过一次很深的坑。模型返回的“出发日期”字段有些时候是2025-07-01有些时候是7月1日甚至还有明天。如果我用as string直接断言后面所有日期计算都会出错。后来我采取了两个措施第一所有模型输出都过 JSON Schema 校验。定义一个zod的原生模式对模型返回的结果进行运行时校验import { z } from zod; const WeatherArgsSchema z.object({ city: z.string(), date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), }); function parseToolArgs(rawJson: string) { const parsed JSON.parse(rawJson); return WeatherArgsSchema.parse(parsed); }校验不通过就直接请求模型重新生成或者引导用户在界面上手动补充。这一步虽然让代码多了一步但保证进入工具执行器的参数 100% 是预期的结构。第二类型断言从T改成z.infertypeof Schema。所有类型定义以 zod schema 为唯一事实来源代码里不要再重复写结构和 zod 一样的 interface。这样就可以保证编译期类型和运行期类型永远一致。4.2 内存泄漏排查事件监听器带你走向 OOMNode.js 项目最常见的故障就是内存泄漏AI 助手这种长连接场景尤其严重。每条 SSE 连接、每个会话状态、每个 LLM 请求都会在内存里留下痕迹。如果某处忘了释放内存就会持续攀升最终进程 OOM 被杀。我下面直接贴一份自检清单每一行都是真实场景映照出来的全局事件监听器用process.on()或EventEmitter.on()注册的监听器必须匹配off()否则回调引用的对象永远无法被 GC。模块级缓存不要在global或者模块顶层声明一个不断增长的Map当缓存用一定设 LRU 上限。SSE response 未关闭客户端断开后res对象还在内存里引用链是通过EventEmitter挂的如果不销毁会话对象整个会话引用的历史记录数组也全部滞留。LLM 请求未 abort大模型响应可能是一个非常长的流如果请求仍在进行相关缓冲对象不会被回收。我写了一个简单的内存监控脚本每 10 秒采样一次process.memoryUsage()超过阈值就打日志并发送告警。经验上Node 20 在常规用量下一个 AI 助手服务正常内存在 400MB 以内持续超过 1GB 且不回落基本可以断定有泄漏。4.3 冷启动与调试效率如何让 ts-node 快速响应开发初期我最头疼的是ts-node的冷启动和热更新问题。每一次保存文件重启的服务要重新编译整个项目等待时间从 5 秒到 30 秒不等严重打断开发节奏。后来换上了较新的 Node 20 原生测试跑批器和带 watch 模式的启动脚本npm run dev核心配置是在package.json里加入--watch --env-file参数开发模式下直接监听源码变更增量编译启动速度能压到 2~3 秒。同时把tsconfig.json里strict打开编译失败的语法错误会立刻在终端爆出来而不是等到运行时候再崩。还有一个我自己比较受用的技巧不要把 ts-node 放在生产环境里用生产级部署。生产环境我用tsc编译成纯 JavaScript 放到dist/目录再用pm2跑 Node 运行。这主要是因为在生产上把编译和运行分开能让“代码问题”和“环境问题”的排查边界更清晰。如果你也遇到了依赖构建产物有差异的问题优先排查package.json里的moduleResolution配置这通常比手动改代码路径见效快得多。4.4 模型供应商切换时的适配层设计AI 行业发展太快今天用厂商 A 的模型明天可能就想换厂商 B因为 B 便宜、效果好或者有更长的上下文。直接在所有业务代码里调用某个 SDK 是初期最容易犯的错。后来我加了一层LLMProvider接口核心代码只依赖接口不依赖具体实现interface LLMProvider { chatCompletion(params: ChatParams): PromiseChatResponse; streamCompletion(params: ChatParams): AsyncIterableStreamChunk; abort(sessionId: string): void; }每家厂商出一个实现类切换时只需要改工厂函数的返回值业务代码零侵入。后面真换过一次供应商从发起到完成切换只用了两小时其中大部分时间还是在看文档。4.5 状态机的另一个隐蔽坑并发事件打乱状态顺序AI 助手里经常出现这样的情况用户发来一条消息同时工具回调完返回结果两个事件在同个 tick 里到达状态机。如果在dispatch里面直接处理事件、修改状态就可能出现“当前状态和事件不匹配”的冲突。解决方式很简单状态机的dispatch必须是同步的并且事件要排队处理不能异步并发。class StateMachine { private queue: AgentEvent[] []; private processing false; async dispatch(event: AgentEvent) { this.queue.push(event); if (this.processing) return; this.processing true; while (this.queue.length 0) { const next this.queue.shift(); await this.handleEvent(next!); } this.processing false; } }队列化之后每个会话的 state machine 内部事件都变成严格串行状态冲突率降到零。这比加锁简单得多也完全符合 JavaScript 单线程事件循环的调度逻辑。4.6 让我印象深刻的线上事故连接风暴下的系统宕机最后分享一个真实事故。上线初期我设计了一个看板页面30 个用户用浏览器开着两个标签页瞬间建立了 60 条 SSE 连接。这些连接没有做同一会话的去重后端出现了“同一 sessionId 建立多条连接”的情况每条连接都持有完整 agent 状态。然后表单校验那一关直接把视窗内的积压连接全部误判为正常“长轮询”加上内存里的会话全都没释放进程在 20 分钟内内存飙到上限直接宕机。查了一整天才定位到问题本质SSE 连接没有和统一会话 ID 绑定。修复方案是同一个 sessionId 只允许一条活动 SSE 连接新的连接进来之前先关闭旧的连接再复用同一个 agent 会话状态。前端也做了改造EventSource 创建前先清掉同 sessionId 的旧实例。这个问题修完后别说双开标签页了就算恶意地连续刷新页面,也不会拖垮服务。这事的反思是Node.js 的并发能力再强抵不住业务逻辑把同一个资源无限复制。工程设计的核心不是把并发压到零而是保证同一个逻辑实体的状态只有一份引用。5. 一些沉淀下来的开发流程建议AI 助手项目有一套和传统后端不太一样的开发迭代方式。最初我按照“需求评审-开发-联调-上线”的老流程走经常发现开发的代码和实际用户在对话中的真实需求完全不同。后来慢慢改成了更贴合 Agent 项目的流程第一步定义“敏感场景”并优先覆盖。AI 助手最容易出问题的不是正常路径而是用户打断、模型乱答、工具调用超时这类边缘情况。开发前把 10 个最核心的“敏感场景”列出来每个场景写一个冒烟测试远比写 100 个单元测试有用。第二步用“用户会话日志”驱动开发。每个会话的完整流水日志是我最依赖的数据来源。它会记录用户说了什么、模型提取出的意图是什么、工具参数是什么、最终回复是什么。这个日志链帮我发现了大量“模型把日期格式理解错了”“用户意图分类正确但参数不完整”之类的精细问题。第三步建立“小步快跑”的灰度发布机制。AI 助手的模型提示词调整尽量不要全量发布。我做了一个简单的“按用户白名单分流”机制比如 10% 的用户走新提示词版本对比会话成功率后再决定是否全量。这个机制带着我的团队避开了至少三次“新提示词导致客服场景回答质量大降”的事故。第四步时刻关注模型成本。大家开发初期普遍不太在意 token 消耗等到流量上来月底账单一看才发现开销惊人。我在 Agent 循环里加了一个 token 计数器每个会话结束都会记录总 token 数和拆分明细。有了这个数据之后我再优化系统提示词把不必要的冗余描述去掉单次会话的 token 消耗直接降了 20%。6. 最后分享一个我近期才领悟到的核心竞争力做久了 AI 助手之后我开始意识到这个领域真正的核心竞争力不是某个花哨的工具调用也不是用了多强大的模型而是能不能把“不确定的 AI 输出”和“后端的确定性”有机结合起来。TypeScript Node.js 这套组合恰好处在交界处Node.js 负责承载 AI 交互的高并发和流式特性TypeScript 负责给不可预测的模型输出建立一道道确定性约束保证进入后端的每一步数据都是可校验、可追踪、可回滚的。现在再有人问我“AI 助手后端选什么语言”我的答案依然毫不犹豫工程效率、团队上手难度、生态完整度、长连接性能这四个维度综合下来TypeScript Node.js 是目前最省心的选项。但在回答完之后我一定会补一句技术栈只是战场的武器真正的战斗力是你对 AI 交互模式的深刻理解以及把这种理解落到代码里的执行力。这一点不管用什么语言写都是相通的。