用 AI 把书变 Skills,运行时调用交给 TaoToken

用 AI 把书变 Skills,运行时调用交给 TaoToken 1. 从“整本书塞进上下文”改成“切片 检索 TaoToken 运行时”如果你正在用 Skills 把整本书变成随身技能最先遇到的通常不是模型不够聪明而是长文导入后上下文超限、多轮问答 Token 飙升、以及 401/429 这类运行时错误。我的处理方式是把切片、索引和检索放在本地把最终生成调用交给 TaoToken先到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbook_skills_intro 拿 Key再把 Base URL 设为 https://taotoken.net/api。很多教程演示“把整本书喂给 AI”时会直接上传 EPUB/PDF然后让模型一次性总结。短书可以稍微厚一点就会触发context_length_exceeded。更麻烦的是如果你每轮问答都把整本书重新塞进去Token 消耗会线性膨胀响应延迟也会越来越不可控。RAG 开发者的思路不是“把书塞进上下文”而是“把书切成可检索片段用检索结果拼出最小必要上下文”。这样 Skills 负责的是随身问答体验TaoToken 负责运行时模型调用长文导入和多轮问答的 Token 消耗就从“整本重发”变成“按需取片段”。本篇会给出可复现的切片参数、向量化命令、检索回答对照以及 Claude Code、Codex、CC Switch 的配置方式。你不需要改工具链只需要把 Base URL 换成 TaoToken 的 https://taotoken.net/apiKey 用YOUR_API_KEY占位然后按下面的步骤跑一遍。先准备环境python -m venv .venv source .venv/bin/activate pip install openai pypdf ebooklib beautifulsoup4 sentence-transformers numpy tqdm导出运行时变量Base URL 不要在后面乱加 UTMexport TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL你的模型ID这里TAOTOKEN_MODEL建议在模型对话页确认可用模型后再填不要硬编码一个不存在或当前套餐不可用的模型名。2. 书稿预处理EPUB/PDF 到 chunks.jsonl 的可复现切片参数切片不是越细越好。切得太碎检索出来的片段缺少上下文切得太大单次请求 Token 又会上升。我的默认参数是参数建议值说明chunk_size768 字符中文书稿按字符估算英文书按 token 估算可降到 512overlap128 字符避免跨段答案被切断min_chars160 字符过滤目录、页眉、空段max_chars1200 字符单个片段上限防止超长段直接进请求切分优先级段落 句子 标点优先保留自然段元数据book_id、chapter、chunk_id方便回答时标注引用下面这个脚本可以直接把 EPUB/PDF 转成 JSONL。注意命令和依赖都在本地执行不需要连接任何生产库。# chunk_book.py import argparse import json import re from pathlib import Path from ebooklib import epub, ITEM_DOCUMENT from bs4 import BeautifulSoup def split_text(text, chunk_size768, overlap128, min_chars160, max_chars1200): text re.sub(r\r\n?, \n, text) text re.sub(r[ \t], , text) paragraphs [p.strip() for p in text.split(\n) if p.strip()] chunks [] current for p in paragraphs: if len(current) len(p) 1 chunk_size: current f{current}\n{p} if current else p else: if current: chunks.append(current) if len(p) max_chars: current p else: sentences re.split(r(?[。.!?]), p) buf for s in sentences: if len(buf) len(s) chunk_size: buf s else: if buf: chunks.append(buf) buf s current buf if current: chunks.append(current) overlapped [] for i, c in enumerate(chunks): if i 0: overlapped.append(c) else: prev chunks[i - 1] overlapped.append((prev[-overlap:] c)[-max_chars:]) return [c for c in overlapped if len(c) min_chars] def read_epub(path): book epub.read_epub(str(path)) texts [] for item in book.get_items(): if item.get_type() ITEM_DOCUMENT: soup BeautifulSoup(item.get_content(), html.parser) t soup.get_text(\n) if t.strip(): texts.append(t) return \n.join(texts) def main(): ap argparse.ArgumentParser() ap.add_argument(--input, requiredTrue) ap.add_argument(--out, requiredTrue) ap.add_argument(--chunk-size, typeint, default768) ap.add_argument(--overlap, typeint, default128) args ap.parse_args() p Path(args.input) if p.suffix.lower() .epub: text read_epub(p) else: from pypdf import PdfReader reader PdfReader(str(p)) text \n.join(page.extract_text() or for page in reader.pages) chunks split_text(text, args.chunk_size, args.overlap) with open(args.out, w, encodingutf-8) as f: for i, c in enumerate(chunks): f.write(json.dumps({ chunk_id: fbook-{i:05d}, text: c }, ensure_asciiFalse) \n) print(fwrote {len(chunks)} chunks - {args.out}) if __name__ __main__: main()运行命令python chunk_book.py --input book.epub --out chunks.jsonl --chunk-size 768 --overlap 128如果输入是 PDFpython chunk_book.py --input book.pdf --out chunks.jsonl --chunk-size 768 --overlap 128chunks.jsonl每行类似{chunk_id: book-00001, text: 第一章 习惯的回路……} {chunk_id: book-00002, text: 提示、惯常行为、奖赏构成了一个闭环……}到这里整本书已经被改成可检索片段。Token 消耗方从“每次问答重发整本书”变成“每次只带 top-k 片段”这是把书变成随身技能的关键一步。3. 向量化命令本地 embedding 建索引不把长文重复烧在生成接口上向量化建议放在本地做。生成模型调用可以走 TaoToken但 embedding 不一定需要走同一个接口尤其是你有很多书稿要反复建索引时本地 embedding 更省成本也更可控。下面以BAAI/bge-m3为例也可以替换成BAAI/bge-small-zh-v1.5这类更轻量的中文模型。# build_embedding.py import argparse import json import numpy as np from sentence_transformers import SentenceTransformer def main(): ap argparse.ArgumentParser() ap.add_argument(--chunks, requiredTrue) ap.add_argument(--model, defaultBAAI/bge-m3) ap.add_argument(--out, requiredTrue) args ap.parse_args() texts [] with open(args.chunks, encodingutf-8) as f: for line in f: texts.append(json.loads(line)[text]) model SentenceTransformer(args.model) emb model.encode( texts, normalize_embeddingsTrue, show_progress_barTrue, batch_size32 ) np.save(args.out, emb.astype(float32)) print(emb.shape) if __name__ __main__: main()向量化命令python build_embedding.py \ --chunks chunks.jsonl \ --model BAAI/bge-m3 \ --out embeddings.npy如果你已经装了 GPU 环境SentenceTransformer会自动优先使用 GPU没有 GPU 也能跑只是建索引慢一点。建完后你得到两个文件chunks.jsonl # 片段文本与 chunk_id embeddings.npy # 归一化后的向量矩阵检索脚本如下# retrieve.py import argparse import json import numpy as np from sentence_transformers import SentenceTransformer def main(): ap argparse.ArgumentParser() ap.add_argument(--chunks, requiredTrue) ap.add_argument(--emb, requiredTrue) ap.add_argument(--question, requiredTrue) ap.add_argument(--top-k, typeint, default5) ap.add_argument(--model, defaultBAAI/bge-m3) args ap.parse_args() chunks [json.loads(l) for l in open(args.chunks, encodingutf-8)] emb np.load(args.emb) model SentenceTransformer(args.model) q model.encode([args.question], normalize_embeddingsTrue)[0] scores emb q idx np.argsort(-scores)[:args.top_k] for rank, i in enumerate(idx, 1): print(f[{rank}] score{scores[i]:.4f} chunk_id{chunks[i][chunk_id]}) print(chunks[i][text][:300].replace(\n, )) print( * 40) if __name__ __main__: main()检索命令python retrieve.py \ --chunks chunks.jsonl \ --emb embeddings.npy \ --question 这本书如何解释习惯养成与反馈回路 \ --top-k 5如果检索结果里出现大量目录页或版权页可以在切片阶段用min_chars过滤也可以在元数据里加chapter检索后按章节优先。不要用 Agent 或 MCP 直连生产库所有命令由读者在本地执行索引文件也留在本地。4. 检索回答对照Top-k、重排与 TaoToken Chat 调用先说检索回答对照。以下是我用同一本书稿做的一次实测记录问题为“这本书如何解释习惯养成与反馈回路”top_k5chunk_size768overlap128。排名分数chunk_id命中内容是否进入最终 Prompt10.812book-00042提示、惯常行为、奖赏构成习惯回路是20.778book-00043奖赏强化行为情绪标记影响重复是30.741book-00057反馈回路与延迟满足是40.702book-00061环境线索触发习惯是50.688book-00088案例章节重复描述否去重后截断最终回答要点习惯养成不是单靠意志力而是由提示触发行为再由奖赏强化回路如果奖赏延迟或模糊行为重复概率会下降。回答中标注了book-00042、book-00043、book-00057三个片段。这个对照说明top_k5已经足够覆盖核心概念但第 5 条是重复案例可以在重排阶段过滤。接下来把检索结果拼成 Prompt调用 TaoToken# ask_taotoken.py import os import json import numpy as np from openai import OpenAI from sentence_transformers import SentenceTransformer BASE_URL https://taotoken.net/api API_KEY os.environ.get(TAOTOKEN_API_KEY, YOUR_API_KEY) MODEL os.environ.get(TAOTOKEN_MODEL, 你的模型ID) EMB_MODEL os.environ.get(EMB_MODEL, BAAI/bge-m3) client OpenAI(base_urlBASE_URL, api_keyAPI_KEY) def load_chunks(path): return [json.loads(l) for l in open(path, encodingutf-8)] def retrieve(question, chunks, emb_path, top_k5): emb np.load(emb_path) model SentenceTransformer(EMB_MODEL) q model.encode([question], normalize_embeddingsTrue)[0] scores emb q idx np.argsort(-scores)[:top_k] return [(chunks[i], float(scores[i])) for i in idx] def build_prompt(question, hits): context \n\n.join( f[{c[chunk_id]}] {c[text]} for c, _ in hits ) return f你是书籍检索助手。只根据下面片段回答并标注引用的 chunk_id。 如果片段中没有答案直接说“资料中未找到”。 片段 {context} 问题{question} 回答 def main(): chunks load_chunks(chunks.jsonl) question 这本书如何解释习惯养成与反馈回路 hits retrieve(question, chunks, embeddings.npy, top_k5) prompt build_prompt(question, hits) resp client.chat.completions.create( modelMODEL, messages[{role: user, content: prompt}], temperature0.2, max_tokens800, ) print(resp.choices[0].message.content) if __name__ __main__: main()运行export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_MODEL你的模型ID python ask_taotoken.py多轮问答时不要把整本书重新拼进去。正确做法是维护一个messages列表每次只把“本轮检索到的片段”作为新的 system 或 user 内容追加历史轮次只保留最近 3 轮并且对历史内容做摘要或截断。这样 Token 消耗方就集中在 Skills 的长文导入与多轮问答上而不是每轮重发整本书。5. Claude Code 配置settings.json ANTHROPIC_* 指向 TaoTokenClaude Code 走的是ANTHROPIC_*环境变量。配置文件放在~/.claude/settings.jsonBase URL 填https://taotoken.net/apiKey 用YOUR_API_KEY。{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: 你的模型ID } }如果你更喜欢用 shell 环境变量也可以这样export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODEL你的模型ID写入文件的命令mkdir -p ~/.claude cat ~/.claude/settings.json JSON { env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: 你的模型ID } } JSON检查是否生效cat ~/.claude/settings.json如果 Claude Code 报 401优先检查ANTHROPIC_AUTH_TOKEN是否复制完整以及 Base URL 是否被误加/v1。如果你还没拿 Key先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_code_setup 创建或复制 Key。6. Codex 配置config.toml 单独写不要把 ANTHROPIC_* 混进去Codex 使用config.toml不要套用ANTHROPIC_*。它的供应商配置在~/.codex/config.toml典型写法如下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_KEY写入示例mkdir -p ~/.codex cat ~/.codex/config.toml TOML model 你的模型ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat TOML再次强调Claude Code 用ANTHROPIC_*Codex 用config.toml和TAOTOKEN_API_KEY。不要把ANTHROPIC_BASE_URL或ANTHROPIC_AUTH_TOKEN写进 Codex 配置否则会出现鉴权混乱或 401。7. CC Switch 三件套Base URL、API Key、Model 的统一切换如果你同时用 Claude Code、Codex 和普通 Chat 客户端CC Switch 的价值是快速切换供应商。对 TaoToken 来说三件套就是项目填写值Base URLhttps://taotoken.net/apiAPI KeyYOUR_API_KEYModel在模型对话页确认可用模型后填写在 CC Switch 里新增一个供应商名称可以叫TaoTokenBase URL 填https://taotoken.net/apiKey 填YOUR_API_KEY模型按你的套餐选择。对于 Claude Code profile底层仍然生成ANTHROPIC_*对于 Codex profile底层仍然写config.toml。三件套分开维护切换时只换 profile不要手改多个文件。8. 长文导入与多轮问答的 Token 账本切片、top_k、缓存与限流一本书变成 Skills 后Token 消耗主要发生在两处长文导入时的摘要/结构化以及多轮问答时的上下文拼接。下面是一张优化对照表策略影响推荐值降低chunk_size单片段 Token 下降但检索碎片化512-768提高overlap跨段答案更完整但索引变大96-128控制top_k直接决定 Prompt 长度4-6历史轮次截断降低多轮累积 Token最近 3 轮检索结果去重避免重复片段浪费 Token按chunk_id去重高频问题缓存减少重复检索与重复生成本地 SQLite 或 JSON 缓存并发退避降低 429 概率指数退避最大 3 次你可以用 SQLite 在本地记录问答缓存命令由读者本地执行CREATE TABLE IF NOT EXISTS qa_cache ( question_hash TEXT PRIMARY KEY, question TEXT NOT NULL, answer TEXT NOT NULL, created_at INTEGER NOT NULL );写入前先查question_hash命中就直接返回。这样高频问题不会反复消耗模型调用。TaoToken 控制台里可以查看 API Keys 和用量情况建议把不同项目拆成不同 Key方便排查是哪个 Skills 在消耗 Token。创建 Key 的入口在文末 CTA 里。9. 排障清单401、404、429、context_length_exceeded 怎么定位报错/现象常见原因处理方式401 invalid api keyKey 未填、复制不全、环境变量未导出检查YOUR_API_KEY重新导出TAOTOKEN_API_KEY404 model not found模型 ID 不可用或写错到模型对话页确认可用模型429 rate limit并发过高或短时间请求过多降低并发加指数退避context_length_exceededtop_k太大、历史太长、片段过长降低top_k缩小chunk_size截断历史连接超时Base URL 写错或网络层配置问题确认 Base URL 为https://taotoken.net/api返回内容不引用片段Prompt 约束不够在 Prompt 中要求标注chunk_id排障时先确认配置层Claude Code 看~/.claude/settings.jsonCodex 看~/.codex/config.toml普通脚本看环境变量。如果你在多个工具间切换建议回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttroubleshooting 核对 Key 和 Base URL 的填写方式避免把不同工具的变量混用。10. 把随身技能跑起来从模型对话到 Coding Plan 的落地路径把整本书变成随身技能核心不是“把书塞进多大上下文”而是“让检索结果精准进入运行时调用”。你现在已经有一条可复现链路切片参数 768/128本地向量化生成embeddings.npy检索 top-k 后拼 Prompt再通过https://taotoken.net/api调用 TaoToken。长文导入和多轮问答的 Token 消耗被控制在可预期范围内Skills 也更容易长期运行。接下来按这个路径落地先在模型对话页验证模型可用性和回答风格https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentbook_skills_chat根据长文导入和多轮问答的频率选择套餐https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentbook_skills_plan创建项目专用 Key把YOUR_API_KEY替换掉https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentbook_skills_keys按 Claude Code 文档配置settings.json让 Skills 运行时统一走 TaoTokenhttps://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentbook_skills_claude_code如果你还没拿 Key可以从 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentfinal_cta 进入控制台。配置时记住Base URL 是https://taotoken.net/apiClaude Code 用ANTHROPIC_*Codex 用config.tomlCC Switch 只维护 Base URL、API Key、Model 三件套。把切片、向量化、检索留在本地把运行时调用交给 TaoToken整本书就能以更低 Token 成本变成真正可用的随身技能。