authentik 的 Agent 友好架构:从 llms.txt 文档索引到 code-mode MCP 的落地实践
2026/9/12 12:42:27 网站建设 项目流程

authentik 的 Agent 友好架构:从 llms.txt 文档索引到 code-mode MCP 的落地实践

【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik

本文基于仓库中的设计文档 2026-06-24-authentik-llm-architecture-design.md 编写,并结合website/下已落地的 Docusaurus 插件源码展开。文中涉及的 Layer 2、Layer 3 及authentik-agent-marketplace为设计文档中标注的外部配套仓库/规划内容,当前 monorepo 内可以验证的是 Layer 1 的实现与文档中描述的其余层设计。

authentik 是一个开源身份认证平台,版本迭代频繁,AI 编码 Agent(如 Claude Code、Cursor)基于预训练数据的知识往往滞后于真实版本。为此,authentik 设计了一套"三层 Agent 友好架构":用随每个 release 自动再生的文档产物喂给 Agent,让 Agent 优先做实时检索(live retrieval)而不是依赖训练数据。读完本文,你将掌握这套架构的完整设计(llms.txt 文档索引 → marketplace 技能 → code-mode MCP 服务器)、Layer 1 在仓库中的真实实现细节,以及"给登录流加验证码"这一贯穿三层的验收用例。

为什么需要"检索优先":问题与设计目标

文档开篇就点明了动机:authentik 在两个 release 之间的变化非常大,Agent 的预训练知识经常过期。因此目标不是把更多内容塞进模型上下文,而是:

  • 让 Agent 通过一个廉价的索引(docs 侧是llms.txt,API 侧是schema.yml)起步;
  • 按需按需拉取/执行(fetch/execute on demand),而不是一次性把内容或工具灌进上下文。

两条检索面遵循同样的形状:先给 Agent 一个轻量索引,再让它按需取内容。这样"稳态维护成本趋近于零"——因为索引和 API 面都来自 authentik 每个 release 自动再生的产物。

三类问题的路由

设计文档给出了清晰的请求路由:

问题类型例子路由
文档问题"如何配置 SAML?"、"App 和 Provider 有什么区别?"L1 + L2,不需要实例
实例问题"显示最近 10 次失败的登录"、"我运行的是哪个版本?"L3
混合/操作问题"给我的登录流加个验证码"、"重置我的管理员密码"L2 读文档理解概念,L3 写代码执行

非目标(YAGNI)

设计文档明确划定了不做的事,防止架构膨胀:

  • 不做第三方域名上的主llms.txt(docs/integrations 通过互链解决);
  • 不做预计算的 docs→API 映射注册表(code-mode 的search对实时 spec 的检索覆盖了它);
  • 不做 tool-per-endpoint 的 MCP:authentik 的 API 有数百个端点,每个端点暴露一个工具会淹没上下文且每个 release 都要维护;code-mode 把工具面固定在约 3 个工具上,与 API 规模无关;
  • 不采用社区authentik-mcp(原始 HTTP、手写 tool-per-endpoint、4 倍重复、无守卫,每次 release 维护负担高,且形态错误)。

Layer 1 — Docusaurusllms.txt插件(仓库内已实现)

Layer 1 从docusaurus-plugin-llms移植并精简,落在共享主题包里。仓库中真实的目录是 website/docusaurus-theme/llms-txt/(注意与设计文档中的docusaurus-theme/llms-txt/相对位置对应,位于website/之下),与releases/redirects/平级,以源码形式发布(无构建步骤),与既有模式一致。

文件组成

  • plugin.mjs— Docusaurus 插件工厂(默认导出);
  • node.mjs— 文件发现、MDX 解析、URL 解析逻辑;
  • generate.mjs— 各类输出(根索引、全文、分组索引、单页.md)的字符串组装;
  • common.mjs— 选项与数据类型;
  • markdown.mjs— MDX → 干净 Markdown 的清洗管线;
  • 配套*.test.mjs单元测试与__fixtures__/样例文件。

在 website/docusaurus-theme/package.json 的exports中注册./llms-txt/plugin./llms-txt/node./llms-txt/common等入口。

Hook 选择:postBuild而非loadContent

