返回知识库
0

第十四章:API Gateway 与 SDK

本章目标:帮助你理解 dsh 的 BFF(Backend-for-Frontend)架构、JSON-RPC 协议细节、TypeScript/Python SDK 的实现差异、以及连接管理与重连策略。阅读本章后,你应该能回答"前后端如何通信"以及"SDK 如何连接到 dsh 服务"。


14.1 BFF 架构

14.1.1 概念

BFF(Backend-for-Frontend)是 dsh 的 API 网关模式,为前端提供专门的 API 接口。

预览
源码

External

Host (Server)

Client (Browser)

React App

WebSocket Client

API Gateway

Cordis Runtime

Session Service

DeepSeek API

graph TB
    subgraph "Client (Browser)"
        A[React App]
        B[WebSocket Client]
    end

    subgraph "Host (Server)"
        C[API Gateway]
        D[Cordis Runtime]
        E[Session Service]
    end

    subgraph "External"
        F[DeepSeek API]
    end

    A --> B
    B --> C
    C --> D
    D --> E
    D --> F

14.1.2 api/ 包组织

预览
源码

依赖

api/ 服务端包

api/gateway/ (网关核心)

api/session-controller/ (会话控制)

api/health/ (健康检查)

core/session

core/agent

sdk/server

graph TB
    subgraph "api/ 服务端包"
        A["api/gateway/ (网关核心)"]
        B["api/session-controller/ (会话控制)"]
        C["api/health/ (健康检查)"]
    end

    subgraph "依赖"
        D["core/session"]
        E["core/agent"]
        F["sdk/server"]
    end

    A --> B
    A --> C
    B --> D
    B --> E
    A --> F

14.2 JSON-RPC 协议

14.2.1 协议规范

dsh 使用 JSON-RPC 2.0 作为前后端通信协议。

// 请求格式
interface JsonRpcRequest {
  jsonrpc: '2.0'
  id: string | number
  method: string
  params?: unknown
}

// 成功响应
interface JsonRpcSuccessResponse {
  jsonrpc: '2.0'
  id: string | number
  result: unknown
}

// 错误响应
interface JsonRpcErrorResponse {
  jsonrpc: '2.0'
  id: string | number
  error: {
    code: number
    message: string
    data?: unknown
  }
}

14.2.2 方法注册

// 来自 packages/sdk/server/src/server.ts
class HarnessSdkJsonRpcServer {
  /** 注册 JSON-RPC 方法 */
  private registerMethods(): void {
    // 会话管理
    this.server.method('session.create', this.createSession.bind(this))
    this.server.method('session.list', this.listSessions.bind(this))
    this.server.method('session.open', this.openSession.bind(this))
    
    // 消息发送
    this.server.method('session.send', this.sendMessage.bind(this))
    
    // 事件订阅
    this.server.method('event.subscribe', this.subscribeEvents.bind(this))
    this.server.method('event.unsubscribe', this.unsubscribeEvents.bind(this))
    
    // 工具执行
    this.server.method('tool.execute', this.executeTool.bind(this))
    
    // 投影查询
    this.server.method('projection.get', this.getProjection.bind(this))
  }
}

14.2.3 方法实现

// 会话创建
async createSession(params: CreateSessionParams): Promise<SessionHeader> {
  const session = await this.ctx.agents.create({
    sessionId: generateSessionId(),
    meta: params.meta
  })
  
  return session.header()
}

// 消息发送
async sendMessage(params: SendParams): Promise<MessageResult> {
  const session = await this.ctx.sessions.open(params.sessionId)
  
  // 追加用户消息
  await session.log.append({
    type: 'user/message',
    data: { content: params.content }
  })
  
  // 触发 Agent 响应
  const agent = this.ctx.agents.get(params.sessionId)
  agent.inbox.insert({
    role: 'user',
    content: params.content
  })
  
  return { success: true }
}

14.3 TypeScript SDK

14.3.1 客户端实现

// 来自 packages/sdk/client/src
class HarnessClient {
  private connection: JsonRpcConnection
  
  constructor(private options: ClientOptions) {
    this.connection = new JsonRpcConnection(options.endpoint)
  }
  
  /** 创建会话 */
  async createSession(options?: CreateSessionOptions): Promise<Session> {
    const header = await this.connection.request('session.create', options)
    return new Session(this.connection, header)
  }
  
  /** 列出会话 */
  async listSessions(): Promise<SessionHeader[]> {
    return this.connection.request('session.list')
  }
  
  /** 打开会话 */
  async openSession(sessionId: string): Promise<Session> {
    const header = await this.connection.request('session.open', { sessionId })
    return new Session(this.connection, header)
  }
}

14.3.2 Session 对象

class Session {
  constructor(
    private connection: JsonRpcConnection,
    public readonly header: SessionHeader
  ) {}
  
  /** 发送消息 */
  async send(content: string): Promise<MessageResult> {
    return this.connection.request('session.send', {
      sessionId: this.header.id,
      content
    })
  }
  
