01 - 类型系统:建筑的图纸 github 源码(欢迎star)

目标

理解 TurboClaw 中所有的类型定义,这是后续代码的基础。


1. 为什么要先学类型?

类比:建房子

没有图纸直接建

  • 工人不知道墙多厚
  • 不知道门开在哪
  • 最后房子可能倒塌

先画图纸再建

  • 每个人都知道该做什么
  • 尺寸、材料都确定
  • 房子稳固可靠

类型定义 = 建筑图纸


2. 完整类型定义

创建文件 src/core/types.ts

/**
 * 核心类型定义 - 整个项目的"字典"
 */

// ============================================================================
// 第一部分:消息类型(最基础的数据单元)
// ============================================================================

/**
 * 消息角色
 * 说明:这条消息是谁发的?
 */
export type MessageRole = 
  | 'user'      // 用户发的
  | 'assistant' // AI 助手发的
  | 'system'    // 系统提示(如 "你是一个助手")
  | 'tool';     // 工具执行结果

/**
 * 消息结构
 * 
 * 类比:就像微信的一条消息
 * - id: 消息的唯一编号
 * - role: 谁发的(你/对方/系统)
 * - content: 内容
 * - timestamp: 发送时间
 * - metadata: 额外信息(如图片、位置等)
 */
export interface Message {
  id: string;           // UUID,唯一标识
  role: MessageRole;    // 发送者角色
  content: string;      // 消息内容
  timestamp: number;    // 时间戳(毫秒)
  metadata?: Record<string, unknown>;  // 额外数据
}

/**
 * 工具调用
 * 
 * LLM 说:"我需要调用 write_file 工具"
 * 这就是那个调用指令
 */
export interface ToolCall {
  id: string;                    // 唯一标识
  name: string;                  // 工具名称,如 "write_file"
  parameters: Record<string, unknown>;  // 参数,如 {path: "test.txt"}
}

/**
 * 工具执行结果
 * 
 * 工具执行完了,返回这个结果
 */
export interface ToolResult {
  toolCallId: string;   // 对应哪个 ToolCall
  output: string;       // 输出内容
  error?: string;       // 错误信息(如果有)
}

// ============================================================================
// 第二部分:Agent 类型(核心 AI)
// ============================================================================

/**
 * Agent 状态
 * 
 * 就像红绿灯,告诉外界 Agent 现在在干什么
 */
export type AgentState = 
  | 'idle'      // 空闲,等待输入
  | 'thinking'  // 思考中(正在调用 LLM)
  | 'acting';   // 行动中(正在执行工具)

/**
 * Agent 配置
 * 
 * 创建 Agent 时需要什么参数?
 */
export interface AgentConfig {
  model: string;           // 模型名称,如 "deepseek-chat"
  apiKey: string;          // API 密钥
  baseUrl?: string;        // 自定义 API 地址(可选)
  maxTokens?: number;      // 最大输出长度(可选)
  temperature?: number;    // 创造性程度 0-2(可选)
  systemPrompt?: string;   // 系统提示词(可选)
  maxToolDepth?: number;   // 最大工具调用深度,防无限循环(可选)
}

/**
 * Agent 思考结果
 * 
 * LLM 返回的内容解析后变成这个结构
 */
export interface AgentThought {
  content?: string;        // 文本回复内容
  toolCalls?: ToolCall[];  // 需要调用的工具列表
  isComplete: boolean;     // 是否已完成(不需要再调用工具)
}

// ============================================================================
// 第三部分:工具类型(Agent 的能力扩展)
// ============================================================================

/**
 * 工具参数定义
 * 
 * 描述这个工具需要什么参数
 * 类比:函数参数定义
 */
export interface ToolParameter {
  type: string;           // 参数类型:string/number/boolean
  description: string;    // 参数描述(LLM 靠这个理解参数用途)
  required?: boolean;     // 是否必须
  enum?: string[];        // 枚举值,如 ["red", "green", "blue"]
}

/**
 * 工具定义
 * 
 * 一个完整的工具包含:
 * - 名字、描述
 * - 需要什么参数
 * - 怎么执行
 */
