首页 › LLM大语言模型 › Deepseek › DeepSeek Harness 中文教程 › 第 5 章:LLM 能力

第 5 章:LLM 能力

这一章讲模型适配层(packages/llm/):会话与流式词汇、LlmAdapter 接缝、DeepSeek 与 pi-ai 提供方适配器、重试策略与 token 计量。​

5.1 五个包​

​

​
包​
​
​
角色​
​
​
ctx key​
​
​
dsh-llm​
​
​
LLM 服务与共享流式词汇(同时承担 Service Definition 与 Consumer 角色)​
​
​
ctx.llm​
​
​
dsh-token-meter​
​
​
可回放的 token 计量​
​
​
ctx.tokenMeter​
​
​
dsh-llm-retry​
​
​
provider 作用域的重试策略​
​
​
(监听 agent/request-error)​
​
​
dsh-llm-deepseek​
​
​
直连 DeepSeek 适配器​
​
​
(注册在 ctx.llm)​
​
​
dsh-llm-pi-ai​
​
​
多 provider 的 pi-ai 适配器​
​
​
(注册在 ctx.llm)​
​

​

适配器在接缝上注册 provider 路由;重试与 token 计量是独立的消费者。细节见LLM 流式子模块。​

5.2 会话词汇:Message 与 ContentBlock​

一段对话是 Message 的序列;一条消息是类型化 content block 的数组。block union 从 ContentBlockMap 派生:​

​

代码块​

TypeScript

interface ContentBlockMap {​

'text': TextBlock​

'reasoning': ReasoningBlock​

'image': ImageBlock​

'tool-call': ToolCallBlock​

'tool-result': ToolResultBlock​

}​

​

五种块:TextBlock(可见文本)、ReasoningBlock(思考,区别于可见文本)、ImageBlock(持久图片附件)、ToolCallBlock(id: CallId、name、raw-JSON arguments)、ToolResultBlock(toolCallId、嵌套 content、isError?)。​

​

代码块​

TypeScript

interface Message {​

readonly id: MessageId​

readonly role: 'system' | 'user' | 'assistant'​

readonly content: ContentBlock[]​

readonly source: MessageSource​

}​

​

消息来源是 merge-extensible 求和类型(MessageSourceMap:user / plugin / model / tool)。生产者的身份(kind:谁产生的)与呈现形式(form:什么信息)是两条独立轴;form 的值是语义化的——instructions / catalog / snapshot / notice / relay / recall,颜色、图标、排序等视觉决定归消费者,不进这个 union。​

5.3 原始流协议:StreamChunk​

流式响应交错着几种类型化块(文本、思考、多个工具调用)。index 把每个 delta 绑到它的块;block-end 携带已组装完成的 ContentBlock,消费者不必自己重装 delta。它是一个封闭判别联合——switch 以 assertNever 收尾,新增变体会在必须处理它的每个消费者处编译失败。​

​

代码块​

TypeScript

type StreamChunk =​

| { type: 'block-start'; index: number; blockType: ContentBlockType }​

| { type: 'text-delta'; index: number; text: string }​

| { type: 'reasoning-delta'; index: number; text: string }​

| { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }​

| { type: 'block-end'; index: number; block: ContentBlock }​

| { type: 'usage'; usage: TokenUsage }​

| { type: 'finish'; reason: FinishReason; replayState?: unknown }​

​

其中 FinishReason 从 merge-extensible 的 FinishReasonMap 派生(stop/tool-calls/max-tokens/aborted/error,后两者携带 failure: LlmFailure)。约定:适配器在终态 finish 之前发 usage,其后不再发任何东西;工具参数保持 raw JSON 字符串。适配器实现可以抛错,但 LlmRuntime.stream() 把失败规范化为携带 kind:'error'|'aborted' 的 finish 再暴露给消费者。​

5.4 适配器契约:LlmAdapter​

LlmAdapter 是 provider 契约:子类化它、实现 stream(),用 ctx.llm.registerAdapter(providers, adapter) 注册一个适配器实例。GenerateOptions.provider 选择已注册的适配器;GenerateOptions.model 传给该适配器,无需在生命周期启动时注册。重复的 provider 路由原子失败。​

​

代码块​

TypeScript

declare abstract class LlmAdapter {​

abstract stream(options: GenerateOptions): AsyncIterable​

}​

​

两条受认可的失败路径,一个 LlmFailure 类型:​

•

从 stream() 抛(传输/协议错误);​

•

或以 finish {kind:'error'|'aborted', failure} 结束流(provider 带内错误)。​

一次适配器调用是一次 provider 尝试:适配器禁用库级重试;agent 级恢复另开一个持久编号的 turn;直接 ctx.llm.stream() 调用者保持单次尝试。​

replay state 是适配器自有的:成功的 finish 可携带重建原生 provider 响应所需的 lossless-JSON 状态;循环把它和组装好的 assistant 消息一起存储。之后请求时,仅当历史 provider 与目标 provider 当前都注册到同一个适配器实例,LlmRuntime 才把该状态传给该适配器;其它适配器收到 provider 中性内容 + provider/model 字段,而没有私有状态。​

5.5 路由与重试​

Provider 配置在路由注册前解析成不可变判别联合:normal 模式带有限 maxRetries、retryableCodes 与必需的退避字段;always 模式带同样的退避字段但没有有限上限。llm-retry 监听 agent/request-error,追加 llm/retry / llm/retry-started 会话事件。​

agent/request 与 llm/stream 是两个 waterfall:前者替换冻结的调用配置(LlmCallConfig),后者包裹每一次流式模型调用(重试、回放、路由)——调用 next() 到达已解析适配器的 stream,或产出自己的 chunk 来短路。​

5.6 token 计量​

dsh-token-meter(ctx.tokenMeter)拥有按会话隔离的回放折叠:不可变的标量测量与位置化回放测量,配以已消费日志修订号。压缩消费者(compaction-basic)共享这些不可变修订测量,判定上下文压力。​

5.7 适配器如何注册路由​

以 DeepSeek 为例,适配器注册 deepseek-official 路由到 ctx.llm。换模型提供方 = 写一个 LlmAdapter 子类、实现 stream()、registerAdapter(providers, adapter)——其余产品(agent 循环、UI、工具)都通过 ctx.llm 的 provider 中性接口工作,不感知具体适配器。​

下一步​

模型适配器负责「生成」,而「执行」由能力接缝负责。进入第 6 章:执行类能力——shell、subprocess、terminal、code-runtime、sandbox 如何共享一个执行世界。​