1. 复现 TheAgentCompany 前,先分清 Bash 接口与类型化接口的 Token 账
复现 TheAgentCompany 时,Bash 工具接口与类型化工具接口的差异,最终都会落到 Token 消耗和错误恢复上。微软那篇论文在 TheAgentCompany 和 APEX-Agents 上用 Opus-4.8、GPT-5.5 做对比,结论是 Bash 在企业 Agent 任务里并不吃亏,甚至超过类型化接口。要复现这套评测,先到 TaoToken 官网 拿 Key,再把 Base URL 填为https://taotoken.net/api。本文不把论文复述成新闻,而是从企业 Agent 评测复现者的视角,给出最小任务脚本、Key 注入配置、Bash 与类型化接口对照表,并重点解释 Bash Agent 的 Token 消耗从哪里来、怎么压。
很多团队第一次跑 Agent 评测时,会把注意力放在模型名和任务成功率上,结果跑完一轮才发现成本曲线不对:Bash 工具调用看起来简单,却因为命令输出回灌、重试、多轮试错,把上下文迅速撑大;类型化接口看起来规范,却因为 JSON Schema、参数校验、工具数量膨胀,在首轮 prompt 里就吃掉大量 Token。TheAgentCompany 这类任务很适合暴露这个问题,因为任务本身包含文件操作、命令执行、结果校验和跨步骤状态传递。复现者要做的不是简单“跑通”,而是把接口类型、模型、Token 消耗、重试次数、完成状态都记录下来,形成可比较的本地实验。
如果你还没有 Key,建议先打开 TaoToken 官网 完成注册并创建 Key。Key 是后续 Claude Code、Codex、Python 脚本调用 Opus-4.8 或 GPT-5.5 的入口,Base URL 统一填https://taotoken.net/api。下面从 Key 注入开始,一步步搭出可复现的最小环境。
2. 用 TaoToken 发 Key 给 Opus-4.8:API Key 注入与环境变量配置
企业 Agent 评测最怕配置散落在多个文件里。建议把 Key、Base URL、模型名收敛到环境变量,再分别映射到 Claude Code、Codex 和 Python 脚本。首先在 TaoToken 官网 的 API Keys 页面创建 Key,复制后不要提交到 Git。本地可以这样注入:
# 通用环境变量,Python 脚本和 Codex 都可以引用 export TAOTOKEN_API_KEY="YOUR_API_KEY" export TAOTOKEN_BASE_URL="https://taotoken.net/api" # 如果使用 OpenAI 兼容 SDK,可额外映射 export OPENAI_API_KEY="$TAOTOKEN_API_KEY" export OPENAI_BASE_URL="$TAOTOKEN_BASE_URL" # 复现时建议固定模型,避免不同轮次漂移 export TAOTOKEN_MODEL="claude-opus-4-8"注意模型 ID 以 TaoToken 控制台展示为准。Opus-4.8 是本文复现目标之一,但不同控制台可能显示为带版本后缀的 ID。生产脚本里不要把模型名硬编码到业务代码,放到环境变量或配置文件中,便于 Bash 接口和类型化接口使用同一模型做对照。
Claude Code 使用settings.json和ANTHROPIC_*变量。可以在用户级或项目级配置中加入:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "claude-opus-4-8", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5" } }这里的关键是ANTHROPIC_BASE_URL指向https://taotoken.net/api,ANTHROPIC_AUTH_TOKEN使用刚才创建的 Key。不要把ANTHROPIC_*这套变量写进 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 = "responses"然后确保 shell 中已经导出TAOTOKEN_API_KEY。如果你用 CC Switch 管理多套配置,检查“三件套”是否一致:第一,Base URL 是否指向https://taotoken.net/api;第二,Key 引用是否指向YOUR_API_KEY对应的环境变量;第三,默认模型是否与当前评测轮次一致。任何一处写错,都会表现为 401、404 或模型不存在。
可以用一个最小请求验证 Key 是否可用。以下命令假设你已安装curl,并且只在本地执行:
curl -sS https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "'"$TAOTOKEN_MODEL"'", "messages": [{"role": "user", "content": "只回复 ok"}], "max_tokens": 16, "temperature": 0 }'如果返回 401,先检查 Key 是否复制完整、是否有多余空格、是否在请求头中带了Bearer。如果返回模型不存在,回到控制台确认 Opus-4.8 的实际模型 ID。如果返回路径错误,检查 Base URL 是否误写成https://taotoken.net/api/并额外拼了多余斜杠。
3. TheAgentCompany 最小任务脚本:本地跑 Bash Agent 与类型化接口
为了公平比较 Bash 与类型化接口,我们设计一个最小任务:本地目录中有一个sales.csv,要求 Agent 读取文件,筛选出状态为error的行,统计数量,并写出result.json。任务不连接任何生产数据库,所有命令都在本地隔离目录执行。这个任务足够小,但又包含读取、筛选、写入三个阶段,能观察两种接口在 Token 消耗和错误恢复上的差异。
项目结构如下:
theagentcompany-min/ ├── theagentcompany_min.py └── agent_work/ └── sales.csv先用 Python 生成一份测试数据:
mkdir -p theagentcompany-min/agent_work cd theagentcompany-min python - <<'PY' import csv, pathlib p = pathlib.Path("agent_work/sales.csv") rows = [ {"id": "1", "region": "east", "amount": "120", "status": "ok"}, {"id": "2", "region": "west", "amount": "80", "status": "error"}, {"id": "3", "region": "east", "amount": "200", "status": "ok"}, {"id": "4", "region": "north", "amount": "50", "status": "error"}, ] with p.open("w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["id", "region", "amount", "status"]) writer.writeheader() writer.writerows(rows) print(p) PY然后写最小 Agent 脚本。它支持两种工具集:bash模式只给一个run_bash工具;typed模式给typed_read_csv、typed_filter_rows、typed_write_json三个类型化工具。脚本使用 OpenAI 兼容 SDK,Base URL 指向https://taotoken.net/api。
# theagentcompany_min.py import os import json import csv import subprocess import pathlib from openai import OpenAI BASE_URL = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") API_KEY = os.environ["TAOTOKEN_API_KEY"] MODEL = os.environ.get("TAOTOKEN_MODEL", "claude-opus-4-8") client = OpenAI(base_url=BASE_URL, api_key=API_KEY) WORKDIR = pathlib.Path(__file__).parent / "agent_work" WORKDIR.mkdir(exist_ok=True) def run_bash(command: str) -> str: """在本地隔离目录执行 bash,返回截断后的输出。""" proc = subprocess.run( command, shell=True, cwd=WORKDIR, capture_output=True, text=True, timeout=20, ) output = (proc.stdout or "") + (proc.stderr or "") # 关键:截断输出,避免大文件回灌推高 Token return output[:4000] def typed_read_csv(path: str, limit: int = 20) -> str: rows = [] with (WORKDIR / path).open(newline="", encoding="utf-8") as f: reader = csv.DictReader(f) for i, row in enumerate(reader): if i >= limit: break rows.append(row) return json.dumps(rows, ensure_ascii=False) def typed_filter_rows(path: str, column: str, value: str) -> str: rows = [] with (WORKDIR / path).open(newline="", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: if row.get(column) == value: rows.append(row) return json.dumps(rows, ensure_ascii=False) def typed_write_json(path: str, data: str) -> str: obj = json.loads(data) (WORKDIR / path).write_text( json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8", ) return f"written:{path}" TOOLS_BASH = [ { "type": "function", "function": { "name": "run_bash", "description": "在本地隔离目录执行 bash 命令,用于查看、筛选、统计和写入文件。", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "要执行的 bash 命令"} }, "required": ["command"], }, }, } ] TOOLS_TYPED = [ { "type": "function", "function": { "name": "typed_read_csv", "description": "读取 CSV 的前 N 行,返回 JSON 数组。", "parameters": { "type": "object", "properties": { "path": {"type": "string"}, "limit": {"type": "integer", "default": 20}, }, "required": ["path"], }, }, }, { "type": "function", "function": { "name": "typed_filter_rows", "description": "按列值筛选 CSV 行,返回 JSON 数组。", "parameters": { "type": "object", "properties": { "path": {"type": "string"}, "column": {"type": "string"}, "value": {"type": "string"}, }, "required": ["path", "column", "value"], }, }, }, { "type": "function", "function": { "name": "typed_write_json", "description": "把 JSON 字符串写入指定文件。", "parameters": { "type": "object", "properties": { "path": {"type": "string"}, "data": {"type": "string"}, }, "required": ["path", "data"], }, }, }, ] def dispatch_tool(name: str, args: dict) -> str: if name == "run_bash": return run_bash(args["command"]) if name == "typed_read_csv": return typed_read_csv(args["path"], args.get("limit", 20)) if name == "typed_filter_rows": return typed_filter_rows(args["path"], args["column"], args["value"]) if name == "typed_write_json": return typed_write_json(args["path"], args["data"]) return f"unknown_tool:{name}" def run_agent(mode: str) -> str: tools = TOOLS_BASH if mode == "bash" else TOOLS_TYPED task = ( "本地 agent_work/sales.csv 包含销售记录。请筛选 status 为 error 的行," "统计数量,并写入 agent_work/result.json。所有操作只允许在 agent_work 目录内完成。" ) messages = [ { "role": "system", "content": "你是企业 Agent 评测复现助手。只使用提供的工具,本地执行,不要连接生产数据库。", }, {"role": "user", "content": task}, ] for step in range(8): resp = client.chat.completions.create( model=MODEL, messages=messages, tools=tools, tool_choice="auto", temperature=0, ) msg = resp.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content or "done" for tc in msg.tool_calls: args = json.loads(tc.function.arguments or "{}") result = dispatch_tool(tc.function.name, args) messages.append( { "role": "tool", "tool_call_id": tc.id, "content": result, } ) return "max_steps_reached" if __name__ == "__main__": import sys mode = sys.argv[1] if len(sys.argv) > 1 else "bash" print(run_agent(mode))运行方式:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export TAOTOKEN_MODEL="claude-opus-4-8" python theagentcompany_min.py bash python theagentcompany_min.py typed跑完后再检查结果:
cat agent_work/result.json这个脚本不是完整 Agent 框架,但它把关键变量固定下来了:同一个任务、同一个模型、同一份数据,只切换工具接口。复现者可以在client.chat.completions.create前后记录 Token 用量,或者在每轮工具返回后统计上下文长度。Bash 模式通常会把命令执行结果直接回灌,类型化模式则会把参数和返回值结构化,二者对 Token 的消耗模式不同。
4. Bash 与类型化接口对照表:Token 消耗、错误恢复与可观测性
下表不是论文数字复刻,而是复现者在本地跑最小任务时应该记录的维度。你可以把每一列替换成自己环境的实际值,形成可比较的实验记录。
| 维度 | Bash 工具接口 | 类型化工具接口 |
|---|---|---|
| 工具定义 | 通常只有一个run_bash,schema 短 | 每个动作一个工具,schema 多,首轮 prompt 更长 |
| 输入 Token | 集中在命令构造和输出回灌 | 集中在工具描述、参数约束、多轮校验 |
| 输出 Token | 命令 stdout/stderr 可能很长,需要截断 | 返回值通常是 JSON,体积可控 |
| 错误恢复 | 模型可读 stderr,自行换命令重试 | 参数错误直接返回,重试路径明确 |
| 可观测性 | 命令日志直观,但结构化程度低 | 调用链结构化,便于审计和统计 |
| 适用任务 | 文件探索、批量处理、环境检查 | 写操作、审批流、严格参数校验 |
| 主要风险 | 大输出、危险命令、注入风险 | 工具膨胀、schema 占 Token、多轮参数纠错 |
| 复现注意 | 限制目录、截断输出、白名单命令 | 控制工具数量、拆分读写、记录参数错误 |
从复现经验看,Bash 在企业 Agent 任务里“超过类型化接口”往往不是因为它更聪明,而是因为它减少了工具之间的协调成本。类型化接口要求模型先理解每个工具的参数语义,再在多轮里拼装调用链;Bash 只要求模型理解 shell 和文件系统,组合方式更自由。但自由也意味着 Token 消耗更不可控。一次cat大文件、一次find /、一次未截断的日志输出,就可能把上下文推到高位。
类型化接口的优势在于边界清晰。比如typed_write_json只接受路径和 JSON 字符串,模型不容易误写系统目录。对于企业评测,写操作、删除操作、外部提交操作应该优先类型化,读操作和探索操作可以交给 Bash。这样既保留 Bash 的灵活性,又用类型化接口守住关键路径。
5. 控制 Bash Agent Token 消耗:从输出截断到命令白名单
如果你的评测目标是“用 Opus-4.8 复现 Bash Agent 的 Token 曲线”,下面 7 个手段建议直接写进工具包装器,而不是依赖模型自觉。
第一,输出截断。run_bash返回前统一截断到固定字符数,例如 4000 字符。超出部分写完整日志到本地文件,只把摘要返回给模型。示例:
def run_bash_limited(command: str, max_chars: int = 4000) -> str: proc = subprocess.run( command, shell=True, cwd=WORKDIR, capture_output=True, text=True, timeout=20, ) output = (proc.stdout or "") + (proc.stderr or "") if len(output) > max_chars: # 完整输出落盘,模型只拿摘要 (WORKDIR / "last_command.log").write_text(output, encoding="utf-8") return output[:max_chars] + f"\n...[truncated, full log: last_command.log]" return output第二,避免cat大文件。模型经常用cat sales.csv查看数据,这会把整份文件塞进上下文。可以在系统提示里要求:查看文件优先用head、wc -l、sed -n、jq。更好的做法是在 Bash 工具描述里明确“禁止无参数 cat 大文件”。
第三,结构化中间产物。用jq -c压缩 JSON,用cut选择列,用awk聚合。例如统计 error 行数可以用:
awk -F, 'NR>1 && $4=="error" {c++} END {print c+0}' agent_work/sales.csv第四,控制命令回显。set +x避免脚本调试输出被模型看到。只把必要 stdout 回传,stderr 可以摘要化。
第五,临时目录隔离。所有 Bash 命令限制在agent_work目录内,防止模型探索到无关文件,既安全也省 Token。
第六,重试只回传错误摘要。第一次失败时返回完整 stderr,后续重试只返回错误类型和关键行。可以在包装器里做去重和截断。
第七,关键写操作用类型化接口兜底。Bash 负责读和试探,写结果、改配置、提交产物走类型化工具。这样 Token 消耗集中在探索阶段,写操作不会因为命令拼接错误反复重试。
这些手段不会改变模型能力,但会显著改变 Token 曲线。复现论文结论时,如果不控制 Bash 输出,很容易把“Bash 更省钱”误判成“Bash 更贵”。
6. 排障清单:401、模型名、Base URL、流式中断与 CC Switch 冲突
复现企业 Agent 评测时,环境问题比模型问题更常见。下面按症状排查。
401 Unauthorized:检查YOUR_API_KEY是否完整,请求头是否为Authorization: Bearer YOUR_API_KEY,环境变量是否真的被当前 shell 读取。Claude Code 的ANTHROPIC_AUTH_TOKEN和 Python 的TAOTOKEN_API_KEY可以指向同一个 Key,但不要混用变量名。
模型不存在:Opus-4.8、GPT-5.5 在控制台可能有不同模型 ID。不要凭记忆写死,先到模型对话页面确认可用模型,再填到脚本或配置。Python 脚本里用TAOTOKEN_MODEL,Claude Code 里用ANTHROPIC_MODEL,Codex 里用model。
Base URL 错误:统一填https://taotoken.net/api。注意不要末尾多斜杠,也不要在 SDK 已经拼接路径的情况下再手动加/v1。如果 curl 验证失败,先换成 SDK 最小请求,排除路径拼接问题。
流式中断:长任务中 Bash 输出过多可能导致响应变慢或中断。降低max_tokens,对工具输出做截断,关闭不必要的流式回显。评测脚本里记录每轮耗时,便于区分网络问题和模型问题。
CC Switch 冲突:如果你用 CC Switch 管理多套配置,确认当前激活项的三件套一致。常见问题是 Base URL 切到了 TaoToken,但 Key 还是旧环境的;或者 Claude Code 的ANTHROPIC_*被误写进 Codex 的config.toml。Codex 应使用model_providers.taotoken和env_key = "TAOTOKEN_API_KEY",不要套用 Anthropic 变量。
本地命令安全:所有 Bash 命令由读者在本地隔离目录执行,不要把 Agent 指向生产数据库、线上容器或真实凭证目录。评测数据用生成的 CSV 或脱敏样例,写操作只落在agent_work下。
7. 复现记录模板:把论文里的对比变成可重复的本地实验
要把 Bash 与类型化接口的对比做扎实,建议每轮实验记录以下字段:
| 字段 | 说明 |
|---|---|
| run_id | 本轮实验唯一 ID |
| model | claude-opus-4-8或gpt-5.5,以控制台为准 |
| interface | bash或typed |
| task | 固定任务描述 |
| input_tokens | 请求侧输入 Token |
| output_tokens | 请求侧输出 Token |
| tool_calls | 工具调用次数 |
| retries | 参数错误或命令失败后的重试次数 |
| truncated | 是否触发输出截断 |
| status | success / failed / max_steps |
| elapsed_ms | 总耗时 |
可以在 Python 脚本中把resp.usage写入 JSONL:
import json, pathlib def log_usage(run_id, mode, model, usage, step, extra=None): path = pathlib.Path("runs.jsonl") record = { "run_id": run_id, "mode": mode, "model": model, "step": step, "input_tokens": getattr(usage, "prompt_tokens", None), "output_tokens": getattr(usage, "completion_tokens", None), } if extra: record.update(extra) with path.open("a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n")跑完bash和typed两组后,对比input_tokens、output_tokens、tool_calls、retries。你会发现 Bash 的 Token 消耗经常集中在工具输出回灌,而类型化接口的 Token 消耗集中在工具定义和多轮参数校验。这个观察比单纯看总 Token 更有解释力。
如果要把实验扩展到 TheAgentCompany 风格的多步骤任务,可以逐步加入:读取多个文件、生成中间报告、校验输出格式、失败重试。每加一步,都重新记录 Token 和重试次数。不要一次性把任务放大,否则很难判断差异来自接口还是任务复杂度。
8. 文末 CTA:拿 Key、跑通最小任务、再决定 Coding Plan
复现 TheAgentCompany 里的 Bash 与类型化接口对比,第一步不是写复杂框架,而是把 Key、Base URL、模型名固定下来。先到 TaoToken 官网 创建 Key,Base URL 填https://taotoken.net/api,然后用本文的最小脚本跑通bash和typed两组任务。确认 Opus-4.8 可用后,再逐步加入输出截断、命令白名单和 Token 日志。
推荐路径如下:
- 先打开 模型对话,确认 Opus-4.8、GPT-5.5 等模型在当前控制台的实际模型 ID。
- 如果你要长期跑企业 Agent 评测,查看 Coding Plan,选择适合连续实验的套餐。
- 到 API Keys 创建或轮换 Key,填入
YOUR_API_KEY。 - 使用 Claude Code 的话,参考 Claude Code 文档 配置
settings.json和ANTHROPIC_*变量;使用 Codex 则改用config.toml,不要混用 Anthropic 变量。
把 Bash Agent 的 Token 消耗记录清楚,再和类型化接口放在同一张表里比较,你得到的不只是一个论文复现结果,而是一套能持续迭代的企业 Agent 评测基线。