我们维护着一套跑在自有内网的多节点 AI Agent 协作网络:一台主控负责调度,若干节点分别承担网关隔离、模型推理、记忆检索、桌面自动化等角色,节点之间靠 SSH、文件通道和定时任务协同。
腾讯公司QClaw已发布停运公告后,我们决定提前启动迁移准备 —— 把原本由它承担的职能(定时派活、GUI 测试调度、深度研究任务)逐步转移到替代方案上,避免临期被动。
评估下来,腾讯的 CodeBuddy Code(CLI,@tencent-ai/codebuddy-code)是合适的候选:
但"能装"和"能无人值守地被调度"是两回事。 下面的排查过程,记录了我们从"桌面能用、脚本跑不通"到"完全本地化落地"的完整路径。
第一轮验证就出现了桌面侧和脚本侧的对照:
桌面侧:在 WorkBuddy 桌面里发消息,Agent 正常执行、正常回复。
脚本侧:用同样的网关接口发任务,返回 202 accepted 并给出 runId,但任务永远停在"进行中":
{"data":{"runId":"<runId>","status":"accepted"}}
# 之后无论等多久,查询状态始终 active:true日志里最先暴露的是一行不起眼的报错:
[Error] Failed to read settings file for scope user: ENOENT继续追,发现状态机走了一半就停住:
RUN_PREPARING → RUN_ACCEPTED → AGENT_STARTED → ...(此后没有任何输出)
[WorkflowMemProbe] tick runId=(none)任务被受理了、Agent 被拉起来了,但模型调用从未发生。
我们把范围缩小到认证。关键日志:
[FileAuthenticationStorage] [AtRestEncryption] unavailable
adapter=credential-fields resource=auth/<desktop>.info
[CliDispatcher] [CredentialBootstrap] CBC receiver
{"route":"standalone","policy":"unspecified",...}
[ExternalLinkAuthenticationProvider] fetch auth accounts error: 401
[AuthenticationManager] Failed to refresh token: account list is empty三条信息拼起来就是答案:
CredentialBootstrap 的 route 是 standalone —— 说明这个进程没有拿到宿主注入的凭据,是"野生"启动的。AtRestEncryption unavailable —— 凭据存储采用了静态加密(在 Windows 平台,这类静态加密通常会走系统的 DPAPI,但这里只是我们的推断),而当前进程无法解密。fetch auth accounts error: 401 —— 于是它去问服务端,服务端说你没凭据。这里有个容易踩的坑:我们一度认为"只要用桌面自己拉起的 sidecar 就能借用登录态",于是去接管了桌面的 prewarm 预热进程池(见第五节)。结果拿到了一个免密、状态为"已认证"的网关:
{"authEnabled":false,"authenticated":true}但发任务依然卡在 AGENT_STARTED。 authenticated:true 只表示"这个进程认为自己是登录态"——我们理解,真正的凭据注入发生在桌面进程与它 spawn 的子进程之间,一旦从外部(比如 WSL)通过 IPC 接管,这条注入链就断了。(与 §3.3 同理,此为推断,我们未做对照实验。)
我们还试过从 WSL 直接启动 Windows 侧的进程去跑 CLI,甚至用交互式计划任务在用户会话里启动。结果一致:Authentication required。
日志里能确定的只有一件事:静态加密模块在该进程内不可用([AtRestEncryption] unavailable),随后凭据刷新失败。
我们推测,这类静态加密通常依赖当前登录会话持有的用户凭据材料(密钥派生自用户口令),而由 WSL 侧拉起的 Windows 进程往往处在非交互式上下文,因此可能拿不到解密所需的那部分材料。但这只是推测——我们没有做对照实验去证实这一层机制,就不在本文下结论了。
不过,真正有实操价值的一点是确定的:绕开这个问题的正确做法,不是去改造登录会话(换启动方式、调计划任务参数),而是换一条路——让 CLI 自己完成一次登录。 这正是下一节的内容。
排查到这里,我们做了一次思路切换:不要试图复用桌面的登录态,而是让 CLI 自己登录。
验证发现——CodeBuddy CLI 在 Linux 侧的认证是纯文件存储(~/.codebuddy/),不涉及任何操作系统级加密,自然也没有会话隔离问题。
这一步是整个问题的解。
npm i -g @tencent-ai/codebuddy-code --registry=https://registry.npmmirror.com
codebuddy --version提示 1:包名是
@tencent-ai/codebuddy-code。桌面端里内置的那份走的是另一条路径,不要混淆 —— 前者是公开发行渠道,后者是桌面私有集成。 提示 2:上面用--registry参数只影响本次安装。若写成npm config set registry ...会全局覆盖 npm 源,装完记得改回,或改用前者。
CLI 提供 /login 交互命令,但我们实测发现:
用 pty 驱动 TUI 注入按键极不稳定 —— 菜单时好时坏,登录 URL 会因终端宽度不够被折行截断(state= 参数只取到一半)。折腾了几轮之后我们换了条路:
# 仅绑本机回环;仅为打通登录流程,完成登录后请改回启用鉴权(见 §6.3)
codebuddy --serve --port 8322 --auth none然后在同机浏览器打开 http://localhost:8322/。
环境说明:本文 CLI 装在 Linux 侧的开发机(WSL 环境),浏览器在 Windows 侧。WSL2 默认会把
localhost转发到 Windows,所以直接访问即可,无需额外配置。如果在纯远程服务器上操作,用 SSH 端口转发(ssh -L 8322:127.0.0.1:8322)也能达到同样效果 —— 不要把该端口直接暴露到网络上(见 §6.3 安全边界)。
Web UI 里有完整的登录入口,点一下、扫码/SSO 走完,凭据就落到用户目录(~/.codebuddy/)了。整个过程不需要和 TUI 搏斗。
补充:如果你确实需要走 TUI 拿登录链接,先把终端宽度设到 200 列以上(
stty cols 220),否则 URL 必被截断。
$ codebuddy -p "reply with exactly: PONG"
PONG
$ codebuddy -p "run the shell command 'echo HELLO' and tell me its output" -y
Output: `HELLO` (exit code 0)工具调用(Bash)通了 —— 这是 Agent 能力的核心,也是后面一切的基础。
CodeBuddy 有一个"预热进程"机制:提前冷启动若干进程待命,需要时唤醒,省掉冷启动时间。该机制由产品官方提供(具体行为请查阅官方文档)。
我们做了一次边界验证:外部程序能否激活这些预热进程、并借它拿到可用的 Agent 能力?
结论是"能接管,但不能绕过认证":
AGENT_STARTED,模型调用永远不会发出(原因见 3.2)。所以这条路不能作为认证的替代方案。 想打通,还是要让 CLI 自己完成一次登录。我们把它记在这里,是为了给同样想"少走一步"的人省下一次尝试 —— 这个方向看起来很像捷径,实际是死路。
说明:本文不展开该机制的具体协议与调用细节。若你需要了解,请以官方文档为准。
认证通了之后,我们把 CLI 封装成常驻网关 + 命令行客户端,接入现有的调度体系。
形态 A:一次性调用(适合低频、独立任务)
codebuddy -p "任务文本" # 只读问答
codebuddy -p "任务文本" -y # 允许工具调用形态 B:常驻网关(适合高频、多任务复用)
# 仅绑本机回环;生产环境请按官方文档启用鉴权(见下方安全边界)
codebuddy --serve --port 8322
curl -s -H "<标识头名>: <值>" \
http://127.0.0.1:8322/api/v1/health
curl -s -X POST http://127.0.0.1:8322/api/v1/runs \
-H "<标识头名>: <值>" -H "Content-Type: application/json" \
-d '{
"id":"<uuid>",
"type":"message",
"source":{"platform":"generic","sender":{"id":"scheduler"},
"conversation":{"id":"<uuid>","type":"direct"}},
"payload":{"text":"任务文本"}
}'
# → {"data":{"runId":"...","status":"accepted"}}坑一:网关要求携带产品自身的请求标识头。 缺了它,请求直接 403,而且错误信息不会告诉你原因 —— 对着 403 排查半天才发现是缺头。具体头字段名与取值请以官方文档为准。
坑二:任务正文不在 REST 响应里。 /api/v1/runs/{id} 只返回 active 状态,流式接口在任务完成后会直接 404。正文要从会话记录里取:
ls -t ~/.codebuddy/projects/*/*.jsonl | head -1
# 逐行 JSON,过滤 type=message && role=assistant 的 text 字段这一条官方文档没写,但对接时不解决它就没法拿到结果。
从运行日志看,这个网关的能力远不止"发任务":它同时还暴露了会话管理、统计、追踪、常驻服务状态、定时任务、IM 通道接入等一组接口。
其中几个值得注意:
generic / wecom / wechat-kf(企业微信、微信客服)—— 意味着它自带 IM 通道接入能力,可以直接对接到群聊/客服工作流⚠️ 安全边界(重要) 这个网关能执行本机 shell 工具调用(见 §4.3 的验证),因此它实质上等同于一条本机命令执行入口。无论用哪种方式启动,都请守住三条底线:
127.0.0.1)。切勿改为 0.0.0.0,也不要通过端口转发、反向代理把端口服到内网或公网。真正让这套方案"值得长期跑"的,是模型侧的调整。
CodeBuddy 支持通过 ~/.codebuddy/models.json 注入自定义模型(OpenAI 兼容协议):
{
"models": [{
"id": "local-model",
"name": "local-model",
"vendor": "Custom",
"url": "http://<本地推理服务地址>:8000/v1",
"supportsToolCall": true,
"supportsImages": false,
"supportsReasoning": false,
"maxInputTokens": 128000,
"maxOutputTokens": 16384
}]
}配好之后,整个 Agent 的推理就跑在内网的 GPU 节点上了。
采集环境:本地 GPU 节点(内网 HTTP 服务,OpenAI 兼容接口),客户端与服务端同网段;单并发、无其他负载。数字为观察值,非严格基准测试。
指标 | 数值 | 说明 |
|---|---|---|
单轮 -p 调用(3 次) | 2.63s / 2.69s / 3.84s | 含进程冷启动 |
网关模式端到端 | 约 5s 量级 | 含一次工具调用;因客户端轮询间隔粗(5s),无法给出亚秒级精度,仅供参考 |
首 Token 延迟(TTFT) | 0.5 – 1.5s | 随上下文长度浮动 |
单次请求 prompt 规模 | ≈ 29K tokens | Agent 系统提示的固定开销 |
单次请求 completion | 17 – 43 tokens | 短任务场景 |
本地模型端点连通 | 88ms | 内网 HTTP 探测 |
两个观察:
五条最值得记的结论:
适用场景建议:
场景 | 推荐形态 |
|---|---|
脚本化、无人值守的 Agent 调用 | 独立 CLI(本文方案) |
需要窗口/截图/鼠标等桌面操作 | 桌面版(独立 CLI 不具备) |
高频短任务 | 常驻网关 + 本地模型 |
低频长任务 | 一次性 -p 调用即可 |
这套方案的实质,是把一个面向"编码"的 Agent,改造成面向"调度"的通用执行节点:它不再需要人坐在 IDE 前面,而是作为一个可编程的进程,被上层网络按需唤醒、派活、回收。
对我们来说,这是在原工具停运前完成的职能承接准备;对更广泛的场景来说,它提供了一个思路 —— 当下主流的编码 Agent,其内核能力(工具调用、多轮推理、MCP)已经足够通用,缺的往往只是"如何把它安全地接入你自己的自动化体系"这一层封装。
而这一层,官方文档通常只讲到一半。
声明:本文所有操作均在自有测试环境完成,不涉及任何未授权访问。文中内网地址、主机名、凭据、进程号与身份信息均已做脱敏处理;涉及的产品接口行为基于特定版本实测,后续版本可能变化,请以官方文档为准。文中所述为个人技术实践,不代表任何组织立场;实际部署请遵守产品许可协议与所在组织的信息安全规定。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。