# DSH × pi-ai：数据交互流程、核心对象，以及同类框架选型

> **快照**：2026-10-25。DSH 当前安装 `@deepseek-ai/dsh` 0.2.1-rc.2；`dsh-llm-pi-ai` 依赖 `@earendil-works/pi-ai` ^1.1.0（当前解析到 1.4.0）。  
> **HTML 版（排版更友好）**：[dsh-pi-ai-deep-dive.html](dsh-pi-ai-deep-dive.html)  
> **一句话**：DSH 把 pi-ai 当成“多 provider 的模型接入库”，不是当成 Pi harness，也不是当成网关；DSH 自己保留 agent loop、session、工具、审批和重试，pi-ai 只负责 provider/catalog/auth/协议/流事件。

---

## 0. 先把关系摆正

```text
Pi harness（@earendil-works/pi-coding-agent）
   └── 使用 ──► @earendil-works/pi-ai
                    ▲
                    │ 被包装
DSH（@deepseek-ai/dsh）─► dsh-llm ─► dsh-llm-pi-ai ─┘
```

- **Pi** 是 Earendil Works 的独立 coding agent harness，monorepo 里有 `pi-coding-agent`、`pi-agent-core`、`pi-tui` 等。
- **pi-ai** 是 Pi monorepo 里的 `packages/ai`：一个统一的多 provider LLM 库，包含 provider 目录、认证/OAuth、模型元数据、协议适配、流式事件。
- **DSH** 是 DeepSeek 的独立 harness，有自己的 `dsh-agent`、`dsh-llm`、工具、session、Web/CLI 和插件体系。
- **`dsh-llm-pi-ai`** 是 DSH 的一个 adapter：把 pi-ai 的 `Context` / `AssistantMessageEvent` / `Model` 转成 DSH 的 `GenerateOptions` / `StreamChunk` / `ReplayEnvelope`。

所以：**Pi 和 DSH 是并列的 harness；它们共享 pi-ai 这个模型层。不是 fork，不是同一个运行时。** DSH 的 `dsh-llm-pi-ai` 只依赖 `@earendil-works/pi-ai`，不依赖 `@earendil-works/pi-coding-agent` 或 `pi-agent-core`；Pi 的 CLI、TUI、工具、扩展都不会进入 DSH。

---

## 1. DSH 的数据交互流程

### 1.1 端到端路径

```text
Agent loop
  └─ 构造 GenerateOptions
       └─ ctx.llm.prepareCall() / ctx.llm.stream()
            └─ LlmRuntime
                 ├─ 选择 adapter + 冻结本次调用的配置
                 ├─ 投影 file / image / tool update
                 ├─ 剥离“不属于当前 adapter”的 replay state
                 └─ PiAiAdapter.stream()
                      ├─ 取当前 profiles 的不可变 snapshot
                      ├─ 解析精确 model + capabilities
                      ├─ 解析 credential（API key / OAuth / auth store）
                      ├─ DSH Message → pi-ai Context
                      ├─ models.streamSimple(model, context, options)
                      │    └─ pi-ai provider → 原生 wire（Anthropic / OpenAI / Google / …）
                      └─ pi-ai AssistantMessageEvent → DSH StreamChunk
       └─ BlockAssembler 把 chunk 拼成 content blocks
  └─ 记录 AssistantMessage + source.replayState
```

### 1.2 每一步做什么

**① Agent loop 产出 `GenerateOptions`**

DSH 的 loop 从 session 里取历史，构造一份“模型将看到的精确输入”：

- `provider` / `model`
- `messages`：DSH 的 `Message[]`，内容块是 `text` / `reasoning` / `image` / `file` / `tool-call` / `tool-addition` / `tool-removal`
- `tools` / `toolHistory`
- `system`（one-shot 调用）
- `reasoningEffort` / `temperature` / `maxTokens` / `stop`
- `sessionId` / `purpose`
- `signal`

这份请求会被 deep-freeze；adapter 只能读，不能改。