export interface Tool {
  name: string;                                // 工具名
  description: string;                         // 工具描述
  parameters: Record<string, ToolParameter>;   // 参数定义
  execute: (params: Record<string, unknown>) => Promise<string>;
  // ↑ 执行函数,接收参数,返回字符串结果
}

// ============================================================================
// 第四部分:通道类型(用户交互)
// ============================================================================

/**
 * 通道配置
 */
export interface ChannelConfig {
  type: string;            // 通道类型:terminal/websocket
  enabled: boolean;        // 是否启用
  options?: Record<string, unknown>;  // 额外选项
}

/**
 * 通道接口
 * 
 * 定义了所有通道必须实现的方法
 * 类比:所有手机都必须能打电话、发短信
 */
export interface Channel {
  readonly type: string;           // 通道类型标识
  readonly isConnected: boolean;   // 是否已连接
  
  // 生命周期方法
  start(): Promise<void>;   // 启动通道
  stop(): Promise<void>;    // 停止通道
  
  // 消息处理(由 Gateway 注入)
  onMessage?: (
    message: Message, 
    reply: (msg: Message) => void
  ) => void;
  
  // 发送消息
  send(message: Message): Promise<void>;
}

// ============================================================================
// 第五部分:Gateway 类型(控制中心)
// ============================================================================

/**
 * Gateway 配置
 */
export interface GatewayConfig {
  port: number;            // 服务端口
  host?: string;           // 绑定地址
  authToken?: string;      // 访问令牌(可选)
  workspace: string;       // 工作目录
  agent: AgentConfig;      // Agent 配置
  channels: ChannelConfig[];  // 通道配置列表
}

/**
 * 会话
 * 
 * 维护一个用户的完整对话历史
 * 类比:微信聊天窗口
 */
export interface Session {
  id: string;              // 会话 ID
  channelType: string;     // 来自哪个通道
  messages: Message[];     // 消息历史
  createdAt: number;       // 创建时间
  updatedAt: number;       // 更新时间
  metadata: Record<string, unknown>;  // 额外数据
}

// ============================================================================
// 第六部分:LLM 类型(大模型接口)
// ============================================================================

/**
 * LLM 请求
 */
export interface LLMRequest {
  model: string;
  messages: Array<{
    role: string;
    content: string;
    tool_calls?: Array<{
      id: string;
      type: 'function';
      function: { name: string; arguments: string };
    }>;
    tool_call_id?: string;
  }>;
  tools?: Array<{
    type: 'function';
    function: {
      name: string;
      description: string;
      parameters: Record<string, unknown>;
    };
  }>;
  maxTokens?: number;
  temperature?: number;
}

/**
 * LLM 响应
 */
export interface LLMResponse {
  content?: string;
  toolCalls?: Array<{
    id: string;
    type: 'function';
    function: { name: string; arguments: string };
  }>;
  usage?: {
    inputTokens: number;
    outputTokens: number;
  };
}

/**
 * LLM Provider 接口
 * 
 * 支持多种模型(OpenAI、DeepSeek 等)
 */
export interface LLMProvider {
  readonly name: string;
  complete(request: LLMRequest): Promise<LLMResponse>;
}

// ============================================================================
// 第七部分:Memory 类型(长期记忆)
// ============================================================================

export type MemoryType = 
  | 'fact'             // 事实
  | 'todo'             // 待办
  | 'user_preference'  // 用户偏好
  | 'code'             // 代码
  | 'summary'          // 总结
  | 'note';            // 笔记

export interface MemoryEntry {
  id: string;
  content: string;
  type: MemoryType;
  tags: string[];
  createdAt: number;
  updatedAt: number;
  source?: string;
  relevance?: number;
}

export interface MemoryQuery {
  keywords?: string[];
  type?: MemoryType;
  tags?: string[];
  limit?: number;
  minRelevance?: number;
}

3. 类型关系图

