1. OpenClaw Native 线程到底解决什么问题
OpenClaw Native 线程是 OpenClaw 为外部独立 Harness(比如 codex-harness、claude-cli-harness)准备的专用原生执行线程池。它要处理的核心矛盾很具体:网关主循环跑的是轻量协程,擅长高并发 IO,但外部 CLI 智能体是本地二进制进程,启动慢、会阻塞、吃 CPU,一旦直接塞进协程事件循环,整个网关的 IM/HTTP 接入都会被拖住。Native 线程就是把这类阻塞型任务从协程体系里剥出来,放到独立的 OS 线程池里执行。
它适合谁?如果你正在本地跑 OpenClaw,并且接入了 Codex、Claude CLI 或自研二进制 Agent,同时希望网关在长推理、代码执行、绘图这类重任务下不卡死,那 Native 线程就是你绕不开的一层。它不适合纯内置 Harness 或 HTTP 模型 Provider 的场景,那些走协程就够了。
我试过把 codex-harness 直接挂在协程里跑,结果一次长代码生成就把整个会话通道堵住,其他请求全部排队。后来把外部 Harness 切到 Native 线程池,网关主循环立刻恢复流畅。这篇就按“线程池 → 协程 → Harness”的协作关系,给你一份可复制的 config.toml 与 settings.json 骨架,并给出线程数、并发与 Harness 参数的验证动作。
2. 前置准备:TaoToken 统一 Key/API 通道
在动 OpenClaw 配置之前,先把模型通道统一掉。OpenClaw 的 Native 线程负责调度外部 Harness,但 Harness 内部真正调模型时,仍然需要一个稳定的 API 入口。TaoToken 在这里的角色就是统一 Key/API 通道,让你不用在 codex-harness、claude-cli-harness 里各配一套密钥。
你需要先拿到 API Key,入口在控制台的 API Keys 页面:
https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite拿到 Key 之后,模型对话调试可以用:
https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite如果你后面要长期跑编码类 Agent,建议直接看 Coding Plan:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite接入文档在:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewriteAPI 基础地址统一用:
https://taotoken.net/api注意:API 地址不要加 UTM 参数,只有页面类 deep link 才带 utm_source 和 utm_content。
Claude Code / Anthropic 兼容通道的说明页在:
https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite把 Key 和 Base URL 准备好,后面 config.toml 里的 Harness 段会直接引用这两个值。
3. 可复制配置:config.toml 与 settings.json 骨架
OpenClaw 的 Native 线程配置分两层:一层是线程池与队列的运行时参数,放在 config.toml;另一层是 Harness 选择与回退策略,放在 settings.json。下面这份骨架可以直接改。
3.1 config.toml:线程池与队列
[claw.native] # Native 线程池最大并发数,建议等于物理 CPU 核心数 thread_pool_size = 8 # 队列最大等待任务数 queue_max = 32 # 队列满策略:reject 直接返回繁忙 / wait 阻塞协程等待空位 queue_strategy = "reject" # 单 Native 任务最大执行超时 task_timeout = "300s" # 线程池缩容开关,离线环境可置 0 关闭 enabled = true [claw.native.sandbox] # 每个 Worker 独立临时工作目录 workdir_cache = "/var/lib/openclaw/native/workdir" # 线程销毁时清理临时文件 cleanup_on_exit = true [claw.native.observability] # 线程内 Hook 继承上游协程快照 inherit_trace = true # 标签区分协程与 Native 执行 exec_thread_type = "native_worker"线程池容量和队列的关系要理解清楚:thread_pool_size是同时能跑的外部 Harness 数量,queue_max是排队等待的上限。当两者都打满,queue_strategy = "reject"会让新任务直接返回 session busy,而不是无限堆积把网关拖死。
3.2 settings.json:Harness 选择与回退
{ "harness": { "selection": { "codex-harness": { "runtime": "native", "binary": "/usr/local/bin/codex", "api_base": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY" }, "claude-cli-harness": { "runtime": "native", "binary": "/usr/local/bin/claude", "api_base": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY" }, "builtin-openclaw-harness": { "runtime": "coroutine" } }, "fallback": { "enabled": true, "target": "builtin-openclaw-harness", "on": ["process_crash", "task_timeout"] } }, "concurrency": { "max_concurrent_sub_agents": 4, "session_lock": true } }这里的关键点是runtime字段:只有标了native的 Harness 才会走 Native 线程池,builtin-openclaw-harness标coroutine,永远在协程里跑。fallback段负责在外部进程崩溃或超时后,把任务切回内置协程 Harness,同时释放 Native 线程配额。
3.3 环境变量插值
生产环境建议用环境变量控制线程数,方便不同部署差异化:
export NATIVE_WORKER_COUNT=8 export TAOTOKEN_API_KEY="你的Key"config.toml 里对应写成:
thread_pool_size = "${NATIVE_WORKER_COUNT:8}"离线私有化部署时把NATIVE_WORKER_COUNT设为 0,Native 线程池直接关闭,所有外部 Harness 自动回退到内置协程 Harness。
4. 验证请求:确认线程池与 Harness 真的在工作
配置写完不能只看文件,要实际发请求验证。下面分三步:查线程池状态、模拟 Native 执行、看日志。
4.1 查看线程池忙闲
claw native pool status预期输出类似:
Native Thread Pool size: 8 busy: 2 idle: 6 queue_waiting: 0 fallback_count: 0 exec_thread_type: native_worker如果busy长期等于size且queue_waiting持续大于 0,说明线程池容量不够,需要调大thread_pool_size或检查外部 Harness 是否卡死。
4.2 模拟外部 Harness 线程执行
claw harness test --thread=native --harness=codex-harness这个命令会走一遍完整的 Native 线程链路:协程投递任务 → 线程池消费 → 启动 codex 子进程 → 流式返回 chunk → 回收资源。成功时你会看到:
[ok] task dispatched to native worker [ok] context snapshot bound to TLS [ok] codex process started [ok] stream chunk received [ok] turn result returned to coroutine [ok] native worker released任何一步失败,都会在对应阶段报错,方便定位是快照绑定问题还是进程启动问题。
4.3 打开 Native 调试日志
claw.log.level.native=debug日志里会打印线程创建、任务投递、进程启停、回退的完整过程。重点看两个字段:native_thread_id和harness_runtime,它们能帮你确认任务确实跑在 Native 线程而不是协程里。
4.4 验证模型通道
Harness 内部调模型时,确认请求打到的是 TaoToken 的 API 地址。你可以在模型对话页发一条测试消息:
https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite如果返回正常,说明 Key 和 Base URL 配置无误,Native 线程里的 Harness 也能复用同一套通道。
5. 本篇常见错排查
5.1 队列满返回 session busy
现象:请求直接返回繁忙,日志里出现queue_strategy=reject。原因是thread_pool_size和queue_max都被占满。先查claw native pool status,如果busy等于size且queue_waiting等于queue_max,要么调大容量,要么检查是否有任务卡死没释放。
5.2 任务超时后线程未回收
现象:task_timeout到了但busy没降。检查cleanup_on_exit是否为 true,以及外部进程是否被强制 kill。如果子进程没被回收,线程会一直占着配额,最终导致所有 Codex 任务繁忙。
5.3 回退没有触发
现象:外部 Harness 崩溃后请求直接失败,没有切到内置 Harness。检查 settings.json 里fallback.enabled是否为 true,on数组是否包含process_crash和task_timeout。回退目标必须是builtin-openclaw-harness,不能指向另一个 native Harness。
5.4 上下文快照串扰
现象:不同会话的 trace 混在一起。原因是跨线程传递了原始可变上下文引用。框架强制只读快照,你不需要手动传上下文,但要确认没有在自定义 Hook 里直接读协程本地存储。Native 线程内所有观测只能读 TLS 里的快照。
5.5 子代理耗尽线程配额
现象:主任务正常,但子代理一多就繁忙。子代理调用外部 Harness 同样占用 Native 线程池,受max_concurrent_sub_agents和thread_pool_size双重限制。调低子代理并发,或调大线程池。
5.6 线程池关闭后 Harness 仍尝试 native
现象:enabled = false后请求报错。检查 settings.json 里对应 Harness 的runtime是否还是native。关闭线程池后,这些 Harness 应该通过 fallback 切到coroutine,或者直接把runtime改成coroutine。
6. 长期编码与 Agent 场景的接入建议
如果你只是偶尔跑一次外部 Harness,上面的配置够用了。但如果你要长期跑编码类 Agent,比如让 Codex 持续处理代码任务,建议把线程池容量和 Coding Plan 一起考虑。线程池容量决定同时能跑几个外部进程,Coding Plan 决定模型通道的稳定性和额度。
长期编码场景的接入入口:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewriteAPI Key 管理仍然在:
https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite接入文档:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite生产落地时,线程数按物理 CPU 核心数设置,队列策略统一用 reject,队列长度加监控告警。Langfuse 里用exec_thread_type=native_worker单独筛出 Native 线程流量,统计耗时和故障率。离线部署把NATIVE_WORKER_COUNT设为 0,直接关掉 Native 线程池,所有外部 Harness 回退到协程内置 Harness,资源占用立刻降下来。