恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

CUA 文档索引与 MCP 服务全解析:从 Playwright 爬取、SQLite FTS5 + LanceDB 双索引构建到不可变 Generation 发布

  • 首页
  • 资讯中心
  • /
  • CUA 文档索引与 MCP 服务全解析:从 Playwright 爬取、SQLite FTS5 + LanceDB 双索引构建到不可变 Generation 发布

相关资讯

Java多线程四大核心姿势:启动、协作、保护与传递 2026/9/13 16:57:16
Kubespray 生产级 Kubernetes 集群部署实战指南 2026/9/13 16:57:16
DocuSeal React 嵌入签署表单:DocusealForm 组件全参数与集成指南 2026/9/13 16:57:16

最新资讯

高中生编程入门:拿下计算机二级Python,为综合评价加码
Common Room 联系人信号解读指南:基于 knowledge-work-plugins 的 Contact Signals 实战解析
tomllib 自带TOML文件解析库
Python全栈开发学习笔记:涵盖Web前端、Django、Flask、机器学习与数据库实战
MediaMTX 浏览器推流指南:通过 Media-over-QUIC 与 WebRTC 从网页发布实时流
Argo CD 通知故障排查指南:`argocd admin notifications` 命令组实战与常见错误修复

今日推荐

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
Flutter应用改名全指南:从Android到iOS的配置与工具实践

本周热门

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
Flutter应用改名全指南:从Android到iOS的配置与工具实践

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

CUA 文档索引与 MCP 服务全解析:从 Playwright 爬取、SQLite FTS5 + LanceDB 双索引构建到不可变 Generation 发布