插件使用postBuild钩子,而不是主题其他插件常用的loadContent/contentLoaded模式。这是有意为之:postBuild能拿到最终解析好的路由 URL 列表(routesPaths,而准确的最终 URL 必须等构建后阶段才有。在 plugin.mjs 中可以看到postBuild里用props.routesPaths调用buildLLMSOutputs并写入props.outDir

有意思的是,插件同时实现了loadContent(用于 dev server)——它用routesPaths: []resolveDocumentUrlFromSource的源码级 URL 解析,并通过configureWebpack把生成的产物挂到 dev server 的静态目录上,保证开发环境也能预览 llms.txt 产物。

三级"索引的索引"输出

按照 llmstxt.org 约定,对 docs 和 integrations两个站点docs.goauthentik.iointegrations.goauthentik.io两个子域)分别生成:

  1. /llms.txt— 分组根索引。头部带指向姊妹站点/llms.txt的交叉链接;docs 按主题分组,integrations 按类别分组(类别由categories.mjs驱动)。仓库中该头部由 generate.mjs 的 buildHeader 生成,交叉链接渲染为Related: label行。
  2. <dir>/llms.txt— 每个主题/类别一个索引(如integrations.goauthentik.io/cloud-providers/llms.txtdocs.goauthentik.io/add-secure-apps/llms.txt),只索引该子树,并向上交叉链接到父索引。实现在generatePerGroupIndexes(generate.mjs)。
  3. /llms-full.txt— 站点全文拼接。设计文档强调它并不冗余:这是给 RAG 索引种子和批量/离线下载的最佳单一载荷。插件既然已经遍历了整个站点,生成它的成本很低(generateFullText把所有页面的清洗内容按## 标题拼接,页间用---分隔)。
  4. 每页<page>.md最后一跳(last-hop)载荷,索引链接指向这些.md文件(llmstxt.org 的.md后缀约定)。URL 后缀由 generate.mjs 的 applyMdExtension 处理——根首页的载荷是/index.md,其余页面是<url>.md

每页.md载荷:核心而非可选

这是设计评审中"决定性的修正":没有每页.md,Agent 走完整条索引链后没有小块内容可拉取——只剩过大的llms-full.txt或渲染后的 HTML。所以每页.md的产出是核心功能。

难点在于 authentik 的 MDX 不是普通 Markdown,它使用了:

  • partial importsimport X from '_shared.mdx');
  • 自定义 remark 指令:::ak-version,以及 enterprise/preview/support 徽章)。

直接拷贝.mdx会泄露未解析的 import(硬性失败——内容直接缺失)和指令噪音。因此清洗步骤必须:

  1. 在 React 组件注入之前拿到解析后的 MDX AST
  2. 内联 partial imports(复用源插件的resolvePartialImports思路);
  3. 剥离自定义指令(丢弃:::ak-*/ 徽章节点——对 Agent 是噪音);
  4. 剥离 frontmatter,然后序列化为干净的 Markdown

残留的 JSX 是可接受的——现代 LLM 能解析它,真正的风险是信息丢失而非残留 JSX。这是一次性、构建稳定的投入。

源码中的真实实现

清洗管线实现在 markdown.mjs 的cleanMdxToMarkdown

  • partial 内联inlinePartials用正则匹配import X from '..._partial.mdx'resolve出真实路径读取内容(跳过 frontmatter),再把 JSX 用法<X />替换为正文;对 Markdown 转义的下划线(\_partial.mdx)会先反转义再解析,并用chain集合防止 partial 循环导入自身;
  • 节点剥离stripNodesPluginunist-util-visit遍历 AST,把mdxjsEsmmdxJsxFlowElementmdxTextExpression等节点替换为其文本子节点,把containerDirective/leafDirective/textDirective指令节点解包为纯文本;
  • admonition 围栏stripAdmonitionFences逐行处理:::note等围栏标记(保留内文),且感知代码块——绝不改动围栏代码内部的内容;
  • 正则兜底:当严格 MDX 解析抛错(格式复杂的 JSX 等)时,走regexClean正则兜底并计入统计日志。plugin.mjs 里会汇总输出(N skipped — no route; M used the regex fallback)

解析过程中的描述提取也相当讲究:node.mjsextractDescription会跳过标题、MDX import/export、admonition/JSX、CVE reporter 署名和纯列表块,cleanDescriptionText会剥离 blockquote 标记、-- 署名行、列表符号、图片、链接(保留链接文本)、粗斜体与行内代码,最后firstSentence截到第一句,让索引行是一条干净的短描述。

插件接线