  /** 订阅事件 */
  subscribe(callback: (event: SessionEvent) => void): Disposable {
    const subscription = this.connection.subscribe('event', {
      sessionId: this.header.id
    })
    
    subscription.on('event', callback)
    
    return () => subscription.unsubscribe()
  }
  
  /** 获取投影 */
  async getProjection<K extends string>(key: K): Promise<ProjectionState<K>> {
    return this.connection.request('projection.get', {
      sessionId: this.header.id,
      key
    })
  }
}

14.4 Python SDK

14.4.1 客户端实现

# 来自 python/deepseek_harness/client.py
class HarnessClient:
    def __init__(self, endpoint: str = 'ws://localhost:3000'):
        self.connection = JsonRpcConnection(endpoint)
    
    async def create_session(self, **options) -> Session:
        """创建会话"""
        header = await self.connection.request('session.create', options)
        return Session(self.connection, header)
    
    async def list_sessions(self) -> list[dict]:
        """列出所有会话"""
        return await self.connection.request('session.list')
    
    async def open_session(self, session_id: str) -> Session:
        """打开会话"""
        header = await self.connection.request('session.open', {
            'sessionId': session_id
        })
        return Session(self.connection, header)

14.4.2 Session 对象

class Session:
    def __init__(self, connection, header):
        self.connection = connection
        self.header = header
    
    async def send(self, content: str) -> dict:
        """发送消息"""
        return await self.connection.request('session.send', {
            'sessionId': self.header['id'],
            'content': content
        })
    
    def subscribe(self, callback):
        """订阅事件"""
        subscription = self.connection.subscribe('event', {
            'sessionId': self.header['id']
        })
        subscription.on('event', callback)
        return subscription
    
    async def get_projection(self, key: str):
        """获取投影"""
        return await self.connection.request('projection.get', {
            'sessionId': self.header['id'],
            'key': key
        })

14.5 连接管理

14.5.1 连接池

// 概念性描述
class ConnectionPool {
  private connections = new Map<string, JsonRpcConnection>()
  
  /** 获取连接 */
  async getConnection(endpoint: string): Promise<JsonRpcConnection> {
    let connection = this.connections.get(endpoint)
    
    if (!connection || connection.isClosed()) {
      connection = new JsonRpcConnection(endpoint)
      this.connections.set(endpoint, connection)
    }
    
    return connection
  }
  
  /** 关闭所有连接 */
  async closeAll(): Promise<void> {
    for (const connection of this.connections.values()) {
      await connection.close()
    }
    this.connections.clear()
  }
}

14.5.2 重连策略

// 概念性描述
class ReconnectStrategy {
  /** 重连逻辑 */
  async reconnect(connection: JsonRpcConnection): Promise<void> {
    let attempt = 0
    const maxAttempts = 5
    const baseDelay = 1000  // 1 秒
    
    while (attempt < maxAttempts) {
      try {
        await connection.connect()
        return  // 重连成功
      } catch (error) {
        attempt++
        const delay = baseDelay * Math.pow(2, attempt - 1)  // 指数退避
        await sleep(delay)
      }
    }
    
    throw new Error('Failed to reconnect after max attempts')
  }
}

14.6 流式响应

14.6.1 概念

对于长时间运行的操作(如 LLM 流式输出),SDK 支持流式响应。

// 概念性描述
class StreamableSession {
  /** 流式发送消息 */
  async *sendStream(content: string): AsyncIterable<StreamChunk> {
    const subscription = this.connection.subscribe('stream', {
      sessionId: this.header.id,
      content
    })
    
    try {
      for await (const chunk of subscription) {
        yield chunk
      }
    } finally {
      subscription.unsubscribe()
    }
  }
}

14.6.2 Python 流式

class StreamableSession:
    async def send_stream(self, content: str):
        """流式发送消息"""
        subscription = self.connection.subscribe('stream', {
            'sessionId': self.header['id'],
            'content': content
        })
        
        try:
            async for chunk in subscription:
                yield chunk
        finally:
            subscription.unsubscribe()

14.7 配置

14.7.1 SDK 服务器配置

# cordis.patch.yml
- id: sdk-server
  config:
    # 服务器端口
    port: 3000
    
    # CORS 配置
    cors:
      origin: '*'
      methods: ['GET', 'POST']
    
    # 认证
    auth:
      type: 'none'  # 'none' | 'token' | 'oauth'

14.8 小结

概念一句话解释
BFFBackend-for-Frontend 架构
JSON-RPC前后端通信协议
TypeScript SDKTypeScript 客户端库
Python SDKPython 客户端库
连接管理连接池和重连策略
流式响应长时间运行操作的流式输出

下一步第十五章:凭证与授权——深入理解凭证接缝、API Key 管理和 OAuth 流程。

DeepSeek-Harness / 14-API Gateway 与 SDK 0 0 iliuqi
2026-09-04T07:48:53.459572260Z 2026-09-04T07:58:21.214732817Z