十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

测 Iris 35B 同量级成绩,TaoToken 接住每轮搜索调用

测 Iris 35B 同量级成绩,TaoToken 接住每轮搜索调用 1. Iris 搜索 Agent 本地评测Token 消耗不在最终答案而在 search_planner 与 answer_synthesizer最近复现 AllSpark 开源的 Iris 搜索 Agent 本地评测时日志里最先暴露的不是权重加载失败而是search_planner阶段返回401 invalid api key。我把 OpenAI 兼容调用切到 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_intro先到官网拿 Key再把评测脚本里的base_url设为https://taotoken.net/api。Iris 35B / 397B 这类 Search Agent 不是一次 chat completion 就结束评测问题先进入搜索规划器生成查询、改写查询、过滤检索结果最后才由答案汇总器组织输出。真正按轮次消耗 Token 的是搜索规划和答案汇总这两类 OpenAI 兼容调用。很多人第一次跑 Search Agent 评测会把注意力都放在模型权重、GPU 显存和检索指标上。实际跑起来后最容易卡住的是调用链搜索规划器要多次调用模型来产出查询词答案汇总器要把检索片段拼进上下文再生成答案。如果评测集有 500 条问题每条问题规划 3 次、汇总 1 次那就是 2000 次左右的 API 调用。每一次调用都有 prompt token、completion token、延迟和状态码任何一次 401、404、429 都会污染结果。所以本文不是讨论 Iris 权重怎么合并也不是重复开源公告。这里聚焦一个更工程化的问题本地评测 Iris 搜索 Agent 时如何把search_planner和answer_synthesizer的 OpenAI 兼容调用统一接到 TaoToken并把base_url配成https://taotoken.net/api回填YOUR_API_KEY后跑通评测命令再通过日志核对每轮搜索调用的成本与错误。如果你还没有 Key可以先通过官网入口进入控制台https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_get_key 。拿到 Key 后不要写进代码后面所有配置都用环境变量或本地未提交配置文件。2. 先拿 Key再确认 OpenAI 兼容调用的 base_url 与请求路径TaoToken 的接入方式和大多数 OpenAI 兼容服务类似一个 API Key一个 Base URL。Base URL 是https://taotoken.net/api注意这个 Base URL 在工具配置里不要额外加 UTM 参数也不要手写/v1再重复。很多 404 都来自路径拼接错误。比如你的代码使用https://taotoken.net/api作为base_url那么 chat completions 的实际请求路径通常是https://taotoken.net/api/chat/completions如果你在 SDK 里又配置了base_urlhttps://taotoken.net/api/v1或者环境变量里重复拼了/chat/completions就可能出现 404。排查时先把请求路径打印出来。创建 Key 的步骤如下打开 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_get_key 。登录后进入 API Keys 页面创建 Key。文末也会给出 API Keys deep link。复制 Key只显示一次不要提交到 Git。本地设置环境变量用YOUR_API_KEY占位替换。export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL你的可用模型 ID export IRIS_EVAL_LOGruns/taotoken_iris_calls.jsonl如果你用 OpenAI Python SDK可以这样初始化import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) resp client.chat.completions.create( modelos.environ[TAOTOKEN_MODEL], messages[ {role: user, content: 只回复 ok} ], temperature0, ) print(resp.choices[0].message.content)如果你更想先用 curl 验证 Key 和路径可以本地执行curl -sS https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL, messages: [ {role: user, content: 只回复 ok} ], temperature: 0 }这一步通过后再改 Iris 评测脚本。否则你会把 Key 问题、路径问题、评测脚本问题混在一起排查日志会非常乱。3. 可复制的 Iris 评测脚本把 search_planner 和 answer_synthesizer 接到 TaoToken下面给一个最小可运行的评测骨架。它不是完整 Iris 复现而是把 Search Agent 评测里最耗 Token 的两类调用抽出来搜索规划和答案汇总。你可以把它接到自己的检索函数、数据集和评分脚本里。先安装依赖pip install httpx然后创建iris_eval.py#!/usr/bin/env python3 import argparse import json import os import random import time from typing import Any import httpx BASE_URL os.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api) API_KEY os.getenv(TAOTOKEN_API_KEY) MODEL os.getenv(TAOTOKEN_MODEL) LOG_PATH os.getenv(IRIS_EVAL_LOG, runs/taotoken_iris_calls.jsonl) if not API_KEY: raise SystemExit(请先 export TAOTOKEN_API_KEYYOUR_API_KEY) if not MODEL: raise SystemExit(请先 export TAOTOKEN_MODEL你的可用模型 ID) def append_log(record: dict[str, Any]) - None: parent os.path.dirname(LOG_PATH) if parent: os.makedirs(parent, exist_okTrue) with open(LOG_PATH, a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n) def chat(stage: str, messages: list[dict[str, str]], temperature: float 0.2, max_tokens: int 1024) - str: url f{BASE_URL}/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: MODEL, messages: messages, temperature: temperature, max_tokens: max_tokens, } last_error None for attempt in range(5): start time.time() try: resp httpx.post(url, headersheaders, jsonpayload, timeout180) elapsed_ms int((time.time() - start) * 1000) usage {} content if resp.status_code 200: data resp.json() usage data.get(usage, {}) content data[choices][0][message][content] else: content resp.text[:800] append_log({ stage: stage, model: MODEL, status: ok if resp.status_code 200 else http_error, http_status: resp.status_code, elapsed_ms: elapsed_ms, attempt: attempt 1, usage: usage, preview: content[:200], }) if resp.status_code 429: sleep_s (2 ** attempt) random.random() time.sleep(sleep_s) last_error f429 rate limited, retry after {sleep_s:.2f}s continue resp.raise_for_status() return content except Exception as exc: elapsed_ms int((time.time() - start) * 1000) last_error str(exc) append_log({ stage: stage, model: MODEL, status: exception, http_status: None, elapsed_ms: elapsed_ms, attempt: attempt 1, error: last_error, }) time.sleep((2 ** attempt) random.random()) raise RuntimeError(f{stage} failed after retries: {last_error}) def search_planner(question: str) - list[str]: system 你是搜索规划器。只输出 JSON 数组每项是一条检索查询不要解释。 user f问题{question}\n请给出 1 到 3 条检索查询。 text chat( search_planner, [ {role: system, content: system}, {role: user, content: user}, ], temperature0.1, max_tokens256, ) try: queries json.loads(text) if isinstance(queries, list): return [str(q) for q in queries][:3] except Exception: pass return [question] def local_search(queries: list[str]) - list[dict[str, str]]: 这里接你本地的检索函数。 可以是 BM25、向量库、已有评测包里的 retriever。 本文不直接连接任何生产库检索命令只在本地执行。 docs [] for idx, q in enumerate(queries): docs.append({ title: f本地检索片段 {idx 1}, snippet: f与查询“{q}”相关的本地测试文本。请替换为真实检索结果。, source: local_fixture, }) return docs def answer_synthesizer(question: str, docs: list[dict[str, str]]) - str: context \n\n.join( f[{i 1}] {d.get(title, )}\n{d.get(snippet, )} for i, d in enumerate(docs) ) system 你是答案汇总器。只基于给定检索片段回答不要编造不存在的信息。 user f问题{question}\n\n检索片段\n{context}\n\n请给出简洁答案。 return chat( answer_synthesizer, [ {role: system, content: system}, {role: user, content: user}, ], temperature0.2, max_tokens2048, ) def run_one(item: dict[str, Any]) - dict[str, Any]: question item[question] queries search_planner(question) docs local_search(queries) answer answer_synthesizer(question, docs) return { id: item.get(id), question: question, queries: queries, answer: answer, doc_count: len(docs), } def main() - None: parser argparse.ArgumentParser() parser.add_argument(--dataset, requiredTrue) parser.add_argument(--out, requiredTrue) parser.add_argument(--limit, typeint, default50) parser.add_argument(--concurrency, typeint, default1) args parser.parse_args() os.makedirs(os.path.dirname(args.out), exist_okTrue) items [] with open(args.dataset, r, encodingutf-8) as f: for line in f: if line.strip(): items.append(json.loads(line)) items items[: args.limit] with open(args.out, w, encodingutf-8) as out: for item in items: try: result run_one(item) out.write(json.dumps(result, ensure_asciiFalse) \n) out.flush() print(fok {result.get(id)} queries{result.get(queries)}) except Exception as exc: error_row {id: item.get(id), error: str(exc)} out.write(json.dumps(error_row, ensure_asciiFalse) \n) out.flush() print(ffailed {item.get(id)}: {exc}) if __name__ __main__: main()这个脚本的关键点有三个第一BASE_URL默认是https://taotoken.net/api不额外加 UTM。第二Key 从TAOTOKEN_API_KEY读取代码里只出现YOUR_API_KEY占位。第三每次chat调用都写入 JSONL 日志包含 stage、http_status、elapsed_ms、usage。这样你才能知道 Token 消耗到底发生在搜索规划还是答案汇总。准备一个本地评测集data/iris_eval.jsonl每行至少包含id和question{id: q001, question: Iris 搜索 Agent 在本地评测时搜索规划和答案汇总分别消耗什么资源} {id: q002, question: 如何把 OpenAI 兼容调用切换到 TaoToken 并验证 base_url}然后本地执行mkdir -p runs data export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL你的可用模型 ID export IRIS_EVAL_LOGruns/taotoken_iris_calls.jsonl python iris_eval.py \ --dataset data/iris_eval.jsonl \ --out runs/taotoken_iris.jsonl \ --limit 20 \ --concurrency 1先跑 20 条不要一上来跑全量。Search Agent 的调用链比普通问答长错误会在后期放大。4. 对照日志每轮搜索调用的 Token、延迟、错误码怎么看跑完后不要只看最终答案。打开runs/taotoken_iris_calls.jsonl逐行看 stage。典型日志会分成两类{stage:search_planner,status:ok,http_status:200,elapsed_ms:842,usage:{prompt_tokens:120,completion_tokens:45,total_tokens:165}} {stage:answer_synthesizer,status:ok,http_status:200,elapsed_ms:3210,usage:{prompt_tokens:2860,completion_tokens:310,total_tokens:3170}}从这里可以直观看到搜索规划器单次 completion token 不高但调用次数多答案汇总器调用次数少但 prompt token 很高因为检索片段被拼进了上下文。Iris 35B 与 397B 同量级成绩对比时如果你只记录最终准确率不记录调用侧成本就无法解释为什么某些配置慢、贵或失败。可以用 jq 快速汇总jq -r select(.stage search_planner or .stage answer_synthesizer) | [.stage, .status, .http_status, .elapsed_ms, (.usage.prompt_tokens // 0), (.usage.completion_tokens // 0)] | tsv runs/taotoken_iris_calls.jsonl | column -t再统计每个 stage 的总 tokenjq -s group_by(.stage) | map({ stage: .[0].stage, calls: length, prompt_tokens: (map(.usage.prompt_tokens // 0) | add), completion_tokens: (map(.usage.completion_tokens // 0) | add) }) runs/taotoken_iris_calls.jsonl常见错误码与处理方式401 invalid api keyKey 没设置、复制不完整、环境变量未生效。先回到 TaoToken 官网检查 API Keyshttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_debug 。404 not foundbase_url拼接错误。确认工具里配置的是https://taotoken.net/api不要重复/v1或/chat/completions。429 rate limit并发过高或短时间调用太多。把--concurrency降到 1增加重试退避。Iris 评测里搜索规划器可能连续发请求特别容易触发。timeout答案汇总器上下文太长或者检索片段 top_k 太大。先减少每次传入的片段数量再考虑增加超时。JSON 解析失败搜索规划器没有严格输出 JSON 数组。把 temperature 降到 0提示词里明确“只输出 JSON 数组”并在脚本里保留 fallback。日志里还有一类隐性错误HTTP 200 但内容是空字符串。这通常是模型输出被截断或参数不兼容。把max_tokens调大或检查模型 ID 是否正确。5. Claude Code settings.json 与 Codex config.toml 的正确写法如果你在 Iris 评测之外还要用 Claude Code 或 Codex 改评测脚本、整理日志建议把 TaoToken 的配置分开写。不要把 Anthropic 的环境变量套到 Codex 上也不要把 Codex 的 TOML 字段套到 Claude Code 上。Claude Code 使用settings.json和ANTHROPIC_*环境变量。可以在项目级.claude/settings.json或用户级配置里写{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: 你的可用模型 ID, ANTHROPIC_SMALL_FAST_MODEL: 你的可用小模型 ID } }这里的ANTHROPIC_BASE_URL仍然使用https://taotoken.net/api不带 UTM。ANTHROPIC_AUTH_TOKEN用YOUR_API_KEY占位。配置完成后在终端启动 Claude Code先做一个简单任务确认不会出现 401 或 404。更多细节按官方文档走https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_claude_code_doc 。Codex 使用config.toml不要写ANTHROPIC_*。示例model 你的可用模型 ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat然后在本地设置export TAOTOKEN_API_KEYYOUR_API_KEYCodex 启动后会读取TAOTOKEN_API_KEY。如果你的 Codex 版本要求不同的wire_api字段以本地codex --help和实际报错为准。关键原则是Claude Code 用ANTHROPIC_*Codex 用 TOML 里的model_providers两者不要混。6. CC Switch 三件套供应商、模型映射、项目环境变量如果你同时在多个项目里切换 Claude Code、Codex 和 Iris 评测脚本手工改配置很容易把 Key 和 Base URL 搞乱。CC Switch 的价值是把“供应商切换”和“项目配置”分开。建议按三件套管理第一件供应商清单。只记录供应商名称、Base URL、Key 环境变量名。例如name: TaoToken base_url: https://taotoken.net/api env_key: TAOTOKEN_API_KEY第二件模型映射。把不同工具需要的模型 ID 写进环境变量或映射表不要硬编码到每个脚本里。export TAOTOKEN_MODEL你的可用模型 ID export TAOTOKEN_SMALL_MODEL你的可用小模型 ID第三件项目级环境变量文件。Iris 评测、Claude Code、Codex 都从同一组变量读但各自只读自己需要的字段。可以创建.env.taotokenTAOTOKEN_API_KEYYOUR_API_KEY TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODEL你的可用模型 ID IRIS_EVAL_LOGruns/taotoken_iris_calls.jsonl本地加载set -a source .env.taotoken set a python iris_eval.py --dataset data/iris_eval.jsonl --out runs/taotoken_iris.jsonl --limit 20注意.env.taotoken要加入.gitignore。不要把YOUR_API_KEY替换成真实 Key 后提交。CC Switch 切换供应商时只改变量文件或供应商条目不要改评测代码。这样 Iris 评测的 base_url、Claude Code 的 settings.json、Codex 的 config.toml 互不污染。7. 本地评测 Iris 35B / 397B 的排障清单Iris 35B 和 397B 是不同规模的搜索 Agent 模型本地评测时资源差异很大。无论你只跑评测 harness还是把模型用 OpenAI 兼容服务托管在本地下面这份清单都适用。第一先区分“模型推理”和“工具调用”。如果你把 Iris 本地托管为 OpenAI 兼容服务那么search_planner的base_url可能指向本地服务而答案汇总或裁判模型可以指向 TaoToken。不要把所有调用都混在一个 base_url 里。建议在日志里记录provider字段。第二控制输入长度。答案汇总器最容易爆 token因为检索片段太多。先用 top_k3 到 5 跑通再逐步加。Iris 搜索 Agent 的评测重点是可复现不是一次把所有上下文塞满。第三搜索规划器强制 JSON。提示词要短而硬你是搜索规划器。只输出 JSON 数组例如 [查询1,查询2]。 不要输出 Markdown不要解释。第四并发不要太高。本地模型服务可能能承受并发但外部 API 调用会有速率限制。Iris 评测脚本里搜索规划器会连续请求建议先--concurrency 1稳定后再加。第五日志必须落盘。只在控制台看输出跑完就找不回 429 和空响应。JSONL 日志按行追加方便后续统计。第六区分缓存与评测。为了提高速度你可能会缓存搜索规划结果。但正式评测时缓存会让不同模型共享同一批查询影响对比。建议缓存只用于调试。第七不要直接连接生产数据库。Search Agent 的检索函数应该使用本地索引、测试快照或脱敏数据。所有 SQL 和检索命令由读者在本地执行。第八Key 不要出现在报错截图中。日志里只记录状态码和错误类型不记录 Authorization 头。preview字段也不要包含 Key。8. 把每轮搜索调用成本压到可观测再谈同量级成绩对照复现 AllSpark Iris 搜索 Agent 时最值得先做的不是刷榜单而是建立可观测的调用链。每轮搜索调用至少记录这些字段{ stage: search_planner, model: 你的模型 ID, provider: taotoken, status: ok, http_status: 200, elapsed_ms: 842, attempt: 1, usage: { prompt_tokens: 120, completion_tokens: 45, total_tokens: 165 } }有了这些字段你才能回答几个关键问题搜索规划器平均调用几次答案汇总器平均 prompt token 是多少429 出现在哪个 stage哪个模型 ID 在本地评测集上更稳定Iris 35B 与 397B 的同量级成绩对照应该建立在同一份评测集、同一套检索函数、同一套日志口径上而不是只用最终答案分数。优化顺序建议先修 401、404、429、timeout。再降search_planner的 completion token限制它只输出查询。然后压缩answer_synthesizer的输入片段去掉重复文本。最后才增加并发和批量评测。如果你要长期跑 Claude Code、Codex 和搜索 Agent 评测建议直接准备一个稳定的 Coding Plan再用 API Keys 管理不同项目的 Key。先用模型对话验证请求格式再把它接进评测脚本最后对照日志修正参数。文末按这个路径操作先试模型对话确认 Key 和 Base URL 可用再考虑 Coding Plan然后创建独立 API Key最后按 Claude Code 文档配置本地工具。模型对话https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_chatCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_coding_plan创建 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_api_keysClaude Code 文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_claude_code_docTaoToken 官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentiris_eval_footer把base_url固定为https://taotoken.net/apiKey 用YOUR_API_KEY回填先跑 20 条 Iris 评测再打开runs/taotoken_iris_calls.jsonl对照每轮搜索调用的 Token、延迟和状态码。能稳定复现日志再去比较 Iris 35B / 397B 在你本地评测集上的同量级表现结论才站得住。
返回列表