发布时间:2026/9/13 16:57:16
CUA 文档索引与 MCP 服务全解析:从 Playwright 爬取、SQLite FTS5 + LanceDB 双索引构建到不可变 Generation 发布 CUA 文档索引与 MCP 服务全解析从 Playwright 爬取、SQLite FTS5 LanceDB 双索引构建到不可变 Generation 发布【免费下载链接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.项目地址: https://gitcode.com/GitHub_Trending/cua/cua导读本文基于 cua 仓库的 docs/scripts/README.md 及 docs/scripts 目录下的完整实现系统讲解 CUA 官方文档索引与 MCP 服务的完整流水线如何用 Playwright 从https://cua.ai/docs抓取文档语料如何基于同一份抓取快照构建 SQLite FTS5 全文索引与 LanceDB 向量索引这一配对索引如何通过不可变 Generation 原子指针current.json实现可回滚的发布以及容器消费端如何校验并激活最新一代索引。读完本文你将掌握一套可复用的文档检索基础设施工程模式并能在本地完整复现构建、验证、离线测试与 S3 消费同步的全过程。一、整体架构一条抓取 → 构建 → 发布 → 消费的流水线整个 docs-mcp 体系是一条严格分段的流水线每一段之间以已验证产物作为交接物阶段入口/脚本产物抓取crawl_docs.pydocs/crawled_data/_corpus.json唯一语料快照构建generate_db.py / generate_sqlite.pydocs/docs_db/generations/build-id/docs.sqlitedocs.lance/manifest.json激活docs_index.py 的activate()docs/docs_db/current.json原子指针调度发布modal_app.pyS3 上的不可变对象 docs_db/current.json提交标记消费sync_docs_index.py / main.py本地已校验的 Generation 目录与 MCP 查询服务核心设计理念贯穿始终所有中间产物都必须经过校验才能进入下一阶段失败时永远保留上一个可用状态绝不部分激活。二、本地构建用锁定环境跑通三步流水线原文档明确要求使用服务端的锁定环境以保证读写双方使用兼容的 LanceDB、PyArrow 与 embedding 包。在仓库根目录依次执行# 1. 安装 Playwright 的 Chromium仅首次需要 uv run --project docs/scripts/docs-mcp-server --locked --with playwright playwright install chromium # 2. 抓取文档站点 uv run --project docs/scripts/docs-mcp-server --locked --with playwright docs/scripts/crawl_docs.py # 3. 构建并激活双索引 uv run --project docs/scripts/docs-mcp-server --locked docs/scripts/generate_db.py三条命令分别对应抓取、构建、激活三段。为什么必须--project docs/scripts/docs-mcp-server --locked因为 docs_index.py 定义了写索引所需的精确版本WRITER_VERSIONS {lancedb: 0.37.1, sentence-transformers: 5.7.0, pyarrow: 25.0.1}而write_vectors()docs_index.py在写 LanceDB 前会用importlib.metadata.version逐包比对版本不匹配直接抛RuntimeError。与此同时pyproject.toml 与 modal_app.py 中lancedb0.37.1、sentence-transformers5.7.0、pyarrow25.0.1三处引脚必须保持一致——原文档与源码都强调一旦两侧各自漂移写端会生成新版 Lance 清单读端将无法打开表被误报为数据库缺失。兼容入口说明generate_db.py与generate_sqlite.py是等价的入口后者的存在是为了兼容旧工作流二者都调用build_generation()构建完整配对索引。只运行其中一个、运行一次即可重复运行会在docs/docs_db下生成新的 Generation 并切换指针。三、抓取层原理只抓 /docs、只保留正文、并发限速crawl_docs.py 是 Playwright 异步爬虫其行为可以拆成四条规则1. 站点与路径白名单。is_valid_url()crawl_docs.py只接受cua.ai/www.cua.ai且路径为/docs或以/docs/开头的 URL并跳过.pdf、.png、.jpg、.svg、.css、.js、.woff2等资源扩展名以及#、mailto:、javascript:链接。种子 URL 是https://cua.ai/docs通过页内href递归发现新链接——它只覆盖从该根可链接到的页面不保证覆盖仓库中未被链接的页面这是原文档明确的覆盖边界。2. 只等正文、不等网络空闲。load_docs_page()crawl_docs.py以domcontentloaded加载页面然后wait_for_selector(article, main)——等待文档正文挂载而非背景网络活动这既提升速度又避免误判超时。3. 正文净化。HTMLToMarkdowncrawl_docs.py是零依赖的 HTML→Markdown 转换器优先限定在article回退到main容器内提取丢弃script/style/svg/nav/aside/footer等站点外壳。这样抓下来的语料是文档正文而不是每页重复的导航树——后者如果进入 embedding 语料会严重稀释检索质量。html_to_markdown()crawl_docs.py通过正则探测article/main标签来决定作用域。4. 并发限速与 URL 归一化。MAX_CONCURRENT 5、DELAY_BETWEEN_REQUESTS 0.5秒normalize_url()去掉尾部斜杠与锚点防止重复抓取。每个页面单独落盘为 JSON文件名由 URL 路径替换/为_生成并汇总出_all_pages.json、_summary.json——这些在原文档中明确标注为诊断输出不是索引输入。四、语料快照一次抓取只能产生一份完整语料爬虫结束后调用capture_corpus()docs_index.py生成docs/crawled_data/_corpus.json。该函数是流水线的第一道完整性闸门存在failed_urls、空 URL、重复 URL、或已发现 URL 集合 ≠ 已抓取页面 URL 集合时直接抛ValueError(Incomplete crawl: ...)通过后才按 URL 排序写入快照快照包含format_version、source、captured_atUTC ISO 时间、corpus_id页面内容的 SHA-256与expected_urls全集。写盘采用先写_corpus.pending.json再replace为_corpus.json的原子替换方式crawl_docs.py。_corpus.json只有在每个发现的 URL 都成功抓取后才会产生失败时上次完成的快照会被保留调度工作流在进入生成阶段之前即停止。load_corpus()docs_index.py加载时会重新计算corpus_id做身份校验防止快照被篡改或损坏后继续进入构建。五、生成层一份快照、两个索引、一次校验generate_db.py的逻辑极简——它只是build_generation()activate()的薄封装generate_db.py。真正的工作在 docs_index.py 的build_generation()准备页面prepare_pages()docs_index.py用markdown-it-py解析每页 Markdown抽取正文纯文本没有可检索文本的页面被记录进exclusions原因 no searchable text而不是静默丢弃。生成 build IDuuid.uuid4().hex32 位十六进制字符串。分块make_chunks()docs_index.py以 1000 字符窗口、800 字符步长做重叠分块每个 chunk 携带build_id且短页面与短段落同样进入两个索引不因页面短而丢失。写 SQLitewrite_sqlite()建三张表——pagesurl/title/category/content/build_id/description/subcategory/page_name/raw_markdown、pages_ftsFTS5 外部内容表指向pages的content列、index_metadata记录 build_id/corpus_id/captured_at/source。写 LanceDBwrite_vectors()用sentence-transformers注册表加载all-MiniLM-L6-v2384 维按DocChunk模式text/vector/url/title/category/chunk_index/build_id分批每批 100 条写入docs.lance/表docs。配对校验validate_pair()docs_index.py以只读模式打开 SQLite校验index_metadata与 manifest 一致、pages.url集合等于included_urls、向量表的 URL 集合与 chunk 数完全对齐、两侧所有行的build_id都与 manifest 一致。写 manifest 与文件哈希manifest 记录语料 ID、抓取时间、embedding 模型与维度、期望/包含/排除 URL、chunk 数以及所有文件的 SHA-256 清单。每次成功构建在docs/docs_db下产生如下布局docs/docs_db/ current.json # build ID 和 manifest hash原子指针 generations/build-id/ manifest.json # corpus ID、期望/包含 URL、排除项、抓取时间、 # embedding 模型、文件哈希 docs.sqlite # pages、pages_fts、index_metadata docs.lance/ # 携带相同 build ID 的 chunks关键语义激活前必须验证 URL 集合、build ID、chunk 数与文件哈希失败构建会保留上一个活跃 GenerationGeneration 一旦激活即不可变为回滚而保留垃圾回收是独立的运维操作activate()docs_index.py对本地文件系统采用写临时指针文件再原子 rename的方式先写.current-uuid.json再替换current.json。六、调度与发布Modal 每日任务与指针最后写入6.1 两条调度任务modal_app.py 定义了 Modal 应用cua-docs-mcp挂载两个持久卷cua-docs-data、cua-code-index通过 GitHub Token Secret 克隆仓库、通过 OIDC 联邦身份假设 AWS IAM 角色无静态密钥写入 S3文档scheduled_crawl()每日6:00 UTC执行crawl_docs()→generate_docs_indexes()→sync_to_s3(publish_codeFalse)代码scheduled_code_index()每日5:00 UTC执行代码索引在文档之前。6.2 生成必须先建后提generate_docs_indexes()modal_app.py体现了提交顺序即正确性在本地临时目录执行build_generation()构建并校验配对索引把完整 Generation 目录复制到 volume 上的generations/build-id/verify_files()复核文件清单与哈希第一次docs_volume.commit()让完整 Generation 对消费者可见写current.jsonpointer_for(manifest)即{build_id, manifest_sha256}第二次docs_volume.commit()让指针最后生效。因为 Modal volume 不支持原子 rename所以必须先提交 Generation、再提交指针——任何一次中断都不可能让指针指向不完整的 Generation。6.3 发布契约先传对象再写提交标记publish_generation()docs_index.py是 S3 侧的发布协议先verify_files()并确认 manifest 与本地一致然后按文件名清单上传docs_db/generations/build-id/下的所有对象与 manifest.json最后put_object写docs_db/current.json。任何中断都只会留下未指向的孤儿对象不会改变对外服务指针。发布侧注意事项原文档明确文档调度传publish_codeFalse不发布也不删除代码索引对象代码索引仍走旧的_publish_dir()发布路径modal_app.py非原子、会清理 stale 对象并传publish_docsFalse文档 Generation 契约不背书代码索引的新鲜度——二者是独立生命周期存在每日调度不等于新鲜度证据源构建、对象存储发布、消费者拷贝、读者激活是四个独立步骤调度只是发起动作。七、消费端契约5 步校验协议与 S3 同步实操7.1 读者MCP 服务端的刷新逻辑容器每次收到 docs 请求都会检查current.jsonmain.py 通过GenerationReader(DOCS_DB_PATH)管理。GenerationReader.get()docs_index.py执行读取current.json指针校验 build_id 为 32 位十六进制若指针与当前活跃 Generation 相同则直接返回复用句柄否则读取generations/build-id/manifest.json校验 manifest 的 build_id 与 SHA-256 与指针一致verify_files()校验文件清单与每个文件哈希打开 LanceDB 表并validate_pair()打开 SQLite两个句柄都成功后才替换self.current。任何一步失败都会记录 Docs generation refresh rejected 并保留上一对良好句柄若从未加载过任何已验证配对docs 查询会显式失败而非降级。值得注意遗留的扁平docs.sqlite/docs.lance/文件不被视为已验证 Generation有专门测试test_legacy_indexes_are_not_claimed_as_verified覆盖。7.2 存储消费者必须实现的契约任何文档存储消费者都应遵循以下 5 步原文档原文契约只读一次docs_db/current.json记住该 build ID 与 manifest 哈希下载其 manifest 与清单中的每个文件到服务 Generation 目录之外的 staging 目录校验 manifest 哈希与每个文件哈希把完整目录安装到DOCS_DB_PATH/generations/build-id/不得改动任何已有 Generation仅在完整目录可见后原子替换本地current.json在读者仍持有旧句柄期间保留旧 Generation验证SELECT * FROM index_metadata应报告预期的 build 与 corpus用精确 URL 向量查询比对每个pages.url含短页面对照 manifest 检查一个被刻意排除的空页面。7.3 官方 S3 消费者sync_docs_index.py仓库提供了现成的 S3 消费者 sync_docs_index.py需与服务进程分离运行对 docs 目录有写权限、通过标准 AWS 凭证链提供只读 S3 凭证python /app/sync_docs_index.py --bucket DOCS_BUCKET --root /data/docs_db将DOCS_BUCKET替换为源桶名--root也可用DOCS_DB_PATH环境变量提供。其行为细节只下载 manifest 白名单内的文件safe_relative_path()sync_docs_index.py只接受docs.sqlite或docs.lance/下的相对路径拒绝..、.、//、反斜杠、绝对路径与 manifest 碰撞拒绝符号链接staging 与已装 Generation 都会_reject_symlinks()下载后verify_files()复核哈希本地 advisory 锁串行化写者destination_locksync_docs_index.py对.sync-docs-index.lock做flock非阻塞抢锁默认最多等 300 秒失败清理范围最小只删除自己未完成的 staging 目录.stage-build-id-uuid不动已有 Generation 与遗留文件幂等重复运行会先校验已装 Generation 再决定是否复用verify_existing_generation()测试test_idempotent_rerun_downloads_nothing保证二次运行零下载边界参数--allow-missing-pointer仅用于首次迁移——远程无指针且本地也无current.json时允许成功绝不容忍访问被拒、Generation 损坏或本地已激活后远程指针缺失装好首个 Generation 后必须去掉该标志--lock-timeout锁等待秒数默认300sync()还会校验其为有限非负数。7.4 本地文件系统镜像对于不使用 S3 的本地镜像copy_generation(source, destination)docs_index.py实现校验复制与指针激活读取源current.json→ 校验 build_id 与 manifest 身份 →verify_files→copytree到目标generations/build-id/→activate()。原文档特别强调把桶整体用无序 sync 复制一遍不是激活协议部署配置也属于本目录之外的运维范畴。八、回滚、迁移与发布纪律回滚Generation 不可变且被保留回滚即把current.json指针切回旧 build发布顺序纪律先灰度存储消费者的 staging-copy 支持 → 生成并发布一对已验证索引 → 验证文件已安装 → 再部署携带docs_index.py与匹配锁定依赖的读者镜像。若新读者只面对遗留扁平文件部署docs 查询将不可用端到端检查通过前保留旧镜像与旧 Generation读者发布 ≠ 存储迁移读者发布不会自动迁移存储消费者或创建配对索引必须显式完成并验证这些前置条件后再合并/发布可能被自动化部署捡走的读者变更权限边界生产环境的生成、发布、部署命令会变更远端状态需要相应的运维授权。九、代码索引覆盖标签分组、文件过滤与容量上限代码索引与文档索引共享 MCP 服务但生命周期独立由scheduled_code_index()5:00 UTC驱动标签分组parse_tag()modal_app.py把可解析的仓库标签按前缀分组component-vmajor.minor.patch...归入对应组件裸v...标签归入cua。每个标签都会扫描仓库全树的匹配文件而非仅扫描组件自身目录——因此组件标签标识的是标签家族不是该名称下每个产品的覆盖证明。文件过滤SOURCE_EXTENSIONS {.py, .ts, .js, .tsx}Rust 与 Swift 文件不在该过滤器的覆盖范围内。容量上限以解码后字符串长度计SQLite 接受最多1,000,000 字符向量索引接受最多100,000 字符MAX_FILE_SIZE_SQLITE/MAX_FILE_SIZE_EMBEDDINGS见 modal_app.py含 NUL 字节的前 1KB 视为二进制跳过。覆盖枚举版本、组件、语言覆盖必须以实际服务数据库为准不能凭调度存在与否推断-- 有哪些组件 SELECT DISTINCT component FROM code_files; -- 有哪些语言 SELECT DISTINCT language FROM code_files; -- 某个组件有哪些版本 SELECT DISTINCT version FROM code_files WHERE component agent;构建方式各组件并行index_component.starmap最多 4 并发产出code_index_component.sqlite与.lancedb再由aggregate_code_databases()汇并为code_index.sqlite与code_index.lancedb通过ATTACH DATABASE复制行FTS 触发器自动填充并保留向量。注意代码索引的每日调度同样不证明某个标签、组件或语言一定存在于服务索引中。十、MCP 服务端四个只读工具与冷启动优化main.py 基于 fastmcp 2.xfastmcp2.14.0,3pyproject.toml暴露四个只读工具工具数据源用途query_docs_dbpages/pages_ftsSQL 查询与 FTS5 全文检索仅允许 SELECTquery_docs_vectorsdocs.lance/自然语言语义检索384 维 embeddingquery_code_dbcode_files/code_files_fts代码 SQL/FTS5 检索query_code_vectorscode_index.lancedb代码语义检索启动时的关键优化main.pyembedding 模型启动即加载all-MiniLM-L6-v2避免首次检索冷启动数据库连接启动即初始化docs 侧通过GenerationReader惰性刷新code 侧在路径存在时立即以只读 URI 模式打开 SQLite、连接 LanceDB 表——code 索引缺失时工具调用显式报错而非静默降级所有查询都经过 OpenTelemetry 埋点mcp_requests_total计数与mcp_request_duration_seconds直方图可观测性内置。MCP 工具还内置了工作流指引语义检索用query_docs_vectors、关键词检索用 FTS5MATCH代码侧通过SELECT component, COUNT(DISTINCT version) FROM code_files GROUP BY component枚举版本面检索时按componentversion:path引用来源。十一、离线验证合成 embedding 跑测试、锁文件校验在不访问外网的情况下可以用合成 embedding 运行三组核心测试原文档给出的命令uv run --no-project --with pytest --with pytest-asyncio --with markdown-it-py \ --with lancedb0.37.1 --with pyarrow25.0.1 python -m pytest -q \ docs/scripts/tests/test_docs_index_corpus.py \ docs/scripts/tests/test_docs_index_publication.py \ docs/scripts/tests/test_docs_index_sync.py uv lock --project docs/scripts/docs-mcp-server --check --offline测试矩阵覆盖了流水线的全部关键不变量是理解系统行为的最佳入口test_docs_index_corpus.py页面就绪不依赖网络空闲、缺失发现页/损坏快照/缺失向量页/混合 build_id 均拒绝构建、失败 embedding 构建保留上一 Generation、读写依赖版本引脚一致test_reader_writer_dependency_pins_matchtest_docs_index_publication.py不完整发布保留旧指针、发布标记最后写入、读者无重启刷新双句柄、不完整消费者拷贝保持 last-good、指针与 manifest 不匹配/文件损坏均保留 last-good、遗留扁平索引不被认作已验证、调度发布不污染另一索引test_docs_index_sync.py只下载白名单键、幂等重跑零下载、中断下载保指针可重试、manifest/文件哈希不匹配在激活前失败、畸形/超大指针拒绝、不安全路径与符号链接在下载前拒绝、已装 Generation 冲突不改动、损坏 Generation 关闭式失败、源指针在拷贝期间变化时安装指定 Generation。十二、实操自检清单在本地或生产复现整套体系时建议按以下顺序验证uv lock --project docs/scripts/docs-mcp-server --check --offline通过且lancedb/sentence-transformers/pyarrow三处版本在 pyproject.toml、modal_app.py 与 docs_index.py 中一致跑完三步本地命令后docs/crawled_data/_corpus.json存在且_summary.json无失败 URLdocs/docs_db/current.json指向的generations/build-id/内manifest.json、docs.sqlite、docs.lance/齐全且verify_files逻辑可复核哈希用sync_docs_index.py --allow-missing-pointer完成首次迁移后去掉该标志再跑一次确认输出为{status: current, ...}幂等用SELECT * FROM index_metadata与SELECT DISTINCT component FROM code_files分别确认识别到预期 build/corpus 与代码组件覆盖。这套不可变 Generation 原子指针 双端校验的模式为 CUA 文档与代码检索提供了可回滚、可审计、读端无状态切换的基础设施其每一步都可在 docs/scripts 目录及对应测试中找到可验证的实现依据。【免费下载链接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.项目地址: https://gitcode.com/GitHub_Trending/cua/cua创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号