website/docusaurus-theme/config.js 提供createLLMSPlugin(options)工厂,返回["@goauthentik/docusaurus-theme/llms-txt/plugin", options]元组。两个站点各自调用:

  • docs(website/docs/docusaurus.config.esm.mjs):sections: [{ path: ".", routeBasePath: "/" }]groupBy: "topic"categories: topics(来自 website/docs/topics.mjs,如core → Core Conceptsglossary → Glossary等显示标签)、regroup: [["core/glossary", "glossary"]]把术语表从 Core Concepts 中拆成独立小节,crossLinks指向 integrations 的llms.txt
  • integrations(website/integrations/docusaurus.config.esm.mjs):groupBy: "category"categories来自自己的 categories.mjs,overviewPages: ["index", "applications"]把落地页作为## Overview内联为散文而非链接行,并ignoreFiles: ["**/template/**"]跳过脚手架模板。
选项速查(来自common.mjsLLMSPluginOptions
选项类型说明
sections{ path, routeBasePath, label? }[]要扫描的一个或多个 docs 根(必填,为空会抛错)
siteUrlstring覆盖站点 URL(优先级最高)
title/descriptionstring覆盖站点标题/标语
ignoreFilesstring[]额外的 glob 排除
crossLinks{ label, url }[]头部姊妹站点链接
groupBy"topic" \| "category"根索引的分组方式
categories[slug, label][]分组显示名覆盖
regroup[pathPrefix, groupSlug][]把某子树拆分/并入指定组
overviewPagesstring[]内联进根索引## Overview的页面

siteUrl的解析还有个细节:resolveSiteUrl会检查 Netlify 环境变量CONTEXTDEPLOY_PRIME_URL,在 deploy-preview / branch-deploy 时把链接指向部署预览源,而不是硬编码生产子域,避免预览构建里的链接指向错误来源。

移植时的增删取舍

  • 保留:两个核心生成器(索引 + 全文)、基于路由的 URL 解析、glob ignore、排序、section/category 分组、partial import 解析 + 指令剥离、批量处理。
  • 删除:blog 包含、pathTransformation(路由解析已覆盖)、customLLMFileskeepFrontMatteraddPaths/ignorePaths
  • 依赖gray-matter+minimatch(主题本已使用fast-glob)。

Layer 2 — Marketplace 技能(authentik-agent-marketplace

Layer 2 位于设计文档标注的外部仓库authentik-agent-marketplace(当前 monorepo 中不含其源码),其形态是两个角色拆分的插件,每个插件是一组技能。关键设计原则是:技能是"指针 + 方法",绝不是知识倾倒(knowledge dumps)

  • ak-admin(12 个技能)— 按 authentik 对象模型组织:conceptsapplicationsproviderssourcesflows-stagesauthenticators-mfapolicies-rbacusers-directoryoutpostsevents-monitoringtroubleshootingoperations
  • ak-dev(11 个技能)— 面向为 authentik 做贡献:dev-environment、backend、frontend、docs、testing、linting、contributing、community、de-slop。

每个技能由name+description+ Purpose + "When to invoke" + "Not this skill" 组成,并且每个技能都携带"优先检索而非预训练,authentik 随 release 变化"的指令

两条接缝(seams)

连接三层的正是这一对接线:

  • L2 → L1(文档):每个技能只指向稳定的根入口 URL(docs.goauthentik.io/llms.txtintegrations.goauthentik.io/llms.txt),并指示 Agent动态遍历链接、拉取页面.md。技能散文里不写死深路径,因此能扛住文档重组。Layer 1 生成的llms.txtURL 就是这些技能的入口。
  • L2 → L3(实例)ak-admin技能追加一行转向指令——"要检查或修改在线实例,使用 code-mode MCP:先search找端点,再在execute/execute_write里写ak.request(...)。先从文档学习概念。"其中events-monitoring指向executeflows-stagesusers-directory等指向execute_write

设计文档还提到,.mcp.json注册和SessionStart依赖安装钩子已在仓库中为 Layer 3 服务器搭好了脚手架(属外部仓库内容,当前 monorepo 中不可见)。


Layer 3 — Code-mode MCP 服务器

核心思路一句话:不要以 N 个工具暴露 authentik 的 API,而是暴露"可搜索的 OpenAPI spec + 一个代码沙箱",让 Agent 对着一个经过认证的ak.request(...)辅助函数写代码。这就是 code-mode 模式(Cloudflare、Ronacher 提出)——LLM 写代码远比发工具调用在行,而且代码面把数百端点的 API 折叠成一个固定的约 3 工具、token 占用近乎不变的表面:它不会随 API 增长而增长,且免费跟随运行实例的版本

三个工具(v1 与 v2 共用)

  • search(query)— 查询解析后的schema.yml(所有$ref已内联),只返回匹配的操作:method + path + summary + param / request / response 的 schema 切片。这是唯一随 API 规模伸缩的输出,且返回切片,从不返回整份 spec。(它取代了此前find_endpoint/describe_endpoint这对工具——search直接返回 Agent 构造调用所需的参数/响应 schema。)
  • execute(code)— 在沙箱中运行 Agent 的 JS/TS,沙箱唯一能力是一个绑定的ak.request(method, path, { query, body })辅助函数。只读:拒绝任何非 GET 动词。
  • execute_write(code)— 同一个沙箱,完整ak.request(所有动词)。每次调用都需要确认。因为它也携带读操作,所以一条"查找→创建→绑定"的混合链路可以作为一个确认块运行——code-mode 的可组合性得以保留。工具名本身也在审计轨迹中表明了意图。

Agent 循环示意:search("captcha stage")→ 读取端点 → 写一个调用ak.request(...)的代码块,按需链式调用,放进executeexecute_write

v1 — 本地 stdio 服务器(可构建目标)

位于authentik-agent-marketplace/mcp-servers/code-mode/(npm workspace 已搭好脚手架,.mcp.json+SessionStart依赖安装钩子已存在):

  • 配置/认证AUTHENTIK_URL+AUTHENTIK_TOKEN环境变量(社区authentik-mcp验证过的部署模型)。token 携带管理员自己的权限
  • Schema 来源:启动时拉取<AUTHENTIK_URL>/api/v3/schema/,让发现逻辑总是匹配运行实例的版本,另有内置schema.yml作为兜底。这正是零维护属性——发现逻辑跟随实例,这里无需再生成任何东西。
  • 沙箱——"绑定即边界":进程内node:vm/worker_thread,全局对象剥离到只剩ak+console——没有fs,没有通用fetchak.request是唯一的出口,在execute中它仅限 GET。对抗性隔离刻意做弱,因为信任模型是"管理员用自己的 token 对自己的实例运行代码"——Agent 本来也能通过任何精选调用做同样的事,所以真正的控制是绑定(只读默认 + 写入闸门),而不是 VM(Ronacher 的观点)。
  • 写入闸门execute_write触发 MCP 确认(elicitation)后才运行;批准后仅该次调用写武装。每次execute_write都在本地记录日志。
  • 暴露的工具:只有searchexecuteexecute_write
  • @goauthentik/api不是关键路径ak.request是对schema.yml路径的通用认证 fetch;生成的客户端Configuration/runtime 只是可选的传输便利,不是调用面本身。

v2 — authentik 原生 OAuth 端点(已设计,v1 验证后构建)

这是 authentik 作为身份提供商的天然优势,几乎 1:1 映射到 enterprise-mcp 参考架构:

  • 传输:authentik 在产品内提供远程 MCP 端点(HTTP/SSE),如/mcp
  • 认证:MCP 客户端向 authentik 自身执行 OAuth(authentik 就是 OIDC 提供方)——没有 API token 交接。Agent以已认证用户的身份行动。
  • 授权 = authentik 自己的 RBACak.request在服务端以用户身份运行,authentik 现有的 per-object / role 权限决定代码能读写什么。无需发明新的 scope 系统——复用 authentik 已强制的内容。(MCP 层的 OAuth scope 仍可作为execute_write的粗粒度开关。)
  • 沙箱:服务端运行,因此有真正的隔离可用(isolate/worker 池),不像 v1 的进程内 VM。
  • 审计:每次execute/execute_write写入 authentik现有的事件日志——与events-monitoring管理技能查询的是同一条轨迹。

设计文档给出的诚实警告:v2 是产品/后端工作、随 release 门控,有真实的安全面(面向公众的代码执行挂在 IdP 后面)。它是路线图设计,必须由 v1 先行验证——在 v1 的 captcha 转折测试通过之前不要启动它。


验收测试:"给我的登录流加验证码"

这一条混合任务能锻炼完整的 L1→L2→L3 回路,也是大规模铺开前的门禁。用 code-mode 的术语讲:

  1. flows-stages技能解释 stages/flows 概念(内容来自 Layer 1 发布的页面.md);
  2. Agent 调用search("captcha stage", "flow stage binding")
  3. 写出一个execute_write块:创建 captcha stage、找到目标 flow、POST 绑定——一次确认调用,没有 per-endpoint 工具

MVP 可以对着一个 mock 服务器和一小片schema.yml切片运行。它验证的是:schema 可搜索、代码可写、确认闸门可用,三个层真正串起来。


构建顺序(依赖排序)

设计文档给出 10 步、含 4 个门禁的推进路线,核心逻辑是"先索引、后载荷、再技能、最后原生端点":

  1. L1 — 核心索引 + 全文postBuild插件生成/llms.txt<topic>/llms.txt/llms-full.txt(docs 站点)。关键路径。
  2. 🛑 门禁 — 索引健全性:对真实 docs 快照——每个链接可解析、分组正确、全文包含内容。
  3. L1 — 每页.md:partial 解析 + 指令剥离,所有索引链接指向.md关键路径。
  4. 🛑 门禁 — 内容质量:抽 5 个不同类型的页面——无残留 import、无徽章杂物、Markdown 可读;喂给 LLM 确认它能列出步骤。
  5. L2 —ak-docs技能骨架:入口 URL + 遍历-拉取方法。(门禁 2 修复入口 URL 后即可并行。)
  6. L3 v1 — code-mode 服务器核心search(基于schema.yml)+execute(只读沙箱)+execute_write(需确认)。(可与 L2 并行,只依赖 spec;在转折测试关键路径上。)
  7. 🛑 门禁 — captcha MVP 端到端(转折点):Agent 走 L2 → 读 captcha.mdsearchspec → 写一个execute_write块(mock 服务器即可)。失败时诊断的是 schema/散文清晰度,而不是工具。
  8. L1 — integrations 子域。(设计文档标注已随 PR #23360 一起上线。)
  9. L2 — 把ak-admin/ak-dev技能接到llms.txt入口(L2→L1),并加 code-mode 转向行(L2→L3)。可并行。
  10. L3 v2 — authentik 原生 OAuth 端点只有v1 转折点通过后才做;产品/后端投入,单独 spec + 计划。

横切关注点:安全、分发与维护

安全

  • v1:本地 stdio 把管理员 token 留在自己的环境里;沙箱只暴露ak(无fs/fetch);execute仅 GET;execute_write每次调用确认并记日志。绑定即边界。
  • v2:对 authentik 做 OAuth;授权是 authentik 自己在用户身份下的 RBAC;每次调用都审计进事件日志。

分发

Claude Code + Cursor 的 manifest 已在 marketplace 中;ak-admin/ak-dev两个插件同时服务两者。

维护论点

docs(.md+ 索引)和 API 面(对实时schema.ymlsearch每个 release 都直接来自 authentik;L2 是薄胶水,L3 没有 per-endpoint 代码要打补丁。稳态人工维护趋近于零。


待规划阶段解决的开问题

设计文档保留了明确的开放问题,可作为后续实施的路线图注记:

  • L3search如何对schema.yml的操作做排序/过滤(对 path+summary+tags 做关键词匹配;每次命中返回多少 schema 切片而不撑爆上下文)。
  • L3node:vmvsworker_thread的最终选择,全局如何剥离到只剩ak+consoleexecute_write的 MCP 确认(elicitation)如何在 Claude Code / Cursor 之间呈现。
  • L3ak.request的传输——纯认证fetchvs@goauthentik/apiConfigurationruntime;启动时拉取/api/v3/schema/vs 内置schema.yml兜底如何选择。
  • L2 接线:确认每个ak-admin技能的单条转向行,以及稳定的llms.txt入口 URL(只依赖已上线的 Layer 1)。

结语

这套架构的核心价值可以浓缩为三句话:docs 教概念(Layer 1 + Layer 2),code-mode 执行动作(Layer 3),而两个检索面都由 authentik 每个 release 自动再生——Agent 的知识不再依赖"最后一次训练时点的快照",而是始终跟随运行实例的当前版本。对于要复刻这套模式的团队,Layer 1 是唯一在仓库内完整落地的部分,其 llms-txt 插件 从postBuild路由解析、partial 内联、指令剥离到三级索引输出,提供了可以直接参照的完整实现样例。

【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询