AI Workshop with Neo4j and Google, Cambridge 2026: Knowledge Graphs for Agents
Table of Contents
- Event
- What the organizers advertise
- Why this one is worth attending
- Open questions to bring
- What happened
- The questions brought, and what came back
- Lessons
- An answer from a template and a generated answer look the same
- Read the generated tool before the answer
- A shared, writable demo database drifts for the length of the class
- A constraint from one load decides what the next can write
- The transport succeeding is not the statement succeeding
- A loader that has only met a loaded database is untested
- Neo4j's vector index is approximate, and at this size that costs more than it saves
- Write the gate before the run
- Small and local before a service
- What is left is what was captured
- Driving a browser lab from an agent
- What was not checked
Event
| Field | Value |
|---|---|
| Event | AI Workshop with Neo4j and Google |
| Hosts | Neo4j, Google Cloud |
| Date | Thursday, 8 October 2026 |
| Time | 13:30 EDT |
| Venue | Google Cambridge, 355 Main St, Cambridge MA |
| Format | Hands-on workshop, live cloud environment |
| Capacity | 50, described as "qualified builders" |
| Registration | 94 RSVPs on 2026-10-08 against 50 seats (21 on 2026-09-22) |
Speakers: Ben Lackey (Neo4j), Upasana Pati (Google). The September listing named Ezra Uzosike for Google; the listing on 2026-10-08 names Pati.
What the organizers advertise
Building AI agents powered by knowledge graphs, working against real datasets and deploying Neo4j alongside Google Cloud Gemini Enterprise in a live cloud environment. The advertised path runs end to end: parse and load data, then construct agents that query and reason over the connected result.
The four stated outcomes, verbatim:
Build AI agents that leverage knowledge graphs Integrate Neo4j with Google Cloud Gemini Enterprise in a production-ready environment Traditional vs. AI-driven data parsing and loading Practical approaches to graph-based AI using real-world datasets
Audience is given as developers, data engineers, data scientists and tech leaders.
Why this one is worth attending
The third bullet is the one with a live question behind it. Traditional versus AI-driven parsing and loading is a comparison, and comparisons are where the interesting failure lives: the two paths produce different graphs from the same source, and the workshop format means both get built in the same room against the same data. What to watch for is whether the session states an acceptance criterion for the AI-driven path, or only demonstrates that it runs. An extraction pipeline that has never been shown a document it should refuse is not yet a pipeline.
This repository already runs a code knowledge graph — GitNexus, currently 1473 symbols and 3469 relationships over this site's sources, used for impact analysis before edits. That graph is built by a deterministic parser. The question this workshop is positioned to answer is what an LLM-driven extractor buys over that, and what it costs in precision, on a corpus where the ground truth is known.
Related, and close enough to read together:
- DevFest:Extended Boston 2026 — "escape naive loops with graph engineering", 33 days later at the Google Boston office. Same thread, different vendor framing: DevFest treats the graph as agent control flow, this one treats it as the data substrate.
- Code Search and Code Graph MCP Servers on FreeBSD — where Neo4j already appears in this corpus, surveyed rather than run.
- Agent Memory as Institutional Knowledge — the retention side of the same question.
Open questions to bring
- What is the acceptance test for an LLM-extracted graph? Precision and recall against a hand-labelled subset, or eyeballing the visualization?
- Where does Gemini Enterprise sit relative to the graph — generating Cypher, or consuming retrieved subgraphs as context?
- What happens to the graph when the source changes? Incremental update, or rebuild?
- Does the "production-ready environment" claim survive contact with authentication and multi-tenancy, or is that out of scope for the session?
What happened
The session was three hours and nine labs, run in a browser against a hosted Aura instance and a lab Google Cloud project. Labs 1 to 5 and 7 to 9 ran as written. Lab 6 did not: the account had no Document Intelligence, so its result was reproduced through the LLM Graph Builder's API. The lab account, project and instance expired at about 21:20 EDT the same evening.
The working repository is jwalsh/neo4j-google-ai-workshop-2026: the notes, every query and prompt a lab had a person type, and Clojure tooling that does as much of each lab as code can. After the instance expired the work moved to embedded Neo4j servers on a laptop: the filings from Lab 4, then papers, books, Boston's rail network from its GTFS feed, and a hotel graph from another Neo4j workshop.
The questions brought, and what came back
- What is the acceptance test for an LLM-extracted graph? The notes record none. The labs show that an extraction runs. The Graph Builder wrote 117 entity nodes over two runs; nothing in the lab says how many there should be.
- Where does Gemini Enterprise sit? It generates Cypher. The marketplace
agent of Labs 7 and 8 writes statements,
CREATEincluded. The Aura agent of Lab 9 has both: Cypher templates a person can read, and a Text2Cypher tool that writes its own. - What happens when the source changes? Not covered. Every load in the labs and in the repository merges. Nothing removes a node whose source row is gone.
- Does "production-ready" survive authentication and multi-tenancy? Out of scope for the session. The one shared database the class wrote to is the evidence on multi-tenancy: see the third lesson.
Lessons
An answer from a template and a generated answer look the same
Lab 9's agent was asked for companies held by exactly three managers. Its
Text2Cypher tool wrote m.name and c.name, which do not exist, and got no
rows. The agent's own reasoning says so. The answer was three tidy rows
anyway, one of them a company with nine owners in the database. The answers
that came from a Cypher template were right. On the page the two kinds are
indistinguishable.
Exporting the agent and running its templates from a REPL is what told them apart. The local version that followed keeps only the first kind: a small model chooses one of a person's templates and fills its parameters, and never writes a statement. One model of four passed the gate set for that.
Read the generated tool before the answer
"Uncommon companies" was answered by a generated template that requires at least two owners and sorts by name. The 805 single-owner companies never appear, and the first ten rows are the start of the alphabet. The agent stated the ceiling and not the floor. A vague word in a question is defined by whatever statement answers it.
A shared, writable demo database drifts for the length of the class
Lab 8 says the instance behind the Gemini Enterprise agent holds the SEC filings and that creating a second "Bob" overwrites the first. By the time it was queried it held a CRM-shaped sample and 28 Bobs under four label schemes. Describe a database before believing the handout about it.
A constraint from one load decides what the next can write
Lab 4 puts a node key on (:Company {cusip}). The Graph Builder's extraction,
run into the same database for Lab 6, then fails the moment the model produces a Company from a 10-K: no CUSIP, no node.
Neither tool mentions the other. Before a second source writes into a
database, list its constraints.
The transport succeeding is not the statement succeeding
The same shape bit twice. First, curl could not resolve the host, and the
pipeline's exit status was jq's: a wait loop built on it broke out on its
first, false, success. Then Aura's Query API reported a failed statement with
HTTP 200 and an errors array beside empty data, and the same pipeline
printed the empty data and exited 0.
A loader that has only met a loaded database is untested
The Form 13 loader returned early on an instance that already held the data,
which is the only kind it had seen. On an empty local server it stopped at
the first IN TRANSACTIONS statement, which needs an auto-commit session.
Running the lab's load on an embedded Neo4j is what found it, in time to fix
it and run it against Aura before the instance expired.
Neo4j's vector index is approximate, and at this size that costs more than it saves
Two of the local graphs carry a vector store: 7,331 vectors of 768 dimensions from a small embedding model run locally. Over twelve questions, the vector index returned 78 % of the true ten nearest on one graph and 79 % on the other.
| Search, on the larger store | Median, 7,036 vectors | Finds the true ten |
|---|---|---|
| Neo4j vector index | 8.8 ms | 78 % |
| Exact cosine in Cypher | 115.7 ms | all |
| Exact, sqlite-vec, one 25 MB file | 10.8 ms | all |
Measured 2026-10-11. At a few thousand vectors exact search is affordable and the index is the wrong default. The same run found a second thing: one book repeats a heading a dozen times with identical vectors, so which ten come back is arbitrary under any search. Duplicates in the source are ties in the answer.
Write the gate before the run
Embedding through a shared gateway with a per-seat key was checked against the local model with seven gates written and committed first. Six passed: a question's vector agreed to six decimal places by either route. The seventh, "the same top five either way", failed for one question in twelve. A probe showed both routes agree exactly under exact search; the disagreement was the index above. Without the gate the miss would have been blamed on the gateway.
Small and local before a service
The order that worked: a file, then an embedded server in the same JVM, then anything hosted. Lab 4's database and Lab 9's agent had local stand-ins within three days, and the loader bug above was found only on the stand-in. The hosted environment is also the part that expired.
What is left is what was captured
Three days on, the lab account is gone. What remains is the queries, the prompts as files, redacted screenshots, the agent's export and the numbered findings. A lab is repeatable to the extent those were written down while it ran. Committing in small pieces as each step works is the same rule applied to the tooling; the stretch where that lapsed is the stretch that would be hardest to rebuild.
Driving a browser lab from an agent
The labs were driven over the DevTools protocol in a browser with an empty profile, so the lab's account never met a personal one. What that cost:
- One driver at a time. Two hands on one field garbled a sign-in, and a failed sign-in on a lab account counts against a small limit.
- A driver must not take focus. A script that raised the window sent the person's next keystrokes into the query editor, twice.
- Click where the page reports an element is, with a real input event.
element.click()opens no tab, and coordinates read off a scaled screenshot land above the target. - A screenshot of the instance-created page holds the password in clear.
What was not checked
- Whether the Graph Builder's 117 entities are the right 117. No labelled subset exists.
- The Lab 9 agent through its endpoint or as an MCP server; only the console's preview chat was used.
- The vector comparison on anything larger than these two stores, or with a tuned index.