1. 项目概述与核心价值最近在折腾AI智能体Agent的开发发现一个挺普遍但很棘手的问题怎么让Agent快速、准确地“学会”使用某个工具或框架比如你想让一个Agent帮你写Flutter代码或者调用某个API它首先得理解这个工具或框架的官方文档吧。但直接把官网链接扔给它或者把一堆HTML页面塞给它效果往往很差。文档网站结构复杂充斥着导航栏、广告、侧边栏等“噪音”真正有用的核心内容被稀释了。为了解决这个问题我深度使用并改造了一个开源项目Agent Skills Generator。它的核心使命非常明确——将任何文档网站高效、精准地转换为结构清晰、适合AI Agent和大型语言模型LLM直接“消化”的Markdown技能文件。简单来说它就是一个文档“蒸馏”工具。想象一下你有一大桶浑浊的河水原始文档网站里面有你需要的纯净水核心知识但也混入了泥沙、树叶无关的UI元素。这个工具的作用就是帮你搭建一套过滤和提纯系统最终输出一瓶瓶可以直接饮用的纯净水Markdown文件。这些Markdown文件我们称之为“技能”Skills它们格式统一、内容干净、富含元数据可以被直接嵌入到像LangChain、AutoGen或自定义Agent的提示词Prompt或知识库中极大提升Agent的专业能力和响应准确性。这个项目提供了两套互补的解决方案适合不同场景的开发者一个是VS Code扩展适合喜欢图形化界面、在IDE内一站式操作的开发者另一个是Go语言编写的CLI工具适合追求高性能、自动化集成和批量处理的后端或运维工程师。我花了不少时间研究它的源码、调整配置并在实际项目中应用积累了一些超出官方文档的实战心得和避坑指南接下来就和大家详细拆解。2. 核心设计思路与方案选型解析为什么我们需要一个专门的工具来做这件事而不是简单地用curl下载页面或用Python的BeautifulSoup写个脚本这背后涉及到几个关键的设计考量也是这个项目价值所在。2.1 从“文档”到“技能”的范式转换首先我们要理解“文档”和“技能”的区别。一份完整的官方文档其设计目标是服务于人类读者。它需要清晰的导航、渐进式的教程、丰富的示例和美观的排版。但对于AI Agent来说这些人类友好的设计反而成了干扰信息。AI更需要的是密集、结构化的事实性知识以及清晰的操作指令和参数说明。Agent Skills Generator 所做的正是完成这种范式转换。它不仅仅是将HTML转为Markdown很多工具都能做更是进行了一系列针对LLM的优化去噪与提纯自动剥离导航栏、页脚、广告、侧边栏评论等与核心内容无关的HTML元素。这通常通过配置CSS选择器或智能分析页面结构来实现确保输出的Markdown只包含标题、正文、代码块和关键列表。结构扁平化与标准化文档网站通常有复杂的目录层级。本工具可以配置为“扁平化”存储将所有页面放在一个目录下并通过文件名或元数据保持可追溯性。这特别适合后续构建检索增强生成RAG系统因为扁平结构能简化向量数据库的文档切分和检索逻辑。注入元数据每个生成的Markdown文件头部都会添加YAML Frontmatter包含url原始链接、title页面标题、source网站名称等。这些元数据对于RAG系统至关重要当Agent引用某段知识时可以明确指出来源增强可信度和可追溯性。2.2 双工具架构的权衡VS Code扩展 vs. Go CLI项目提供了两种形态的工具这不是功能重复而是针对不同工作流的设计。VS Code扩展的优势在于“所见即所得”和快速迭代。当你探索一个新的文档网站时你可以在IDE内直接添加一个URL规则实时点击“Fetch Skills”立刻在侧边栏看到生成的Markdown预览。你可以快速调整“包含/忽略”的路径规则比如发现它爬取了太多版本选择页面如/v1.0/,/v2.0/你可以立即添加一条忽略规则*/v*/intro。这种交互式探索对于制定精确的爬取策略非常高效。它把爬虫配置这个原本需要写YAML文件的工作变成了一个可视化表单操作。Go CLI的优势则在于性能、稳定性和自动化。Go语言编译出的单文件二进制程序无需Node.js环境部署和运行极其简单。其并发爬取能力虽然项目基础版本可能未极致优化但Go的goroutine模型为高性能爬虫提供了天然基础远超基于Node.js的VS Code扩展。更重要的是CLI工具可以轻松集成到CI/CD流水线中。想象一个场景你维护着一个基于某开源框架的Agent该框架每月发布新版本文档。你可以写一个定时任务Cron Job每周用CLI工具自动爬取最新文档生成新的技能文件并自动更新到你的Agent知识库中实现技能的“自动驾驶”式更新。我的选型建议初期探索和规则制定阶段强烈推荐使用VS Code扩展。用它来摸清目标网站的结构调试出完美的爬取规则。一旦规则稳定下来就将配置导出为JSON或YAML迁移到Go CLI进行自动化、批量化生产。两者配合效率最高。2.3 技术栈选择的背后逻辑为什么是Go和TypeScript/Node.js这体现了作者对工具属性的深思熟虑。Go用于CLIGo的静态编译、卓越的并发支持goroutine和强大的标准库特别是net/http和html解析使其成为编写高性能、高可靠性网络工具如爬虫的绝佳选择。生成的二进制文件跨平台、无依赖符合CLI工具的核心要求。TypeScript/Node.js用于VS Code扩展VS Code扩展生态本身就是基于Node.js的。使用TypeScript能提供更好的类型安全和开发体验便于管理扩展的复杂状态如规则列表、配置项。此外Node.js生态有丰富的HTML解析库如jsdom,cheerio虽然性能不及Go但对于交互式、小批量的GUI操作完全足够。3. 核心功能深度解析与实操要点了解了为什么需要它以及它的大致形态后我们深入到核心功能层看看它具体是如何工作的以及在使用中需要注意哪些关键点。3.1 可视化规则管理不仅仅是URL列表在VS Code扩展中“规则”Rules是核心概念。一条规则不只是一个URL而是一个抓取策略。我们以爬取https://docs.flutter.dev/为例进行深度解析。当你添加一条规则时需要配置几个关键字段URL Pattern起始URL。工具会从这里开始爬取。Actioninclude或ignore。决定匹配此规则的页面是被抓取还是被跳过。Subpaths一个布尔值开关。这是最容易产生误解也最重要的选项之一。如果开启subpaths: true爬虫会递归抓取该URL所有子路径下的页面。例如从https://docs.flutter.dev/开始它会抓取/get-started,/ui,/widgets等所有目录下的内容。这适用于你想抓取整个文档站。如果subpaths: false则仅抓取该精确URL指向的单个页面。这适用于你只需要某个特定的API说明页。Include/Ignore Patterns这是一组用于细粒度控制的正则表达式或通配符模式。这是控制爬取质量的生命线。实操心得如何制定高效的Include/Ignore策略盲目开启subpaths: true往往会抓取到大量无用页面如不同语言版本/zh/,/es/、打印页面?print1、PDF下载链接等。我的经验是先宽后严初始规则可以只设一个include的根URL并开启subpaths运行一次小规模测试可在配置中设置maxDepth: 2限制深度。分析结果查看生成的Markdown文件列表找出那些你不需要的页面路径模式。添加Ignore规则例如忽略语言路径*/zh/*,*/ja/*忽略查询参数*?*忽略特定功能页*/api/*如果你只需要教程。必要时使用Include如果网站结构复杂用Ignore排除太麻烦可以反转思路。关闭subpaths然后添加多条明确的include规则如https://docs.flutter.dev/get-started/*,https://docs.flutter.dev/ui/*只抓取你关心的板块。3.2 HTML到Markdown的转换优化与损耗转换引擎是这个工具的灵魂。它通常基于一个强大的开源库如Go的blackfriday或Node的turndown但在此基础上增加了针对文档网站的优化。优化措施通常包括智能选择主内容区不是简单转换整个body。工具会尝试识别包含核心文章的容器常见策略是寻找article,main标签或者计算div的文本密度文本长度与标签数量的比值找到内容最集中的区域。清理无用元素通过预定义的或可配置的CSS选择器列表移除nav,footer,.sidebar,.ad-container等。代码块格式化确保precode被正确转换为Markdown的代码块并尝试从class属性中识别语言如classlanguage-dart添加语法高亮标记。链接处理将相对链接如./install转换为基于基URL的绝对链接确保生成的Markdown在脱离原站上下文后链接仍然是可追溯的。不可避免的转换损耗与应对没有任何转换是完美的。复杂表格、特殊图表如Mermaid、交互式组件在转换后可能会丢失或变形。应对策略对于极其重要的、转换失真的页面可以考虑将其URL加入“手动处理清单”后续通过自定义脚本或手动方式单独处理。或者接受这部分信息的损耗因为对于大多数API文档和教程文本而言核心的文字和代码信息能被完美保留就已达到主要目的。3.3 元数据提取与Frontmatter标准化每个生成的Markdown文件顶部都会有一个YAML Frontmatter块。这是一个看似简单但极其重要的设计。--- title: 安装与环境配置 | Flutter 文档 url: https://docs.flutter.dev/get-started/install source: Flutter Documentation ---元数据的核心作用溯源与引用当你的Agent回答“如何安装Flutter”并引用这段内容时它可以明确给出来源链接增强可信度。RAG检索优化在构建向量数据库时除了对正文内容做嵌入embedding你也可以将title和source字段作为元数据过滤条件。例如你可以让检索器优先从“Flutter Documentation”源中寻找关于“安装”的信息。文件管理当你拥有成千上万个技能文件时通过source和title来组织和搜索文件会非常方便。注意事项工具的元数据提取依赖于网页的title标签和Open Graph协议。有些网站的动态标题可能不理想如包含“| 公司名”后缀。你可以在后期处理中编写一个简单的脚本根据文件名或URL规则对Frontmatter进行批量清洗和规范化这能让你的技能库更加整洁。4. 完整实操流程从零构建Flutter开发技能库理论说得再多不如动手做一遍。下面我将以“为AI Agent构建一个Flutter开发技能库”为目标演示使用Go CLI的完整流程。选择CLI是因为它更贴近最终的生产环境。4.1 环境准备与工具安装首先确保你的系统已安装Go1.19。然后获取并编译工具。# 1. 克隆仓库 git clone https://github.com/rodydavis/agent-skills-generator.git cd agent-skills-generator # 2. 编译Go CLI工具 cd go-cli go build -o agent-skills-generator main.go # 此时会在当前目录生成一个名为 agent-skills-generator 的可执行文件 # 3. 验证安装 ./agent-skills-generator --help如果看到帮助信息说明编译成功。你可以将这个二进制文件移动到系统的PATH路径下如/usr/local/bin方便全局调用。4.2 配置文件设计与编写接下来是核心步骤编写爬取规则配置文件。我们在项目根目录创建一个flutter_skills.yaml文件。# flutter_skills.yaml output: ./generated_skills/flutter # 技能文件输出目录 flat: true # 使用扁平化结构存储 maxDepth: 6 # 最大爬取深度防止陷入无限循环 delay: 200ms # 请求间隔避免对目标服务器造成压力礼貌爬虫 rules: # 规则1包含Flutter核心文档主站 - url: https://docs.flutter.dev/ subpaths: true action: include # 忽略非英语文档和某些特定路径 ignorePatterns: - */zh/* - */ja/* - */ko/* - *?* # 忽略带查询参数的URL如搜索页、排序页 - */archive/* # 忽略归档版本 - */api/* # 假设我们暂时不需要纯API参考更关注教程和指南 # 规则2明确包含我们关心的cookbook部分如果主站规则未能覆盖 - url: https://docs.flutter.dev/cookbook subpaths: true action: include # 规则3忽略所有来自“flutter.cn”的链接中文站避免重复 - url: https://flutter.cn/ subpaths: true action: ignore配置项解读flat: true所有Markdown文件将直接放在./generated_skills/flutter目录下文件名可能由URL路径转换而来如get-started-install.md。这比保持原始目录树结构更简洁尤其适合后续的文本切分。maxDepth: 6对于大型文档站设置深度限制是安全措施防止因某些页面链接循环而导致爬虫失控。delay: 200ms这是必须设置的道德和安全选项。不加延迟的连续请求会被网站视为攻击可能导致你的IP被屏蔽。200ms是一个比较保守且友好的间隔。4.3 执行爬取与生成配置文件准备好后执行爬取命令。# 在包含 flutter_skills.yaml 的目录下执行 ./path/to/agent-skills-generator crawl --config flutter_skills.yaml执行后工具会开始工作在控制台输出日志信息显示正在访问的URL、成功转换的页面以及被忽略的页面。这个过程可能会持续几分钟到几十分钟取决于网站的规模和网络速度。4.4 输出结果验收与后处理爬取完成后检查./generated_skills/flutter目录。generated_skills/flutter/ ├── index.md # 对应 https://docs.flutter.dev/ ├── get-started-install.md ├── get-started-editor-setup.md ├── ui-widgets-intro.md ├── cookbook-navigation-navigation-basics.md ├── cookbook-design-themes.md └── ... (数百个文件)验收 checklist数量是否合理是否抓取了预期数量的页面如果数量远少于预期可能是ignorePatterns过于严格或maxDepth太小。内容是否干净随机打开几个文件检查Markdown内容是否聚焦于核心教程/文档导航栏、页脚等是否已被移除。链接是否正常检查文件内的链接是完整的绝对URL还是破碎的相对链接。元数据是否完整每个文件的Frontmatter是否都正确填充了title和url。常见的后处理操作批量重命名你可能不喜欢默认生成的文件名如带-连接。可以写一个Python/Shell脚本读取每个文件的Frontmatter中的title将其转换为更友好的文件名如安装与环境配置.md。内容精校对于少数转换不佳的页面如复杂表格错乱进行手动修正。由于数量不多这是可接受的成本。技能索引创建创建一个_index.md文件列出所有技能及其简要描述方便后续管理。这个索引文件本身也可以作为Agent的一个“技能目录”技能。至此一个专属于Flutter开发的、干净、结构化的AI技能库就构建完成了。你可以将这些Markdown文件用于后续的RAG系统构建或者直接作为上下文嵌入到Agent的提示词中。5. 高级技巧与集成应用方案掌握了基础用法后我们可以探索一些更高级的应用场景和技巧让这个工具发挥更大价值。5.1 构建多源、版本化的技能知识库一个成熟的AI Agent可能需要掌握多个不同工具的知识。你可以为每个工具如Flutter, Docker, PostgreSQL分别运行一次爬取生成独立的技能目录。然后通过一个统一的元数据管理文件如一个JSON索引来管理它们。更高级的场景是处理版本化文档。例如Docker的API文档有v1.40, v1.41等多个版本。你可以这样组织agent_skills/ ├── docker/ │ ├── v1.40/ │ │ ├── (技能文件...) │ │ └── version_info.json # 记录版本号、爬取时间 │ └── v1.41/ │ ├── (技能文件...) │ └── version_info.json └── flutter/ └── stable/ └── (技能文件...)在配置爬取规则时使用url: https://docs.docker.com/engine/api/v1.41/这样的精确版本路径。在你的Agent系统中可以根据用户问题中隐含的版本需求选择对应版本的技能库进行检索。5.2 与RAG管道无缝集成生成的Markdown技能文件是构建RAG系统的绝佳原料。以下是典型的集成步骤加载与切分使用LangChain的DirectoryLoader和MarkdownLoader来加载技能目录。然后使用RecursiveCharacterTextSplitter对长文档进行切分设置合适的chunk_size如1000和chunk_overlap如200。向量化与存储使用OpenAI或本地的嵌入模型如text-embedding-3-small将文本块转换为向量然后存入向量数据库如Chroma, Pinecone, Weaviate。检索时利用元数据在存储时将每个文本块对应的源文件Frontmattertitle,url,source作为元数据一并存储。在检索时除了语义相似度还可以加入元数据过滤器。例如“在Flutter文档中寻找关于‘状态管理’的内容”。Agent调用当用户提问“如何在Flutter中实现页面跳转”时Agent首先查询RAG系统获取最相关的3-5个文本块包含来源URL然后将这些信息作为上下文生成最终答案。通过这种方式你的Agent就不再是仅凭训练时的静态知识回答问题而是能实时“查阅”最新的、经过你精心整理的官方技能手册。5.3 性能调优与错误处理对于大规模爬取需要考虑性能和稳定性。并发控制查看Go CLI的源码或帮助看是否支持设置并发工人数concurrency或workers。适当提高如5-10可以加快速度但必须与delay配合避免触发反爬。错误重试网络请求可能失败。一个健壮的爬虫应该有重试机制。检查工具是否内置如果没有你可能需要修改源码在HTTP请求处添加带退避策略的重试逻辑。增量爬取文档网站更新后重新爬取全部内容很浪费。理想的方案是支持增量更新。虽然该工具可能不直接支持但你可以实现一个简单方案爬取前先获取sitemap.xml如果网站提供或记录每个页面的最后修改时间Last-Modified头只爬取那些疑似更新的页面。这需要一定的定制开发。6. 常见问题排查与实战避坑指南在实际使用中你肯定会遇到各种问题。下面是我踩过坑后总结的常见问题及解决方案。6.1 爬取结果为空或数量极少可能原因及排查步骤检查ignorePatterns这是最常见的原因。你的忽略规则可能过于宽泛意外匹配并过滤了所有目标页面。尝试暂时注释掉所有ignorePatterns再次运行看是否能抓到页面。检查maxDepth如果起始页的链接层级较深而maxDepth设置太小如1或2爬虫可能无法到达内容页。尝试将其增大到5或6。网站使用JavaScript渲染如果目标网站是单页应用SPA内容由JavaScript动态加载传统的HTTP爬虫无法获取到有效HTML。此时需要换用支持Headless Browser如Puppeteer, Playwright的爬虫工具。本项目的基础爬虫可能无法处理这种情况。触发了反爬机制检查控制台日志是否有大量403/429错误。增加delay时间并考虑添加User-Agent头模拟真实浏览器。6.2 生成的Markdown内容包含大量导航文本或垃圾信息可能原因及解决方案主内容区识别失败工具的默认CSS选择器可能不适用于目标网站的结构。你需要自定义清理规则。查阅工具的进阶文档看是否支持通过配置指定要移除的CSS选择器列表。例如在配置文件中添加cleanupSelectors: - nav - .sidebar - .header - footer - .ad - script - style你需要通过浏览器的开发者工具检查目标网站的HTML结构找到那些包含无关内容的容器元素的类名或ID添加到列表中。转换器缺陷对于极其复杂的页面布局转换器可能无法完美处理。如果只有少数关键页面如此考虑手动处理这些页面。如果普遍存在可能需要向项目提Issue或考虑换用其他更强大的HTML-to-Markdown库并修改工具源码。6.3 链接处理问题图片缺失或链接错误问题表现生成的Markdown中图片显示为破碎链接或页面内的锚链接#section指向错误。图片问题工具可能默认只处理文本不下载图片。对于技能库纯文本通常已足够。如果必须保留图片你需要一个更复杂的流程爬虫需要下载图片并处理相对路径。这通常超出了本工具的范畴可能需要二次开发。内部锚链接工具将页面内的#section这样的哈希链接原样保留。当你在非浏览器环境中阅读Markdown时这些链接无效。这通常可以接受因为重点在于文本内容。如果必须修正需要在后处理阶段根据标题生成新的锚点或直接移除这些哈希链接。6.4 性能瓶颈与内存占用问题爬取大型网站如数千页时速度慢或Go CLI进程内存占用过高。速度慢首先确保delay设置合理不低于100ms。其次检查是否启用了并发如果支持。最后网络延迟可能是主要因素可以考虑在离目标服务器更近的机器上运行如云服务器。内存高Go程序一般内存管理很好。但如果同时处理大量HTML文档且缓存不当可能导致内存上升。可以尝试在配置中减少并发数或者分批次爬取将一个大站按目录拆分成多个YAML配置依次运行。6.5 配置管理团队协作与版本控制最佳实践将你的爬取规则配置文件.yaml纳入Git版本控制。这样团队所有成员都可以使用同一套规则生成完全一致的技能库。在README中详细记录每个配置文件的用途、目标网站以及最后一次成功爬取的时间。当网站改版导致爬取失败时可以快速回溯和修改配置。最后这个工具是一个强大的起点但它不是万能的。它最适合的是结构相对规范、以内容为主的文档网站如ReadTheDocs, GitBook, 各种官方API文档站。对于交互复杂、严重依赖JS的现代Web应用或者需要登录才能访问的内容则需要更专业的爬虫方案。但无论如何将“文档即技能”的思路融入到你的AI Agent开发流程中无疑是提升其专业性和实用性的关键一步。