**② `LlmRuntime` 做 provider-neutral 的预处理**

在真正进 adapter 前，DSH 会统一处理：

- `file` block 永远不直接发给 provider，投影成带路径的文本句柄；
- 不支持图片的模型，图片投影成文本占位符；
- `tool-addition` / `tool-removal` 按模型的 `toolUpdate` 能力投影（`in-history` / `addition-only` / 完整列表）；
- 如果历史 assistant 消息的 `source.provider` 不是当前 adapter 拥有的 provider，就剥掉 `replayState`（跨 adapter 不能复用 provider 签名）；
- 通过 `prepareCall()` 冻结本次调用的 adapter generation、model info、retry policy，保证配置变更只在下一步生效，不会在飞行中的请求里换模型。

**③ `PiAiAdapter` 解析 snapshot**

DSH 的 pi-ai adapter 每个请求会：

- 从当前 profiles 取一个不可变 snapshot：`profiles + Models collection`；
- 解析精确 `Model`（provider/id/api/contextWindow/input/reasoning/thinkingLevelMap/compat）；
- 校验 reasoning effort 是否被该模型支持（不支持就抛 `UNSUPPORTED_REASONING_EFFORT`，不静默 clamp）；
- 解析 API key：显式 `apiKeyEnv` 走 DSH credential seam，解析不到就 fail loud；没配凭据则交给 pi-ai 的 provider-native ambient auth / OAuth store；
- 处理图片：读取 attachment、按路由预算 resize、base64、超限时抛 `IMAGE_OFFLOAD_REQUIRED`；
- 处理 `system` / `developer` 消息：支持 mid-conversation system 的模型保留 system；否则转成 `<system-reminder>` user 消息，避免后续 system 文本改动整个 prompt 前缀。

**④ DSH Message → pi-ai Context**

`toPiContext()` 负责转换：

- 请求级 `system` + 开头连续的 system 历史 → pi-ai `Context.systemPrompt`；
- 其余 system / developer / assistant / tool / user → pi-ai `messages`；
- `tool-call` → pi-ai `toolCall`（arguments 从 DSH 的原始 JSON 字符串解析成对象）；
- `tool` 消息 → pi-ai `toolResult`，`toolName` 从前一条 assistant 的 tool call 里恢复；
- `developer` 的 `tool-addition` / `tool-removal` → pi-ai `system` 消息的 `toolsAdded` / `toolsRemoved`（或模型不支持时降级）；
- 图片 → pi-ai `ImageContent`；文件永远不进 provider，只进文本句柄。

**⑤ 调 pi-ai**

```ts
const events = models.streamSimple(model, context, {
  apiKey,
  reasoning,
  temperature,
  maxTokens,
  sessionId,
  headers,
  maxRetries: 0, // DSH 自己拥有重试，pi-ai SDK 不重试
  signal: watchdog.signal,
})
```

`streamSimple()` 是 pi-ai 的统一简化入口：provider 内部走原生协议，输出统一的 `AssistantMessageEvent`。

**⑥ pi-ai event → DSH StreamChunk**

`toStreamChunks()` 做一一映射：

| pi-ai event | DSH StreamChunk |
|---|---|
| `start` | 忽略 |
| `text_start` | `block-start`（`text`） |
| `text_delta` | `text-delta` |
| `text_end` | `block-end`（`text`） |
| `thinking_start` | `block-start`（`reasoning`） |
| `thinking_delta` | `reasoning-delta` |
| `thinking_end` | `block-end`（`reasoning`） |
| `toolcall_start` | `block-start`（`tool-call`） |
| `toolcall_delta` | `tool-call-delta`（argumentsDelta） |
| `toolcall_end` | `block-end`（`tool-call`，arguments 重新 stringify 成原始 JSON） |
| `done` | `usage` + `finish`（带 `replayState`） |
| `error` | `usage` + `finish`（error/aborted） |

**⑦ 回到 DSH**

