如何用400行代码构建OpenClaw

如何用400行代码构建OpenClaw你可以用 400 行代码构建一个行为类似于 OpenClaw 的智能体 只需使用 TypeScript Anthropic SDK Slack SDK 和一个 YAML 解析库 无需框架 也无需复杂的抽象 只需在一个脚本中包含几个函数 截至 2026 年 2 月 19 日 OpenClaw 的代码库拥有超过 50 万行 TypeScript 代码 但其核心可以简化为一个非常短小精悍的智能体

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



你可以用400行代码构建一个行为类似于OpenClaw的智能体。只需使用TypeScript、Anthropic SDK、Slack SDK和一个YAML解析库。无需框架,也无需复杂的抽象——只需在一个脚本中包含几个函数。

截至2026年2月19日,OpenClaw的代码库拥有超过50万行TypeScript代码。但其核心可以简化为一个非常短小精悍的智能体:它在Slack中响应,使用技能,跨对话记住事实,浏览计算机上的文件,执行命令,访问互联网,并自主行动——无需人工干预。就像OpenClaw一样。

在这篇文章中,你将了解它的内部工作原理。我将引导你从零开始构建一个类似OpenClaw的智能体,这样你就能更好地理解你可能已经在使用的工具——并将这些想法应用到你自己的智能体系统中。

我们不会重现完整的OpenClaw体验。这篇博客文章的目标是阐明OpenClaw背后的核心原则,而不是精确地重现它。我们不会构建Web界面、Telegram和WhatsApp集成、语音支持或其他生活质量功能。

然而,我们将构建一个功能齐全的智能体,它能够:

  • 响应来自授权用户的Slack私信
  • 使用计算机
  • 访问互联网
  • 跨对话保持记忆
  • 使用智能体技能
  • 学习用户的偏好
  • 无需明确提示即可主动行动

这篇博客文章旨在让你能够跟着一起构建智能体。每个部分都会添加一个新功能,并在前一个功能的基础上进行构建,这样你就可以逐步看到和使用智能体的演变。

让我们从创建一个简单的脚本开始,它接收来自Slack的消息并进行回复。

初始化一个新的TypeScript项目。这里,我们将使用Bun:

 
  
    
    
GPT plus 代充 只需 145bun init

安装Slack SDK:

bun add @slack/bolt

这是完整的逻辑。将其保存到一个名为index.ts的文件中。

GPT plus 代充 只需 145import { App } from "@slack/bolt"; 

const app = new App({ token: process.env.SLACK_BOT_TOKEN, appToken: process.env.SLACK_APP_TOKEN, socketMode: true, });

app.event("message", async ({ event, client }) => await client.chat.postMessage({

channel: event.channel, thread_ts: event.thread_ts ?? event.ts, text: `Hey! Your Slack user id is `${event.user}` - you'll need it later.`, 

}); });

console.log("Slack agent running"); app.start();

GPT plus 代充 只需 145 

你需要创建一个Slack应用来运行它:

  1. 最简单的方法是访问这个链接:它预先填写了创建新应用所需的所有权限,以便与机器人交互。该链接是使用这个脚本生成的。
  2. 创建后,在"基本信息"页面上生成一个具有connections:write范围的应用级令牌。Slack会要求你为其命名——任何名称都可以。该令牌将是SLACK_APP_TOKEN环境变量的值。
  3. 然后,通过访问"安装应用"页面并将应用安装到工作区来生成SLACK_BOT_TOKEN环境变量。

现在使用以下命令运行机器人:

 
  
    
    
GPT plus 代充 只需 145SLACK_BOT_TOKEN=xoxb-... SLACK_APP_TOKEN=xapp-... bun run index.ts

在Slack中找到它。它会在搜索栏中显示为"picobot"。向它发送一条私信,它应该会回复。

现在让我们使用LLM生成回复。我们将使用Anthropic的SDK:

 
  
    
    
GPT plus 代充 只需 145bun add @anthropic-ai/sdk

LLM将能够通过工具调用发送Slack消息:

import Anthropic from "@anthropic-ai/sdk"; 

type ToolWithExecute = Anthropic.Tool & { execute: (input: any) => Promise ; };

function createTools(channel: string, threadTs: string): ToolWithExecute[] { return [

GPT plus 代充 只需 145{ name: "send_slack_message", description: "Send a message to the user in Slack. This is the only way to communicate with the user.", input_schema: { type: "object", properties: { text: { type: "string", description: "The message text (supports Slack mrkdwn formatting)", }, }, required: ["text"], }, execute: async (input: { text: string }) => { await app.client.chat.postMessage({ channel, thread_ts: threadTs, text: input.text, blocks: [ { type: "markdown", text: input.text, }, ], }); return "Message sent."; }, }, 

]; }

 

我们将创建一个函数,该函数调用Anthropic的API来生成响应,如果响应包含工具调用,则执行工具调用。

