1. 在 TheAgentCompany 复现 Bash 接口时,先把 TaoToken Key 和 Base URL 固定下来
在 TheAgentCompany 里复现 Bash 接口时,最先遇到的往往不是任务逻辑,而是模型请求直接返回401 invalid_api_key:先把供应商切到 TaoToken,从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_start 获取 Key,再把 Base URL 填为 https://taotoken.net/api,后续才能稳定跑 GPT-5.5。很多复现者会把注意力放在论文里的接口对比,但真正卡住跑通的是三件事:Key 是否可用、Base URL 是否被 SDK 正确拼接、模型 ID 是否和控制台一致。本文按 TheAgentCompany 复现者视角,把 Bash 接口配置、GPT-5.5 调用日志和 Token 消耗拆成可跟做的步骤,所有命令都在你本地容器或本地工作区执行,不连接生产库。
先明确一个最小目标:让 TheAgentCompany 的任务循环成功调用一次 Bash 工具,并拿到可记录的prompt_tokens、completion_tokens、total_tokens。只要这三项能稳定落盘,后面再比较 Bash 接口与类型化工具接口才有意义。原文讨论的是微软论文在 TheAgentCompany、APEX-Agents 上用 Opus-4.8、GPT-5.5 比较 Bash 与类型化工具接口;我们这里不重复论文结论,而是把“复现环境怎么接 TaoToken、Bash schema 怎么写、日志怎么记”讲清楚。
第一步不是改 agent 代码,而是准备 TaoToken 的 Key。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_key_prepare ,在控制台创建 API Key。Key 只显示一次,复制后放到本地环境变量,不要写进代码仓库。建议用占位符YOUR_API_KEY先写配置模板,再通过 shell 注入真实值。Base URL 统一写:
https://taotoken.net/api注意这个地址不加 UTM 参数,UTM 只用于官网页面和 deep link。很多 401 不是 Key 错,而是 Base URL 被误写成了带utm_source的网页地址,SDK 把整个 URL 当成 API 根路径,自然无法请求。
可以用本地 shell 验证环境变量是否生效:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export OPENAI_API_KEY="$TAOTOKEN_API_KEY" export OPENAI_BASE_URL="https://taotoken.net/api" export OPENAI_MODEL="gpt-5.5" python - <<'PY' import os print("base_url =", os.environ.get("OPENAI_BASE_URL")) print("model =", os.environ.get("OPENAI_MODEL")) print("key_exists =", bool(os.environ.get("TAOTOKEN_API_KEY"))) PY如果这里key_exists为False,先别跑 TheAgentCompany,先把 shell 配置修好。Windows 下如果用 PowerShell,对应写法是:
$env:TAOTOKEN_API_KEY="YOUR_API_KEY" $env:OPENAI_API_KEY=$env:TAOTOKEN_API_KEY $env:OPENAI_BASE_URL="https://taotoken.net/api" $env:OPENAI_MODEL="gpt-5.5"还有一个容易忽略的点:TheAgentCompany 的 harness 可能只用 LiteLLM、OpenAI SDK 或自研 client。你要找到实际发出模型请求的层,把base_url和api_key改到这一层,而不是只改 agent 配置文件。否则会出现“agent 配置看起来已经改了,但日志里还是旧供应商”的情况。排查时直接搜代码里的base_url、api_key、OPENAI_API_KEY、model四个字段。
2. TheAgentCompany 最小复现路径:任务、容器与 Bash 工具入口
TheAgentCompany 的复现重点是企业任务环境:任务通常需要在本地容器、临时目录或模拟工作区里完成。复现 Bash 接口时,不要一上来跑全量任务,先用一个最小命令验证工具调用链。建议按下面的顺序推进:
- 准备本地 Python 环境,尽量用 3.10 或 3.11,并确认 Docker 可用。
- 克隆 TheAgentCompany 仓库到本地目录,具体仓库地址以你手头资料为准,正文不展开外部链接。
- 按仓库说明安装依赖,优先使用虚拟环境,避免污染系统 Python。
- 找到 agent 运行入口,确认它支持
--model、--base-url、--tool-interface这类参数,或支持通过环境变量注入。 - 准备一个最小任务,例如读取工作区文件、生成摘要、创建临时文件,而不是直接上复杂企业流程。
- 先跑通一次 Bash 工具调用,再切换到类型化工具接口做对照。
一个可复用的 runner 配置模板如下。字段名需要按你的 harness 实际字段映射,但结构可以照搬:
agent: name: gpt-5.5-bash-repro provider: taotoken base_url: https://taotoken.net/api api_key_env: TAOTOKEN_API_KEY model: gpt-5.5 tool_interface: bash max_turns: 30 shell: workdir: ./workspace timeout_seconds: 120 max_output_chars: 8000 logging: jsonl_path: ./logs/agent.jsonl log_usage: true log_tool_calls: true这里的tool_interface: bash表示只暴露一个 Bash 工具,模型通过自然语言生成 shell 命令。与之相对,类型化工具接口会把每个操作定义成固定 schema,例如read_file、write_file、search、run_test等。论文比较这两种路线时,会关注任务完成率、步骤数、错误恢复和 Token 消耗。我们复现时先保证 Bash 路线可跑,因为你只有把日志打全,才能知道 GPT-5.5 到底把 Token 花在命令生成、输出回传还是错误重试上。
最小运行命令可以先用本地脚本包装:
cd /path/to/your/theagentcompany source .venv/bin/activate export TAOTOKEN_API_KEY="YOUR_API_KEY" export OPENAI_API_KEY="$TAOTOKEN_API_KEY" export OPENAI_BASE_URL="https://taotoken.net/api" export OPENAI_MODEL="gpt-5.5" python -m your_agent_runner \ --task ./tasks/minimal_read_file.json \ --tool-interface bash \ --model "$OPENAI_MODEL" \ --base-url "$OPENAI_BASE_URL" \ --max-turns 10如果 runner 没有--base-url参数,就改配置文件或改环境变量。关键原则是:模型请求必须实际走https://taotoken.net/api,而不是其他地址。你可以在日志里打印最终请求 URL,确认没有多拼或少拼路径。
复现时建议准备三个任务层级:
- 只读任务:读取一个本地文件并返回摘要,验证 Bash 工具是否能执行
pwd、ls、cat。 - 写入任务:创建临时目录、写文件、检查内容,验证 stdout/stderr 回传。
- 多步任务:先搜索文件,再读取片段,再写入结果,验证多轮工具调用和 token 统计。
每层任务都记录一次日志。这样当 GPT-5.5 在某一步开始重复命令时,你能快速定位是任务描述太长、工具输出太大,还是 Bash 返回格式不稳定。
3. Bash 接口配置:schema、执行器与 GPT-5.5 工具调用循环
Bash 接口的核心很简单:只给模型一个函数,名字叫bash,参数只有一个command。模型生成 shell 命令,你的本地执行器运行命令,把stdout、stderr、exit_code回传。相比类型化工具接口,Bash 的灵活度更高,但输出不可控,Token 也更容易膨胀。因此配置重点有三个:schema 要合法、执行器要截断输出、日志要记录 usage。
一个 OpenAI 兼容的 Python 调用示例:
import json import os import subprocess from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url="https://taotoken.net/api", ) tools = [ { "type": "function", "function": { "name": "bash", "description": "在本地任务容器中执行一条 shell 命令,返回 stdout、stderr 和 exit_code。", "parameters": { "type": "object", "properties": { "command": { "type": "string", "description": "要执行的 shell 命令,尽量单行、可重复执行。", } }, "required": ["command"], }, }, } ] def run_bash(command: str, timeout: int = 120) -> dict: proc = subprocess.run( command, shell=True, text=True, capture_output=True, timeout=timeout, ) return { "exit_code": proc.returncode, "stdout": proc.stdout[-8000:], "stderr": proc.stderr[-4000:], }上面的[-8000:]和[-4000:]不是随便写的。Bash 命令一旦输出大文件,完整回传会把上下文迅速撑大,GPT-5.5 在后续轮次里会重复携带历史输出,Token 消耗会明显上升。复现论文中的 Bash 接口时,建议把输出裁剪做成默认策略,而不是等超限后再补。
接下来是工具调用循环:
def run_agent(task_prompt: str, model: str = "gpt-5.5", max_turns: int = 30): messages = [ { "role": "system", "content": "你是企业 Agent 复现器,只能通过 bash 工具操作本地工作区。不要连接生产库,不要执行破坏性命令。", }, {"role": "user", "content": task_prompt}, ] for turn in range(max_turns): resp = client.chat.completions.create( model=model, messages=messages, tools=tools, tool_choice="auto", temperature=0, ) msg = resp.choices[0].message messages.append(msg) usage = resp.usage print({ "turn": turn, "model": getattr(resp, "model", model), "prompt_tokens": getattr(usage, "prompt_tokens", None), "completion_tokens": getattr(usage, "completion_tokens", None), "total_tokens": getattr(usage, "total_tokens", None), "tool_calls": [c.function.name for c in (msg.tool_calls or [])], }) if not msg.tool_calls: break for call in msg.tool_calls: args = json.loads(call.function.arguments) result = run_bash(args["command"]) messages.append({ "role": "tool", "tool_call_id": call.id, "content": json.dumps(result, ensure_ascii=False), })这段代码里有两个复现关键点。第一,temperature=0能降低工具参数随机性,便于复现。第二,每次请求都打印 usage,不要只打印最终结果。Bash 接口的 Token 消耗往往分散在多轮工具调用里,只看最后一轮会低估。
如果你在 TheAgentCompany 里使用 LiteLLM,可以把它配置成 OpenAI 兼容供应商。示意配置:
model_list: - model_name: gpt-5.5 litellm_params: model: openai/gpt-5.5 api_base: https://taotoken.net/api api_key: os.environ/TAOTOKEN_API_KEY这里同样注意api_base使用纯 Base URL,不要加官网 UTM。LiteLLM 通常会自动拼/chat/completions,如果出现 404,优先检查你传入的api_base是否被重复拼接,或者模型 ID 是否写错。
Bash 接口还有一个常见坑:模型返回的command里包含换行和引号,json.loads失败。解决思路不是让模型“小心一点”,而是在 schema 描述里要求单行命令,并在执行器里对command做长度限制。如果确实需要多行脚本,可以要求写成bash -lc '...',但仍要限制总长度。日志里记录command的前 200 个字符,足够排查重复调用。
4. Claude Code、Codex 与 CC Switch 三件套接入 TaoToken 的配置写法
虽然 TheAgentCompany 复现主要在 Python harness 里完成,但很多读者会同时用 Claude Code、Codex 做辅助排查和配置生成。这里把工具链配置分开写,避免把ANTHROPIC_*套到 Codex,也避免把 Codex 的config.toml套到 Claude Code。
Claude Code 使用settings.json。在用户目录下创建或修改~/.claude/settings.json:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "YOUR_MODEL_ID", "ANTHROPIC_SMALL_FAST_MODEL": "YOUR_SMALL_MODEL_ID" } }其中YOUR_MODEL_ID和YOUR_SMALL_MODEL_ID按 TaoToken 控制台或模型列表里显示的 ID 填写。ANTHROPIC_BASE_URL固定为https://taotoken.net/api,不要带官网页面的查询参数。改完后重启 Claude Code,让配置生效。如果你的环境使用项目级.claude/settings.json,也可以放在项目根目录,但不要把真实 Key 提交到仓库。
Codex 使用config.toml。在~/.codex/config.toml中写:
model = "gpt-5.5" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"然后在 shell 中设置:
export TAOTOKEN_API_KEY="YOUR_API_KEY"注意 Codex 这里用的是TAOTOKEN_API_KEY,不是ANTHROPIC_AUTH_TOKEN。把 Claude Code 的变量名搬到 Codex,会导致 Key 读不到。反过来也一样,不要把 Codex 的env_key写法塞进 Claude Code 的settings.json。
CC Switch 可以理解为多套供应商配置的切换器。这里建议按“三件套”管理:
- 供应商 Base URL:
https://taotoken.net/api - API Key:
YOUR_API_KEY - 模型 ID:
gpt-5.5或控制台实际模型 ID
在 CC Switch 里分别给 Claude Code、Codex 建 profile,不要共用一个包含错误变量名的 profile。Claude Code profile 使用ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN;Codex profile 使用model_provider、base_url、env_key。这样切换时不会污染当前 shell。更多配置入口可以看 TaoToken 官网:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_toolchain 。
如果你用 CC Switch 后仍然报 401,检查三处:
- CC Switch 当前选中的 profile 是否真的是 TaoToken。
- 终端里旧的
OPENAI_API_KEY、ANTHROPIC_AUTH_TOKEN是否还残留。 - 工具是否读取了项目级配置,覆盖了全局配置。
这三步能解决大部分“配置看起来没问题,请求却仍然失败”的情况。
5. 模型调用日志:观察 GPT-5.5 在 Bash 任务中的 Token 消耗
复现 Bash 接口时,日志不是附加项,而是核心产出。你需要记录每次模型调用的 usage、每次工具调用的命令、退出码和输出长度。建议使用 JSONL,一行一个事件,便于后面用jq统计。
先写一个日志函数:
import json import time def append_jsonl(path: str, record: dict) -> None: record["timestamp"] = time.time() with open(path, "a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n")在模型调用后记录:
append_jsonl("./logs/agent.jsonl", { "event": "model_call", "task_id": "minimal_read_file", "turn": turn, "model": getattr(resp, "model", model), "request_id": getattr(resp, "id", None), "prompt_tokens": getattr(usage, "prompt_tokens", None), "completion_tokens": getattr(usage, "completion_tokens", None), "total_tokens": getattr(usage, "total_tokens", None), "tool_call_count": len(msg.tool_calls or []), })在 Bash 执行后记录:
append_jsonl("./logs/agent.jsonl", { "event": "bash_call", "task_id": "minimal_read_file", "turn": turn, "tool": "bash", "command_head": args["command"][:200], "exit_code": result["exit_code"], "stdout_chars": len(result["stdout"]), "stderr_chars": len(result["stderr"]), })有了 JSONL 后,可以按任务统计总 Token:
jq -s ' map(select(.event == "model_call")) | group_by(.task_id) | map({ task_id: .[0].task_id, calls: length, total_tokens: (map(.total_tokens) | add), prompt_tokens: (map(.prompt_tokens) | add), completion_tokens: (map(.completion_tokens) | add) }) ' logs/agent.jsonl也可以统计 Bash 调用次数和失败次数:
jq -s ' map(select(.event == "bash_call")) | group_by(.task_id) | map({ task_id: .[0].task_id, bash_calls: length, failed_calls: (map(select(.exit_code != 0)) | length), stdout_chars: (map(.stdout_chars) | add) }) ' logs/agent.jsonl这些统计能直接回答一个复现问题:GPT-5.5 的 Token 到底花在哪。如果prompt_tokens随轮次快速上升,通常是历史工具输出太长;如果completion_tokens偏高,可能是模型生成命令过于冗长或反复解释;如果 Bash 失败次数多,可能是任务环境路径不对或命令权限不足。
控制 Token 的实用策略:
- 在工具结果里只回传
stdout末尾 8000 字符、stderr末尾 4000 字符。 - 长文件不要
cat全量,优先head -n 80、grep -n、sed -n '1,120p'。 - 对重复命令做去重,相同
command连续出现时直接返回缓存结果。 - 在 system prompt 里要求模型先
pwd、ls,再执行具体命令。 - 每轮日志记录
total_tokens,超过预算就停止任务并保存现场。
Bash 接口的灵活性和 Token 风险是一体两面。类型化工具接口通常返回结构化短结果,Bash 接口可能返回大段文本。复现论文对比时,如果不控制输出长度,很容易把“接口差异”变成“输出长度差异”。
6. 常见报错排查:从 Key、Base URL 到 Bash 超时
复现过程中大概率会遇到下面几类问题。建议按报错顺序排查,不要一上来就改 agent 逻辑。
6.1 401 或 403:invalid_api_key / permission denied
先确认 TaoToken Key 是否创建成功,再确认 shell 中变量是否可见:
python - <<'PY' import os key = os.environ.get("TAOTOKEN_API_KEY", "") print("key length =", len(key)) print("prefix =", key[:6]) PY如果长度为 0,说明当前终端没有加载环境变量。如果长度正常但依然 401,检查请求头是否真的带上了 Bearer。可以在代码里临时打印base_url,但不要把完整 Key 打进日志。
6.2 404 model not found
常见原因是模型 ID 写错,或者 Base URL 写成了官网页面地址。正确写法是:
https://taotoken.net/api不要写成带utm_source的网页链接。模型 ID 以 TaoToken 控制台或模型列表显示为准。如果你在配置文件里写gpt-5.5,但控制台实际模型 ID 带有版本后缀,也会 404。
6.3 400 Bad Request:tool schema 不合法
Bash 工具的 schema 必须符合 JSON Schema。重点检查:
type是否为object。properties是否定义了command。required是否包含command。- 是否存在尾随逗号。
一个最小合法 schema 就是前文例子,不要额外加复杂字段,先跑通再扩展。
6.4 JSONDecodeError:工具参数解析失败
如果json.loads(call.function.arguments)报错,先记录原始参数字符串:
raw = call.function.arguments try: args = json.loads(raw) except json.JSONDecodeError: append_jsonl("./logs/agent.jsonl", { "event": "tool_args_error", "raw_head": raw[:500], }) raise常见原因是命令包含未转义换行或引号。解决方法是限制单行命令,并在 schema 描述里强调“command 必须是单行字符串”。
6.5 Bash 超时或挂起
给执行器加超时:
result = run_bash(args["command"], timeout=120)如果命令本身需要更久,不要在模型侧无限等待。可以拆成多个短命令,或者让命令把结果写入临时文件,下一次只读取尾部。所有命令都在本地容器执行,不要连接生产数据库或共享环境。
6.6 Token 消耗异常升高
先看日志里的prompt_tokens是否逐轮上升。如果是,检查工具结果是否太长。其次看completion_tokens是否异常。如果是,检查 system prompt 是否太长、模型是否在解释而不是执行。最后看是否有重复命令循环。加一个简单去重:
seen_commands = set() def run_bash_once(command: str) -> dict: if command in seen_commands: return {"exit_code": 0, "stdout": "[cached] command already executed", "stderr": ""} seen_commands.add(command) return run_bash(command)这不是为了减少模型能力,而是为了复现实验可控。
7. 把复现结果沉淀为可重复流程
一次成功的 Bash 接口复现,应该留下四类文件:
- 运行配置:
.env.example、config.yaml、settings.json模板。 - 任务输入:最小任务 JSON 或 YAML,不包含敏感数据。
- 模型调用日志:
logs/agent.jsonl,包含 usage。 - 工具调用日志:同一份 JSONL 里的
bash_call事件,包含命令、退出码、输出长度。
建议写一个本地检查脚本:
#!/usr/bin/env bash set -euo pipefail test -n "${TAOTOKEN_API_KEY:-}" || { echo "TAOTOKEN_API_KEY missing"; exit 1; } test "${OPENAI_BASE_URL:-}" = "https://taotoken.net/api" || { echo "OPENAI_BASE_URL mismatch"; exit 1; } python -m your_agent_runner \ --task ./tasks/minimal_read_file.json \ --tool-interface bash \ --model "${OPENAI_MODEL:-gpt-5.5}" \ --base-url "${OPENAI_BASE_URL}" \ --max-turns 10 jq -s ' map(select(.event == "model_call")) | { calls: length, total_tokens: (map(.total_tokens) | add), prompt_tokens: (map(.prompt_tokens) | add), completion_tokens: (map(.completion_tokens) | add) } ' logs/agent.jsonl这个脚本只做三件事:确认 Key 和 Base URL、跑最小任务、汇总 Token。它不依赖任何生产系统,也不会自动修改工作区外文件。
如果你要扩展到 APEX-Agents 或类型化工具接口,建议保持同一套日志格式。这样 Bash 接口和类型化工具接口的差异才能被公平比较。不要在没有日志的情况下凭感觉判断哪个接口更省 Token。GPT-5.5 在 Bash 任务里可能因为多轮 shell 输出而增加上下文,也可能因为少写 schema 而减少准备成本。只有prompt_tokens、completion_tokens、total_tokens和工具失败次数同时记录,才能解释差异来自接口本身还是任务环境。
最后提醒三点:
- TheAgentCompany 的任务尽量在本地容器或临时工作区执行,不要连接 Oracle、生产数据库或其他共享系统。
- Bash 工具执行器必须设置超时和输出截断,否则复现实验很容易被长输出拖垮。
- 每次改配置后,先跑最小
pwd、ls、cat任务,再跑多步任务。
8. 下一步:按顺序配置 TaoToken 并跑通 GPT-5.5 Bash 任务
如果你已经准备好复现 TheAgentCompany,建议按下面顺序操作:
- 先到模型对话页面确认可用模型和模型 ID:https://taotoken.net/models/detail/chat?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_chat
- 如果打算长期跑任务,查看 Coding Plan 的配置方式:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_plan
- 创建 API Key,并把
YOUR_API_KEY替换到本地环境变量:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_key - 如果你同时用 Claude Code 排查配置,参考 Claude Code 文档:https://taotoken.net/doc/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_claudecode
- 回到官网总入口,检查模型、Key 和文档入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=theagentcompany_final
配置时再次确认:Base URL 是https://taotoken.net/api,不是带 UTM 的网页地址;Key 占位符统一写YOUR_API_KEY;Claude Code 用ANTHROPIC_*和settings.json;Codex 用config.toml和TAOTOKEN_API_KEY;CC Switch 三件套按供应商、Key、模型分别管理。把这些固定下来后,再跑 TheAgentCompany 的 Bash 接口任务,日志里的 GPT-5.5 Token 消耗才有可比性,复现结果也才能从“跑过一次”变成“每次都能重跑”。