- `BlockAssembler` 把 chunk 拼成 DSH 的 content blocks；
- `AssistantMessage.source` 记录 `{ provider, model, replayState }`；
- 下一次请求，只有同一个 adapter 实例同时拥有历史 provider 和目标 provider 时，replayState 才会被传给 adapter；否则剥离，降级为 provider-neutral 历史。

---

## 2. 核心数据对象

### 2.1 DSH 侧

| 对象 | 关键字段 / 作用 |
|---|---|
| `GenerateOptions` | `provider`、`model`、`messages`、`system`、`tools`、`toolHistory`、`reasoningEffort`、`temperature`、`maxTokens`、`stop`、`sessionId`、`purpose`、`signal` |
| `Message` | `system` / `developer` / `user` / `assistant` / `tool` 五种 role；assistant 带 `source` |
| `ContentBlock` | `text`、`reasoning`、`image`、`file`、`tool-call`、`tool-addition`、`tool-removal` |
| `StreamChunk` | `block-start`、`text-delta`、`reasoning-delta`、`tool-call-delta`、`block-end`、`usage`、`finish` |
| `TokenUsage` | `inputTokens`、`outputTokens`、`totalTokens`、`cacheReadTokens`、`cacheWriteTokens`、`reasoningTokens` |
| `FinishReason` | `stop`、`tool-calls`、`max-tokens`、`aborted`、`error` |
| `ReplayEnvelope` | `response` + `blocks`：adapter-private 的原生回放元数据；DSH 只负责存储和对齐，不解释内容 |
| `LlmResolvedModelInfo` | context、defaultMaxTokens、reasoning efforts、input modalities、`systemPromptUpdate`、`toolUpdate` |
| `PreparedLlmCall` | 冻结的 config + model metadata + retry policy + 单次 dispatch |

### 2.2 pi-ai 侧

| 对象 | 关键字段 / 作用 |
|---|---|
| `Context` | `systemPrompt`、`messages`、`tools` |
| `Message` | `user` / `assistant` / `toolResult` / `system` |
| `AssistantMessage` | `content`（`text` / `thinking` / `toolCall`）、`usage`、`stopReason`、`responseId`、`responseModel`、`providerThinkingLevel`、签名 |
| `AssistantMessageEvent` | `start`、`text_*`、`thinking_*`、`toolcall_*`、`done`、`error` |
| `Model` | `provider`、`id`、`api`、`baseUrl`、`reasoning`、`input`、`contextWindow`、`maxTokens`、`thinkingLevelMap`、`compat` |
| `Models` | provider 集合；`getModel()`、`streamSimple()`、`getModels()` |
| `SimpleStreamOptions` | `apiKey`、`reasoning`、`thinkingBudgets`、`cacheRetention`、`transport`、`timeoutMs`、`headers`、`maxRetries` |
| `Usage` | `input`、`output`、`cacheRead`、`cacheWrite`、`totalTokens`、`reasoning` |

### 2.3 最容易踩的转换点

1. **arguments 的类型**：pi-ai 给的是已解析对象，DSH 的 `tool-call` 保留原始 JSON 字符串。adapter 在 `toolcall_end` 重新 `JSON.stringify`；如果参数非法，DSH 会报 `MALFORMED_RESPONSE`（除非是 length 截断）。
2. **toolResult 的 toolName**：pi-ai 的 `toolResult` 需要 `toolName`，而 DSH 的 `tool` 消息只有 `toolCallId`；adapter 从前一条 assistant 的 tool call 建立 `id→name` 映射来补。
3. **usage 语义**：pi-ai 把 reasoning 折进 output；DSH 的 `outputTokens` 包含 reasoning，但 cache 字段只在非零时出现。
4. **error 的 cause**：pi-ai 会把底层 Error 压平成 `error.message`，丢掉 `cause`；DSH 的 `stream.ts` 里有一段 `XXX(pi-ai upstream)` 注释，承认只能靠正则匹配错误文本来分类 AUTH / RATE_LIMIT / TIMEOUT / TRANSPORT。这是一个真实的集成摩擦点。
5. **tool 参数流式解析性能**：DSH 仓库对 pi-ai 打了 patch，去掉每个 delta 对整个累计 JSON 的重复解析（否则多 MB 参数流会 O(n²) 卡事件循环）。这是“库级集成”的典型代价。
6. **system reminder**：pi-ai 对不支持 mid-conversation system 的模型会把后续 system 拼到初始 prompt，破坏 prefix cache；DSH adapter 提前把后续 system 转成 `<system-reminder>` user 消息，保留原生 system 的模型则不动。

