1000行代码实现极简版openclaw(附源码)(2)
TurboClaw 类型系统详解:作为项目基础架构的"建筑图纸"。系统定义了六类核心类型:1)消息类型(Message/ToolCall),构建基础数据单元;2)Agent类型(状态/配置/思考结果),实现AI核心逻辑;3)工具类型(参数/定义),扩展Agent能力;4)通道类型(配置/接口),处理用户交互;5)Gateway类型(配置/会话),作为系统控制中心;6)LLM接口
·
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 通过 description 和 parameters 理解工具:
{
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 工具的返回结果设计类型:
// 应该返回什么类型?
更多推荐


所有评论(0)