Skill-Graph Acquisition — Robustness Ledger¶
A running catalogue of per-site discrepancies and failure modes found while migrating legacy skill-graphs, and the robustness mechanism each one drove. The goal is a build pipeline that picks the right acquisition strategy per site automatically and never silently degrades.
The detector is skill_graph_pipeline.detect_scrape_strategy(url) (the
SiteProfiler); every build records what it detected in sources.json
under scrape_profile so a graph carries its own provenance of how it was built.
Strategy ladder (what the SiteProfiler picks, in order)¶
llms-full.txt— the whole docs corpus in one fetch (no JS, no bot-block, no shrink). Best case. Split by H1/H2; coalesced to ~48 KB files past 400 sections so huge corpora stay navigable.llms.txtindex — a curated[name](url)link list; each page fetched (HTML stripped to markdown if the link doesn't serve.md).sitemap.xml(or aSitemap:line in robots.txt) → bounded web crawl.- recursive render (crawl4ai) — last resort for JS-rendered sites with no machine-readable index.
Scope matters: each of (1) and (2) is probed at the docs-path scope first
(/docs/llms-full.txt), then the domain root — because a root /llms.txt is
often a marketing-site index, not docs.
Ledger¶
| # | Site / case | Discrepancy observed | Root cause | Robustness mechanism |
|---|---|---|---|---|
| R1 | langchain (docs.langchain.com) |
Legacy graph had only 2 files; recursive crawl yielded almost nothing | Client-side-rendered docs site; blind HTML crawl gets shell pages | SiteProfiler detects llms-full.txt → 301 files / 11.8 MB full corpus |
| R2 | langchain | A naive H1 split of llms-full.txt makes 2,548 tiny per-API-method files |
Each API method is its own # heading |
_split_llms_full coalesces adjacent sections into ~48 KB files past _MAX_LLMS_SECTIONS=400 → 301 navigable files |
| R3 | mariadb (mariadb.com) |
Root llms.txt returned marketing pages (pricing/about-us/industry), not docs |
Domain-root /llms.txt indexes the marketing site; real docs corpus is at /docs/llms-full.txt |
_llms_scopes probes the docs-path scope before root; mariadb now resolves to /docs → 107 real-doc files |
| R4 | mariadb | Fetched pages stored as raw <!doctype html> in the corpus |
llms.txt links served HTML, not markdown; index path stored the body verbatim |
_fetch_llms_index detects HTML (_looks_like_html) and strips to markdown (_html_to_markdown: trafilatura → regex fallback). 1.65 MB raw HTML → 10.8 KB clean text; 0 HTML leaks |
| R5 | uptime-kuma, gcp | No llms.txt; recursive crawl is slow/hang-prone |
Sites publish a sitemap but no LLM index | SiteProfiler falls back to sitemap.xml-driven bounded crawl before recursive render |
| R6 | two queued repositories (Wave-1 KG pass) | KG-processing returned 0 nodes after another repository succeeded in the same batch | Embedder/engine dropped mid-batch (accelerator endpoint → 502) — not a content problem | Bounded embed + per-cycle embedder + health-gate already abandon-on-timeout instead of hanging; retry once the engine is healthy (open) |
| R7 | All llms-full.txt |
A tiny stub or 404 body can be mistaken for a real corpus | Some sites return a 200 with a near-empty llms-full.txt |
_fetch_llms_docs requires len(full) > 2000 before treating it as the corpus; else falls through to the llms.txt index / sitemap |
| R8 | Batch migration | One slow web crawl (gcp first in the batch) blocked all 24 following graphs for up to the 900 s crawl bound — zero visible results | A serial batch runs in list order; a heavy crawl at the front hides every fast result behind it | Batch runner pre-detects strategy (HTTP-only) and runs llms graphs first, web crawls last; web crawls get a tight SKILL_GRAPH_CRAWL_TIMEOUT=240 + SKILL_GRAPH_MAX_PAGES=400 so a slow site fails fast and keeps existing content |
| R9 | gcp (cloud.google.com) |
A generic depth-3 / 1000-page crawl from /docs/overview is unbounded-feeling and low-value (pulls the whole GCP marketing+docs tree) |
cloud.google.com is enormous and has no llms.txt; "overview" is not a scoped corpus |
Deferred: a meaningful gcp graph needs a scoped product path (e.g. /run/docs, /compute/docs) as the seed, not the docs root. Open — pick target product(s) (open) |
Open items¶
- R6: re-run KG-processing for
chakra-ui-docsandpandas-docsonce the KG engine/embedder is confirmed healthy (the recorded fault was hardware-related). The reference trees are already migrated; only the KG ingest needs a retry. - gcp (
cloud.google.com): very large; sitemap crawl must stay bounded (max_pages) — confirm the cap is logged, not silently truncating.
Invariants the pipeline now guarantees¶
- Never store raw HTML in a reference corpus (R4).
- Prefer a curated docs corpus over a marketing index (R3).
- Keep file counts navigable in either case of corpus size (R2).
- Degrade strategy gracefully (llms → sitemap → render), never hang (bounded
fetch + process-group kill), and record the chosen strategy in
sources.json.