---

## 3. Replay：DSH 为什么能保留 provider 原生签名

DSH 的 durable 内容（text / reasoning / tool-call）是 source of truth；pi-ai 的 replay metadata 只负责恢复“原生保真度”：

- `response`：`api`、`provider`、`model`、`responseId`、`responseModel`、`providerThinkingLevel`、`stopReason`
- `blocks`：逐块保存 `textSignature`、`thinkingSignature`、`redacted`、`toolCall.thoughtSignature`、`namespace`

流程：

```text
pi-ai done
  → toPiReplayState() 提取签名/ID
  → DSH ReplayEnvelope 存到 assistant.source.replayState

下一次请求
  → toPiAssistant() 校验 provider/model/block 数量/类型
  → 重建 pi-ai AssistantMessage（带签名/ID）
  → 交给 pi-ai provider
  → 否则降级为 provider-neutral 历史，并记 warning
```

这解释了 DSH 为什么需要“库级 replay 元数据”而不是简单的 OpenAI 格式：thinking signature / thought signature 是跨轮工具循环质量的一部分，网关翻译层很容易丢掉。

---

## 4. 为什么 DSH 选 pi-ai：仓库明说的 + 我的推断

### 4.1 仓库/代码明确说到的

1. **“design-verification twin”**：DSH 同时有 `dsh-llm-deepseek`（直连 fetch）和 `dsh-llm-pi-ai`（库实现）。两套不同内部实现跑同一个 `StreamChunk` 契约，用来验证“provider-neutral”不是只适配了 DeepSeek 的假抽象。  
   来源：`packages/llm/llm-pi-ai/package.json` 的 description；`.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md`。

2. **多 provider 目录 + 认证/OAuth + 模型元数据**：catalog 路由继承 pi-ai 的 endpoint、protocol、model catalog；OAuth 登录、credential store、环境变量发现都不需要 DSH 自己为每个 provider 重写。  
   来源：`packages/llm/llm-pi-ai/README.md`、`src/index.ts`。

3. **只给自定义 route 开 3 种协议**：`openai-completions`、`openai-responses`、`anthropic-messages`。因为这三者可以完全用“key + baseURL + models”描述；Bedrock/Vertex/Azure/Codex 需要 SigV4、项目/位置/ADC、api-version、OAuth 等，profile 无法完整表达，所以只能走各自 catalog provider。  
   来源：`src/provider.ts` 的注释与 `PROTOCOLS` 表。

4. **按需加载，避免 bundle 膨胀**：dormant 的 `dsh-llm-pi-ai` 不 import pi-ai aggregate entry，避免把 TypeBox 和数百个模块带进每次启动；catalog/login 走 `providers/all`，协议实现走 `api/*.lazy`。  
   来源：`.agents/notes/archived/architecture/2026-09-15-narrow-pi-ai-runtime-imports.md`。

5. **重试归 DSH**：pi-ai 调用设 `maxRetries: 0`，DSH 的 `dsh-llm-retry` 在 agent step 边界重试；adapter 一次调用就是一次 SDK attempt。  
   来源：`src/adapter.ts` 的 `profileOptions()`。

6. **prefix cache 保护**：pi-ai 对不支持 mid-conversation system 的模型会拼接 system；DSH 改成 `<system-reminder>` user 消息，避免后续 system 更新破坏缓存前缀。  
   来源：`.agents/notes/implemented/architecture/2026-10-19-pi-ai-system-reminder-fallback.md`。

