☰
Agent Harness 架构到底需要些什么?从 settings.json 到 TaoToken 统一 Key 的落地骨架
2026/9/27 18:17:02 网站建设 项目流程

1. 从 settings.json 开始:Agent Harness 的配置层到底要装什么

Agent Harness 这个词最近被聊得很多,但落到代码里,它其实就是一个把模型、工具、权限、会话串起来的运行时外壳。同一个模型放进不同的 harness,任务完成率能差出几十个百分点——模型不是瓶颈,壳才是。而壳的第一层,就是配置文件。

我见过太多人一上来就写 Agent Loop,结果工具接不进来、模型通道散落在十几个文件里、换个模型要改二十处代码。问题不在循环写得不好,在于配置层没有骨架。settings.json 就是这根骨架:它决定了工具从哪加载、模型走哪条通道、运行参数怎么下发、权限边界画在哪。

这篇聚焦一件事:用一份可复制的 settings.json,把 Agent Harness 的配置层搭起来。适合正在写 harness、或者准备把散落的配置收拢成一份文件的开发者。读完之后你应该能拿到一份能直接跑的结构,并且知道启动后怎么验证通道连通、怎么确认调用日志真的落下来了。

需要说明的是,settings.json 不是某个产品的专属格式,它是一个通用思路:把 harness 的配置收敛成声明式文件,让运行时去解释它。你可以把它理解成 harness 的"启动清单"——工具、模型、参数、权限四类信息各归其位,运行时按图索骥。

2. 前置准备:TaoToken 统一 Key 与通道概念

在写配置之前,先把模型通道这件事解决掉。harness 最怕的就是模型适配层写死,一旦要换模型就得动业务代码。我的做法是:所有模型请求走一个统一的 OpenAI 兼容入口,harness 只认 base_url 和 api_key 两个变量。

TaoToken 在这里扮演的就是这个统一入口。它的 API 地址是https://taotoken.net/api,兼容 OpenAI 的/v1/chat/completions协议,也就是说你的 harness 里所有模型调用都可以指向同一个 base_url,换模型只改 model 字段,不改代码。这对 harness 的模型适配层来说是最省事的一种结构——适配层越薄越稳。

你需要先拿到一个 Key。登录后在控制台创建:

  • 控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
  • API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite

创建完把 Key 复制出来,先别急着写进 settings.json。这里有个安全习惯要养成:Key 不进版本库。settings.json 里只放环境变量名,真实值走.env或者系统环境变量。harness 启动时读环境变量注入,这样配置文件可以放心提交。

注意:不要把 Key 硬编码进 settings.json 再提交到 Git。哪怕仓库是私有的,Key 一旦进历史就很难彻底清掉。用${TAOTOKEN_API_KEY}这种占位符,运行时替换。

如果你还没决定用哪个模型,可以先在模型对话页面试一下通道是否正常:

  • 模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite

确认能正常返回之后,再回到 harness 的配置层。这一步的意义是:把"模型通道可用"这个前提先验证掉,后面排查问题时就能排除掉通道因素,专注在 harness 本身。

3. 可复制的 settings.json 骨架

下面这份配置是我实际用过的结构,分四个区块:model(模型通道)、tools(工具接入)、runtime(运行参数)、permissions(权限边界)。你可以直接复制,改掉路径和模型名就能用。

