1. 一条消息的完整旅程:从Slack到AI技能调用
在OpenClaw架构中,一条用户消息从发送到获得AI回复的全过程堪称精妙的交响乐演奏。让我们以"帮我清空收件箱里今天的GitHub通知,顺便给我一个总结"这条Slack消息为例,深入解析每个环节的技术实现。
1.1 消息接收与适配器转换
当用户在Slack输入消息并按下发送键时,OpenClaw系统会通过两种方式之一接收这个消息:
- Socket Mode:开发环境默认使用WebSocket长连接
- Events API:生产环境推荐使用HTTP Webhook
typescript复制// 使用@slack/bolt框架初始化连接
const app = new App({
token: config.botToken,
appToken: config.appToken,
socketMode: true // 开发模式使用WebSocket
});
app.message(async ({ message, event }) => {
const inbound = adaptSlackMessage(event); // 关键转换
emitInbound(inbound); // 进入系统管道
});
消息适配的核心是将平台特定的数据结构转换为OpenClaw通用格式:
typescript复制interface InboundMessage {
id: string; // 唯一消息ID
channel: string; // 来源渠道
peerId: string; // 发送者标识
chatId: string; // 会话标识
text: string; // 消息内容
timestamp: Date; // 时间戳
raw: any; // 原始数据
}
关键设计:每个通信渠道(Slack/Discord等)都有独立的适配器目录,保持代码隔离和可扩展性。适配器必须实现统一的ChannelAdapter接口。
1.2 安全验证与会话管理
消息进入系统后首先面临安全关卡:
typescript复制// 安全检查逻辑伪代码
async function checkInboundSecurity(msg) {
if (isGroupChat(msg.chatId)) return { allowed: true }; // 群组直接放行
const isApproved = await whitelist.isApproved(msg.peerId);
if (!isApproved) {
if (policy === "pairing") {
const code = generatePairingCode(); // 生成6位配对码
await sendPairingInstructions(code); // 发送配对指引
return { allowed: false, reason: "needs_pairing" };
}
return { allowed: false }; // 拒绝未授权消息
}
return { allowed: true };
}
安全验证通过后,系统会建立或恢复会话:
typescript复制// 会话解析流程
async function resolveSession(msg) {
const key = `${msg.channel}:${msg.chatId}`; // 会话唯一键
let session = await sessionStore.findByKey(key);
if (!session) {
session = await sessionStore.create({
id: generateUUID(),
owner: msg.peerId,
activationMode: "passive", // 默认被动模式
createdAt: new Date()
});
}
return session;
}
实战经验:会话的lastActiveAt字段要谨慎更新,避免高频IO。我们采用写缓冲策略,每5分钟批量更新一次活跃时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. AI路由与上下文构建
2.1 智能路由决策
系统会根据消息内容和会话状态选择最合适的AI处理:
typescript复制// 路由决策逻辑
function routeToAgent(msg, session) {
// 1. 检查是否命中特殊路由规则
for (const rule of routingRules) {
if (matchesRule(rule, msg)) {
return getAgentById(rule.targetAgentId);
}
}
// 2. 使用会话默认AI
if (session.defaultAgentId) {
return getAgentById(session.defaultAgentId);
}
// 3. 返回系统默认AI
return getDefaultAgent();
}
路由规则支持多种匹配条件:
- 关键词匹配
- 正则表达式
- 意图识别结果
- 自定义函数判断
2.2 上下文装配
AI处理前需要准备完整的上下文:
typescript复制async function buildAgentContext(session, msg) {
return {
// 最近20条对话历史
recentMessages: await messageStore.loadWindow(session.id, 20),
// 相关记忆片段
memorySnippets: await memoryStore.search({
owner: session.owner,
query: msg.text,
limit: 5
}),
// 用户偏好设置
profile: await profileStore.load(session.owner)
};
}
性能优化:上下文加载采用并行请求,并使用Redis缓存高频访问的用户配置数据。记忆片段搜索使用向量数据库实现语义检索。
3. AI思考与工具调用
3.1 模型输入构造
typescript复制function buildModelInput(msg, context) {
const systemPrompt = `
You are ${agent.name}, ${agent.description}.
Capabilities: ${agent.capabilities.join(", ")}.
User preferences: ${JSON.stringify(context.profile)}.
`;
return {
system: systemPrompt,
messages: [
...context.recentMessages,
{ role: "user", content: msg.text }
],
tools: getAvailableTools() // 可用工具列表
};
}
3.2 ReAct多轮推理
AI处理采用ReAct模式,可能包含多轮工具调用:
typescript复制async function* runAgentLoop(input) {
let currentInput = input;
for (let turn = 0; turn < MAX_TURNS; turn++) {
const output = await model.chat(currentInput);
if (output.type === "text") {
yield { type: "text", content: output.text };
break;
}
if (output.type === "tool_calls") {
const results = await executeTools(output.tool_calls);
currentInput = appendResults(currentInput, results);
yield { type: "action", tools: output.tool_calls };
}
}
}
工具执行示例(处理GitHub通知):
bash复制# 实际执行的底层命令
himalaya search "from:notifications@github.com date:today is:unread"
himalaya move INBOX archive $message_ids
4. 回复生成与持久化
4.1 流式响应处理
typescript复制// 流式响应处理
const replyBuffer = [];
for await (const chunk of agentLoop) {
if (chunk.type === "text") {
replyBuffer.push(chunk.text);
await sendToClient({
type: "text_chunk",
text: chunk.text,
done: chunk.done
});
}
if (chunk.done) {
await persistMessage({
sessionId: session.id,
content: replyBuffer.join(""),
direction: "outbound"
});
}
}
4.2 审计日志记录
typescript复制await auditLog.record({
event: "message_processed",
sessionId: session.id,
durationMs: Date.now() - startTime,
toolsUsed: toolCalls.map(t => t.name),
tokenUsage: {
input: modelStats.inputTokens,
output: modelStats.outputTokens
}
});
5. 关键设计解析
5.1 技能系统设计
OpenClaw采用独特的"无代码技能"设计:
code复制skills/
gmail/
SKILL.md # 技能说明文档
examples.txt # 使用示例
github/
SKILL.md
...
技能通过自然语言描述而非代码实现,模型通过提示词学习技能用法。这种设计带来三大优势:
- 无需开发即可扩展新技能
- 技能文档即使用说明
- 支持非技术人员贡献技能
5.2 安全沙箱机制
工具执行在严格隔离的环境中运行:
typescript复制async function executeTool(toolCall) {
const sandbox = createDockerSandbox({
cpuLimit: "0.5",
memoryLimit: "256MB",
readOnlyFilesystem: true
});
try {
const result = await sandbox.exec(toolCall.command);
return { success: true, output: result };
} catch (error) {
return { success: false, error: error.message };
}
}
6. 性能优化实践
6.1 缓存策略
typescript复制// 三级缓存体系
const cachedSession = await cache.get(sessionKey, {
// L1: 内存缓存 (5s TTL)
l1: { ttl: 5000 },
// L2: Redis缓存 (5m TTL)
l2: { ttl: 300000 },
// L3: 数据库查询
fallback: () => db.query("...")
});
6.2 批量写入
typescript复制// 使用写缓冲区提升IO性能
const writeBuffer = new WriteBuffer({
maxSize: 100, // 100条记录
maxAge: 5000, // 5秒
flush: async (items) => {
await db.batchInsert(items);
}
});
// 使用示例
writeBuffer.push(messageLog);
7. 异常处理机制
7.1 错误分类处理
typescript复制try {
await processMessage(msg);
} catch (error) {
if (error instanceof RateLimitError) {
await sendRateLimitWarning();
} else if (error instanceof ToolExecutionError) {
await sendToolErrorReply(error.toolName);
} else {
await sendGenericErrorMessage();
await notifyDevTeam(error);
}
}
7.2 重试策略
typescript复制// 指数退避重试
async function withRetry(fn, maxAttempts = 3) {
let attempt = 0;
while (attempt < maxAttempts) {
try {
return await fn();
} catch (error) {
attempt++;
const delay = Math.pow(2, attempt) * 100;
await sleep(delay);
}
}
throw new Error(`Failed after ${maxAttempts} attempts`);
}
在消息处理的每个关键阶段,OpenClaw都实现了完善的监控指标:
typescript复制// 监控指标示例
metrics.timing("message.processing.time", durationMs);
metrics.increment("tool.executions.count", 1);
metrics.gauge("active.sessions.count", sessionCount);
这些指标通过Prometheus收集,Grafana展示,并设置智能告警规则。当P99延迟超过500ms或错误率超过1%时,会自动触发告警通知值班工程师。
