1. 为什么我要用 CLI 拆解 DeepSeek Harness 的 Agent Runtime
DeepSeek Harness 是一套开源的 Agent Runtime 工程底座,它把模型推理之外的所有事情——工具调度、会话状态、权限控制、沙箱隔离、失败恢复、插件扩展——都收进了一个可运行、可观测、可复现的系统里。它适合谁?适合那些不满足于“调个 API 写个 prompt”的开发者,尤其是想系统验证 Agent 行为、需要做对照实验、要把 Agent 从 demo 推进到工程系统的人。我关注它不是因为它多了一个 CLI 界面,而是因为它把“模型之外的世界”暴露成了可研究的对象:一次运行里模型看到了什么上下文、哪些工具被允许调用、工具结果和错误如何回流到下一轮推理、进程重启后状态怎么恢复、新能力挂在哪个扩展点。这些问题单靠 prompt 永远解决不了,必须靠 Runtime 来回答。
而 CLI 是研究 Runtime 最合适的入口。Web 界面会隐藏装配细节,SDK 封装会吞掉中间状态,只有 CLI 能把 Profile、Bundle、Patch、Loader、Agent Loop、Session Event Log 这些环节一层层摊开给你看。你可以用一条命令启动,用固定输入跑出固定输出,改一个参数再跑一遍,对比差异。这就是“可复现实验”的最小闭环。
我试过用不同 Runtime 参数跑同一组输入,发现输出差异往往不来自模型本身,而来自上下文裁剪策略、工具可见性、权限决策和会话恢复逻辑。这些才是 Agent 行为的关键变量。所以这篇不是产品评测,而是一份可跟做的 CLI 实验手册:从环境准备到配置片段,从运行命令到结果对比,再到常见报错排查,全部围绕“可复现”三个字展开。
核心检索词先明确:DeepSeek Harness 研究理念、Agent Runtime 可复现实验、CLI 配置、固定输入对比输出。下面所有步骤都服务于这个目标。
2. TaoToken 前置:把模型接入层固定下来
做可复现实验,第一件事是把模型接入层固定住。如果每次实验换一个模型供应商、换一个 endpoint、换一套鉴权方式,那输出差异里就混入了太多无关变量,实验结论不可信。我的做法是用 TaoToken 作为统一的模型接入层,把 Base URL、API Key、Model ID 三件套写进配置文件,让 Runtime 只认这套配置。
TaoToken 在这里的角色是“模型网关”:它提供 OpenAI 兼容的接口,你可以在 CLI 里把 base_url 指向https://taotoken.net/api,然后用同一个 Key 切换不同模型做对照。这样 Runtime 层的实验变量(上下文策略、工具权限、会话恢复)和模型层的变量(模型能力)就能分开控制。
你需要先拿到 API Key。访问 API Keys 管理页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,创建一个 Key 并保存好。注意这个 Key 只显示一次,丢了只能重建。
然后确认你要用的 Model ID。不同模型在工具调用、长上下文、指令遵循上的表现差异很大,做 Agent Runtime 实验时建议先固定一个模型,把 Runtime 变量跑清楚,再换模型对比。模型列表和对话测试可以在模型对话页面验证:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite ,先发一条简单消息确认 Key 和网络都通。
接入文档在这里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 endpoint 说明和参数格式。如果你打算长期跑编码类 Agent 实验,Coding Plan 页面值得看一下:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它针对高频编码场景做了额度优化。
这一步的目标不是“注册账号”,而是把模型接入层变成一个常量。常量固定了,实验才有意义。接下来所有 CLI 配置都会引用这套 Base URL + Key + Model ID。
3. 可复制配置:CLI 环境与 Runtime 参数文件
这一节给出可以直接复制的配置片段。路径和字段名按实际工程习惯来,你按自己的目录结构调整即可。核心原则是:所有影响 Agent 行为的参数都写进配置文件,不靠命令行临时传参,这样每次实验的配置可以版本化、可以 diff、可以复现。
先建实验目录:
mkdir -p ~/agent-lab/{configs,runs,logs} cd ~/agent-lab第一个配置文件是模型接入层,命名为configs/model.toml:
[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" model_id = "deepseek-chat" timeout_seconds = 120 max_retries = 2 [provider.headers] Content-Type = "application/json"注意api_key_env指向环境变量,不要把 Key 明文写进文件。设置环境变量:
export TAOTOKEN_API_KEY="sk-你的Key"第二个配置文件是 Runtime 实验参数,命名为configs/runtime.toml:
[runtime] profile = "headless" session_log = "runs/session.jsonl" max_turns = 12 context_window = 32000 compaction_threshold = 0.75 [tools] enabled = ["read_file", "write_file", "run_shell", "search"] shell_timeout = 30 sandbox = true [policy] require_approval = ["run_shell", "write_file"] auto_approve = ["read_file", "search"] [reproducibility] seed = 42 temperature = 0.0 top_p = 1.0 fixed_system_prompt = "configs/system.md"第三个文件是固定输入,命名为configs/system.md,内容保持简短且稳定:
你是一个实验用 Agent。只使用被允许的工具。每次工具调用前说明理由。遇到不确定时停止并报告。第四个文件是实验任务输入,命名为configs/task.json:
{ "task_id": "exp-001", "input": "读取 runs/input.txt,统计行数,把结果写入 runs/output.txt", "expected_tools": ["read_file", "write_file"], "max_turns": 6 }如果你用的是 Claude Code 或类似 CLI 工具做接入实验,配置结构会略有不同,但三件套不变:Base URL 指向https://taotoken.net/api,Key 走环境变量,Model ID 固定。Claude Code 接入可以参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。
配置写完后,用一条命令校验 TOML 语法:
python3 -c "import tomllib; tomllib.load(open('configs/runtime.toml','rb')); print('runtime.toml OK')" python3 -c "import tomllib; tomllib.load(open('configs/model.toml','rb')); print('model.toml OK')"两个都输出 OK 再往下走。配置文件有语法错误时,Runtime 启动会直接失败,报错信息通常指向行号,按行号改即可。
4. 验证请求:固定输入跑出可对比结果
配置就绪后,跑第一次实验。准备输入文件:
printf 'alpha\nbeta\ngamma\ndelta\n' > runs/input.txt wc -l runs/input.txt预期输出4 runs/input.txt。然后启动 Runtime:
dsh --profile headless \ --config configs/runtime.toml \ --model-config configs/model.toml \ --task configs/task.json \ --log runs/exp-001.jsonl如果你的 CLI 参数名不同,以dsh --help为准。关键是三个东西都要传进去:Runtime 配置、模型配置、任务输入。运行结束后检查输出:
cat runs/output.txt cat runs/exp-001.jsonl | head -20output.txt应该包含行数统计结果。exp-001.jsonl是会话事件日志,每一行是一个事件,包含 turn 编号、模型请求、工具调用、工具结果、策略决策。这个日志是可复现实验的核心证据:它记录了模型看到了什么、调用了什么、被允许或拒绝了什么。
现在做对照实验。复制一份配置,只改一个参数:
cp configs/runtime.toml configs/runtime-no-sandbox.toml sed -i 's/sandbox = true/sandbox = false/' configs/runtime-no-sandbox.toml再跑一次:
dsh --profile headless \ --config configs/runtime-no-sandbox.toml \ --model-config configs/model.toml \ --task configs/task.json \ --log runs/exp-002.jsonl对比两次日志:
diff <(jq -S . runs/exp-001.jsonl) <(jq -S . runs/exp-002.jsonl) | head -40你会看到沙箱开关影响了工具执行路径和策略决策事件。如果两次输出完全一致,说明这个参数在当前任务下没有触发差异,换一个会触发沙箱的任务再试。可复现性的验证方法是:同一份配置连续跑三次,日志中除时间戳外的字段应完全一致。把时间戳字段排除后再 diff:
for i in 1 2 3; do dsh --profile headless --config configs/runtime.toml \ --model-config configs/model.toml --task configs/task.json \ --log runs/repro-$i.jsonl done diff <(jq -S 'del(.timestamp)' runs/repro-1.jsonl) <(jq -S 'del(.timestamp)' runs/repro-2.jsonl)没有输出就说明可复现。有输出就检查temperature和seed是否真的生效,有些 Runtime 会把这两个参数透传给模型,有些会在本地做采样,行为不同。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
实验过程中最容易卡住的不是 Runtime 逻辑,而是接入层报错。下面按真实报错逐条排查。
401 Unauthorized。最常见原因是 Key 没设进环境变量,或者设了但当前 shell 没生效。检查:
echo $TAOTOKEN_API_KEY | head -c 8如果输出为空,说明环境变量没设。重新 export 后确认。如果输出有值但仍 401,检查 Key 是否被删除或过期,去 API Keys 页面重建:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。还有一种情况是配置文件里写了api_key字段但值为空字符串,覆盖了环境变量,检查model.toml里不要出现空的api_key。
local proxy failed。这个报错通常出现在 Runtime 尝试通过本地代理转发请求时。检查你的base_url是否被错误地写成了http://localhost:xxxx或某个本地端口。正确值应该是https://taotoken.net/api。另外检查环境变量里有没有残留的HTTP_PROXY/HTTPS_PROXY指向不可用的地址:
env | grep -i proxy如果有,unset 掉再跑。注意不要使用任何非官方的网络转发工具,直接用官方 endpoint 即可。
Error reading choices / reading choices。这个报错说明 Runtime 收到了响应,但解析choices字段失败。常见原因是响应体不是预期的 JSON 结构,可能是 endpoint 路径写错(比如漏了/v1或多了/v1),也可能是模型返回了流式格式但 Runtime 按非流式解析。检查你的请求路径:TaoToken 的兼容接口基址是https://taotoken.net/api,具体路径以接入文档为准:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。先用 curl 单独验证一次:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"ping"}],"max_tokens":8}' | jq '.choices[0].message.content'如果 curl 能拿到结果但 Runtime 报 reading choices,说明 Runtime 的解析逻辑和实际响应格式不匹配,检查 Runtime 版本和配置里的stream字段。
OAuth 相关报错。如果你用的是 Claude Code 类工具,它可能默认走 OAuth 流程而不是 API Key。这时需要在配置里显式指定 API Key 模式,把 Base URL 指向https://taotoken.net/api,并在 settings 里关闭 OAuth。Claude Code 的接入配置参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。三件套必须写全:Base URL、Key、Model ID,缺一个都会回退到默认 OAuth 或报鉴权失败。
排查顺序建议:先 curl 验证接入层,再跑 Runtime 最小任务,最后加实验参数。接入层不通的时候不要动 Runtime 配置,否则会把两个问题混在一起。
6. 把实验变成习惯:从 CLI 到可复现的 Agent Runtime 研究
跑通一次对照实验之后,真正有价值的是把实验流程固定下来。我的做法是每个实验一个目录,目录里放四样东西:配置文件、任务输入、运行日志、结论笔记。配置文件进 git,日志不进 git 但保留最近若干次,结论笔记记录“改了什么参数、观察到什么差异、是否可复现”。
具体操作上,写一个run.sh把启动命令封装起来:
#!/usr/bin/env bash set -euo pipefail EXP_ID="${1:?usage: run.sh <exp-id>}" mkdir -p "runs/$EXP_ID" dsh --profile headless \ --config "configs/$EXP_ID/runtime.toml" \ --model-config configs/model.toml \ --task "configs/$EXP_ID/task.json" \ --log "runs/$EXP_ID/session.jsonl"每次实验复制一份配置目录,改参数,跑,对比。这样实验之间不会互相污染,回看时也能清楚知道每个结论对应哪份配置。
验证可复现性的标准流程是三步:同一配置跑三次,排除时间戳后日志一致;改一个参数再跑,差异只出现在与该参数相关的字段;换回原配置,结果回到第一次的状态。三步都通过,这个实验结论才可信。
如果你要长期做 Agent Runtime 研究,建议把模型接入层单独抽出来管理,用环境变量或独立的 secrets 文件,不要和 Runtime 实验配置混在一起。TaoToken 的 API Key 管理页面可以创建多个 Key 用于不同实验线:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。需要验证模型行为时用模型对话页面快速测试: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 。
最后一步是闭卷复述:关掉所有文档,用自己的话解释一次运行里 Profile 怎么装配、Agent Loop 怎么流转、Session Event Log 记了什么、工具权限在哪一层决策。讲不清楚的地方就是下一轮实验要补的洞。CLI 只是入口,可复现的实验流程才是研究 Agent Runtime 的真正方法。