┌─────────────────────────────────────────────────────────────────┐
│                         Type Hierarchy                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────┐                                                │
│  │   Message   │◄─────────────────────────────────────────────┐  │
│  └──────┬──────┘                                              │  │
│         │                                                      │  │
│    ┌────┴────┬────────┬────────┐                              │  │
│    │         │        │        │                              │  │
│    ▼         ▼        ▼        ▼                              │  │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐                           │  │
│ │ user │ │assist│ │system│ │ tool │                           │  │
│ └──────┘ └──────┘ └──────┘ └──────┘                           │  │
│                                                                  │
│  ┌─────────────┐     ┌─────────────┐                           │  │
│  │  ToolCall   │────►│ ToolResult  │                           │  │
│  └─────────────┘     └─────────────┘                           │  │
│         │                                                      │  │
│         │ uses                                                 │  │
│         ▼                                                      │  │
│  ┌─────────────┐     ┌─────────────┐                           │  │
│  │    Tool     │────►│ ToolParameter│                          │  │
│  └─────────────┘     └─────────────┘                           │  │
│                                                                  │
│  ┌─────────────┐     ┌─────────────┐                           │  │
│  │   Session   │────►│  Message[]  │                           │  │
│  └─────────────┘     └─────────────┘                           │  │
│                                                                  │
│  ┌─────────────┐     ┌─────────────┐                           │  │
│  │ LLMRequest  │     │LLMResponse  │                           │  │
│  └─────────────┘     └─────────────┘                           │  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

4. 关键类型详解

4.1 Message - 为什么如此重要?

Message 是整个系统的数据原子

所有信息流动都是 Message:

  • 用户输入 → Message(role=‘user’)
  • AI 回复 → Message(role=‘assistant’)
  • 工具结果 → Message(role=‘tool’)

为什么需要 metadata?

// 普通消息
{ role: 'user', content: '你好' }

// 包含 tool_calls 的 assistant 消息
{
  role: 'assistant',
  content: '',
  metadata: {
    toolCalls: [{ name: 'write_file', parameters: {...} }]
  }
}

// tool 结果消息
{
  role: 'tool',
  content: '已写入',
  metadata: { toolCallId: 'call-123' }  // 对应哪个调用
}

4.2 Tool - 如何让 LLM 理解工具?

LLM 通过 descriptionparameters 理解工具:

{
  name: 'write_file',
  
  // 这个描述很重要!LLM 靠它理解工具用途
  description: '写入文件内容,自动创建目录',
  
  parameters: {
    path: {
      type: 'string',
      description: '文件路径,如 "src/index.ts"',  // LLM 靠这个知道怎么填
      required: true
    },
    content: {
      type: 'string',
      description: '文件内容',
      required: true
    }
  },
  
  execute: async ({ path, content }) => {
    // 实际执行
  }
}

好的 description 例子

  • ✅ “读取文件内容,支持相对路径”
  • ✅ “计算数学表达式,如 ‘2 + 3 * 4’”
  • ❌ “读文件”
  • ❌ “计算”

4.3 Session - 为什么需要会话?

隔离不同用户的对话

想象两个用户同时聊天:

用户 A: "记住我叫 Alice"
用户 B: "记住我叫 Bob"

如果没有 Session:
- 两个人共享同一个 messages 数组
- 乱了!

有 Session:
- Session A: messages = ["记住我叫 Alice"]
- Session B: messages = ["记住我叫 Bob"]
- 互不干扰

5. TypeScript 技巧

5.1 interface vs type

// interface:可扩展,适合对象定义
interface Message {
  id: string;
  content: string;
}

// type:更灵活,适合联合类型
type MessageRole = 'user' | 'assistant' | 'system';

5.2 可选参数

// ? 表示可选
interface Config {
  apiKey: string;       // 必须
  baseUrl?: string;     // 可选,可能是 undefined
}

5.3 泛型

// Record<K, V> 是 TypeScript 内置的泛型
// Record<string, ToolParameter> = { [key: string]: ToolParameter }
parameters: Record<string, ToolParameter>

6. 练习题

练习 1:添加新类型

添加一个 FileInfo 类型,用于返回文件信息:

interface FileInfo {
  name: string;
  size: number;
  isDirectory: boolean;
  modifiedAt: number;
}

练习 2:理解联合类型

解释这段代码的含义:

type Result = 
  | { success: true; data: string }
  | { success: false; error: string };

练习 3:完善类型

list_files 工具的返回结果设计类型:

// 应该返回什么类型?

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