第 33 讲:Typert RPC 网关

DeepSeek Harness 源码解析 · 第 33 讲

DeepSeek Harness 源码解析系列 第 33 讲:Typert RPC 网关 基于 DeepSeek Harness 源码 · 2026-09-16 💡 本讲一句话: Typert 网关是 Host 与 Client 之间的"海关"——它把一条 /api/goals/edit 的 wire 调用,仲裁成"调哪个 Service、参数里的 id 换成什么实体、返回值过不过 schema",全程 fail loud:多一个字段、少一个字段、provider 对不上,都拒绝执行而不是猜。 一、网关解决什么问题 前几讲我们拆过 Goal、Session、Credentials 这些能力包,它们都是 Host 进程里的 Cordis Service。但 Web 客户端、Python SDK、ACP 自动化都在另一个进程(甚至另一台机器)——它们不能直接 ctx.get('goals') ,只能发一条 RPC。 Typert 网关( @deepseek-ai/dsh-api-gateway )就是这条 RPC 在 Host 侧的落点。它要回答四个问题: 🔹 这个 endpoint 归谁管? —— /api/* 前缀下可能有多个网关,每个 Service 只认领自己导出的方法。 🔹 按什么契约执行? ——参数名、可选性、返回类型从哪来?答案是双轨:编译期生成的 strict 定义优先,运行时 SRC marker 推导兜底。 🔹 wire 上的 id 怎么变成活对象? —— agent 、 session 这类参数在 wire 上只是字符串 id,网关负责通过 lookup provider 换回 Host 实体。 🔹 失败怎么报? ——错误码是封闭集合( service-unavailable 、 lookup-not-found …),调用方可以机读地分类处理,而不是解析报错文本。 本讲地图: 先看 wire 契约(RemoteResult / codec),再看声明侧的 @Remote 装饰器怎么把方法"挂牌",然后进网关内部:认领 endpoint → 解析描述符 → 对账参数 → lookup 换实体 → JSON-safe 断言 → envelope 返回。最后看 typert-loader 如何自动发现并注册这些导出。 所有代码都来自 packages/typert/protocol 、 packages/api/gateway 、 packages/typert/loader ,行号以当前仓库为准。 二、wire 契约:RemoteResult 与 codec 网关和客户端共享一份"编译器无关的协议层"—— @deepseek-ai/dsh-typert-protocol/types 。它不依赖任何具体 carrier(WebSocket、HTTP 都行),只定义两端必须遵守的形状。最核心的是每个 Remote 方法的解析结果: 📄 packages/typert/protocol/src/types.ts (第 47-62 行) export interface RemoteFailure { // 一次远端调用的失败面:carrier 上报时的原始形状 readonly code: string // code 在这里故意保持开放字符串——封闭的 RPC 码联合属于 carrier 包,写死会反转依赖方向 readonly message: string // 面向调用方的诊断文本,不含敏感值 readonly details: object // 结构化细节槽位,由业务方决定放什么 } // 结束该分支块 export type RemoteResult<T> = // 每个生成的 Remote 方法最终都解析成这个二元结果 | { readonly ok: true; readonly value: T } // 成功分支:value 是 Host 方法的业务返回值 | { readonly ok: false; readonly error: RemoteFailure } // 失败分支:carrier 的失败已折叠进这里,消费方无需再包一层 try/catch 为什么这样设计: 把"业务失败"折叠进 Rem…

阅读全文:DeepSeek Harness 源码解析 第 33 讲