从零开发专属AI Agent:基于OpenClaw龙虾智能体完整实战指南(TaoToken统一Key接入篇)

发布时间:2026/10/7 14:09:27
从零开发专属AI Agent:基于OpenClaw龙虾智能体完整实战指南(TaoToken统一Key接入篇) 1. 为什么我要自己撸一个 OpenClaw 龙虾智能体市面上大部分 AI 对话工具本质还是“你问一句它答一句”。关掉窗口它就把你忘了想让它帮你整理个文件、查个天气、跑个定时任务它只会礼貌地告诉你“我做不到”。我想要的不是这种问答机器人而是一个能自己感知、自己规划、自己动手、还能记住事的本地智能体。OpenClaw圈里叫龙虾智能体就是冲着这个目标去的开源框架它把“理解—规划—执行—记忆—迭代”这一整套闭环塞进了一个可以本地跑的 Node.js 项目里。这篇实战指南面向的是想从零手写一个专属 AI Agent 的开发者尤其是习惯 Node.js、想用 SQLite 做本地记忆、又不想被各种模型 Key 管理折腾的人。我会带你搭出 OpenClaw 的最小可运行骨架目录结构、SQLite 建表、Agent 主循环代码最后把模型 endpoint 和 Key 统一改到 TaoToken 通道上发一条测试消息验证闭环真的跑通了。全程可复制踩坑点我会标出来。核心检索词先摆在这OpenClaw 是一个本地自主智能体框架AI Agent 是它的产物Node.js 是运行底座SQLite 是记忆底座。适合谁适合想拥有一个“越用越懂你”的本地数字助手、又愿意动手写点代码的人。下面直接开干。2. 前置准备TaoToken 统一 Key 与 OpenClaw 环境在写代码之前先把两件事搞定模型通道和环境依赖。模型这块我用 TaoToken 做统一入口好处是一个 Key 能覆盖多种模型不用在 OpenClaw 里到处改 provider 配置。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 注意这个 API 地址后面不加任何参数。先去控制台建一个 Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面生成页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到形如sk-xxxx的字符串先存好后面配置里要用。想先确认模型通不通可以直接在模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 发一句话试试能回就说明 Key 没问题。环境依赖三样Node.js 18 以上我用 18.20 实测稳定、Git、SQLite3macOS 和多数 Linux 自带Windows 建议装个 sqlite3 命令行方便调试。检查命令node -v npm -v sqlite3 --version三个都出版本号就 OK。接着建项目目录我习惯叫openclaw-lobstermkdir openclaw-lobster cd openclaw-lobster npm init -y npm install better-sqlite3 node-fetch3这里我选better-sqlite3而不是原生 sqlite3因为它是同步 API写 Agent 主循环时逻辑更直白不用被回调绕晕。node-fetch3用来发模型请求。装完目录里会有node_modules和package.json在package.json里加一行type: module这样后面能用 import 语法。目录结构我规划成这样先建好空文件夹openclaw-lobster/ ├── src/ │ ├── agent.js # Agent 主循环 │ ├── memory.js # SQLite 记忆层 │ ├── llm.js # 模型调用封装 │ └── tools.js # 技能注册 ├── data/ │ └── lobster.db # SQLite 数据库文件 ├── config.json # 模型与网关配置 └── package.jsonmkdir -p src data到这一步TaoToken 的 Key 和环境都齐了。接下来进入真正的代码环节先把记忆底座 SQLite 建起来因为 Agent 的“记性”全靠它。3. 可复制配置SQLite 建表与 config.json 接入 TaoTokenAgent 的记忆分三块原始对话、任务日志、配置信息。我用一张messages表存对话一张tasks表存任务执行记录再加一张kv表存运行时状态。建表语句直接写进src/memory.js的初始化函数里这样每次启动自动建表不用手动跑 SQL。// src/memory.js import Database from better-sqlite3; const db new Database(./data/lobster.db); db.pragma(journal_mode WAL); export function initDB() { db.exec( CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, status TEXT NOT NULL, result TEXT, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS kv ( key TEXT PRIMARY KEY, value TEXT NOT NULL ); ); } export function saveMessage(role, content) { const stmt db.prepare( INSERT INTO messages (role, content, created_at) VALUES (?, ?, ?) ); return stmt.run(role, content, Date.now()); } export function recentMessages(limit 20) { const stmt db.prepare( SELECT role, content FROM messages ORDER BY id DESC LIMIT ? ); return stmt.all(limit).reverse(); } export function saveTask(name, status, result ) { const stmt db.prepare( INSERT INTO tasks (name, status, result, created_at) VALUES (?, ?, ?, ?) ); return stmt.run(name, status, result, Date.now()); }journal_mode WAL这行别省Agent 频繁读写时它能明显减少锁等待。recentMessages里我做了reverse()因为 SQL 是倒序取最近 N 条返回给模型时要按时间正序排。然后是config.json这是接入 TaoToken 的关键。Base URL、Key、Model ID 三件套都在这里{ model: { provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, modelId: gpt-4o-mini, maxTokens: 1024 }, agent: { name: 小钳, heartbeatInterval: 1800000, memoryLimit: 20 } }注意baseUrl写https://taotoken.net/api不要带斜杠结尾也不要在后面拼/v1之类的路径具体路径由llm.js里的请求拼接决定。modelId可以换成你在模型对话页看到的任意可用模型名。Key 建议用环境变量覆盖避免明文进 Git// src/llm.js 里读取时 const apiKey process.env.TAOTOKEN_API_KEY || config.model.apiKey;启动前export TAOTOKEN_API_KEYsk-xxxx即可。这样配置和代码分离换 Key 不用改文件。配置就绪下面写 Agent 主循环。4. 验证请求Agent 主循环跑通首个对话闭环主循环是 OpenClaw 的心脏逻辑是加载历史记忆 → 拼上下文 → 调模型 → 解析回复 → 回写记忆。先写src/llm.js封装请求// src/llm.js import fetch from node-fetch; import fs from fs; const config JSON.parse(fs.readFileSync(./config.json, utf-8)); const apiKey process.env.TAOTOKEN_API_KEY || config.model.apiKey; export async function chat(messages) { const res await fetch(${config.model.baseUrl}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: config.model.modelId, messages, max_tokens: config.model.maxTokens }) }); if (!res.ok) { const errText await res.text(); throw new Error(LLM request failed: ${res.status} ${errText}); } const data await res.json(); return data.choices[0].message.content; }这里res.ok判断很关键401 和 404 都会在这里被拦下来并打印原始错误方便排障。接着写src/agent.js// src/agent.js import { initDB, saveMessage, recentMessages } from ./memory.js; import { chat } from ./llm.js; initDB(); const SYSTEM_PROMPT 你是本地智能体「小钳」回答简洁精准优先给出可执行结果。 涉及文件删除、覆盖等高危操作必须先向用户确认。; export async function runAgent(userInput) { saveMessage(user, userInput); const history recentMessages(20); const messages [ { role: system, content: SYSTEM_PROMPT }, ...history ]; const reply await chat(messages); saveMessage(assistant, reply); return reply; } // 命令行直接测试 const input process.argv.slice(2).join( ) || 你好做个自我介绍; runAgent(input) .then((r) console.log(\n[小钳] r)) .catch((e) console.error(\n[错误] e.message));跑起来验证node src/agent.js 你好你现在能记住我说的话吗如果配置正确终端会打印小钳的回复。再跑一次带上下文的node src/agent.js 我上一句问了你什么第二次能答出第一次的内容说明 SQLite 记忆闭环生效了。这一步是整个实战的验收点模型请求走的是 TaoToken 的https://taotoken.net/apiKey 是统一 Key返回正常就代表接入成功。如果第一次就报错别慌下一节专门排。5. 本篇常见错排查401、local proxy failed 与 reading choices排障这块我按真实遇到的报错来每个都给定位思路。401 Unauthorized。最常见报错长这样LLM request failed: 401 {error:{message:Invalid API key}}。原因通常是 Key 没读到或写错了。先确认echo $TAOTOKEN_API_KEY有值再检查config.json里的apiKey是不是还留着占位符。还有一种情况是 Key 前后带了空格或换行复制时容易带上用trim()处理一下。如果 Key 确认没问题还是 401去 API Keys 页面看这个 Key 是不是被禁用或额度用尽。local proxy failed / ECONNREFUSED。这个报错说明请求根本没发出去卡在本地网络层。检查baseUrl是不是写成了https://taotoken.net/api/多了斜杠或者误加了端口。另外确认机器能正常访问外网curl https://taotoken.net/api看有没有响应。如果公司网络有出口限制换网络环境再试。注意这里不要引入任何本地代理配置直连即可。Cannot read properties of undefined (reading choices)。这个报错说明data.choices是 undefined通常是响应结构和你预期的不一样。可能是modelId写错了服务端返回了一个错误对象而不是正常 completion。打印完整data看看const data await res.json(); console.log(JSON.stringify(data, null, 2));如果看到{error: ...}那就是模型名或参数问题。还有一种可能是baseUrl拼出来的路径不对比如重复拼了/v1实际请求打到了不存在的路由。确认baseUrl是https://taotoken.net/api代码里拼/v1/chat/completions。OAuth / token 过期类报错。如果你用的是某些需要 OAuth 的客户端配置报错会提示 token invalid。OpenClaw 这套走的是标准 Bearer Key不涉及 OAuth 流程。如果看到 OAuth 字样多半是配置文件里混入了别的客户端残留字段把config.json精简成上面那三件套即可。SQLite 报 database is locked。并发写的时候会出现加WAL模式基本能解决。如果还锁检查是不是有另一个进程占着lobster.db关掉再跑。排障的核心思路就一条先看 HTTP 状态码再看响应体原文最后看请求 URL 拼得对不对。把这三样打印出来九成问题能自己定位。6. 继续深入把 OpenClaw 接到 Coding Plan 与文档跑通首个闭环只是起点。接下来你可以给 Agent 加技能比如在src/tools.js里注册一个查天气的工具让模型通过 function calling 自主调用。技能多了之后模型调用量会上来这时候用 Coding Plan 会更划算适合长期编码和 Agent 场景入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。如果你更想先摸清接口细节再动手接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有请求格式和参数说明。我自己的做法是本地开发阶段用按量 Key 调试等 Agent 稳定跑起来、每天调用量上去了再切到 Coding Plan。记忆层也可以继续优化比如把messages表里的远期对话做摘要压缩只把摘要喂给模型控制 token 消耗。这些都在你现有骨架上加不用推倒重来。最后留一个实用技巧给runAgent加个超时控制避免模型卡住时整个循环挂死。const controller new AbortController(); const timer setTimeout(() controller.abort(), 30000); // fetch 里带上 signal: controller.signal30 秒没响应就中断Agent 主循环能继续处理下一条。这个细节在长时间运行的本地智能体里很值钱早加早省心。