7. **性能 patch**：DSH 对 pi-ai 的流式 tool-call 参数解析打补丁，避免 O(n²)。  
   来源：`packages/llm/llm-pi-ai/README.md` 的 Known Limitations；仓库 `patches/`。

### 4.2 我的推断（未在仓库中直接写死）

1. **TypeScript/Node 同栈**：DSH 是 TS/Node 插件系统；pi-ai 是 TS 库，可以直接进进程，不需要像 LiteLLM 那样额外跑 Python 服务。对桌面/CLI/本地优先的 harness，这是决定性因素。
2. **覆盖成本**：要自己接 OpenAI、Anthropic、Google、Bedrock、Vertex、OpenRouter、Groq、Cerebras、xAI、Copilot 等几十家 provider，认证/OAuth/模型目录/流事件都要维护；pi-ai 已经把这些做成 catalog，DSH 可以把精力放在 agent loop、工具、session 上。
3. **原生协议保真**：DSH 的差异化是“provider-neutral 但不丢 provider 语义”；pi-ai 保留 native protocol 和 provider metadata（thinking signature、response id、tool call namespace），比 LiteLLM/OpenRouter 的 Chat 归一化更贴合 DSH 的 replay 设计。
4. **插件化/快照语义**：DSH 的 `prepareCall()` 要冻结一次调用的 adapter generation；pi-ai 的 `Models` collection 可以整体替换，adapter 用不可变 snapshot 包住，正好匹配 DSH 的“配置变更下一步生效”模型。
5. **不需要网关能力**：DSH 不需要中心化 API key 管理、负载均衡、guardrails、团队配额；这些是 LiteLLM/Portkey/Bifrost 的强项，但对本地 harness 是额外服务。DSH 自己已经有 credential seam、retry、session、审批。
6. **生态/许可证**：pi-ai 是 MIT，Pi harness 自己 dogfood；DSH 可以读源码、打小 patch，不需要引入商业网关。

### 4.3 代价与风险

- **上游依赖 + patch 维护**：pi-ai 升级要重新验证 patch、错误文本分类、provider 行为。
- **错误分类靠文本**：pi-ai 丢掉 cause，DSH 只能用正则识别 401/429/5xx/timeout/stream truncated；provider 改文案就会误分类。
- **不是网关**：没有 load balancing、fallback、central quota、guardrails；DSH 只做单次调用，路由/重试在更上层。
- **catalog 漂移**：pi-ai 的模型目录/协议支持变化会影响 DSH 的模型选择；DSH 用 patch/版本 pin 控制。
- **自定义协议受限**：只开 3 种协议是刻意的安全/可配置性取舍；新协议要改 DSH 代码。
- **bundle 成本**：必须坚持 narrow imports，否则启动体积会变大。

---

## 5. 与 pi-ai 类似的框架

### 5.1 先分类：库 vs 网关

| 类别 | 代表 | 形态 | 适合 |
|---|---|---|---|
| 进程内多 provider 库 | **pi-ai**、Vercel AI SDK、LiteLLM SDK、aisuite、any-llm | 代码依赖，直接调用 provider | 嵌入 harness/应用，低延迟，自己控制运行时 |
| 自托管 HTTP 网关 | LiteLLM Proxy、Portkey、Bifrost、TensorZero | 独立服务，统一 API | 团队级 key/配额/路由/观测/guardrails |
| 托管网关 | OpenRouter、Vercel AI Gateway、Cloudflare AI Gateway | SaaS 代理 | 快速接大量模型，免运维 |
| Agent 框架 | LangChain、LlamaIndex、Pydantic AI、Mastra | 编排/agent 抽象 | 构建应用逻辑，不是模型传输层 |

**pi-ai 属于第一类**。DSH 选它，首先是因为它要的是“库”，不是“网关”。

### 5.2 横向对比