GPT plus 代充 只需 145async function generateMessages(args: { 

channel: string; threadTs: string; system: string; messages: Anthropic.MessageParam[]; }): Promise { const { channel, threadTs, system, messages } = args; const tools = createTools(channel, threadTs);

console.log("Generating messages for thread", threadTs); const response = await anthropic.messages.create({

model: "claude-opus-4-6", max_tokens: 8096, system, messages, tools, 

}); console.log(

GPT plus 代充 只需 145`Response generated for thread ${threadTs}: ${response.usage.output_tokens} tokens`, 

);

const toolsByName = new Map(tools.map((t) => [t.name, t])); const toolResults: Anthropic.ToolResultBlockParam[] = []; for (const block of response.content)

try { console.log(`Agent used tool ${block.name}`); const tool = toolsByName.get(block.name); if (!tool) { throw new Error(`tool "${block.name}" not found`); } const result = await tool.execute(block.input); toolResults.push(); } catch (e: any) { console.warn(`Agent tried to use tool ${block.name} but failed`, e); toolResults.push({ type: "tool_result", tool_use_id: block.id, content: `Error: ${e.message}`, is_error: true, }); } 

}

messages.push({ role: "assistant", content: response.content }); if (toolResults.length > 0) {

GPT plus 代充 只需 145messages.push({ role: "user", content: toolResults, }); 

}

return messages; }

 

最后,我们将在消息事件处理程序中调用generateMessages

GPT plus 代充 只需 145app.event("message", async ({ event }) => 

const threadTs = event.thread_ts ?? event.ts; const channel = event.channel;

// Only allow authorized users to interact with the bot if (event.user !== process.env.SLACK_USER_ID) {

await app.client.chat.postMessage({ channel, thread_ts: threadTs, text: `I'm sorry, I'm not authorized to respond to messages from you. Set the `SLACK_USER_ID` environment variable to `${event.user}` to allow me to respond to your messages.`, }); return; 

}

// Show a typing indicator to the user while we generate the response // It’ll be auto-cleared once the agent sends a Slack message await app.client.assistant.threads.setStatus({

GPT plus 代充 只需 145channel_id: channel, thread_ts: threadTs, status: "is typing...", 

});

await generateMessages({

channel: event.channel, threadTs, system: "You are a helpful Slack assistant.", messages: [ { role: "user", content: `User <@${event.user}> sent this message (timestamp: ${event.ts}) in Slack: 
GPT plus 代充 只需 145${event.text} 

You must respond using the send_slack_message tool.`,

 }, ], 

}); });

GPT plus 代充 只需 145 

这是目前为止的完整代码------你可以将其保存到index.ts中。

LLM现在可以响应来自授权用户的Slack消息。请记住在运行机器人之前设置SLACK_USER_ID环境变量。

 
  
    
    
GPT plus 代充 只需 145SLACK_BOT_TOKEN=xoxb-... SLACK_APP_TOKEN=xapp-... SLACK_USER_ID=U... bun run index.ts

机器人会响应消息,但它不记得对话内容。

让我们改变这一点。我们将对话历史记录持久化到~/.picobot/threads/中的JSON文件中。每个文件都将以线程时间戳命名,并包含线程的消息。当收到新的Slack消息时,我们将加载线程并将新消息添加到其中。

 
  
    
    
GPT plus 代充 只需 145import fs from "node:fs"; 

import path from "node:path"; import os from "node:os";

const configDir = path.resolve(os.homedir(), ".picobot"); const threadsDir = path.resolve(configDir, "threads");

interface Thread { threadTs: string; channel: string; messages: Anthropic.MessageParam[]; }

function saveThread(threadTs: string, thread: Thread): void { fs.mkdirSync(threadsDir, { recursive: true }); return fs.writeFileSync(

path.resolve(threadsDir, `${threadTs}.json`), JSON.stringify(thread, null, 2), 

); }

function loadThread(threadTs: string): Thread | undefined { try {

GPT plus 代充 只需 145return JSON.parse( fs.readFileSync(path.resolve(threadsDir, `${threadTs}.json`), "utf-8"), ); 

} catch (e) {

return undefined; 

} }

app.event("message", async ({ event }) => { // … existing code up to the status indicator …

const thread: Thread = loadThread(threadTs) ?? {

GPT plus 代充 只需 145threadTs, channel, messages: [], 

}; const messages = await generateMessages({

channel: event.channel, threadTs, system: "You are a helpful Slack assistant.", messages: [ ...thread.messages, { role: "user", content: `User <@${event.user}> sent this message (timestamp: ${event.ts}) in Slack: 
GPT plus 代充 只需 145${event.text} 

You must respond using the send_slack_message tool.`,

 }, ], 

}); saveThread(threadTs, {

GPT plus 代充 只需 145...thread, messages, 

}); });

 

这是目前为止的完整代码。

机器人现在会记住对话内容:

现在,我们的机器人会记住对话内容,但它会记住所有内容。如果对话持续足够长的时间,它最终会超出LLM的上下文窗口。当这种情况发生时,LLM会开始"失忆",并且无法再引用旧的对话内容。

为了解决这个问题,我们将实现记忆压缩。当对话历史记录变得太长时,我们将要求LLM将其压缩成一个简短的摘要。然后,我们将用摘要替换旧的对话内容,从而为新的对话腾出空间。

