The Graph Was Already There: Answering Questions Across Documentation Silos
Every enterprise tech department documents its own machinery impeccably, and then acts astonished when an engineer tries to connect the engine to the gearbox and finds the manual ends at the bulkhead. Here is how the DITA structure our writers already curate can bridge those chasms without spending millions training a computer to guess the obvious.
Portfolio post, written as an internal knowledge-sharing post for Google's data center documentation team. Google hasn't engaged, sponsored or endorsed this work. Sample content is adapted and modified from Google Cloud documentation, which Google publishes under the Creative Commons Attribution 4.0 licence1. People, questions, issue numbers and projections are written for this post.
Modern cloud infrastructure is built by obsessively specialized teams, and predictably, their manuals reflect the exact same territorial boundaries. The silicon division documents the accelerator ASICs. The networking division documents routing tables. Internally, each documentation corpus is a masterclass in pedantic perfection—meticulously drafted, rigorously reviewed by product specialists, and cross-referenced with religious devotion. But customer workloads do not respect corporate org charts. An engineer training an AI model on a cluster of TPUs requires computing power, network fabric, and storage buckets simultaneously, on a Tuesday afternoon.
Consider this seemingly innocuous technical query:
My TPU VM has no external IP address. How does it reach a package index on the internet?
Open the TPU manual under Configure networking and access. It gives you exhaustive instructions on provisioning a TPU VM devoid of an external IP address, followed by a reassuring note that such instances can happily communicate with internal Google APIs via Private Google Access2. Marvelous. That covers Cloud Storage. It does precisely nothing for pulling down wheels from PyPI or npm. That salvation sits three organizational silos away inside the Cloud NAT manual, where Public NAT allows private IPv4 machines to traverse the public web3. Neither TPU networking page even acknowledges that Cloud NAT exists. Both manuals are technically immaculate, but the vital connective tissue between them exists entirely inside the skulls of three weary senior engineers.
Enter KnowledgeFlow4. It is an agent architecture designed to answer cross-silo questions by traversing a verified path through the existing documentation, validating every assertion with software rigor before presenting it to the engineer. The fundamental insight is simple economics. Spending tens of thousands of dollars instructing an LLM to infer a massive graph from raw paragraphs is madness when our technical writers already built most of one inside our DITA source—complete with maps, classification schemes, shared keys, and relationship tables. Let’s examine this from the writer's desk and the developer's terminal, and then dissect the business metrics.
On one question
Figure 1 tracks our TPU question across the documentation as it stands today, set against the KnowledgeFlow pipeline. The elapsed timestamps are illustrative, but anyone who has worked in enterprise tech will recognize the agony instantly. The real bottleneck is never the computation; it is waiting for an expert on Slack to finish their lunch.
This is hardly an isolated corporate annoyance. The 2024 Stack Overflow Developer Survey polled professional engineers on the hours they flush away every single day either fruitlessly searching for answers, or being badgered by colleagues to provide them5. Roughly two-thirds spend 30 minutes or more per day searching. Nearly half spend another half-hour playing human search engine for someone else. Back in 2022, Stack Overflow calculated that a standard engineering shop of 50 developers bleeds between 333 and 651 productive hours every week just hunting for basic technical answers6.
And then comes the punchline. A full 70.3% of surveyed developers cheerfully declare that they know exactly which system or portal houses the answers they need. Yet in the very same survey, 53% admit that waiting around for critical technical answers constantly derails their daily work5. Both statements are entirely true. You know precisely where your half of the puzzle lives; the trouble is that the remaining piece belongs to a team on the third floor who haven't updated their Slack status since last Thursday.
On writers and graphs
Technical writers who operate in DITA have spent decades thinking in pure graph topology without making a fuss about it. A master map establishes hierarchy and sequential precedence. A relationship table explicitly couples a task to its conceptual theory and its API reference7. A key definition asserts that an operational constant is defined once and injected everywhere. We maintain this machinery because it keeps our publishing builds predictable and sane. But here is the revelation: each of those structures is already a directed, typed, verified edge in an enterprise knowledge graph.
Microsoft’s GraphRAG constructs its knowledge graph by burning enormous compute cycles letting an LLM parse every sentence in a document hoard. Even Microsoft’s own documentation concedes that you should only deploy it when the business payoff dwarfs the eye-watering cost of constructing that synthetic index8. In structured technical documentation, that trade-off makes no sense whatsoever. The index was already compiled by a human writer who agonized over every single link. Figure 4 illustrates how KnowledgeFlow extracts it without breaking a sweat.
On shared subjects
Relationship tables and cross-references operate brilliantly inside a single product's borders. But linking across products typically demands that a writer on Team A understand the intricacies of Team B's release cycles, and maintain brittle external URLs across breaking updates. KnowledgeFlow solves this with far less friction. A DITA subject scheme map establishes a controlled vocabulary of shared operational concepts9, creating a stable semantic taxonomy10. Each product team simply tags their topics with those shared subject keys. If two topics share the no-external-ip subject, the graph instantly establishes a traversable edge between them, without either team ever editing the other's maps.
The architects of the DITA standard explicitly designed the classification domain for this exact purpose: enabling discovery engines to retrieve allied content by subject rather than plain text matching11. Below is the canonical subject map shared across our cloud documentation.
<subjectScheme> <title>Data center documentation subjects</title> <subjectdef keys="products"> <subjectdef keys="cloud-tpu"><topicmeta><navtitle>Cloud TPU</navtitle></topicmeta></subjectdef> <subjectdef keys="cloud-nat"><topicmeta><navtitle>Cloud NAT</navtitle></topicmeta></subjectdef> </subjectdef> <subjectdef keys="tpu-generations"> <subjectdef keys="tpu7x"> <topicmeta> <navtitle>TPU7x</navtitle> <keywords><keyword>Ironwood</keyword></keywords> </topicmeta> </subjectdef> <subjectdef keys="tpu-v6e"><topicmeta><navtitle>TPU v6e</navtitle></topicmeta></subjectdef> </subjectdef> <subjectdef keys="network-access"> <subjectdef keys="no-external-ip"> <topicmeta><navtitle>VMs without external IP addresses</navtitle></topicmeta> </subjectdef> <subjectdef keys="private-google-access"> <topicmeta><navtitle>Private Google Access</navtitle></topicmeta> </subjectdef> <subjectdef keys="public-nat"> <topicmeta><navtitle>Public NAT</navtitle></topicmeta> </subjectdef> </subjectdef> </subjectScheme>
Notice that the tpu7x node explicitly preserves Ironwood as an alias keyword, reflecting its title in the hardware catalog12. When an engineer casually refers to Ironwood in a query, the graph routes straight to the correct hardware entity. One single physical component maps to one discrete graph node, regardless of marketing nomenclature.
Each team then tags their local topics. In the TPU map, standard topicsubject tags attach the relevant taxonomy via subjectref elements13,14. Crucially, the team's internal relationship tables remain completely untouched.
<map> <title>Cloud TPU</title> <mapref href="shared-subjects.ditamap" type="subjectScheme"/> <topicref href="tasks/configure-networking.dita" keys="configure-networking"> <topicsubject keyref="cloud-tpu"> <subjectref keyref="no-external-ip"/> <subjectref keyref="private-google-access"/> </topicsubject> </topicref> <topicref href="reference/tpu7x-specs.dita" keys="tpu7x-specs"> <topicsubject keyref="cloud-tpu"><subjectref keyref="tpu7x"/></topicsubject> </topicref> <!-- Unchanged: the relationship table the team keeps for publishing --> <reltable> <relheader> <relcolspec type="task"/><relcolspec type="concept"/><relcolspec type="reference"/> </relheader> <relrow> <relcell><topicref keyref="configure-networking"/></relcell> <relcell><topicref keyref="tpu-architecture"/></relcell> <relcell><topicref keyref="tpu7x-specs"/></relcell> </relrow> </reltable> </map>
topicsubject and subjectref require the DITA classification domain13. Your document type shell must declare it, otherwise the XML processor will stop dead and complain bitterly.Meanwhile, our Cloud NAT documentation operates in MyST Markdown following our MANIFEST docs-as-code migration15. It captures the exact same semantic taxonomy directly in its YAML front matter. The subjects property matches the keys established in our DITA subject scheme, bridging the format divide instantly.
--- title: Cloud NAT overview subjects: [cloud-nat, no-external-ip, public-nat] ---
The compiler sweeps through these repositories and spits out an edge ledger. Every single human-authored edge records the exact source file and line number that produced it. If an engineer asks why two disparate topics are linked, we can show them the exact line of code that created the relationship.
{"src":"tpu/configure-networking","type":"about","dst":"no-external-ip","status":"authored","from":"tpu.ditamap:6"} {"src":"nat/overview","type":"about","dst":"no-external-ip","status":"authored","from":"nat/overview.md:3"} {"src":"tpu/configure-networking","type":"related","dst":"tpu/tpu7x-specs","status":"authored","from":"tpu.ditamap:18"} {"src":"tpu/tpu7x-specs","type":"states","dst":"key:tpu7x-chips-per-pod","status":"authored","from":"tpu7x-specs.dita:31"} {"src":"tpu/troubleshoot-tpu-vm","type":"mentions","dst":"public-nat","status":"proposed","quote":"Route outbound traffic through a Public NAT gateway."}
On proposed edges
Of course, rigid structural schemas cannot capture every conversational nuance. Technical writers frequently mention another product’s service in passing prose without adding a formal XML cross-reference. Take this sample troubleshooting topic, where a DITA problem definition maps a condition directly to an operational remedy16.
<troubleshooting id="troubleshoot-tpu-vm"> <title>Package downloads time out on a TPU VM</title> <troublebody> <condition> <p>Package installs time out on a TPU VM that has no external IP address.</p> </condition> <troubleSolution> <cause> <p>Private Google Access reaches Google APIs and services. A public package index is a destination on the internet.</p> </cause> <remedy> <steps-informal> <p>Route outbound traffic through a Public NAT gateway.</p> </steps-informal> </remedy> </troubleSolution> </troublebody> </troubleshooting>
The prose clearly tells the reader to deploy a Public NAT gateway, yet there is zero structural wiring connecting this topic to the Cloud NAT repository. When this file changes, Gemini inspects the unlinked prose, extracts the statement, and suggests a provisional mentions edge to our public-nat taxonomy node. A deterministic Python validator verifies that the verbatim term actually exists in the source text. And then the system does something essential for sanity.
KnowledgeFlow can utilize that tentative edge to satisfy an engineer's immediate search, but it explicitly slaps an "unreviewed link" warning across the citation. Simultaneously, it files an edge PR to the information architect:
<reltable> ...+ <!-- Proposed by KnowledgeFlow: mentions public-nat, quote attached in PR #57 -->+ <relrow>+ <relcell><topicref keyref="troubleshoot-tpu-vm"/></relcell>+ <relcell>+ <topicref href="https://docs.cloud.google.com/nat/docs/overview"+ scope="external" format="html">+ <topicmeta><navtitle>Cloud NAT overview</navtitle></topicmeta>+ </topicref>+ </relcell>+ <relcell/>+ </relrow> </reltable>
The moment our architect merges that PR, that link ceases to be a probabilistic inference; it becomes permanent structural metadata. On subsequent compiles, it registers as a verified, human-authored edge that requires zero model calls. Better yet, standard DITA processing turns relationship table rows into live related links on the static web pages7, upgrading the published documentation for human web visitors automatically.
On gaps
If KnowledgeFlow cannot verify a complete traversable path between two concepts, it refuses to guess. It drops the uncertain step, alerts the user, and automatically logs a documentation gap ticket in GitHub. If forty engineers hit the same stumbling block this week, the ticket tallies the volume. Writers finally get genuine, quantified usage telemetry identifying where the documentation fabric is torn.
missing edge 7 questions this week · last asked 2026-10-20 question "How many NAT ports does a multi-host TPU slice need?" reached tpu/quickstart-create-multi-host-slice nat/tune-nat-configuration dropped step 2: no topic in the paths joins slice size to NAT port settings suggest relrow: quickstart-create-multi-host-slice <-> nat/tune-nat-configuration
| For the writer | Today | With KnowledgeFlow |
|---|---|---|
| Learning which links readers need | Support cases and chat threads, when someone forwards them | Gap issues with the question, the topics reached and the missing edge |
| Links between documentation sets | Written by hand, by someone who knows the other product | Shared subjects, classified once by each team |
| Links that a model finds | Unreviewed, inside whatever AI tool the reader uses | Proposals that arrive as pull requests |
| Proof behind an answer | None | A file and line for every authored edge |
On developers and questions
Software engineers experience the documentation silo from both barrels. When you need to understand another division’s service, you waste half a day hunting through their docs before slumping into their Slack channels to beg for assistance. And when you are the resident specialist, you spend your afternoons answering the exact same configuration question for the ninth time this quarter. In the Stack Overflow data, 29.6% of engineers report slamming into knowledge silos ten or more times every single week, and 26.8% have to drag in colleagues from outside their team just as often5.
The distribution of that graph is telling. Developers don't experience a gentle bell curve of mild friction. You either operate in total isolation on a self-contained microservice, or you spend your entire working life entangled in cross-team dependencies. It is precisely that second, beleaguered cohort that KnowledgeFlow liberates.
On the answer
You pose a plain question into the console. Instead of returning an essay filled with conversational padding, KnowledgeFlow returns a tight sequence of steps. Every single step names its exact parent topic and displays the graph relationship that led to it. If a link rests on an unverified model proposal, it states that upfront.
Create the TPU VM with the
--no-addressflag, so it has only an internal IP address.Cloud TPU · Configure networking and accessentry · exact match on --no-addressPrivate Google Access lets the VM reach Google APIs and services, such as Cloud Storage.
Cloud TPU · Configure networking and accesssame topicTo reach destinations on the internet, set up Public NAT. It lets resources that don't have external IPv4 addresses connect to the internet through shared external IP addresses.
Cloud NAT · Cloud NAT overviewabout · no-external-ip
Figure 7The answer the engineer sees. Each step names its topic and the edge that reached it. Answer text adapted from the TPU networking page and the Cloud NAT overview.
Variables resolve from canonical keys. Ask how many compute chips comprise a full Ironwood pod, and the system answers exactly 9,216—because that is the authoritative value declared in the TPU7x key definition12. The search walk is strictly constrained by the hardware generation tag, preventing specs from older TPU generations from bleeding into the response.
On trust
While Gemini generates the prose synthesis, it possesses zero authority over factual correctness. Four immutable Python checks evaluate the output, and the LLM cannot bypass them. If an assertion fails validation, the system forces a revision. If it fails again, the assertion is deleted entirely.
| Check | What it tests | Fails when |
|---|---|---|
| source | Every step cites a topic in the candidate paths | A step cites nothing, or cites a topic the walk didn't reach |
| literals | Numbers, units, flags, fields and commands | A literal in the step isn't in its topic or in a key value it uses |
| path | The cited topic is joined to the question | No edge path links the topic to the entry topics |
| labels | Model-found edges are visible | A step reached through a proposed edge has no label |
The literal validator does the heavy lifting. It is wonderfully unglamorous code that mechanically audits every number, flag, and metric against the source text:
import re LITERAL = re.compile(r"`([^`]+)`|(\d[\d,.]*\s?(?:GB|TB|GBps|seconds?|%)?)") def missing_literals(step: str, topic_text: str, key_values: set[str]) -> list[str]: """Return each literal in a step that its cited topic does not contain.""" missing = [] for code, number in LITERAL.findall(step): literal = (code or number).strip() if literal not in topic_text and literal not in key_values: missing.append(literal) return missing
Below is a trace from a real diagnostic query concerning dropped network connections. Gemini casually hallucinated an extra remediation step unsupported by the corpus. The checker intercepted the fabrication, ordered a rewrite, purged the hallucination, and delivered four bulletproof steps instead of five.
step 1 nat/overview source pass literals 0/0 path pass step 2 nat/tune-nat-configuration source pass literals 3/3 path pass step 3 nat/tune-nat-configuration source pass literals 2/2 path pass step 4 release-notes/2026-03-31 source pass literals 1/1 label pass (proposed edge) step 5 "Restart the TPU VMs to apply the change." no topic in the paths states it source fail revise 1 gemini-3.8-flash removes step 5 step 5 dropped result 4 steps · 4 authored edges · 1 proposed edge, labelled
On the architecture
The backend architecture uses standard cloud engineering primitives rather than esoteric AI machinery. The agent service is built on Google’s open-source Agent Development Kit running on Cloud Run18. Gemini 3.8 Flash handles text synthesis and tool invocation via standard function calling interfaces19,20. The master graph lives in Firestore documents and hydrates into an in-memory NetworkX instance upon cold boot21,22. CLI arguments, flags, and error codes are indexed with BM25 in SQLite FTS523, while semantic similarity uses Firestore vector embeddings via gemini-embedding-00124,25. The graph itself re-indexes during CI on every documentation merge.