尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从整本书到按需调用:book-to-skill技术资料高效喂给AI编程助手

从整本书到按需调用:book-to-skill技术资料高效喂给AI编程助手 之前接手一个技术资料解析项目时团队遇到一个很现实的问题要把一本 400 多页的技术书“喂”给 AI 编程助手让它能像读过全书一样回答开发问题。最初方案很简单——把整本书拆成 Markdown 直接塞进上下文窗口结果刚加载三章上下文就爆了。后来加了自动总结、分段压缩仍然经常出现“已进行多次自动总结但上下文大小仍超出限制”的报错。反复折腾之后我把思路从“怎么塞更多”换成了“怎么让模型真正需要什么就加载什么”最终落地了一套基于 skill 化拆解的资料解析方案。本文就把这套方案完整展开聊聊 book-to-skill 的核心思路、最小实现、上下文占用对比以及落地 AI 编程助手时的高频坑。1. 背景与核心概念1.1 上下文窗口AI 编程助手的隐形瓶颈上下文窗口Context Window指模型在一次请求中能同时处理的 token 数量包括输入指令、参考资料、历史对话和模型输出。无论是 Claude Code 这类 AI 编程工具还是本地部署的开源模型上下文窗口都是整个链路里最稀缺的资源。很多人对上下文窗口的理解存在偏差以为窗口越大越好。实际上更大的窗口只代表“能装的东西更多”并不代表“装进去之后效果一定更好”。当输入内容远超模型的有效处理范围时会出现两个典型问题第一中间部分信息失焦。模型在长上下文中更容易忽略中间区域的细节而对开头和结尾的内容保持较高注意力。第二token 成本非线性上升。每个请求都会把全部上下文重新发送给后端上下文越长单次请求的延迟和费用越高。哪怕使用的是本地模型上下文过大也会导致显存占用飙升、推理速度明显下降。在实际开发中最让人头疼的不是“模型能力不够”而是“代码库和参考资料太长”。尤其是需要参考开源书籍、内部 Wiki、老旧项目文档时几万字只是起点几十万字非常常见。把这样的内容整体灌入上下文很快就到达上限剩下的对话几乎无法继续。1.2 book-to-skill把“整本书上下文”变成“按需技能”book-to-skill 的名字很直白把一本书book转化为可调用的技能skill。核心思想不再是把整本书作为上下文加载而是把书中的知识按照“能解决什么问题、需要哪些输入、输出什么结果”拆成一个一个独立的技能包。从开发者视角看一个技能包可以理解成一个结构化的“小工具定义”。它包含技能名称、适用场景、输入参数、输出格式、关联章节片段和一段精简的提示词模板。只有当用户提出的问题与某个技能匹配时系统才把对应的技能描述、关键代码片段和知识点注入当前上下文。这种做法的本质是“上下文工程”中的按需加载策略。它借鉴了函数式编程和 RPC 的思路不把整份数据暴露给上层的调用者而是封装成函数按需调用只传递必要参数。对于大模型而言技能包就是这个函数上下文窗口就是调用栈。调用栈里永远只保留当前正在执行的函数而不是把整个程序源码都压进去。1.3 与 RAG、长上下文模型的区别为了更清楚 book-to-skill 的定位我们需要把它和两种常见方案放在一起对比。RAGRetrieval-Augmented Generation检索增强生成是目前最主流的长文档问答方案。它的流程是先对原文切块、向量化检索到与问题最相关的片段后再交给模型生成。RAG 的优点是通用性强适合问答、检索、知识库场景缺点是需要额外搭建向量库、处理召回质量并且回答的连贯性受到“检索片段是否准确”的直接影响。长上下文模型则走了另一条路比如部分模型已经支持百万 token 级上下文窗口。这类模型的优势是“省心”不用做繁琐的文档切分和检索只要把资料全量放进去即可。但实际使用时会发现长上下文方案并非没有代价请求成本高、响应慢、中间信息失焦而且不支持长上下文的模型仍然占据了大量生产环境。book-to-skill 处在两者之间。它比 RAG 更结构化不只是“检索到一段文字再回答”而是“定位到一个能力单元再执行”它又比长上下文方案更省资源适合那些“书籍内容并不会被均匀使用”的场景。简单说一本书 80% 的内容可能只应对 20% 的问题skill 化的目标就是让那 80% 不会一直占着上下文。2. 为什么能省 51 倍上下文2.1 全量加载的 token 消耗模型要理解“省 51 倍”首先需要建立一个可量化比较的消耗模型。我们以一本中等厚度的技术书为例。假设全书中文约 20 万字按照中文字符与 token 的常见换算比例20 万字大约对应 20 万到 28 万 token。一些出版商提供的电子版还包含大量目录、索引、页码、注释实际 token 数往往只多不少。如果把整本书直接塞进上下文中无论是一次性请求还是作为一段常驻历史模型每处理一次对话都要重新计算这些 token。也就是说上下文占用至少是 20 万 token甚至更高。如果后续还加入代码库、错误日志、多轮对话上下文容量很快告急表现就是经常看到的“上下文过大”“多次自动总结后仍超限”。2.2 按需技能加载的 token 消耗模型再来看 book-to-skill 的消耗模型。这里的关键不是“不加载”而是“延迟加载”和“局部加载”。首次启动时系统只需要加载一份“技能注册表”。注册表里包含所有技能的名称、功能描述、输入参数、触发条件这个清单通常只有 2000 到 4000 token。技能注册表不包含书籍正文只包含“这本书能做什么、什么情况下调用什么技能”的元信息。当用户提出一个具体问题时系统先根据问题匹配技能比如“这段代码有哪些坏味道”会匹配到“重构技能包”然后只加载该技能包对应的提示词模板和精选章节片段。单个技能包通常控制在 800 到 3000 token 以内。这样单轮请求的总上下文 技能注册表 命中的 1 到 2 个技能包 用户当前问题。按这个模型估算全量加载约 20 万 token按需加载约 3000 到 5000 token中间相差约 51 倍。这个数字并不是精确值而是一个数量级参考。不同书籍、不同技能粒度、不同 token 换算方式省下的倍率会有浮动但方向是稳定的只要书籍内容不是每一页都必须被同时使用skill 化就能带来几十倍的上下文压缩空间。2.3 压缩不是魔法代价在哪里这里还是要泼一点冷水。book-to-skill 并不是无损压缩它的“省”建立在两个前提之上。前提一是知识可以被清晰切分。像编程语言手册、设计模式、运维指南这类结构化书籍天然适合拆分。但如果是强调连续性、有大量上下文铺垫的叙事类或理论类书籍拆分后可能丢失前后逻辑此时需要额外设计“技能之间的引用关系”。前提二是检索命中率足够高。如果技能注册表写得过于模糊或者技能划分得太粗系统很可能匹配不到正确技能最终仍然退化成整书加载或频繁换技能反而增加开销。因此做好 skill 化之后的验证与调优和做好拆分一样重要。3. 环境准备与总体流程3.1 需要的工具与依赖book-to-skill 本身没有强绑定某一种编程语言或框架。为了便于演示本文以 Python 为示例语言按常见开源项目结构整理最小实现。你完全可以用 Java、TypeScript 或 Go 重写核心思路完全一致。建议准备以下环境作用推荐工具运行环境Python 3.10文本解析markdown-it-py 或 python-docx向量匹配可选chromadb / faiss / 本地 embedding 模型提示词组织Jinja2 或 Python f-string调用模型 APIOpenAI SDK / Anthropic SDK / 本地 Ollama版本控制Git GitHub 仓库版本需要根据实际项目调整本文不绑定某个具体版本号重点演示整体设计思路。AI 编程助手方面如果你使用 Claude Code 或其他支持自定义技能的编程助手可以按助手的技能目录规范做适配如果你使用的是自研 Web 应用则可以走 function calling 的路子。3.2 整体流程设计book-to-skill 的完整链路可以分为五个阶段。第一阶段是解析。读取电子书文件清洗目录、页码、脚注等噪声得到干净的正文内容。第二阶段是切分。按章节或主题语义把正文切成若干知识块。第三阶段是提炼针对每个知识块生成技能定义包含技能名称、输入输出、触发条件、示例片段。第四阶段是注册把所有技能定义汇总成技能注册表。第五阶段是调用在对话请求中根据用户问题匹配技能并拼接该技能的专用上下文。下面通过一个实际案例把这五个阶段完整走一遍。4. 完整实战把资料拆成按需技能4.1 设计技能定义结构技能定义是整个方案的基石。一个合理的技能定义应该回答四个问题这个技能解决什么问题、需要什么输入、输出什么格式、哪些情况下应该被触发。下面是一个技能定义的参考格式你可以存成 JSON 文件{ skill_id: refactoring_bad_smell, name: 代码坏味道识别与重构建议, source_book: refactoring-improving-design-existing-code, chapters: [第2章, 第6章], input: { code: 需要分析的代码片段, language: 编程语言 }, output: { format: json, fields: [bad_smell_type, reason, suggested_refactoring, example] }, prompt_template: 请参考《重构》中关于坏味道的定义分析下面的代码片段指出存在的坏味道并给出重构建议\n{code}, trigger_conditions: [代码重复, 函数过长, 类过大, switch 过多, 令人迷惑的命名], estimated_tokens: 1200, references: [ch2_bad_smells.md, ch6_composing_methods.md] }各字段含义如下skill_id 是全局唯一标识用于调用时定位。name 是给人看的名称。source_book 记录技能来源方便追溯。chapters 标记知识来自原书哪些章节。input 声明调用时需要用户提供哪些参数。output 声明模型应该返回怎样的结果。prompt_template 是真正注入上下文的提示词模板。trigger_conditions 用于快速判断当前问题是否命中该技能。estimated_tokens 用于估算该技能被加载后消耗的上下文量。references 指向精简后的章节资料文件。4.2 书籍解析与章节切分在写拆分脚本之前先解决一个问题我们拿到的书籍文件可能是 PDF、EPUB、DOCX 或纯 Markdown不同格式的解析方式差异很大。这里不展开所有格式的解析代码以最干净的 Markdown 为例讲解核心思路。假设你已经把书籍转换为 Markdown目录结构如下book/ book.md chapters/ ch1.md ch2.md ...此时切分脚本的逻辑很简单按章节标题拆分去掉过长的目录和索引区域然后逐章生成初步的知识块。# split_chapters.py import re from pathlib import Path def split_markdown_by_heading(book_path: Path) - list[dict]: 按 Markdown 一级/二级标题拆分书籍内容. text book_path.read_text(encodingutf-8) # 以 ## 或 # 开头的行为章节边界 heading_pattern re.compile(r^(#{1,2})\s(.*), re.MULTILINE) matches list(heading_pattern.finditer(text)) chapters [] for idx, match in enumerate(matches): start match.start() end matches[idx 1].start() if idx 1 len(matches) else len(text) title match.group(2).strip() content text[start:end].strip() if len(content) 50: continue # 跳过空标题或过短片段 chapters.append({ title: title, content: content, chunk_index: len(chapters) }) return chapters if __name__ __main__: chapters split_markdown_by_heading(Path(book/book.md)) print(f切分得到的章节数量: {len(chapters)}) for i, ch in enumerate(chapters[:5]): print(f{i}: {ch[title]} - {len(ch[content])} 字符)切分之后每章内容仍然可能很长。对于技能包而言我们通常不保留整章而是保留该章中最核心的三四段以及所有代码示例。这一步需要结合具体书籍内容提取不能完全自动化。4.3 从章节生成技能定义有了章节数据之后下一步是从中提炼技能定义。这里存在两种做法一种是用人力和 prompt 辅助逐个确认另一种是调用大模型对每个知识块做自动摘要和技能抽取。对于几十章的中型书籍推荐先用模型生成初稿再人工校对。下面的脚本演示了批量生成技能定义的过程。为了让示例可控这里不接入具体模型 SDK而是用模拟函数说明位置。# build_skills.py import json from pathlib import Path def generate_skill_for_chapter(chapter: dict) - dict: 真实项目中这里会调用 LLM API 把 chapter[content] 交给模型要求模型输出 skill_id、name、 prompt_template、trigger_conditions 等字段。 这里使用模拟数据方便理解结构。 title chapter[title] return { skill_id: fskill_{chapter[chunk_index]:03d}, name: title, source_book: example-book, chapters: [title], input: {query: 用户描述自己遇到的问题}, output: {format: text}, prompt_template: f参考《{title}》的内容回答下面的问题{{query}}, trigger_conditions: [title.lower()], estimated_tokens: 800, references: [] } def build_skill_registry(chapters: list[dict], output_path: Path): registry {version: 1, skills: []} for ch in chapters: skill generate_skill_for_chapter(ch) registry[skills].append(skill) output_path.write_text( json.dumps(registry, ensure_asciiFalse, indent2), encodingutf-8 ) if __name__ __main__: # 这里假设 chapters 来自上一节拆分结果 # chapters split_markdown_by_heading(Path(book/book.md)) # build_skill_registry(chapters, Path(skills/registry.json)) print(请先运行 split_chapters.py 生成 chapters 数据)这段代码的核心价值不在业务逻辑而在于展示产出物结构一个 registry.json里面包含所有技能的元信息。之后每次对话系统只加载这个注册表而不是加载整本书。4.4 技能片段压缩技能定义里的 references 指向的是压缩后的章节资料。为什么要压缩而不是用原章节这里涉及“上下文工程”里一个很重要的经验模型并不需要读原文的每一个字它需要的是原文中最关键的定义、规则和例子。一段 5000 字的章节经过压缩后可能只剩下 800 字信息损失有限但 token 消耗大幅下降。下面是一个简单的压缩思路保留章节中的标题、列表、代码块和结论性语句剔除叙述性铺垫# compress_reference.py import re def compress_chapter(content: str, max_len: int 1500) - str: 简单保留标题、列表、代码块和关键句用于生成技能引用片段. blocks [] lines content.splitlines() for line in lines: if line.strip().startswith((#, -, 1., 2., )): blocks.append(line) continue # 保留包含关键提示词的句子 if any(kw in line for kw in [定义, 原则, 注意, 推荐, 禁止, 示例]): blocks.append(line) if len(\n.join(blocks)) max_len: break return \n.join(blocks)需要提醒的是这种“关键词过滤式”压缩只适合作为快速原型。正式项目中建议使用大模型对每个章节做摘要摘要时明确要求“保留所有可以直接使用的代码示例、命令、配置项和结论删除解释性文字”。这样压缩出来的参考文献才真正可用。5. 在 AI 编程助手中按需调用5.1 技能注册表加载与匹配有了技能注册表和技能引用文件之后就可以把 book-to-skill 接入实际的对话链路。在自研应用中最直接的方式是仿照 function calling 的调用模型。第一步把技能注册表交给模型作为工具说明第二步由模型判断当前问题应该调用哪个技能第三步根据技能 ID 加载对应的 prompt_template 和 references把它们拼接到新的用户消息之前。下面给出一个极简的 Python 调用示例# invoke_skill.py import json from pathlib import Path def load_registry(path: Path) - dict: with open(path, encodingutf-8) as f: return json.load(f) def select_skill(query: str, registry: dict): 真实项目中可以用 embedding 相似度或 LLM 工具调用来选择技能. skills registry[skills] for skill in skills: for cond in skill[trigger_conditions]: if cond in query: return skill # 未命中时返回空调用方可以走默认 RAG 流程 return None def build_skill_context(skill: dict, user_code: str) - str: references [] for ref in skill.get(references, []): ref_path Path(skills/references) / ref if ref_path.exists(): references.append(ref_path.read_text(encodingutf-8)) ref_text \n\n.join(references) prompt skill[prompt_template].format(codeuser_code) return f【技能参考】\n{ref_text}\n\n【技能提示词】\n{prompt} if __name__ __main__: registry load_registry(Path(skills/registry.json)) user_query 这段代码的 switch 太多怎么重构 skill select_skill(user_query, registry) if skill: context build_skill_context(skill, user_codeswitch (type) { ... }) print(命中技能:, skill[skill_id]) print(加载的上下文 token 估算:, skill[estimated_tokens]) else: print(未命中技能走默认检索流程)在 AI 编程助手中实现方式会略有差异。Claude Code 等工具已经支持自定义 skill 目录通常的做法是把技能包按目录组织每个目录里有一个 SKILL.md 作为技能描述并附带引用文件。这样助手会在需要时自动加载对应技能。5.2 技能包目录组织假设你已经把一本书拆成了 20 个技能包建议用下面的目录结构组织skills/ registry.json references/ ch2_bad_smells.md ch6_composing_methods.md refactoring_bad_smell/ SKILL.md references/ ch2_bad_smells.md sql_optimization/ SKILL.md references/ index_design.md在这种结构里registry.json 是给自研应用使用的索引各个子目录的 SKILL.md 是给 AI 编程助手使用的技能说明。每次对话开始时助手只会读取 SKILL.md 的描述部分真正命中技能后才读取 references 里的详细文件。这样既兼容了工具化调用也兼容了 AI 编程助手的原生技能机制。5.3 上下文占用对比实测方法如何验证 book-to-skill 真的省了上下文建议在项目里加一个简单的计数器记录每次请求中上下文构造部分的 token 数量。许多 SDK 在返回结果时会附带 usage 信息其中 input_tokens 就是实际消耗的输入 token 数。测试方法如下准备 10 个覆盖不同章节的问题分别走“全量书籍上下文”和“book-to-skill 按需技能”两条链路记录 input_tokens 和回答效果。统计结果后就可以得到类似“节省 51 倍”这样的实际数据。这里要注意的是节省倍数与问题范围强相关。如果 10 个问题覆盖了全部 20 个技能且每个技能都被多次命中那么总节省会变小如果问题集中在少数章节节省倍数会非常可观。6. 常见问题与排查思路在实践 book-to-skill 的过程中最容易遇到下面几类问题。问题现象常见原因解决思路上下文仍然过大报错“已进行多次自动总结但上下文大小仍超出限制”技能注册表过宽或历史对话未清理可能还有 MCP 服务器不断向上下文注入额外内容缩短技能描述限制参考文献长度检查 MCP 服务器或 skill 配置文件中的自动注入内容AI 编程助手新开会话丢失上下文记忆技能状态保存在 Session 内新会话没有重新加载注册表和技能引用将技能注册表作为项目初始化文件每次新会话自动加载匹配不到正确技能技能划分过粗或 trigger_conditions 过简细化技能拆分重写技能触发条件增加同义词和典型场景描述单次技能内容仍然太长参考文献未充分压缩对参考文献做二次压缩只保留代码示例与结论删除解释性段落回答效果不如全文加载技能引用丢失了决定回答质量的关键上下文被压缩掉的段落是否确实是“无关段落”需要人工抽查质量不清楚本地模型到底支持多长上下文没有查看模型上下文长度的工具按模型文档确认 max context length或调用模型接口返回的权限信息再做估算下面挑几个典型问题展开说说。6.1 “多次自动总结仍超限”的排查顺序很多 AI 编程助手有自动压缩上下文的能力但自动总结并不总能解决问题。如果你遇到“已进行多次自动总结但上下文大小仍超出限制”建议按以下顺序排查。先检查是不是 MCP 服务器或外部工具不停向会话注入内容。一些 MCP 服务器会把数据库 schema、大文件内容、日志片段全部塞进上下文即使这些内容与当前技能无关。处理方式是在 skill 配置中关闭无关的 MCP 工具或者调整工具的注入白名单。再检查历史对话累积。多轮对话中的历史消息也会占用上下文而自动总结会压缩历史但无法压缩“每轮都重新注入的固定长文本”。如果技能包本身的 references 过长每一轮都会被重复加载。此时应该优化技能包把 references 缩短到真正必要的范围。最后才考虑调大模型的上下文上限。如果模型支持 1M token 级长上下文换用长上下文模型确实能缓解但要注意成本和响应速度不要把它作为第一方案。6.2 新开会话丢失上下文记忆有读者反馈AI 编程助手新开会话后之前做好的 skill 拆分就不生效了助手又变回“不知道这本书在讲什么”。这个问题通常不是 book-to-skill 方案本身造成的而是因为技能注册表没有在项目初始化时自动加载。解决方法是把技能注册表放入项目根的固定目录例如 .cloudcode/skills/ 或 .agent/skills/并在助手配置中声明“每次会话启动时读取该目录下的技能说明”。如果使用自研应用则需要在对话初始化阶段主动构造包含技能注册表的 system message。6.3 如何查看本地大模型上下文长度不少人在本地跑开源模型却不知道自己当前模型支持多少上下文。查看方式取决于模型服务使用 Ollama 时运行 ollama show 模型名 可以看到 context_length 参数使用 vLLM 部署时可以通过 /v1/models 接口获取模型的 max_model_len使用 Hugging Face Transformers 时读取 model.config.max_position_embeddings。拿到上下文长度后建议把实际使用的上下文控制在模型上限的 70% 左右。剩余空间留给模型输出和突发数据。7. 最佳实践与工程建议7.1 什么内容适合做 skill 化book-to-skill 不是万能的它对适用场景有明确偏好。最适合 skill 化的内容有三个特征第一结构化程度高。书籍章节之间有清晰边界每章解决一类问题例如《重构》《设计模式》《SQL 优化指南》。第二局部独立性高。读者不需要读完前 300 页才能理解第 301 页。第三使用频率集中。绝大多数实际查询只落在十几个关键技能上而不是平均分布在所有章节。反过来如果是需要在大量章节之间做横向比较的内容例如“比较整本书中所有关于缓存的不同观点”skill 化拆分后可能需要同时加载多个技能包此时上下文优势会被削弱。建议这类场景仍然走 RAG 检索或向量库方案。7.2 技能粒度怎么控制技能粒度是 book-to-skill 最核心的设计决策。粒度太粗每个技能包本身的 token 数过大加载两次就会重新触发上下文溢出粒度太细技能注册表会变得非常长且匹配时容易混淆相似技能。一个可参考的经验是单个技能包加载后的总 token 数控制在 1500 到 3000 之间。如果按这个标准一本 20 万字的书籍通常可以拆成 30 到 60 个技能包。技能之间的边界尽量与书籍原有的章节边界保持一致这样能减少知识遗漏也便于追溯来源。7.3 上下文工程不只等于压缩最后聊聊上下文工程的整体思路。book-to-skill 只是上下文工程中的一个环节完整的上下文工程至少包括四个方面一是上下文筛选即只加载与当前任务相关的数据book-to-skill 和 RAG 都属于这一类。二是上下文压缩即用摘要、剥离噪声、精简历史的方式降低 token 数。三是上下文排序即把最关键的指令放在系统提示词或用户消息的开头避免重要信息被埋没在长文本中。四是上下文持久化即把重要知识落在独立的项目文件中而不是依赖多轮对话的临时记忆。实际项目中这四个方面往往要配合使用。book-to-skill 承担“知识库筛选和压缩”的角色自动总结承担“长对话历史压缩”的角色而初始化脚本承担“新会话重新加载技能”的角色。不要指望单一手段解决所有问题。7.4 安全、成本与可维护性提示在真实项目中使用 skill 化方案时还需要注意三个工程细节。第一对技能包做内容审计。从书籍自动拆出来的技能可能包含过时或有风险的建议尤其在安全、权限、数据库操作等场景一定要在技能定义中增加风险提示比如“涉及删除、更新操作前必须备份并确认环境”。第二对技能引用文件做版本管理。书籍可能会更新版本技能包也应该跟着更新建议在 registry.json 中记录 source_book_version。第三评估 API 调用成本。虽然技能加载减少了 token 数但提取技能定义、做摘要压缩本身也需要调用模型接口需要把这部分成本计入总体预算。8. 总结与进阶方向从“整本书塞进上下文”到“把资料拆成按需技能”本质上是把上下文从物理搬运变成了逻辑调度。book-to-skill 这个思路的价值在于它让模型不再被动地读完一本 20 万字的书而是像工程师查手册一样遇到问题只翻对应的几页。结合技能注册表、按需加载、参考文献压缩这三个关键实现你可以把几十万 token 的长文档问题压缩到几千 token 的常规问题。如果你准备在自己的项目中落地这套方案建议从一个小切口开始挑一本你最常用且结构清晰的书籍先拆成 10 个技能包跑通全流程再逐步扩展到更多资料。不要一上来就追求处理整个知识库拆分后的效果评估和技能匹配调试是需要反复迭代的。下一步可以继续研究的方向包括用 embedding 做更精细的技能匹配、为技能包增加自动更新机制、把 skill 化与 RAG 结合形成混合检索策略、以及在支持长上下文的模型中评估“按需技能”和“全量加载”的收益拐点。这些东西都可以在本文的基础上继续延伸核心还是那句话先想清楚哪些上下文是真正必须的再决定怎么把其他内容请出去。
返回列表