
1. 从一个每天要跑几百次的脚本说起把一篇稿子里的机器腔删干净最费时间的从来不是写而是逐句检查有没有首先/其次/最后的三段式有没有在当今这个快速发展的时代这种开场有没有硬凑的排比和破折号有没有把一句话拆成三行来装节奏。42.2K Star 的那个开源 Skill 之所以火就是因为它把这套判断标准固化下来了——一份禁用词表、一份句式模板库、一份自检清单再加一段改完再读一遍的提示词。手动用一两次很爽但要按天处理几十上百篇稿子就必须把它塞进自动化脚本里。我最初的版本很粗糙把 Skill 里的提示词硬编码在 Python 文件里密钥写在.env里直接用官方地址。跑了两天问题集中暴露批量并发一上来就限流单条超时后整个批次卡住模型偶尔换了输出格式导致后面的解析全崩最要命的是换台机器就得重新配一遍。后来我把模型出口统一收拢到 TaoToken到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentskill_intro 取一个 KeyBase URL 固定成https://taotoken.net/api脚本里只读环境变量不落地任何明文凭证。这篇文章就把这套链路从头到尾拆开包括可复现的命令、调用日志怎么记、以及批量去 AI 味的结果怎么验。需要先说明一点下面所有命令和请求都由你在本地终端执行我不建议把这类批处理脚本挂到任何直连内部系统的 Agent 上去跑文本改写这种活儿只需要一个稳定的模型出口就够了。2. 先把 Key 和 Base URL 落到环境变量不管后面用 Python 脚本、Claude Code 还是 Codex第一步都一样让凭证只存在于环境变量里。脚本里出现sk-开头的字符串迟早会被提交到仓库里。到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentapikey_env 拿到 Key 之后本地这么写# ~/.taotoken_env.sh —— 只在本机 source不要提交到仓库 export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_MODELYOU_MODEL_ID # 模型 ID 以控制台模型列表为准 # 用法source ~/.taotoken_env.sh先把连通性跑通再写业务逻辑。用 curl 打一发最小请求确认路径拼接、鉴权和模型名三件事都没有问题# 最小连通性测试只发一条消息确认 200 与返回结构 curl -sS -w \nHTTP_STATUS:%{http_code}\n \ $TAOTOKEN_BASE_URL/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { \model\: \$TAOTOKEN_MODEL\, \messages\: [ {\role\: \system\, \content\: \你是一名中文编辑只做去机器腔改写。\}, {\role\: \user\, \content\: \在当今这个快速发展的时代我们首先要做的是明确目标其次要拆解路径最后要落地执行。\} ], \temperature\: 0.3 }跑通之后你大概率会看到两个现象这也是后面脚本必须处理的第一HTTP_STATUS是 200但返回体里的choices[0].message.content可能带着模型自己的解释比如以下是改写结果。去 AI 味的输出要求只给正文就得在提示词里把这条约束写死并且在脚本里做一次前缀剥离。第二temperature别开太高。去 AI 味本质上是收敛任务不是创作任务。0.2 到 0.4 之间比较稳再高就会出现为了不像 AI而故意写错别字、故意断句的过度表演。3. 把 Skill 的提示词固化成可重跑的批处理脚本Skill 的价值在于规则工程化的价值在于规则可以被版本化、被复用、被验证。我把它拆成三个文件rules/system.txt放角色与硬约束rules/banned.txt放禁用词表rules/checklist.txt放自检清单。脚本每次运行都重新读改规则不需要改代码。# rules/system.txt节选按你自己的 Skill 规则改写 你是中文文本编辑任务是把机器腔文本改写成自然的人类表达。 硬约束 1. 只输出改写后的正文不要任何前言、解释、总结、markdown 标题。 2. 保留原文事实、数字、专有名词不得新增或删除信息点。 3. 禁止使用以下句式首先/其次/最后、总的来说、综上所述、值得注意的是、在当今这个快速发展的时代。 4. 禁止三段式排比凑字数句子长度要有明显长短交错。 5. 不使用破折号制造停顿感不用不是……而是……的模板句。 6. 如果原文本身就是自然表达原样返回不要为了改而改。# batch_deai.py —— 批处理入口逐条调用 重试 落日志 import json import os import pathlib import random import time import urllib.error import urllib.request BASE_URL os.environ[TAOTOKEN_BASE_URL].rstrip(/) API_KEY os.environ[TAOTOKEN_API_KEY] MODEL os.environ[TAOTOKEN_MODEL] IN_DIR pathlib.Path(inbox) OUT_DIR pathlib.Path(outbox) LOG_PATH pathlib.Path(logs/deai.jsonl) OUT_DIR.mkdir(exist_okTrue) LOG_PATH.parent.mkdir(parentsTrue, exist_okTrue) RULES pathlib.Path(rules/system.txt).read_text(encodingutf-8) BANNED [w.strip() for w in pathlib.Path(rules/banned.txt).read_text( encodingutf-8).splitlines() if w.strip()] def call_model(text: str, timeout: int 90) - dict: payload { model: MODEL, temperature: 0.3, messages: [ {role: system, content: RULES}, {role: user, content: text}, ], } req urllib.request.Request( f{BASE_URL}/v1/chat/completions, datajson.dumps(payload, ensure_asciiFalse).encode(utf-8), headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json, }, methodPOST, ) with urllib.request.urlopen(req, timeouttimeout) as resp: return json.loads(resp.read().decode(utf-8)) def strip_preamble(s: str) - str: 模型偶尔会带一句前言这里做最小化剥离 for prefix in (以下是改写结果, 改写后, 输出): if s.startswith(prefix): return s[len(prefix):].lstrip() return s.strip() def deai_one(path: pathlib.Path) - dict: raw path.read_text(encodingutf-8).strip() started time.time() last_err None for attempt in range(4): try: body call_model(raw) content strip_preamble(body[choices][0][message][content]) usage body.get(usage, {}) return { file: path.name, status: ok, attempt: attempt 1, latency_ms: int((time.time() - started) * 1000), tokens: usage, hits: [w for w in BANNED if w in content], out_len: len(content), content: content, } except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError) as e: last_err repr(e) # 指数退避 抖动避免整批同时重试把并发顶穿 time.sleep(min(2 ** attempt, 8) random.random()) return {file: path.name, status: failed, error: last_err} def main() - None: files sorted(p for p in IN_DIR.iterdir() if p.suffix in (.txt, .md)) with LOG_PATH.open(a, encodingutf-8) as logf: for path in files: result deai_one(path) if result[status] ok: (OUT_DIR / path.name).write_text(result[content], encodingutf-8) logf.write(json.dumps(result, ensure_asciiFalse) \n) logf.flush() # 边跑边落盘中断也不丢日志 print(result[file], result[status], result.get(latency_ms, -)) if __name__ __main__: main()几个刻意的设计跑过批量任务的人应该会有共鸣日志用 JSONL 追加写并flush()中途 CtrlC 也不会丢已完成的记录重跑时可以按文件名去重重试带抖动的指数退避比固定间隔重试的存活率高很多hits字段直接记录改写结果里还残留了几个禁用词这就是后面验收的依据。4. Claude Code 侧配置settings.json 与 ANTHROPIC_*如果你习惯在 Claude Code 里先手工调规则、确认提示词稳定了再丢给批处理脚本那就把模型出口也指到同一条链路上。改~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: YOU_MODEL_ID } }这里的变量名是 Claude Code 认的ANTHROPIC_*三件套不要把它抄到 Codex 的配置里去——两者读的键完全不同混用只会得到 401 或者模型不存在。配置来源和可用模型可以在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentclient_config 对照着看两边保持一致脚本和交互式工具才能共用同一个 Key。配完之后建议在项目根目录放一个CLAUDE.md把去 AI 味的规则写进去这样交互式改写和脚本批处理用的是同一套约束# 文本改写约定 - 只输出正文不输出前言、总结、markdown 标题 - 禁用首先/其次/最后、综上所述、值得注意的是、在当今这个快速发展的时代 - 禁止三段式排比句长必须长短交错 - 事实、数字、专有名词一律保留5. Codex 与 CC Switchconfig.toml 和三件套切换Codex 走的是~/.codex/config.toml键名和 Claude Code 完全不一样# ~/.codex/config.toml model YOU_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api/v1 env_key TAOTOKEN_API_KEY wire_api chat注意env_key写的是环境变量的名字不是 Key 本身。先source ~/.taotoken_env.sh再启动 Codex它就会自己去读TAOTOKEN_API_KEY。如果你在这份文件里塞了ANTHROPIC_AUTH_TOKENCodex 不会报配置错误它会安静地用一个空的凭证去请求然后给你一个看不懂的鉴权失败——这类问题排查起来最浪费时间。同机同时装着 Claude Code 和 Codex 的话用 CC Switch 管理切换最省事。它的核心就是三件套Base URL、API Key、默认模型。我一般建两个 profileprofile: taotoken-claude base_url : https://taotoken.net/api api_key : YOUR_API_KEY model : YOU_MODEL_ID profile: taotoken-codex base_url : https://taotoken.net/api/v1 api_key : YOUR_API_KEY model : YOU_MODEL_ID同一个 Key、同一个出口两个 profile 的差别只在 Base URL 的路径尾部和写入的目标配置文件。这样切工具不会把配置文件互相覆盖也不会出现A 工具能用、B 工具 401的鬼故事。6. 用日志和指标证明去 AI 味真的生效了改写类任务最容易自欺欺人的地方在于读起来顺了就以为成了。批处理跑完几百篇你需要几个能算出来的数字。下面这个小脚本读logs/deai.jsonl输出一份验收报告# verify.py —— 从日志里算出去 AI 味效果指标 import json import pathlib import statistics rows [json.loads(l) for l in pathlib.Path(logs/deai.jsonl).read_text( encodingutf-8).splitlines() if l.strip()] ok [r for r in rows if r.get(status) ok] print(f总数 {len(rows)}成功 {len(ok)}失败 {len(rows) - len(ok)}) if ok: lat [r[latency_ms] for r in ok] print(f延迟 P50{statistics.median(lat):.0f}ms fP95{sorted(lat)[int(len(lat) * 0.95)]:.0f}ms) dirty [r for r in ok if r[hits]] print(f改写后仍含禁用词的文件{len(dirty)}) for r in dirty[:10]: print( , r[file], r[hits]) ratio [len(r[content]) / max(r[out_len], 1) for r in ok] print(f输出长度中位数{statistics.median(r[out_len] for r in ok):.0f} 字符)验收标准我一般定三条失败率低于 2%改写后仍命中禁用词的文件数低于 5%人工抽检 20 篇事实和数字零丢失。三条里任何一条不达标回去改rules/system.txt而不是去调temperature——规则问题用参数是治不好的。还有一件事值得单独做把改写前后的文本做一次 diff 抽样人工只看 diff 片段。全篇通读效率太低只看改动位置几分钟就能判断模型是在改机器腔还是在重新创作。7. 排障清单这几类报错基本都遇到过把 Skill 接进脚本的过程里报错来来回回就那么几类按顺序排查能省很多时间。遇到拿不准的鉴权或路径问题回 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttroubleshoot 核对 Key 状态和 Base URL 写法。401 / 403九成是环境变量没source或者把 Key 写进了config.toml的值位置而不是env_key。先在同一个 shell 里echo ${TAOTOKEN_API_KEY:0:6}确认前缀存在再启动工具。404路径拼接错了。Base URL 是https://taotoken.net/apiOpenAI 风格接口的完整路径是/v1/chat/completions如果你在配置里已经写了/v1代码里再拼一次就会变成/v1/v1/...。统一原则Base URL 里不含/v1时由代码拼含/v1时由客户端拼二者只留一个。429并发开太高或者单批文件太大。把并发降到 2 到 4单条文本超过 4000 字就先切段再送。重试一定要带抖动固定间隔的重试会在同一秒形成新的尖峰。超时但服务端其实处理完了改写任务重试是幂等的同样的输入同样的规则输出基本一致所以超时直接重试没有副作用。但要在日志里把attempt记下来重试率突然上升通常是网络或并发问题不是模型问题。返回内容被截断输出长度上限没设够或者输入太长导致模型把预算花在了前半段。批处理场景建议按段落切分每段独立送最后再拼回去这样单条失败也不会毁掉整篇。中文乱码读写全部显式写encodingutf-8请求体用ensure_asciiFalse编码。Windows 上默认编码不是 UTF-8这一条踩过的人最多。同一批文本两次结果差异很大检查temperature和提示词是否完全一致。批处理脚本里凡是从外部读进来的规则文件最好在日志里记一个内容哈希出问题时能确认规则真的没变。8. 把链路收拢成一条命令工程化的终点是换台机器三条命令就能复现整套流程。source ~/.taotoken_env.sh # 1. 注入 Base URL / Key / Model python batch_deai.py # 2. 批处理改写日志落 logs/deai.jsonl python verify.py # 3. 出验收报告看失败率与禁用词残留规则改了怎么办改rules/下的文本文件重跑第二步。想换模型对比效果改TAOTOKEN_MODEL重跑对比两份日志的延迟和禁用词命中率。整个过程不需要动代码也不需要重新配置任何客户端。回到最开始那个问题42.2K Star 的开源 Skill 提供的是怎么改的规则而批处理脚本要解决的是稳定地改一万次。两者的连接点其实很朴素——一个固定的 Base URL 和一个只存在于环境变量里的 Key加上一份能追溯的调用日志。把这三样东西固定下来去 AI 味这件事才从手工活变成了可复现的流水线。接下来可以按这个顺序往下走想先在网页里手工试几轮规则看哪一种改写风格最贴合你的稿子从模型对话入手https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchat_skill如果你不只是跑批处理还要在 Claude Code、Codex 里高频改写和调规则可以看Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_plan_skill还没拿到 Key或者想给脚本单独建一个便于轮换的 Key直接去创建 Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentcreate_key_skillClaude Code 的settings.json与ANTHROPIC_*变量写法以官方文档为准照着抄不会踩键名混用的坑https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_code_doc把 Key 拿到手之后先跑第 2 节那条 curl确认 200再去写你的批处理循环。顺序反了的话你会在 401 和 404 上浪费一整个下午。