ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI编程会话共享实战:让coding session像在线文档一样可协作

AI编程会话共享实战:让coding session像在线文档一样可协作 在过去一段时间的 AI 编程实践中很多人已经习惯了让 AI 独立完成一个功能模块或者在 Cursor、Trae、Claude Code 之类的工具里拉起一个长时间运行的 coding session。AI 写代码不再是新鲜事真正让人头疼的是另一个问题当你的 AI 编程会话已经进行到一半上下文里加载了项目结构、技术方案、历史决策这时候你想让同事接手看一个问题或者想让远程的合伙人快速加入这个“正在进行”的开发现场该怎么办最近在 Hacker News 上看到一个叫 Entangle 的项目标题很有意思把一场实时的 AI coding session 用三个单词交给任何一个人。这里的核心思路是AI 编程会话不应该封闭在发起人的终端里它应该像在线文档一样可以被共享、被接管、被协作。这篇文章会围绕这类“AI 编程会话共享”的需求展开聊聊 session 到底是什么、Entangle 这类工具在解决什么问题并给出一套可以自己动手实现的轻量级会话共享方案。1. 背景与核心概念AI coding session 到底是什么1.1 从一次真实的开发场景说起假设你在终端里运行了一个 AI coding agent它正在根据你的指令修改项目代码。这个 agent 已经读过项目里的核心文件分析过数据库表结构和你确认过技术选型。现在你临时有事需要离开希望让同事继续这个任务但同事打开自己电脑后看到的是一个完全陌生的项目副本他并不知道 AI 之前做了哪些决策、改过哪些文件、下一步计划是什么。传统的做法是写一份交接文档但交接文档永远赶不上真实会话的丰富度。更好的做法是把整个“现场”共享出去让另一个人进入同一个 session看到同样的上下文甚至可以继续向 AI 下达指令。1.2 什么是 AI coding session在 AI 辅助编程工具中session 通常指的是一次从启动到结束的完整交互过程。session 中保存了对话历史、用户指令、AI 生成的代码片段、工具调用记录以及 agent 对项目上下文的理解。从技术组成来看一个典型的 AI coding session 包含这几层组成作用示例对话上下文记录用户与 AI 的交互历史用户需求、AI 回复、修改建议项目上下文AI 已读取的代码文件、配置信息项目结构、依赖清单、关键类定义执行状态agent 当前正在执行或计划执行的任务等待用户确认、正在运行测试、报错重试会话元信息本次会话的配置和标识模型类型、温度参数、会话 ID如果你接触过 Vibe Coding大概会理解这种会话的“流动性”。开发者用自然语言描述需求AI 自主完成代码编写和调试人只看最终结果。这个过程非常依赖会话的连续性一旦 session 丢失AI 对项目的理解就会归零。1.3 为什么“共享 session”是一个值得解决的问题当前大多数 AI 编程工具里的 session 是私有的、单机的。它绑定在发起人的终端进程里别人无法直接查看。但在真实协作场景中代码开发从来不是单人游戏。一个项目可能会有前端工程师负责写页面后端工程师负责 APIAI agent 在中间协调团队负责人想实时看到 AI 正在改哪些文件评估风险远程外包团队需要快速接手一个已经进行一半的 AI 编程任务非技术背景的产品经理想确认 AI 是否理解了需求。这些场景都指向同一个能力把 live session 交给另外一个人对方能无缝加入而不是从零开始。Entangle 用三个单词作为共享口令本质上是在降低分享门槛。用户不需要理解什么 IP 地址、端口转发、SSH 密钥只需要把三个单词发给对方。1.4 三个单词背后的短码机制三个单词共享会话这个交互设计其实借鉴了安全领域的随机单词编码方案。系统把一串随机会话 ID 映射为三个常见英文单词例如apple-sunset-river。相比https://example.com/session/4f8a2d9c-1b3e-4f5a-8c7d-9e0f1a2b3c4d三个单词显然更好记忆、更容易口头传递。这种机制在技术上有几个关键点词库选择需要从常见英文单词中挑选一批避免生僻词或容易混淆的词。编码唯一性三段单词的组合空间足够大可以支撑大量并发会话。过期策略会话口令应该有时效性避免永久有效带来的安全风险。权限控制拿到口令的人拥有什么权限是只读查看还是可写接管需要明确区分。2. 环境准备与版本说明这一节我们围绕“自己实现一个简易的 AI coding session 共享工具”来准备环境。你可以把它理解为 Entangle 的一个最小复刻核心思路是给正在运行的 AI 编程会话生成一个短码另一端通过短码加入。2.1 技术选型考虑到大部分 AI coding agent 都运行在终端环境中我们的共享方案要尽量轻量不引入复杂的服务端编排。本文的示例会用到以下技术组件作用建议tmux终端复用工具保持 session 在后台运行tmux 2.9 及以上Python 3编写短码生成和会话管理脚本Python 3.8Node.js编写轻量 WebSocket 转发服务方案 BNode.js 14WebSocket实现实时消息转发使用 ws 库版本说明这里给出的版本只是常见环境参考具体版本需要根据你的项目实际情况调整。本文重点演示配置思路和代码结构不保证每个版本组合都能直接运行。2.2 项目结构我们准备实现两个方案方案 A基于 tmux 的会话共享适合一个人把自己的 AI 编程终端共享给另一个人操作最简单。方案 B基于 WebSocket 的短码共享服务更接近 Entangle 的形态支持多人查看和权限控制。项目目录规划如下entangle-demo/ ├── README.md ├── package.json # Node.js 项目依赖方案 B ├── server/ │ └── relay-server.js # WebSocket 转发服务 ├── scripts/ │ ├── generate_code.py # 短码生成脚本 │ ├── start_share.sh # 启动共享的脚本方案 A │ └── join_share.sh # 加入共享的脚本方案 A └── examples/ ├── basic_demo.py # 短码生成示例 └── permission_demo.js # 权限控制示例3. 核心原理拆解从 session 到共享会话3.1 终端会话的共享原理先看方案 A。很多 AI 编程工具是基于终端的交互界面例如 Claude Code、OpenAI Codex CLI 等。它们本质上是在你的 shell 里启动了一个交互式进程。如果能把这个终端进程“搬”到另一台机器上另一台机器的人就能看到完整输出并且可以输入指令。tmux 提供了一种非常简单的方式。tmux 的 session 允许多个客户端同时连接同一个会话看到相同的内容。只要 AI 编程进程运行在 tmux session 中另一个人通过 SSH 登录到这台服务器再执行tmux attach就能加入。但直接把 SSH 给别人的做法在生产环境不够安全所以我们可以加一层短码匹配。用户执行分享命令时系统创建一个新的 tmux session并把 AI 编程进程启动在里面然后生成一个短码。加入者提供短码后系统验证短码并执行 attach。3.2 WebSocket 转发原理方案 B 更接近 Entangle 这种面向多人的协作工具。它的原理可以拆成三步分享者启动一个共享会话把自己的终端输入输出转发到中继服务器。中继服务器维护一个会话表每个会话对应一个短码。加入者通过短码连接到中继服务器中继服务器把分享者的终端输出实时转发给加入者并把加入者的输入回传给分享者终端。这里的核心组件是终端模拟。分享者的终端输出不能直接作为纯文本发送因为里面有 ANSI 转义序列、光标控制符等。更规范的做法是使用xterm.js在前端渲染后端使用node-pty启动真实的 PTY 进程。不过本文作为入门演示我们做了简化只转发原始文本流。3.3 会话共享的安全边界不管采用哪种方案安全边界都是必须设计清楚的部分。共享会话至少需要考虑这几类风险风险说明缓解措施未授权访问有人猜到短码或拿到了过期口令口令过期、接入数量限制、校验用户身份指令注入加入者通过终端输入恶意命令默认只读模式需要提升权限才能写敏感信息泄露会话中可能打印数据库密码、API 密钥脱敏过滤、日志审计、不让加入者查看历史输出中间人攻击传输过程中数据被截获使用 WSS 加密传输生产环境必须启用 TLS在生产环境共享会话应该遵循最小权限原则。默认情况下加入者只能看不能写。只有显式授权后才允许接管这样可以有效降低风险。4. 实战案例搭建一个轻量级 session 共享服务下面进入核心部分。我们依次实现短码生成脚本、基于 tmux 的共享方案和基于 WebSocket 的转发服务。4.1 生成三个单词的短码首先生成短码。这里参考常见的随机单词编码方式我们从内置单词表中随机选取三个单词用短横线连接。为了保证可读性过滤掉长度过长或容易混淆的单词。# 文件路径entangle-demo/scripts/generate_code.py import random import string # 从常见单词中挑选 2048 个作为词库 # 生产环境建议使用更大的词库并过滤敏感词 WORD_POOL [ apple, sunset, river, cloud, forest, ocean, stone, breeze, falcon, maple, silver, ember, harbor, willow, comet, meadow, orbit, pine, quartz, raven, summit, thistle, brook, crystal, dune, eagle, fjord, grove, horizon, island ] def generate_share_code(word_count3): 生成由 word_count 个单词组成的分享短码。 例如apple-sunset-river words random.sample(WORD_POOL, word_count) return -.join(words) def validate_share_code(code): 校验短码格式是否合法。 仅允许小写单词和短横线防止注入问题。 allowed set(string.ascii_lowercase -) return all(char in allowed for char in code) if __name__ __main__: code generate_share_code() print(f生成的分享短码: {code}) print(f格式校验: {validate_share_code(code)})运行这段脚本python3 scripts/generate_code.py预期输出生成的分享短码: sunset-river-apple 格式校验: True短码的作用是降低人的记忆成本因此在词库设计上要避免两个问题单词太生僻对方拼不出来两个单词发音相似口述时容易混淆。如果你的团队使用中文交流也可以替换为短语码例如山川-湖泊-星辰原理相同。4.2 方案 A基于 tmux 的实时会话共享方案 A 的核心是 tmux。我们先写一个启动共享的脚本。# 文件路径entangle-demo/scripts/start_share.sh #!/usr/bin/env bash # 用法./start_share.sh 共享会话名 启动命令 # 示例./start_share.sh demo-session claude SESSION_NAME$1 START_COMMAND${:2} if [ -z $SESSION_NAME ] || [ -z $START_COMMAND ]; then echo 用法: $0 会话名 启动命令 echo 示例: $0 ai-demo \claude\ exit 1 fi # 检查 tmux 是否安装 if ! command -v tmux /dev/null; then echo 错误: 未检测到 tmux请先安装。 exit 1 fi # 如果同名 session 已存在提示用户 if tmux has-session -t $SESSION_NAME 2/dev/null; then echo 会话 $SESSION_NAME 已存在正在复用... else echo 创建新会话 $SESSION_NAME ... tmux new-session -d -s $SESSION_NAME $START_COMMAND fi echo 会话已启动。 echo 本地查看: tmux attach -t $SESSION_NAME echo 远程共享: 让别人登录服务器后执行 ./join_share.sh $SESSION_NAME然后写加入共享的脚本。# 文件路径entangle-demo/scripts/join_share.sh #!/usr/bin/env bash # 用法./join_share.sh 会话名 SESSION_NAME$1 if [ -z $SESSION_NAME ]; then echo 用法: $0 会话名 exit 1 fi if ! tmux has-session -t $SESSION_NAME 2/dev/null; then echo 错误: 会话 $SESSION_NAME 不存在。 exit 1 fi # 以只读方式加入会话避免误操作 tmux attach-session -t $SESSION_NAME -r实际使用流程分享者执行./start_share.sh ai-demo claude在 tmux 会话中启动 AI 编程工具。分享者把短码这里简化成会话名告诉同事。同事登录服务器后执行./join_share.sh ai-demo以只读模式查看实时输出。这里需要说明的是tmux 的只读模式并不是严格的权限控制。加入者如果知道 tmux 命令仍然可以通过其他方式切换到可写模式。更安全的做法是使用 tmux 的read-only插件或者在更底层做权限隔离。这个方案适合内部信任团队使用不适合面向不可信的外部协作者开放。4.3 方案 B基于 WebSocket 的短码共享服务如果你希望把共享能力做成一个 Web 服务让别人通过浏览器加入会话就需要 WebSocket 中继服务。先初始化项目依赖cd entangle-demo npm init -y npm install ws创建一个中继服务器// 文件路径entangle-demo/server/relay-server.js const WebSocket require(ws); const http require(http); const { generateShareCode } require(../scripts/generate_code); const PORT process.env.PORT || 8080; const server http.createServer((req, res) { res.writeHead(200, { Content-Type: text/plain }); res.end(Entangle Relay Server is running); }); const wss new WebSocket.Server({ server }); /** * 会话表结构 * sessionId - { * code: string, // 短码 * creator: ws, // 创建者连接 * viewers: Setws, // 观看者连接 * history: string[], // 历史消息 * createdAt: number, // 创建时间戳 * } */ const sessions new Map(); function createSession(creatorWs) { const sessionId Math.random().toString(36).slice(2, 10); const code generateShareCode(); sessions.set(sessionId, { code, creator: creatorWs, viewers: new Set(), history: [], createdAt: Date.now() }); return sessionId; } function findSessionByCode(code) { for (const [sessionId, session] of sessions.entries()) { if (session.code code) { return sessionId; } } return null; } wss.on(connection, (ws) { console.log(新连接建立); ws.on(message, (rawMessage) { let message; try { message JSON.parse(rawMessage); } catch (err) { ws.send(JSON.stringify({ type: error, data: 消息格式错误 })); return; } const { type, data } message; switch (type) { case create_session: // 创建者发起共享会话 { const sessionId createSession(ws); const session sessions.get(sessionId); ws.send(JSON.stringify({ type: session_created, data: { sessionId, code: session.code } })); console.log(创建共享会话 ${sessionId}短码: ${session.code}); } break; case join_session: // 加入者通过短码加入 { const sessionId findSessionByCode(data.code); if (!sessionId) { ws.send(JSON.stringify({ type: error, data: 短码无效或会话已过期 })); return; } const session sessions.get(sessionId); session.viewers.add(ws); ws.sessionId sessionId; ws.role viewer; ws.send(JSON.stringify({ type: join_success, data: { sessionId, history: session.history } })); console.log(加入会话 ${sessionId}当前观看者 ${session.viewers.size} 人); } break; case forward_output: // 创建者转发终端输出 { const sessionId ws.sessionId; if (!sessionId) return; const session sessions.get(sessionId); if (!session) return; if (session.creator ! ws) { ws.send(JSON.stringify({ type: error, data: 无权限转发输出 })); return; } session.history.push(data.output); const broadcast JSON.stringify({ type: terminal_output, data: data.output }); for (const viewer of session.viewers) { if (viewer.readyState WebSocket.OPEN) { viewer.send(broadcast); } } } break; case viewer_input: // 观看者输入指令默认可选 { const sessionId ws.sessionId; if (!sessionId) return; const session sessions.get(sessionId); if (!session) return; if (session.creator.readyState WebSocket.OPEN) { session.creator.send(JSON.stringify({ type: viewer_command, data: { viewerSessionId: ws.sessionId, input: data.input } })); } } break; default: ws.send(JSON.stringify({ type: error, data: 未知消息类型 })); } }); ws.on(close, () { if (ws.sessionId) { const session sessions.get(ws.sessionId); if (session) { session.viewers.delete(ws); if (session.creator ws) { // 创建者断开通知所有观看者 for (const viewer of session.viewers) { if (viewer.readyState WebSocket.OPEN) { viewer.send(JSON.stringify({ type: session_closed, data: 会话已结束 })); } } sessions.delete(ws.sessionId); console.log(会话 ${ws.sessionId} 已结束); } } } }); }); server.listen(PORT, () { console.log(中继服务已启动端口 ${PORT}); });以上是一个简化版的中继服务已经实现了短码创建、短码加入、输出转发和观看者输入回传四个核心功能。4.4 客户端调用示例为了演示效果我们写一个简单的 Node.js 客户端。分享者终端模拟器向中继服务推送输出观看者从浏览器或命令行实时接收。// 文件路径entangle-demo/examples/basic_demo.js const WebSocket require(ws); // 连接中继服务 const relayUrl process.env.RELAY_URL || ws://localhost:8080; const ws new WebSocket(relayUrl); // 模拟角色通过启动参数判断 const role process.argv[2] || creator; ws.on(open, () { if (role creator) { ws.send(JSON.stringify({ type: create_session })); } else { const code process.argv[3]; ws.send(JSON.stringify({ type: join_session, data: { code } })); } }); ws.on(message, (raw) { const message JSON.parse(raw); console.log([收到消息], message.type, message.data); if (message.type session_created) { console.log(会话短码:, message.data.code); // 模拟终端输出3 秒后推送一段文本 setTimeout(() { ws.send(JSON.stringify({ type: forward_output, data: { output: [AI] 开始分析项目结构...\n } })); }, 3000); } if (role viewer message.type terminal_output) { console.log([AI 终端输出], message.data); } });启动方式# 终端 1启动中继服务 node server/relay-server.js # 终端 2创建者 node examples/basic_demo.js creator # 终端 3通过 short code 加入 node examples/basic_demo.js viewer apple-sunset-river预期效果创建者终端打印出短码apple-sunset-river观看者输入相同短码后加入3 秒后创建者推送的终端输出会实时显示在观看者终端中。4.5 权限控制示例实际场景中观看者不应该拥有与创建者完全相同的权限。我们可以用角色字段做基础控制例如只有owner可以执行写操作viewer只能观察不能提交指令。// 文件路径entangle-demo/examples/permission_demo.js const permissions { owner: [session:create, session:destroy, terminal:write, terminal:read, viewer:kick], editor: [terminal:write, terminal:read], viewer: [terminal:read] }; function checkPermission(role, action) { if (!permissions[role]) { return false; } return permissions[role].includes(action); } // 模拟不同角色的权限判断 const tests [ { role: viewer, action: terminal:read, expected: true }, { role: viewer, action: terminal:write, expected: false }, { role: editor, action: terminal:write, expected: true }, { role: owner, action: viewer:kick, expected: true } ]; for (const test of tests) { const result checkPermission(test.role, test.action); console.log(角色 ${test.role} 执行 ${test.action}: ${result ? 允许 : 拒绝}); }在真实系统中权限判定最好在服务端完成不要信任客户端传入的 role 字段。用户角色应该由服务端根据登录态、会话创建关系来维护客户端传过来的角色只能作为展示用途。5. 常见问题与排查思路实现 session 共享服务时容易遇到下面几类问题。5.1 会话无法创建或短码无效问题现象常见原因解决思路创建会话后短码不能加入中继服务重启内存中的会话表丢失引入 Redis 持久化会话状态短码加入返回“会话已过期”会话设置了过期时间检查过期字段日志中打印剩余时间短码包含非法字符被拒绝验证逻辑过于严格调整校验规则避免误杀客户端连接后立刻断开WebSocket 握手失败或服务端口未开放检查防火墙、确认中继服务启动成功排查这类问题时我建议先看服务端日志。如果服务端一行请求日志都没有大概率是网络层的问题比如端口没监听、防火墙拦截、Nginx 反向代理没有配置 WebSocket 升级头。5.2 中间人无法看到终端输出问题现象常见原因解决思路观看者加入成功但看不到输出创建者没有调用 forward_output 推送在创建者客户端增加服务端推送钩子输出乱码ANSI 转义序列未处理前端使用 xterm.js 渲染不要直接显示纯文本观看者只看到最后一条消息历史消息覆盖而不是追加修改前端渲染逻辑逐条追加输出这里要特别提醒真实终端输出通常包含大量 ANSI 转义码例如\x1b[31m表示红色文字\x1b[2J表示清屏。如果直接把原始字节当文本展示会看到一堆乱码。正确做法是在前端集成xterm.js这样的终端模拟器把后端字节流直接喂给它。5.3 权限绕过风险问题现象常见原因解决思路普通加入者可以发送写指令客户端伪造 role 字段服务端维护权限表忽略客户端传入角色创建者误以为别人无法操作tmux 只读模式被绕过使用独立账号隔离配合 sudo 权限控制会话中打印了敏感密钥没有做输出脱敏增加敏感信息正则过滤例如.env内容屏蔽权限绕过的危害比较大。在安全设计上应该默认拒绝显式放行。任何敏感操作例如执行命令、删除文件、推送代码都要求更高等级授权。5.4 会话占用资源过高问题现象常见原因解决思路服务器内存持续增长历史消息无限累积设置历史消息条数上限例如最多保留 1000 条CPU 占用高多个终端进程同时运行限制同时活跃会话数量短码被大量暴力尝试短码空间太小或没有限流扩大词库、增加访问频率限制为了防止短码被暴力枚举生产环境应该加入速率限制。同一个 IP 每分钟最多尝试 10 次加入超过后锁定一段时间。另外短码的有效期建议控制在 15 到 30 分钟内过期后需要创建者重新生成。6. 最佳实践与工程建议6.1 会话 ID 与短码分离设计建议内部使用随机 UUID 或哈希值作为会话主键对外展示使用短码。这样即使短码规则被破解攻击者也无法通过短码推导出实际的会话 ID。短码只是定位会话的“门牌号”底层标识必须独立。6.2 消息协议字段规范化WebSocket 中继通信时消息格式要尽量统一。推荐使用类似下面这种结构{ type: event_name, data: {}, requestId: xxx, timestamp: 1700000000000 }加入requestId后客户端可以区分哪些消息是响应、哪些消息是主动推送避免回调错乱。timestamp用于排查延迟和消息乱序。6.3 输出脱敏与审计AI 编程会话中经常出现.env文件内容、数据库连接串、云厂商 AccessKey。做共享功能时必须默认开启脱敏。可以在中继服务中维护一个敏感信息正则列表识别到疑似密钥时替换为***。除了脱敏生产环境还应该记录完整审计日志。谁在什么时间加入了哪个会话执行了什么指令留下了哪些输出这些日志对安全追溯至关重要。审计日志本身也需要考虑存储安全不要直接写入可公开访问的目录。6.4 连接状态感知与心跳WebSocket 连接并不一定可靠客户端崩溃、网络切换都可能导致连接半开。建议客户端每 15 秒发送一次心跳消息服务端如果 60 秒内没有收到心跳就主动关闭该连接并清理会话资源。代码可以这样实现// 心跳处理示例 const heartbeatInterval setInterval(() { if (ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({ type: ping })); } }, 15000); ws.on(close, () { clearInterval(heartbeatInterval); });6.5 生产环境的传输加密任何共享内容上了公网都必须启用 TLS。如果是自建服务建议在 Nginx 层终止 TLS并把 WebSocket 升级请求正确转发到后端server { listen 443 ssl; server_name relay.example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; } }需要强调不要在没有任何加密措施的公网端口上直接暴露共享服务尤其是共享的还是 AI 编程会话这种包含项目上下文的敏感数据。6.6 最小权限原则回顾一下整个共享链路最小权限原则体现在三个层面默认角色是 viewer只能看不能写需要写权限时再提升为 editor。短码有效期短过期后必须重新生成避免长期有效的共享口令。共享范围最小化只共享需要的那个 AI coding session不要共享整个服务器登录权限。7. 从 Entangle 到自己的工程实现结合前面的实战我们可以看到 Entangle 这类工具的本质就是给 AI 编程会话加了一层“可寻址、可邀请、可控权”的传输层。三个单词的短码只是交互设计的一部分真正的价值在于把封闭的终端会话变成了一个可协作的实时空间。如果你只是一个人开发可能觉得共享 session 没什么必要。但只要团队规模超过两个人这个需求就会立刻出现。尤其是用到 AI coding agent 的时候大家本质上是在“共同驾驶”一个半自动的编程引擎会话的交接自然成为高频操作。接下来如果你打算继续深入可以按下面的路径走先熟练掌握 tmux 和终端复用理解 session、window、pane 的区别学习node-pty的用法掌握如何在 Node.js 里创建一个真实的 PTY 进程把 WebSocket 中继服务扩展为支持 xterm.js 的完整终端共享平台引入 Redis 保存会话状态实现重启不丢失加入用户认证体系把短码与具体用户绑定调研 Claude Code、Cursor CLI 等工具是否提供了 headless 模式可以直接接管输出流。动手实践时建议从小范围开始。先在自己两台电脑之间测试共享确认延迟和安全性都能接受再扩大到团队内部。生产环境使用前一定要做好权限管控和数据脱敏避免把项目源码和密钥直接暴露给不相干的人。如果你在实现过程中遇到问题欢迎留言讨论。把会话共享做扎实之后你会发现 AI 编程这件事从“一个人在终端里闷头写”变成“一群人随时可以接手的协作工程”这才是 AI 时代开发方式真正改变的地方。
返回列表