1. 从 settings.json 开始:SaaS 接入 AI Agent 的 Harness Engineering 起点
SaaS 产品接入 AI Agent 时,最先卡住的往往不是模型能力,而是工具侧配置散落各处:Key 写在环境变量里、Base URL 硬编码在代码里、不同 Agent 各用一套通道,换一个模型就要改五六个文件。Harness Engineering 的核心思路是把 Agent 当成需要被“驾驭”的工程对象,用统一的配置骨架把模型通道、工具注册、权限边界收敛到一处。settings.json就是这个骨架的起点——它决定了你的 SaaS 产品里,AI Agent 能不能被稳定地启动、切换和审计。
这篇内容面向正在给 SaaS 产品加 AI Agent 能力的工程师和产品技术负责人。我会用一个可复制的settings.json骨架,演示如何通过 TaoToken 统一 Key 与 API 通道,让工具侧配置一次写好、多处复用,并在本地用一条 curl 请求确认整条调用链路是否生效。适合谁:已经写过 Agent 调用、但配置管理还停留在“能跑就行”阶段的开发者;也适合想把 Agent 配置纳入版本管理、做多环境切换的团队。
我试过把 Key 分散在.env、config.py、前端 localStorage 三个地方,结果一次模型切换排查了两小时。后来把通道配置全部收进settings.json,问题定位时间降到几分钟。下面这套骨架就是从那次的坑里整理出来的。
2. TaoToken 前置:统一 Key 与 API 通道
TaoToken 在这里扮演的角色是“通道层”:你的 SaaS 产品不需要在每个 Agent 里分别配置不同厂商的 Key 和 Base URL,而是通过一个统一的 API 入口来路由模型请求。这样做的好处是,settings.json里只需要维护一份通道配置,Agent 代码只认这个配置,不关心背后接的是哪个模型。
官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
API 地址:https://taotoken.net/api
在开始写settings.json之前,你需要先拿到一个可用的 Key。进入控制台创建:
- API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
拿到 Key 之后,先不要急着写进代码。我们把它放进settings.json的通道配置段,让 Agent 通过读取配置来初始化客户端。这样做的工程意义是:Key 的轮换、通道的切换、不同环境(dev/staging/prod)的隔离,都变成改一个 JSON 文件的事,而不是改代码。
注意:
settings.json里不要提交真实 Key 到版本库。推荐做法是配置里写占位符,运行时用环境变量注入,或者用本地覆盖文件。下面的示例会演示这种模式。
3. 可复制配置:settings.json 骨架
下面这份settings.json是一个可以直接拿去改的骨架。它分成四段:channel管通道,agent管 Agent 行为,tools管工具注册,runtime管运行环境。每段都有明确职责,方便你按需扩展。
{ "channel": { "provider": "taotoken", "base_url": "https://taotoken.net/api", "api_key": "${TAOTOKEN_API_KEY}", "default_model": "claude-sonnet-4-20250514", "timeout_ms": 60000, "max_retries": 2 }, "agent": { "name": "saas-assistant", "system_prompt_file": "./prompts/system.md", "max_iterations": 8, "temperature": 0.3, "stream": true }, "tools": [ { "name": "search_docs", "enabled": true, "endpoint": "/internal/docs/search", "auth": "service_token" }, { "name": "query_metrics", "enabled": true, "endpoint": "/internal/metrics/query", "auth": "service_token" } ], "runtime": { "env": "development", "log_level": "debug", "trace_enabled": true } }几个关键点解释一下。channel.base_url固定指向 TaoToken 的 API 入口,所有模型请求都从这里走。api_key用${TAOTOKEN_API_KEY}占位,运行时从环境变量读取,避免明文入库。default_model是默认模型,切换模型只改这一行。tools数组里每个工具声明了名称、开关、内部端点和鉴权方式,Agent 启动时按这个列表注册工具,不需要在代码里硬编码。
配套的环境变量设置:
export TAOTOKEN_API_KEY="你的Key" export SETTINGS_PATH="./config/settings.json"如果你用 Node.js 读取这份配置,可以这样加载:
import fs from "fs"; const settingsPath = process.env.SETTINGS_PATH || "./config/settings.json"; const raw = fs.readFileSync(settingsPath, "utf-8"); // 替换 ${VAR} 占位符 const settings = JSON.parse( raw.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] || "") ); console.log("channel:", settings.channel.base_url); console.log("model:", settings.channel.default_model);Python 版本:
import json import os import re settings_path = os.environ.get("SETTINGS_PATH", "./config/settings.json") with open(settings_path, "r", encoding="utf-8") as f: raw = f.read() def replace_env(match): return os.environ.get(match.group(1), "") settings = json.loads(re.sub(r"\$\{(\w+)\}", replace_env, raw)) print("channel:", settings["channel"]["base_url"]) print("model:", settings["channel"]["default_model"])这两段代码的作用是把占位符替换成真实环境变量值,然后解析成对象。Agent 初始化时只依赖这个对象,不直接读环境变量,配置来源单一,排查问题时只需要看一份文件。
4. 验证请求:确认调用链路生效
配置写好后,第一步不是跑 Agent,而是先用一条最小请求确认通道是通的。这样能把“配置问题”和“Agent 逻辑问题”分开排查。
用 curl 直接打 TaoToken 的 API:
curl -s https://taotoken.net/api/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: ${TAOTOKEN_API_KEY}" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [ {"role": "user", "content": "只回复两个字:通了"} ] }'如果返回结构里有content字段且文本是“通了”,说明 Key、Base URL、模型名三者都对。如果返回 401,检查 Key 是否注入成功;返回 404,检查base_url是否多了或少了路径段;返回 400 且提示模型不存在,检查default_model拼写。
接着用配置对象跑一次 Agent 初始化,验证工具注册是否正常:
import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.TAOTOKEN_API_KEY, baseURL: settings.channel.base_url, }); const enabledTools = settings.tools.filter((t) => t.enabled); console.log("已注册工具:", enabledTools.map((t) => t.name).join(", ")); const resp = await client.messages.create({ model: settings.channel.default_model, max_tokens: 128, messages: [{ role: "user", content: "列出你当前可用的工具名称" }], }); console.log(resp.content[0].text);实测下来,这条链路跑通后,Agent 侧再出问题基本就集中在工具实现和提示词上,通道层不用再怀疑。成功结果的特征是:curl 返回正常文本,Node 脚本打印出已注册工具列表,且模型回复里能正确引用工具名。
5. 本篇常见错排查
配置骨架落地时,报错集中在几个固定位置。下面按现象、原因、处理三步列出来。
现象一:401 Unauthorized。原因通常是${TAOTOKEN_API_KEY}没有被替换,或者环境变量名拼错。处理:在加载配置的代码里打印替换后的 Key 前四位,确认非空;检查export的变量名和 JSON 里的占位符是否完全一致,大小写敏感。
现象二:404 Not Found。原因多是base_url写成了https://taotoken.net/api/带尾斜杠,或者 SDK 自动拼接了/v1导致路径重复。处理:base_url统一写https://taotoken.net/api,不带尾斜杠;如果 SDK 默认会加/v1,确认最终请求路径是/api/v1/messages。
现象三:模型名报错model not found。原因:default_model用了不存在的名称,或者不同厂商的模型名混用。处理:以接入文档里的模型列表为准,切换模型只改settings.json里这一行,不要改代码。
现象四:工具注册了但 Agent 不调用。原因:tools数组里enabled为false,或者工具描述太模糊,模型判断不出何时该用。处理:确认enabled: true;给每个工具补一句清晰的description,说明“什么时候用、输入是什么、返回什么”。
现象五:超时。原因:timeout_ms设得太短,或者网络到 API 入口不稳定。处理:把timeout_ms调到 60000 以上,max_retries设为 2,让 SDK 自动重试。
提示:排查时把
runtime.log_level设为debug,trace_enabled设为true,能看到每次请求的完整路径和耗时,定位速度会快很多。
6. 把配置纳入工程流程
settings.json骨架跑通之后,下一步是让它进入版本管理和多环境流程。推荐按环境拆文件:settings.dev.json、settings.staging.json、settings.prod.json,公共部分抽到settings.base.json,运行时做浅合并。这样 dev 环境可以开trace_enabled,prod 环境关掉并调高max_retries。
长期做编码和 Agent 开发的团队,可以考虑用 Coding Plan 来统一管理通道和额度:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
如果你只是想先验证模型对话是否正常,可以直接在模型对话页测试:
- 模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
配置骨架的价值不在于它多复杂,而在于它把“通道、Agent、工具、运行时”四件事分开了。分开之后,任何一层出问题都能单独替换和验证。SaaS 产品接入 AI Agent 的 Harness Engineering,第一步就是让配置有骨架、让通道有统一入口、让验证有最小动作。把这三件事做完,后面的工具扩展和多 Agent 协作才有稳定的地基。