| 框架 | 形态/语言 | 协议策略 | 优点 | 代价/限制 | 与 DSH 的匹配 |
|---|---|---|---|---|---|
| **pi-ai** | TS 库 | 每个 provider 走 native protocol；统一 `Context` / `AssistantMessageEvent` | provider 目录全、OAuth/认证/模型元数据、类型好、可嵌进程、MIT | 不是网关；无路由/LB/guardrails；provider 错误会丢 cause；需要自己维护 patch | **最匹配**：TS、in-process、native replay、catalog |
| **LiteLLM** | Python SDK + Proxy（新版 Rust core） | 主要 OpenAI 格式，支持 native passthrough | 100+ provider、成本追踪、路由/fallback、guardrails、企业功能 | 独立 Python 服务；翻译层可能丢 reasoning/cache；运维复杂 | 若 DSH 是团队服务端可选；本地 harness 太重 |
| **Vercel AI SDK** | TS 库 | provider packages 走 native API；统一 `LanguageModel`/stream parts | UI/agent 生态强、provider 多、TS 类型好、Apache-2.0 | 更偏应用/UI；默认 Vercel AI Gateway；不是 provider catalog/auth 库 | 可选替代；DSH 已自建 agent/UI，收益不如 pi-ai |
| **OpenRouter** | 托管网关 | 主 API 是 OpenAI Chat；另有 Anthropic Messages / Responses（stateless） | 400+ 模型、路由/fallback、免运维、统一账单 | 托管、额外网络跳、数据合规、无本地/离线 | 可作为 DSH 的自定义 OpenAI-compatible route，不是内嵌库 |
| **Portkey** | 自托管/托管网关（MIT） | OpenAI-compatible，1600+ 模型 | guardrails、缓存、路由、观测、企业部署 | 独立服务；翻译层；不是 TS 库 | 团队网关可选，不是 DSH 内核 |
| **Bifrost** | Go 网关（Apache-2.0） | OpenAI-compatible，10k+ 模型 | 高性能（<200µs）、failover、语义缓存、MCP | 独立服务；Go 运维；翻译层 | 团队网关可选 |
| **TensorZero** | Rust 网关 + LLMOps（Apache-2.0） | OpenAI-compatible | 观测/评估/优化/实验一体化 | 偏 LLMOps 平台；独立服务 | 适合团队实验，不是本地 harness 库 |
| **aisuite** | Python 轻量库（MIT） | 统一 Chat Completions + Agents | 简单、易学、provider 覆盖 | 功能少、无 gateway/路由；Python | 适合 Python 应用，不适合 TS DSH |
| **any-llm** | Python 库（Apache-2.0） | 单一接口 | 轻量、provider 覆盖、Mozilla 生态 | 无 gateway/路由；Python | 适合 Python 应用 |
| **LangChain / LlamaIndex** | Python/TS 编排框架 | 各自 model wrapper | 生态、RAG/agent 组件 | 不是 wire-level 传输层；抽象重 | 不是同类替代 |

> 注：LiteLLM 的许可证是混合/企业版结构，不能简单说“全 MIT”；Portkey 是 MIT；Bifrost/TensorZero 是 Apache-2.0；Vercel AI SDK 是 Apache-2.0；pi-ai/aisuite 是 MIT；any-llm 是 Apache-2.0。具体以仓库 LICENSE 为准。

### 5.3 关键差异：pi-ai vs LiteLLM

| 维度 | pi-ai | LiteLLM |
|---|---|---|
| 形态 | TS 库，嵌进程 | Python SDK + Proxy，独立服务 |
| 主要价值 | provider catalog / auth / model metadata / 原生流事件 | 统一网关 / 路由 / 成本 / guardrails / 企业治理 |
| 协议 | native per provider，保留 provider metadata | 以 OpenAI 格式为主，翻译/透传 |
| 部署 | 无服务，跟着 app 启动 | 需要部署/运维 Proxy |
| 适合 | coding harness、桌面/CLI、本地优先 | 团队/企业统一入口、集中配额/审计 |
| 对 DSH | 正好补齐“多 provider 接入” | 功能过剩且引入 Python 服务 |