{ "model": { "provider": "openai-compatible", "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "default_model": "claude-sonnet-4-5", "fallback_model": "gpt-4.1", "timeout_ms": 120000, "max_retries": 2 }, "tools": { "load_paths": ["./tools", "./plugins"], "enabled": ["shell", "fs_read", "fs_write", "http_fetch"], "disabled": ["browser"], "approval_required": ["shell", "fs_write"], "timeout_ms": 30000 }, "runtime": { "max_turns": 32, "max_steps_per_turn": 8, "context_window": 200000, "compact_threshold": 0.8, "log_dir": "./logs/agent", "log_level": "info", "session_store": "./sessions" }, "permissions": { "mode": "workspace-write", "workspace_root": "./workspace", "deny_paths": ["/etc", "~/.ssh", "./.env"], "allow_network": true } }

逐块解释一下为什么这么设计。

model块里最关键的是base_url和api_key_env分离。base_url 指向 TaoToken 的统一入口,api_key_env 只写环境变量名。default_model和fallback_model是两个槽位——主模型超时或报错时自动降级,这在长任务里很实用。timeout_ms给到 120 秒是因为有些推理型模型首 token 就慢,别设太短。

tools块用load_paths声明工具从哪扫描,enabled/disabled做白名单和黑名单。approval_required是重点:shell 和 fs_write 这类有副作用的工具必须走审批,fs_read 和 http_fetch 可以放行。这个字段直接对应 harness 的工具流水线——pre 阶段读它决定要不要弹审批。

runtime块管循环参数。max_turns和max_steps_per_turn是两级边界,对应 turn / step 的循环结构,有这两级才有地方挂超时和中断。compact_threshold是上下文压缩触发线,0.8 表示用到 80% 窗口就压缩。log_dir和session_store分开:日志是给人看的,会话是给回放和恢复用的。

permissions块画边界。mode三档(read-only / workspace-write / danger-full-access)是通用做法,workspace_root限定可写范围,deny_paths是硬拒绝清单。allow_network单独一个开关,因为网络访问和文件访问的风险模型不一样。

提示:这份配置里没有一处写死模型名到业务逻辑。harness 代码只读model.default_model,换模型改这一个字段。这就是模型适配层该有的样子。

4. 启动验证:通道连通与调用日志

配置写完不算完,得验证它真的生效。我一般分三步查:通道连通、工具加载、日志落盘。

第一步,验证模型通道。写一个最小脚本读 settings.json,发一次请求:

import json, os, requests with open("settings.json") as f: cfg = json.load(f) api_key = os.environ[cfg["model"]["api_key_env"]] resp = requests.post( f"{cfg['model']['base_url']}/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={ "model": cfg["model"]["default_model"], "messages": [{"role": "user", "content": "ping"}], "max_tokens": 16 }, timeout=cfg["model"]["timeout_ms"] / 1000 ) print(resp.status_code) print(resp.json()["choices"][0]["message"]["content"])

跑通的话你会看到 200 和一段回复。如果返回 401,检查环境变量有没有注入;返回 404,检查 base_url 有没有多写或少写/v1;超时就把timeout_ms调大再试。

第二步,验证工具加载。harness 启动时应该打印加载了哪些工具:

python -m harness.start --config settings.json --dry-run

--dry-run只加载配置不执行循环,输出类似:

[config] model channel: openai-compatible -> https://taotoken.net/api [config] default model: claude-sonnet-4-5 [tools] loaded 4: shell, fs_read, fs_write, http_fetch [tools] approval required: shell, fs_write [runtime] max_turns=32 max_steps_per_turn=8 [permissions] mode=workspace-write root=./workspace

看到这行输出,说明配置被正确解析了。如果某个工具没出现在 loaded 列表里,检查load_paths路径对不对、工具文件有没有导出正确的注册函数。

第三步,验证调用日志。跑一个真实任务,然后看日志目录:

ls -la ./logs/agent/ tail -n 20 ./logs/agent/session-*.jsonl

日志应该是 JSONL 格式,每行一条事件,至少包含seq、type、timestamp、payload四个字段。seq单调递增,这是会话可回放的基础。如果你看到日志里模型请求的消息数组和会话历史对不上,那就是可观测性出了问题——这是 harness 最容易埋雷的地方。

注意:日志里不要记录完整 API Key。harness 在写日志前应该对Authorization头做脱敏,只留前几位和后几位。

5. 本篇常见错排查

配置层的问题大多集中在几个固定位置,我把踩过的坑列一下。

报错一:KeyError: 'TAOTOKEN_API_KEY'

环境变量没注入。检查.env文件有没有被加载,或者 shell 里有没有export。Python 里可以用python-dotenv在启动时加载:

from dotenv import load_dotenv load_dotenv()

报错二:404 Not Found或Invalid URL

base_url 拼接问题。TaoToken 的 API 根是https://taotoken.net/api,请求路径是/v1/chat/completions,拼起来是https://taotoken.net/api/v1/chat/completions。如果你在 base_url 里已经写了/v1,代码里又拼一次,就会变成/v1/v1/...。统一约定:base_url 不带/v1,由请求层拼。

报错三:工具加载了但调用时报tool not found

enabled白名单和实际注册名不一致。工具注册名是fs_read,配置里写成read_file就匹配不上。建议 harness 启动时做一次校验:配置里的每个名字都要能在已加载工具里找到,找不到直接 fail-fast,别等到运行时才发现。

报错四:审批不生效,shell 直接执行了

approval_required字段没被工具流水线读取。检查你的 pre-execute 阶段有没有真的去查这个列表。一个常见错误是把审批逻辑写在工具内部,而不是流水线层——工具内部写会导致每个工具都要重复实现,漏一个就是漏洞。审批必须在流水线统一做。

报错五:日志文件为空

log_dir目录不存在,或者 harness 没有创建目录的权限。启动时应该mkdir -p一下。另外检查log_level,如果是error级别,正常调用不会写日志,改成info。

报错六:会话恢复后上下文错乱

session_store里的会话文件和日志对不上。会话状态应该从日志重建,而不是单独存一份内存快照。如果你的 harness 同时维护了"消息数组"和"事件日志"两份状态,迟早会不一致。以日志为唯一真相,其它都是投影。

6. 下一步:把配置层接进 Coding Plan

settings.json 搭好之后,配置层这块就稳了。接下来要动的是循环和工具流水线——但那两块的前提是模型通道稳定、Key 统一管理。如果你打算长期跑编码类 Agent,或者要把 harness 接到 CI 里做自动化,建议把 Key 和额度管理也收拢起来。

Coding Plan 适合这种长期编码场景,它把模型调用和额度做了统一管理,harness 侧只需要认一个 Key:

  • Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite

接入文档在这里,里面有 base_url、鉴权方式和各语言的最小示例,对着改 settings.json 里的base_url和api_key_env就行:

  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite

如果你用的是 Claude Code 这类现成 harness,想让它的请求走统一通道,可以看这份配置说明:

  • ClaudeCodeAnthropic 配置:https://taotoken.net/doc/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite

配置层的价值在于:它把"模型从哪来"和"harness 怎么跑"解耦了。今天用这个模型,明天换那个,settings.json 改一行,循环代码一行不动。这就是我在开头说的——模型适配层越薄越稳,别把模型名写死在业务逻辑里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询