GPT plus 代充 只需 145 
  
    
    
// ... existing imports ... 

import { dump } from "js-yaml";

// … existing Thread interface …

interface Thread { threadTs: string; channel: string; messages: Anthropic.MessageParam[]; summary?: string; }

// … existing saveThread and loadThread functions …

async function compactThread(thread: Thread): Promise ,

GPT plus 代充 只需 145], 

});

const summary = response.content.map((block) => block.text).join(" ");

return {

...thread, messages: [ { role: "assistant", content: summary }, // Replace old messages with summary ], summary, 

}; }

app.event("message", async ({ event }) => { // … existing code up to the status indicator …

let thread: Thread = loadThread(threadTs) ?? {

GPT plus 代充 只需 145threadTs, channel, messages: [], 

};

// Check if compaction is needed const totalTokens = await anthropic.countTokens({

model: "claude-opus-4-6", messages: thread.messages, 

});

if (totalTokens > 4000) { // Arbitrary threshold for compaction

GPT plus 代充 只需 145thread = await compactThread(thread); 

}

const messages = await generateMessages({

channel: event.channel, threadTs, system: "You are a helpful Slack assistant.", messages: [ ...thread.messages, { role: "user", content: `User <@${event.user}> sent this message (timestamp: ${event.ts}) in Slack: 
GPT plus 代充 只需 145${event.text} 

You must respond using the send_slack_message tool.`,

 }, ], 

}); saveThread(threadTs, {

GPT plus 代充 只需 145...thread, messages, 

}); });

 

这是目前为止的完整代码。

机器人现在会压缩对话历史记录,以避免超出LLM的上下文窗口。

OpenClaw最强大的功能之一是它能够使用技能。技能是LLM可以调用的函数,以执行特定任务。例如,一个技能可以用来搜索网络,另一个技能可以用来生成图像。

我们将添加一个简单的技能,允许LLM搜索网络。我们将使用axios库来发出HTTP请求。

GPT plus 代充 只需 145 
  
    
    
bun add axios

我们将创建一个skills目录,并在其中添加一个web_search.ts文件:

GPT plus 代充 只需 145// skills/web_search.ts 

import axios from "axios";

export async function webSearch(query: string): Promise , }); // Parse the HTML response to extract relevant information // This is a simplified example, a real implementation would use a more robust HTML parser return response.data.match(/ /)?.[1" target="_blank">https://jishuzhan.net/article/"(.*?)">/)?.[1] || "No results found."; }

 

现在,我们将修改createTools函数以包含web_search技能:

GPT plus 代充 只需 145// ... existing imports ... 

import { webSearch } from "./skills/web_search";

function createTools(channel: string, threadTs: string): ToolWithExecute[] { return [

// ... existing send_slack_message tool ... { name: "web_search", description: "Search the web for a given query.", input_schema: { type: "object", properties: { query: { type: "string", description: "The search query.", }, }, required: ["query"], }, execute: async (input: { query: string }) => { return await webSearch(input.query); }, }, 

]; }

GPT plus 代充 只需 145 

现在,LLM将能够使用web_search技能来搜索网络。例如,如果你问它"OpenClaw是什么?",它可能会使用web_search技能来查找相关信息。

OpenClaw最强大的功能之一是它能够主动行动,而无需明确提示。例如,它可以监视GitHub仓库的更新,并在有新提交时通知你。

我们将添加一个简单的机制,允许LLM主动行动。我们将使用一个cron作业来定期触发LLM,并让它决定是否需要采取行动。

 
  
    
    
GPT plus 代充 只需 145// ... existing imports ... 

import { CronJob } from "cron";

// … existing app.event("message") handler …

new CronJob( "0 * * * * *", // Run every minute async () => ,

 ], }); // If the LLM responded with NO_REPLY, do nothing if (messages.length === thread.messages.length + 1 && messages[messages.length - 1].content === "NO_REPLY") { continue; } saveThread(threadTs, { ...thread, messages, }); } 

}, null, // onComplete true, // start "America/Los_Angeles" // timeZone );

GPT plus 代充 只需 145 

这是目前为止的完整代码。

机器人现在会主动行动,而无需明确提示。例如,它可以监视GitHub仓库的更新,并在有新提交时通知你。

在这篇文章中,我们从零开始构建了一个类似OpenClaw的智能体,只用了不到400行代码。我们涵盖了以下功能:

  • 响应Slack消息
  • 作为LLM回复
  • 跟踪对话
  • 记忆压缩
  • 使用技能
  • 主动行动

我希望这篇博客文章能帮助你更好地理解OpenClaw的内部工作原理,并将这些想法应用到你自己的智能体系统中。

你可以在这里找到完整的代码。

  • Anthropic SDK
  • Slack SDK
  • Bun
  • js-yaml
  • axios
  • cron
  • OpenClaw Compaction
  • Blink

小讯
上一篇 2026-03-19 20:36
下一篇 2026-03-19 20:34

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/245033.html