### 5.4 关键差异：pi-ai vs Vercel AI SDK

两者都是 TS 进程内库，但侧重点不同：

- **AI SDK**：更上层，面向“构建 AI 应用/agent”，有 UI streaming、工具、结构化输出、Agent 抽象；默认走 Vercel AI Gateway；provider 包很多。
- **pi-ai**：更底层，重点是 provider catalog、认证/OAuth、模型元数据、原生流事件；没有 DSH 需要的 agent loop，但正好是 DSH 缺的“模型接入层”。
- DSH 已经有自己的 agent loop、工具、UI、session；再引入 AI SDK 的 agent/UI 抽象会重叠。pi-ai 的边界更窄，更贴合 DSH 的 `dsh-llm` seam。

### 5.5 关键差异：库 vs 网关

- **库**（pi-ai / AI SDK / LiteLLM SDK）：调用在进程内，延迟低，凭据在本地，适合桌面/CLI/harness；但没有集中治理。
- **网关**（LiteLLM Proxy / OpenRouter / Portkey / Bifrost / TensorZero）：集中 key、路由、配额、审计、guardrails；但多一跳网络、需要部署/信任第三方、可能翻译协议。
- DSH 是“本地优先的 coding harness”，它的治理边界在 DSH 自己的 credential/session/approval/retry 层；再套一个网关会重复职责。它需要的是“库”，不是“网关”。

---

## 6. 结论

1. **DSH 的 pi-ai adapter 是“模型接入层”，不是 Pi harness，也不是网关。** 它把 pi-ai 的 provider/catalog/auth/native stream 接进 DSH 的 `dsh-llm`，DSH 自己保留 agent loop、工具、session、审批、重试。
2. **数据流是“双向翻译 + 原生保真”**：DSH Message → pi-ai Context → provider wire → pi-ai events → DSH StreamChunk；assistant 的原生签名/ID 通过 `ReplayEnvelope` 存回 DSH，下一轮再恢复。
3. **选 pi-ai 的核心因素**：TS 同栈、in-process、provider 覆盖、OAuth/模型目录、原生协议保真、MIT、可打 patch；以及“twin adapter”用两套实现验证 provider-neutral 契约。
4. **不选 LiteLLM/OpenRouter/Portkey/Bifrost 的核心原因**：它们是网关/服务，不是库；对本地优先的 DSH 来说多一跳、多一套运维，而且 Chat 归一化会丢 reasoning/cache 语义。
5. **代价**：上游依赖/patch、错误 cause 丢失导致文本分类、catalog 漂移、没有网关级路由/LB；这些由 DSH 自己的 retry/credential/session 层补位。

---

## 7. 参考

**DSH**

- [DSH 仓库](https://github.com/deepseek-ai/deepseek-harness)
- [`dsh-llm-pi-ai` README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm-pi-ai/README.md)
- [`dsh-llm` README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/README.md)
- [Twin LLM adapters note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md)
- [Narrow pi-ai runtime imports](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/archived/architecture/2026-09-15-narrow-pi-ai-runtime-imports.md)
- [Pi-ai system reminder fallback](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-10-19-pi-ai-system-reminder-fallback.md)

**Pi / pi-ai**

- [Pi harness](https://github.com/earendil-works/pi)
- [pi-ai package](https://github.com/earendil-works/pi/tree/main/packages/ai)
- [pi-ai README](https://github.com/earendil-works/pi/blob/main/packages/ai/README.md)

**同类框架**

- [LiteLLM](https://github.com/BerriAI/litellm)
- [Vercel AI SDK](https://github.com/vercel/ai)
- [OpenRouter docs](https://openrouter.ai/docs)
- [Portkey Gateway](https://github.com/Portkey-AI/gateway)
- [Bifrost](https://github.com/maximhq/bifrost)
- [TensorZero](https://github.com/tensorzero/tensorzero)
- [aisuite](https://github.com/andrewyng/aisuite)
- [any-llm](https://github.com/mozilla-ai/any-llm)
