尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

借鉴 OpenClaw 等框架思路:用 TaoToken 统一 Key 搭建核心/扩展分离的轻量级 Agent 框架

发布时间:2026/9/26 13:02:26

资讯中心
01
ARTICLE

借鉴 OpenClaw 等框架思路:用 TaoToken 统一 Key 搭建核心/扩展分离的轻量级 Agent 框架

借鉴 OpenClaw 等框架思路:用 TaoToken 统一 Key 搭建核心/扩展分离的轻量级 Agent 框架
1. 从 OpenClaw 的架构里我提炼出了什么如果你正在写自己的 Agent 执行引擎大概率会遇到同一个坎工具越加越多核心调度文件越来越长最后变成一坨谁都不敢动的 if-else。文件操作、命令执行、记忆检索、长文本处理全塞在一个模块里加一个工具就要改一次核心路由单元测试根本没法写。OpenClaw 这类框架给出的答案很直接核心只做编排和注册具体能力全部由扩展模块按需注入。它的三层结构Gateway / Channel / LLM里Gateway 管会话调度和权限Channel 管平台适配LLM 管模型接口和 Tool Calling每一层都通过注册表和统一接口跟扩展交互核心代码几乎不动。我照着这个思路做了一个轻量级 Agent 框架的骨架核心模块只保留 ToolRegistry、AgenticLoop、ChatSession、PromptBuilder 四件事所有内置工具都放到 extensions/ 目录里通过 register_extensions() 统一注册。模型接入这块用 TaoToken 的统一 Key 和 API 通道一个 Key 就能切换不同模型不用在核心层写一堆 provider 分支。这篇会给你可复制的 config.toml 和 settings.json 骨架、完整的模块目录结构以及一次端到端验证本地跑通核心调度 扩展插件加载 模型请求。适合正在搭 Agent 框架、想理清核心与扩展边界的开发者。2. TaoToken 前置统一 Key 与 API 通道在讲框架代码之前先把模型接入这条线理清楚。核心模块不应该关心你用的是哪个模型、走的是哪家通道它只应该拿到一个「能调用的 LLM 客户端」。TaoToken 在这里扮演的角色就是统一入口一个 API Key一个 Base URL兼容 OpenAI 风格的接口模型名通过配置切换。你需要先拿到 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制保存。这个 Key 后面会写进 settings.json由扩展模块读取核心层不直接持有。Base URL 用 https://taotoken.net/api 注意这个地址不带任何查询参数。模型名按你实际要用的填比如 claude 系列或 gpt 系列的标识具体以控制台模型列表为准。如果你想先确认某个模型能不能正常对话可以直接在 https://taotoken.net/models 里试一轮确认通了再写进配置。这里有个设计上的关键点核心模块的 AgenticLoop 只依赖一个LLMClient接口接口方法就两个——chat(messages, tools)和stream(messages, tools)。具体是哪个 provider由 extensions/llm_provider.py 里的注册逻辑决定。这样你以后换模型、加模型都只动扩展不动核心。3. 可复制配置config.toml 与 settings.json 骨架先给目录结构这是整个框架的骨架核心和扩展的边界一眼能看清agent-lite/ ├── core/ # 纯框架层零内置工具实现 │ ├── tool_registry.py # 工具注册表 │ ├── agentic_loop.py # LLM 调用与工具循环 │ ├── chat_session.py # 会话编排 │ └── prompt_builder.py # 提示词构建 ├── extensions/ # 可插拔扩展 │ ├── __init__.py # register_extensions() 统一入口 │ ├── llm_provider.py # 模型 provider 注册 │ ├── file_tools.py # 文件类工具 │ ├── command_tools.py # 命令类工具 │ └── long_text.py # 长文本处理策略 ├── config.toml # 框架级配置 ├── settings.json # 密钥与运行时配置 └── main.py # 启动入口config.toml 管框架行为比如启用哪些扩展、循环上限、超时[agent] name agent-lite max_iterations 8 request_timeout 60 [extensions] enable_file_tools true enable_command_tools true enable_long_text true [llm] base_url https://taotoken.net/api model claude-sonnet stream truesettings.json 管密钥和运行时覆盖注意这个文件不要提交到仓库{ llm: { api_key: sk-你的TaoToken密钥, base_url: https://taotoken.net/api, model: claude-sonnet }, workspace: ./workspace, log_level: info }核心的 ToolRegistry 只做三件事注册、查找、执行。它不关心工具来自 file 还是 command# core/tool_registry.py from typing import Callable, Dict, Optional class ToolEntry: def __init__(self, schema: Dict, handler: Callable): self.schema schema self.handler handler class ToolRegistry: def __init__(self): self._tools: Dict[str, ToolEntry] {} def register(self, name: str, schema: Dict, handler: Callable) - None: self._tools[name] ToolEntry(schemaschema, handlerhandler) def get(self, name: str) - Optional[ToolEntry]: return self._tools.get(name) def execute(self, name: str, tool_input: Dict) - str: entry self.get(name) if not entry: return fError: no executor for tool: {name} return entry.handler(tool_input) def schemas(self): return [e.schema for e in self._tools.values()]扩展的统一注册入口按开关决定加载哪些模块# extensions/__init__.py from . import file_tools, command_tools, long_text def register_extensions(registry, ctx, enable_file_toolsTrue, enable_command_toolsTrue, enable_long_textTrue): if enable_file_tools: file_tools.register(registry, ctx) if enable_command_tools: command_tools.register(registry, ctx) if enable_long_text: long_text.register(registry, ctx)每个扩展模块自己实现 register()把工具塞进注册表。以 file_tools 为例# extensions/file_tools.py import os def read_file(args): path args.get(path, ) with open(path, r, encodingutf-8) as f: return f.read() def register(registry, ctx): registry.register( read_file, { type: function, function: { name: read_file, description: 读取指定路径的文本文件, parameters: { type: object, properties: {path: {type: string}}, required: [path], }, }, }, read_file, )模型 provider 也走同一套注册思路核心的 AgenticLoop 只认接口# extensions/llm_provider.py from openai import OpenAI def build_client(settings): return OpenAI( api_keysettings[llm][api_key], base_urlsettings[llm][base_url], ) def chat(client, model, messages, toolsNone): resp client.chat.completions.create( modelmodel, messagesmessages, toolstools or None, ) return resp.choices[0].message4. 端到端验证跑通核心调度与扩展加载配置和骨架就位后写一个最小启动入口把核心循环和扩展串起来# main.py import json, tomllib from core.tool_registry import ToolRegistry from core.agentic_loop import AgenticLoop from extensions import register_extensions from extensions.llm_provider import build_client, chat def load_config(): with open(config.toml, rb) as f: cfg tomllib.load(f) with open(settings.json, r, encodingutf-8) as f: settings json.load(f) return cfg, settings def main(): cfg, settings load_config() registry ToolRegistry() register_extensions( registry, ctx{}, enable_file_toolscfg[extensions][enable_file_tools], enable_command_toolscfg[extensions][enable_command_tools], enable_long_textcfg[extensions][enable_long_text], ) client build_client(settings) loop AgenticLoop( clientclient, registryregistry, modelsettings[llm][model], max_iterationscfg[agent][max_iterations], ) reply loop.run(列出当前目录下的文件并读取 config.toml 的前 5 行) print(reply) if __name__ __main__: main()AgenticLoop 的核心逻辑就是「调模型 → 看有没有 tool_call → 执行工具 → 把结果塞回消息 → 再调模型」直到没有工具调用或达到上限# core/agentic_loop.py class AgenticLoop: def __init__(self, client, registry, model, max_iterations8): self.client client self.registry registry self.model model self.max_iterations max_iterations def run(self, user_input: str) - str: messages [{role: user, content: user_input}] for _ in range(self.max_iterations): msg self._chat(messages) messages.append(msg) tool_calls getattr(msg, tool_calls, None) if not tool_calls: return msg.content for call in tool_calls: result self.registry.execute( call.function.name, json.loads(call.function.arguments), ) messages.append({ role: tool, tool_call_id: call.id, content: result, }) return 达到最大迭代次数未完成跑起来后终端应该先打印出目录列表再输出 config.toml 的前几行内容。这一步验证了三件事扩展模块被正确加载进注册表、模型通过 TaoToken 通道正常返回 tool_call、核心循环能调度工具并把结果回传。如果模型返回的是纯文本而不是工具调用说明 tools 参数没传进去检查registry.schemas()是否为空。5. 本篇常见错排查报错一Error: no executor for tool: read_file注册表里没有这个工具。先确认 config.toml 里enable_file_tools true再确认 extensions/init.py 里确实调用了file_tools.register(registry, ctx)。常见坑是扩展模块 import 失败被静默吞掉建议在 register_extensions 里加一行日志打印已注册的工具名。报错二401 或鉴权失败settings.json 里的 api_key 没填对或者 base_url 写成了带路径的地址。Base URL 必须是https://taotoken.net/api不要在后面拼/v1或加查询参数。Key 建议重新在 https://taotoken.net/api-keys 生成一次确认复制时没有多余空格。报错三模型不返回 tool_calls只回文本两个原因一是 tools 参数没传检查 AgenticLoop 里self._chat(messages)是否把registry.schemas()带上了二是模型本身不支持工具调用换一个支持 Tool Calling 的模型标识。可以在 https://taotoken.net/models 里先确认模型能力。报错四长文本把上下文撑爆工具返回的内容太长直接塞回 messages 会超限。这就是 long_text 扩展存在的意义——在把工具结果写回消息前先过一遍截断或分段总结。检查enable_long_text true并在 AgenticLoop 里对 tool 结果调用long_text.summarize(result)。报错五循环停不下来max_iterations 设太大或者模型反复调同一个工具。把上限压到 8 以内同时在 ToolRegistry.execute 里加一层同名工具连续调用计数超过阈值直接返回错误提示逼模型换策略。6. 继续往下走核心和扩展分离之后加新能力就变成了一件很轻的事新建一个 extensions/xxx.py实现 register()在 register_extensions 里加一行调用核心代码一行不用改。模型接入也一样TaoToken 的统一 Key 让你在 settings.json 里换个模型名就能切换不用在核心层写 provider 分支。如果你打算把这个骨架用到长期编码或 Agent 场景可以看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它更适合需要持续调用和稳定通道的用法。接入细节和参数说明在文档里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。想先验证模型对话效果直接去 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 试一轮确认通了再回来改配置。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。