← Projects
Part 3 · KnowledgeFlow · Knowledge graphs · DITA · Docs as code · AI agents · 14 min read

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.

CLOUD TPU DOCS · DITACLOUD NAT DOCS · MYSTConfigure networkingCloud NAT overviewno-external-ipshared subjectproposed by Gemini,waiting for review
Fig. 0Two documentation sets, one shared subject, and an edge that nobody has reviewed yet.

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.

29.6%of professional developers hit knowledge silos ten or more times a week5
70.3%say they know which system to use for answers, yet 53% say waiting on answers disrupts their work5
6 of 7edge types in the KnowledgeFlow graph come from DITA and MyST structure, with no model call4
TODAYWITH KNOWLEDGEFLOWSearch TPU docs0:00Private Google Access0:05pip install still fails0:20Ask in team chat0:35Expert: “use Cloud NAT”2:10Read NAT overview2:201 h 35 minwaiting on chatAsk KnowledgeFlow0:00:00Answer: 3 cited steps0:00:15Open NAT overview0:00:45Steps 1 and 2 cite the TPU page.Step 3 cites Cloud NAT,joined by subject.no expert is interrupted,and the path is visible
Fig. 1The same question today and with KnowledgeFlow. Illustrative times. Steps 1 and 2 of the answer come from the TPU page2, step 3 from the Cloud NAT overview3.
TPU · Cloud NAT

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.

0%10%20%30%40%Under 15 min9.3%20.5%15 to 30 min27%32.4%30 to 60 min37.9%30%60 to 120 min18.3%12.8%Over 120 min7.6%4.3%time spent searching for answerstime spent answering questions
Fig. 2Daily time professional developers spend searching for answers and answering questions. Stack Overflow 2024 Developer Survey, about 28,900 respondents for each question5.
I know which system or resource to use to find answersagree: 70.3%17.9%52.4%18.3%9.4%Waiting on answers often disrupts my workflowagree: 53.0%16%37%23.6%19.5%Strongly agreeAgreeNeitherDisagreeStrongly disagree
Fig. 3Knowing where to look, and waiting anyway. Stack Overflow 2024 Developer Survey, 28,747 respondents5.

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.

Through the writer's lens

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.

WHAT WRITERS MAINTAINHOW IT IS READEDGEbuild_graphno model callfile and linerecordedMap hierarchy · MyST toctree<topicref> nestingcontainsRelationship table row<reltable> <relrow>relatedContent reference · MyST include@conref · {include}reusesKey reference · MyST substitution@keyref · {{ key }}statesCross-reference · MyST link<xref> · [text](target)links-toSubject reference · front matter<subjectref> · subjects:aboutProse, no linkchanged topics onlypropose_mentionsGemini, then a fixed checkmentionsproposed
Fig. 4From the structure writers maintain to typed edges in the graph. Six edge types are read with no model call. Only unlinked mentions in prose go to Gemini, and they stay proposed.
DITA subject scheme

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.

shared-subjects.ditamapSubject scheme map · shared by both teams
 <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.

tpu.ditamapMap · TPU docs team
 <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>
One requirementtopicsubject 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.

nat/overview.mdMyST page · networking docs team
 --- 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.

graph/edges.jsonlbuild_graph output · one line per edge
 {"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."}
Gemini · a person approves

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.

tasks/troubleshoot-tpu-vm.ditaTroubleshooting topic · adapted sample
 <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>
Gemini proposesquotes the sentenceName in the quote?fixed checkStored as proposedmentions edgeAnswers label itunreviewed linkEdge pull requestfor the architectDroppedname not in the quoteIF APPROVEDRelationship table rowmerged into the DITA mapNext build: authoredrelated edge, no model callIF CLOSEDRejected listnever proposed againevery approved edge movesfrom the model to the writer
Fig. 5The life of a proposed edge. A proposed edge never becomes authored without a person's approval. Approved edges become structure, so the next build reads them without the model.

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:

tpu.ditamapEdge pull request #57 · for 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.

GitHub issues

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.

Gap issue #58Opened by report_gap · illustrative
 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 writerTodayWith KnowledgeFlow
Learning which links readers needSupport cases and chat threads, when someone forwards themGap issues with the question, the topics reached and the missing edge
Links between documentation setsWritten by hand, by someone who knows the other productShared subjects, classified once by each team
Links that a model findsUnreviewed, inside whatever AI tool the reader usesProposals that arrive as pull requests
Proof behind an answerNoneA file and line for every authored edge
What stays with the writerTaxonomy design, structural curation, and the final approval on every candidate edge. The AI cannot autonomously inject an edge into our production graph as authored. Branch protection requires human sign-off on every PR before any relationship row is compiled into truth17.
Through the developer's lens

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.

0%20%40%60%Never52.1%59.9%1 to 2 times2.8%1.7%3 to 5 times12.4%9.6%6 to 10 times3.1%2%10 or more times29.6%26.8%encounter knowledge silosneed help from outside the team
Fig. 6How often professional developers meet knowledge silos and need outside help. Stack Overflow 2024 Developer Survey, about 28,500 respondents5.

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.

The ask console

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.

My TPU VM has no external IP address. How does it reach a package index on the internet?Lena Hoffmann · ML engineer
  1. Create the TPU VM with the --no-address flag, so it has only an internal IP address.

    Cloud TPU · Configure networking and accessentry · exact match on --no-address
  2. Private Google Access lets the VM reach Google APIs and services, such as Cloud Storage.

    Cloud TPU · Configure networking and accesssame topic
  3. To 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
2 documentation sets3 cited steps3 authored edges0 proposed4 checks passed

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.

Checks and citations

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.

CheckWhat it testsFails when
sourceEvery step cites a topic in the candidate pathsA step cites nothing, or cites a topic the walk didn't reach
literalsNumbers, units, flags, fields and commandsA literal in the step isn't in its topic or in a key value it uses
pathThe cited topic is joined to the questionNo edge path links the topic to the entry topics
labelsModel-found edges are visibleA 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:

knowledgeflow/checks.pycheck_answer · literal check, simplified
 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.

check_answer · question Q-0412Check log
 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
EngineerAsk pageAgentCloud RunFirestoreSQLiteGeminiAPIRepoBigQueryquestionpost to the agentfind entry topics (BM25, vector)walk the graph in memorycompose from the pathsdraft answerrun four checksanswer with cited stepsanswerquestion log row, gap issue if droppedabout 7.5 s
Fig. 8Sequence for one question. About 7.5 seconds in this illustration.
Cloud Run · Firestore

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.

Still being finishedThis post is still being finished. The remaining sections and the references will be added here.
Siddharth