Onyx craft-documentation 内置技能:基于 llms.txt 模式的文档问答工作流实现解析
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
本文以 Onyx(Danswer)仓库中的内置技能定义 SKILL.md 为主体,完整拆解 Onyx Craft 中craft-documentation技能的设计:它如何引导 Agent 通过llms.txt索引与 Mintlify 提供的机器可读.md页面来回答关于 Onyx 及其 Craft 功能的文档问题,并结合同仓库源码说明该技能的注册、校验、下发到沙箱以及webfetch工具权限放行等完整落地链路。读完后你可以掌握“文档驱动型 Agent 技能”的编写模式,并理解 Onyx 技能体系从注册表到沙箱挂载的实现细节。
1. 技能定位:让 Agent 查文档而不是凭记忆猜
craft-documentation是 Onyx Craft(Onyx 的编码/代理工作区)内置的一项技能,其目标非常明确:当用户询问 “Craft 能做什么”“某个功能如何工作”“如何设置技能或应用”“如何部署、配置或管理 Onyx” 这类问题时,Agent 应当从官方文档 https://docs.onyx.app 获取答案,而不是凭记忆猜测。
技能的前置元数据(YAML frontmatter)如下,它决定了这个技能叫什么、何时被触发:
--- name: craft-documentation description: Answer questions about how Onyx and Onyx Craft work using the official documentation at docs.onyx.app. Use when the user asks what Craft can do, how a feature works, how to set up skills or apps, or how to deploy, configure, or administer Onyx. ---其中description承担“触发条件说明”的职责:它向模型描述了这个技能的适用场景(功能查询、技能/应用配置、部署与运维管理),使 Agent 在收到相关问题时优先加载该技能。
值得注意的是,这份 frontmatter 在仓库中不是随意书写的文本,而是有严格的解析与校验规则。从 metadata.py 的parse_skill_document可以看到:
- 文件必须以两行
---包裹的 YAML frontmatter 开头,正文才是指令 Markdown(split_skill_md); name与所在目录名必须一致——craft-documentation恰好就是 builtin/craft-documentation/ 目录名;- 字段约束由 models.py 的
SkillMetadata强制:name最长 64 字符且只允许小写字母、数字与连字符(正则^[a-z0-9]+(?:-[a-z0-9]+)*$),description不能为空且最长 1024 字符; - frontmatter 使用自定义的
_UniqueKeySafeLoader解析,出现重复 key 会直接报错,避免静默吞掉配置错误。
2. 核心工作流:llms.txt 索引 → 定向抓取 .md 页面 → 带引用作答
技能正文定义的三段式工作流是全文的核心,必须完整保留:
第一步:抓取站点索引。先请求https://docs.onyx.app/llms.txt。该文件把文档站的每个页面都列为一个 Markdown 链接,并附一行摘要,Agent 应当先用它定位相关页面,再决定读取什么——这是典型的llms.txt模式:为 LLM 提供一份轻量级的站点目录,避免盲目遍历。
第二步:按页定向抓取。选中与问题匹配的页面后,在 URL 末尾追加.md后缀即可拿到干净的机器可读副本,例如:
https://docs.onyx.app/overview/core_features/craft.md文档明确指出,Craft 相关主题主要分布在以下四个目录段下:
overview/core_features/admins/managing_features/deployment/security/architecture/
第三步:基于读到的内容作答,并逐条引用来源——每个引用的页面都要给出标题和 URL,保证答案可追溯、可核查。
该工作流的关键设计点在于第二句前提:“The docs are published with Mintlify, which serves clean, machine readable copies of every page. Fetch those with thewebfetchtool; there is no need to render the site in a browser.” 即文档站由 Mintlify 托管,直接返回纯文本页面,因此用轻量的webfetch工具抓取即可,完全不需要启动浏览器渲染页面——这与仓库中另一份内置技能 browser/SKILL.md 中的建议形成呼应:“for basic reads of static pages, prefer thewebfetchtool — it returns clean markdown, is faster, and is cheaper. Reach forbrowseronly when the page needs JavaScript/SPA rendering…”。两者共同体现了 Onyx Craft 工具选择的分层策略:能用webfetch就不动用浏览器,能用llms.txt索引定位就不做全站抓取。
3. 两级文档检索策略与“不猜测”兜底
技能文档的 Notes 部分给出了两条补充规则,它们与工作流同等重要:
- 优先定向抓取,整库兜底。整套文档还有一个全量版本
https://docs.onyx.app/llms-full.txt,但体积很大,只建议在问题横跨多个页面时才使用。由此形成清晰的两级检索策略:- 常规问题:
llms.txt(索引)→ 若干*.md单页; - 复杂问题:
llms-full.txt(全文语料)一次性获取。
- 常规问题:
- 文档未覆盖时明说,绝不猜测。如果文档中没有相关内容,Agent 应当直接说明“文档未覆盖”,并指向最接近的相关页面,而不是编造答案。这一条是文档问答类技能最重要的质量护栏:把“不知道”作为合法输出,把“有据可查”作为唯一事实来源。
4. 源码链路:这个技能如何被注册、下发并生效
技能文档本身只是“内容”,它能否在 Craft 会话中被 Agent 实际加载,取决于 Onyx 后端的四条链路。以下均来自仓库源码可确认的事实。
4.1 注册表:_REGISTRY是唯一事实源
skills/built_in.py 中的_REGISTRY收录了所有内置技能,craft-documentation对应一条:
SeededBuiltInProvider(skill_id="craft-documentation"),SeededBuiltInProvider表示该技能的数据库行由 Alembic 迁移负责创建(区别于ExternalAppBuiltInProvider——后者在管理员连接外部应用时才按需建行)。注册表在导入时即完成校验:两个 provider 不允许共享同一skill_id,且每个技能的目录必须存在SKILL.md(或SKILL.md.template)并通过parse_skill_document解析,缺文件直接抛ValueError。BUILTIN_SKILLS_PATH指向 builtin/ 目录,source_dir由skill_id推导,保证磁盘布局与注册信息不会漂移。
4.2 数据库种子:迁移写入 skill 行
Alembic 迁移 c5d9662b3c50_seed_craft_documentation_built_in_skill.py 负责把craft-documentation的skill行写入数据库,使技能成为部署后默认可见的内置项。
4.3 下发到沙箱:会话建立时推送到 /workspace/managed/skills
skills/push.py 定义了挂载点常量SKILLS_MOUNT_PATH = "/workspace/managed/skills"。会话建立时,后端按技能名把builtin/<skill_id>/下的静态文件(排除__pycache__、隐藏文件、.template源文件)组织成文件集推送进沙箱,并用compute_skill_runtime_hash对技能文件做 SHA-256 摘要——一旦技能内容变化,摘要不一致会判定会话过期并触发热重载。也就是说,craft-documentation/SKILL.md的内容会原样出现在每个 Craft 沙箱的受管技能目录中,供 Agent 读取。
4.4 权限放行:webfetch 在沙箱策略中默认 allow
工作流依赖webfetch工具,这一点在沙箱权限模板中得到确认:opencode_config.py 的默认权限模板中显式包含"webfetch": "allow"。这意味着该技能“抓取公开文档页面”的动作在 Craft 沙箱的默认策略下是放行的,无需管理员额外配置;而disabled_tools参数可以按部署需求覆盖(被禁用的工具会被改写为"deny")。
5. 对技能作者的启示:从 craft-documentation 学到的编写要点
以craft-documentation为样本,一个合格的 Onyx 文档驱动型技能应满足以下要点,且每一条都能在仓库中找到实现对应物:
| 要点 | 说明 | 仓库依据 |
|---|---|---|
| 目录名即技能名 | builtin/craft-documentation/与 frontmattername一致 | parse_skill_document |
| description 写清触发条件 | 明确“何时用我”,覆盖问题类型枚举 | SkillMetadata |
| 工作流步骤化 | 用编号步骤描述“先查索引、再定向抓取、最后带引用作答” | SKILL.md |
| 给出检索降级路径 | llms.txt→llms-full.txt两级策略,并说明各自适用场景 | 同上 Notes 部分 |
| 声明兜底行为 | 文档未覆盖时明说并指向最接近页面,不猜测 | 同上 |
| 注册 + 迁移双就位 | 注册表条目与 Alembic 种子迁移缺一不可 | built_in.py、种子迁移 |
6. 小结
craft-documentation虽然只有一份简短的 SKILL.md,但它浓缩了 Onyx Craft 技能体系的一个完整范式:用 frontmatter 声明触发条件,用llms.txt+.md后缀的模式构建低成本的文档检索链路,用“引用来源 + 不猜测”约束答案质量;而后端则通过注册表、数据库种子、沙箱推送与工具权限四重机制,保证这份指令文档准确、一致地进入每个 Agent 会话。理解这一链路,既是读懂该技能的关键,也是为 Onyx 部署编写同类文档问答技能(例如指向自有文档站)时的可参照模板。
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考