这个助手不是孤岛。它要对接不同公司的 AI 模型、要证明你的身份、要在网络出错时自救、要接外部工具和服务、要被编辑器指挥、还要被手机远程控制。本篇讲这六件事。
7.1 换个大脑:为什么换成别家公司的模型它也能用
第 1 篇说过,真正思考的模型在远端服务器上。问题是:市面上有多家公司提供模型,它们的"说话方式"(请求格式、返回格式)各不相同。为什么这个助手换一家模型照样能用?
每家模型公司说不同的"方言"
不同公司的模型服务,接口差别不小:
- 请求里消息怎么组织、工具怎么描述,格式各一套;
- 返回的流式数据(第 3 篇讲的事件流)结构完全不同;
- 用量统计(花了多少字)字段名、含义有差异;
- 有的模型把"工具调用"叫 function call,有的叫别的,参数写法也不一样。
如果核心循环直接跟某一家的格式绑死,换模型就得重写核心循环------那是灾难。
解法:在边界处配翻译官
项目的做法(在 @ant/model-provider 这个独立小包里)是:核心循环只认一种"普通话" (就是第 3 篇讲的那套统一事件:message_start / 内容块 / 增量 / message_stop),然后为每一家模型公司写一个适配器(adapter,可以理解成"翻译官"),负责把"普通话"翻译成那家的"方言"发出去,再把那家返回的"方言"翻译回"普通话"。
核心循环 ──说普通话──► 翻译官 ──说 OpenAI 方言──► OpenAI 的模型
核心循环 ──说普通话──► 翻译官 ──说 Gemini 方言──► Gemini 的模型
核心循环 ──说普通话──► 翻译官 ──说......方言──► 其他模型
翻译官具体干三件事:
- 翻译输入:把内部的消息列表、工具说明书,转换成这家模型要求的格式;
- 翻译输出流:把这家模型流式返回的数据,"听译"成统一的事件流------这家说"choices0.delta",那家说"parts\[\].text",翻译完都变成"内容块增量";
- 翻译账单:把各家不同的用量字段,统一折算成"输入多少字、输出多少字、缓存多少"。
这个模式的威力
- 核心代码零改动:加一家新模型,只要再写一个翻译官,核心循环、界面、工具系统完全不用碰;
- 有些方言很像:有些公司的接口刻意模仿了别家(比如有些服务兼容 OpenAI 的格式),那它们可以直接共用同一个翻译官,只改个地址;
- 差异被关进笼子:各种稀奇古怪的兼容性问题,都局限在翻译官内部,不会污染主程序。
这就是第 3 篇强调的那个设计原则的完整落地:在系统边界做翻译,内部保持统一。工具系统、界面、压缩逻辑都建立在"事件格式统一"的地基上,而地基能统一,靠的就是这层适配器。
7.2 登录这件事:怎么证明"你是你"
用模型服务要先证明身份(不然谁为这次调用付费?)。不同服务的身份验证方式五花八门,这一章梳理清楚。
最简单的:一把钥匙
最基础的方式是 API 密钥(API key)------一长串密码一样的字符,你在服务商网站申请,程序每次请求时带着它,相当于"报密码进门"。简单直接,缺点是密钥要妥善保存,泄露了别人能花你的钱。
更省心的:账号登录(OAuth)
另一种方式是用你的账号登录,类似"用微信账号登录其他 App":
markdown
1. 程序说"需要登录",打开浏览器跳到服务商的登录页
2. 你在浏览器里登录、点"授权"
3. 服务商把一张"临时通行证"发回给程序
(通过一个临时在本地开的小接收端口)
4. 程序以后带着通行证访问,不用你再输密码
5. 通行证快过期时,程序用"续期凭证"自动换新的,不用你重新登录
通行证(access token)寿命短(泄露了损失有限),续期凭证(refresh token)寿命长且被妥善保管。这样既安全又省心。
云厂商的验证方式
如果模型部署在各大云平台上,验证方式又不一样:有的用云平台自己的账号体系(登录云平台命令行工具后自动获得身份)、有的用访问密钥加一套复杂的签名算法(每个请求都用密钥算一个签名,密钥本身不发出去)。这些差异同样被收在各自的对接层里,用户按云平台的常规方式登录即可。
凭证存哪
凭证(密钥、通行证)的存储讲究安全优先级:
- 优先存进操作系统的钥匙串(macOS 的 Keychain、Windows 的凭据管理器)------这是系统级加密存储;
- 系统钥匙串不可用时,降级为加密文件存储;
- 通行证过期前自动刷新,对用户透明。
统一的登录状态
不管底层是哪种方式,程序内部维护一个统一的"认证状态":你是谁、用的哪家服务、登录是否有效。/login、/logout 这些命令就是操作这个状态。所有对外请求在发出前,由对接层自动附上对应的身份凭证,核心循环不用关心这些细节。
7.3 出错了怎么办:网络抖动、限流、问太多时的自救
网络请求总会失败。一个成熟的助手不能一遇错就崩给你看,它有一整套自救策略。
先分清错误类型
程序收到错误后先分类,不同错误不同处理:
| 错误 | 大白话 | 应对 |
|---|---|---|
| 限流(429) | "你问太快/太多了,稍等" | 按服务器说的等待时间退避重试 |
| 服务器忙(529/503) | "我这边忙不过来" | 退避重试 |
| 网络超时/断开 | 网络抖了一下 | 重试 |
| 内容太长 | "你这问题塞的资料超我容量了" | 先压缩,再重试 |
| 认证失败 | "你是谁?不认" | 提示重新登录 |
| 模型持续出错 | 这家模型这会不行 | 换一家/一档模型试试 |
| 参数非法 | 请求本身有问题 | 不重试,直接报错给用户 |
退避重试:别一个劲猛敲
遇到"忙不过来",不能立刻重试(会火上浇油),也不能傻等。策略是指数退避:第一次等 1 秒,不行等 2 秒,再不行等 4 秒......逐渐拉长间隔。而且服务器通常会在错误里明确告诉你"请等 N 秒"(Retry-After),程序严格照做。重试次数有上限,超过就放弃并告知用户。
内容太长:自动压缩后重试
如果错误是"资料超容量",程序不直接失败,而是触发第 5 篇讲的压缩(划重点),腾出空间后自动重新提问。用户往往只感觉到"它停顿了一下,然后继续了"。
模型 fallback:这家不行换那家
有一种错误表示"当前模型出问题了"(比如该模型临时不可用)。程序可以自动切换到备用模型(fallback,"后备方案")重试------比如主力模型繁忙,临时换到另一档模型把这轮请求完成。切换会在界面上有提示,事后可以切回去。
工具失败 ≠ 对话失败
特别要区分两类失败。工具执行失败(比如命令报错、文件不存在),不算系统错误------失败信息会作为"工具结果"正常返回给模型,模型看到报错往往会自己调整("命令不存在?那我换个命令试试")。这是 Agent 循环的正常组成部分,不需要打断用户。只有"对话本身进行不下去"(网络、认证、模型崩溃)才触发上面的错误处理。
不掩盖问题
重试和 fallback 是静默的,但最终失败时不会藏着:界面会清楚展示错误原因、错误编号(方便排查),并尽量给出可操作建议("请重新登录""请稍后再试")。日志里也会留下完整记录。
7.4 外接能力:怎么接上 GitHub、数据库、公司内部系统
核心工具(读写文件、跑命令)是内置的,但大量能力在外部:操作 GitHub、查数据库、发 Slack 消息、调用公司内部系统......这些怎么接进来?答案是一套叫 MCP(模型上下文协议) 的标准。
先理解问题:为什么需要一套标准
外部服务成百上千,如果每接一个都要写专门的代码、专门的适配,双方都累------服务方要为每个 AI 助手单独对接,助手方要为每个服务单独适配。
MCP 是一套"插座标准"(第 1 篇打过这个比方):它规定了"外部能力提供方"和"AI 助手"之间对话的统一格式。任何服务只要按这个标准提供自己的能力(叫 MCP 服务端),任何 AI 助手只要支持这个标准(叫 MCP 客户端),两者插上就能用,互不挑对方。
arduino
GitHub 服务端 ┐
数据库服务端 ├─ 都说 MCP 这套"普通话" ──► 助手(MCP 客户端)
公司内部服务 ┘ 把它们提供的工具
纳入自己的工具箱
外部能力能提供什么
不只是工具,一个外部服务可以提供三样东西:
- 工具:能执行的动作("创建 issue""执行 SQL 查询");
- 资源:能读取的数据("这个文档""这张表的结构");
- 提示模板:预设的常用操作流程。
助手把外部工具纳入第 4 篇讲的工具箱(走同样的权限关卡),把外部资源当作可读取的资料。
三种连接方式
- 本地进程 :外部能力作为一个本地小程序运行(比如
npx 某个服务),助手通过标准输入输出和它对话。最常见、最简单; - 远程长连接:通过网络连一个常驻服务,适合企业内部部署;
- 网页式连接:通过普通网页请求通信,适合云端服务。
配置从哪来:五个层级
接哪些外部服务,配置可以来自五个层级,优先级从低到高叠加:
公司管理员统一下发(只读,员工改不了) ← 最低
机器级配置
你的个人配置(全局,对你所有项目生效)
项目级配置(存在项目里,团队共享,进 git)
项目级个人覆盖(不进 git,只对你本机生效) ← 最高
这样企业能管控"员工只能用这几个服务",团队能共享"我们项目接了这几个服务",个人又能临时加自己的服务,互不冲突。
外部服务也要登录:授权流程
很多外部服务需要授权(访问你的 GitHub 当然要你同意)。MCP 内置了标准的授权流程:助手发现服务需要登录时,自动打开浏览器让你在该服务网站上授权,授权完通行证自动存好,过期自动续------和 7.2 的登录机制是同一套思路。
安全把关
外部工具和内置工具过同一道权限关卡(第 4 篇):外部工具想删东西、发消息,照样问你。而且外部工具会打上来源标签("这是 GitHub 服务提供的工具"),你能清楚区分哪些动作来自内置能力、哪些来自外部。管理员还能设"白名单",只允许指定的服务启动。
助手自己也能当"服务端"
反过来,助手也可以把自己的能力包装成一个 MCP 服务端对外提供,让别的 AI 工具调用。这在架构上就是"同一套能力,既能当客户端消费外部服务,也能当服务端被别人消费"。
7.5 让编辑器指挥它:别的软件怎么跟它对话
第 1 篇提到第三种形态:编辑器(Zed、Cursor 等)可以把这个助手当作后端来驱动。这一篇讲它们之间的对话规矩。
问题场景
编辑器厂商想集成 AI 助手能力,但不想自己造一个;助手想被编辑器用,但编辑器有很多家。双方需要一套对话协议 ------类似 MCP 是"接外部能力"的标准,这一套是"编辑器 ↔ AI 助手"的标准,叫 ACP(智能体客户端协议)。
对话怎么进行
助手以一种特殊模式启动(无界面、不显示终端界面),然后和编辑器通过"消息收发"对话。消息有固定格式,类似这样的一问一答:
bash
编辑器:初始化------我是编辑器,我能读写文件、能开终端
助手:好,我是 AI 助手,版本 X,支持这些能力
编辑器:新建会话,工作目录是 /path/to/project
助手:会话建好了
编辑器:用户说了这句话:"帮我重构这个函数"
助手:(开始干活,过程中不断回报)
├─ 我在想......(思考内容,流式)
├─ 我要调用工具:读文件 xxx
├─ 我更新了计划:第一步进行中......
└─ 我要执行"跑命令 npm install",需要你批准 ← 权限请求
编辑器:(弹窗问用户)用户批准了
助手:(继续)......完成了,改动如下
关键:助手不画界面了,改为"汇报状态"
终端模式下,助手自己用第 6 篇讲的技术画界面。ACP 模式下,助手不画任何界面,而是把"现在发生了什么"用消息汇报给编辑器:正在思考、正在调什么工具、计划进度到哪了、需要用户批准什么。编辑器拿到这些状态,用自己的界面风格画出来(每个编辑器长得不一样)。
这就是为什么第 1 篇说"编辑器负责界面,助手负责思考和干活"。
权限请求怎么处理
助手要执行危险操作时,不是自己弹窗(它没有界面可弹),而是发一条"权限请求"消息给编辑器:"我要做 X,选项有:允许这次 / 总是允许 / 拒绝这次 / 总是拒绝"。编辑器负责弹窗给用户,再把用户的选择回传。助手拿到选择后继续------权限模型和终端模式完全一致,只是"问用户"这个动作由编辑器代劳。
计划进度的可视化
助手的待办计划(第 4 篇的 TodoWrite)也通过消息汇报:"计划共 5 步,第 2 步进行中"。编辑器据此渲染自己的进度条。同一份计划数据,终端模式画在终端里,ACP 模式交给编辑器画。
一个独立的"中转"小程序
编辑器和助手之间有时还隔着一个中转程序(acp-link):编辑器连中转程序,中转程序负责启动和管理一个个助手进程、转发消息。好处是编辑器不用关心助手怎么安装、怎么启动,而且中转程序能同时管理多个会话。这也是为什么助手要支持"无界面、纯消息"模式------它可以被任意中转层、任意编辑器驱动。
和 SDK 形态的区别
- 被程序直接调用(第 1 篇第二种形态):调用方用代码直接发起对话,拿到结构化结果,适合自动化脚本;
- ACP(第三种形态):对话围绕"人在编辑器前操作"设计,有权限弹窗、有计划可视化、有流式思考展示,适合交互式使用。
两者底层跑的都是同一台发动机(核心循环)。
7.6 手机远程控制:出门在外怎么让家里电脑上的它干活
最后一种形态:人在外面,用手机指挥家里/公司电脑上跑着的助手。
为什么不能"手机上直接跑一个"
助手干活靠的是你电脑上的环境:你的代码、你的文件、你的终端、你的登录状态。手机上装一个助手,它碰不到你电脑里的东西。所以正确的架构是:助手在你电脑上跑,手机只是个"遥控器 + 显示器"。
手机(遥控器/显示器)
│ 网络
▼
中转服务(帮忙牵线,因为你的电脑通常没有公网地址)
│
▼
你电脑上的助手(真正干活的地方)
怎么建立连接:扫码配对
你在家启动"远程控制"模式后,电脑屏幕上出现一个二维码。手机 App 扫码,完成配对。二维码里包含了连接地址、会话标识、一把临时公钥等信息,手机和电脑之间通过加密握手建立信任------防止别人冒充。
配对成功后,手机上就能看到和终端里一样的对话界面,你发消息,消息一路传回家里的助手执行,结果实时推回手机。
手机上能做什么
- 看:实时看助手的思考、输出、工具执行过程(流式推送,和终端同步);
- 说:发消息、发指令;
- 批:危险操作的权限确认弹窗会推到手机上,你在地铁上点"允许",家里电脑就继续执行;
- 管:可以发起新会话、查看多个并行会话、切换状态。
多设备同时看
支持多个设备同时连着一个会话(手机 + 平板 + 电脑屏幕)。状态会在设备间同步:电脑上切换了模型,手机上也同步显示。权限弹窗会智能地推给最合适的设备。
几个工程细节
- 防睡眠:远程干活时电脑不能休眠,程序会在远程会话期间阻止系统进入睡眠,断开后恢复;
- 消息节流:助手一秒可能产生很多输出,全推给手机既费流量又卡。有个"节流闸门"把短时间内的细碎消息合并(连续的文字片段合并成一条),但关键消息(权限请求)不合并、立即推;
- 代码改动同步:助手改文件时,改动的摘要/diff 会推给手机,让你在手机上也能看到"它改了什么",但不传整个文件,省流量;
- 安全:所有通信加密,设备有临时令牌(短时间有效),你随时可以一键断开所有远程设备;远程模式下危险操作默认更谨慎(多一次确认)。
和编辑器模式的共性
注意它和 7.5 的架构共性:助手内核只管"干活 + 汇报状态",终端界面、编辑器界面、手机界面都是接在外面的"前端"。内核不关心结果显示给谁、权限弹窗弹给谁------这正是同一台发动机能驱动这么多形态的根本原因。
本篇小结
- 多模型靠适配器:核心只认一种统一格式,每家模型配一个翻译官(翻译输入、翻译输出流、翻译账单),加模型不改核心。
- 身份验证有密钥和账号登录两种,账号登录走"浏览器授权 + 临时通行证 + 自动续期";凭证优先存系统钥匙串。
- 错误处理先分类再自救:限流/繁忙用指数退避重试,内容太长先压缩,模型崩了换备用模型,工具失败则正常喂给模型自己处理。
- 外接能力走 MCP 这套插座标准:外部服务提供工具/资源/提示模板,支持本地进程和远程连接,配置分五个层级叠加,外部工具过同一道权限关。
- 编辑器通过 ACP 协议驱动助手:助手不画界面只汇报状态,权限弹窗和计划进度交给编辑器呈现。
- 手机远程控制是"手机当遥控器、电脑干活":扫码配对、加密连接、消息节流、防睡眠、多设备同步。
- 贯穿全篇的主线:内核统一、边界翻译------同一台发动机,终端、编辑器、手机都是外接的前端。
下一篇讲数据:程序里的状态怎么放、对话怎么找回、改坏了怎么退回、钱怎么算。