| name | mistral-demo-skill |
| description | Build the Mistral+Neo4j webinar renewal-intelligence knowledge graph from the hot QBR/account-plan/call-note/win-loss PDFs in data/pdf/, using the pre-built extraction schema in outputs/schemas/renewal_extraction_schema.py. Use when the user wants to run the Mistral webinar demo, load the demo PDFs, build the renewal-intelligence graph, or go from documents to a Q&A report for this specific dataset. Narrower and faster than develop-neo4j-graph: no data modeling or schema design, the schema already exists and is fixed. |
Mistral Demo — Renewal Intelligence Graph from PDFs
This is a trimmed, fast variant of develop-neo4j-graph for one specific dataset and one
specific, already-designed schema. There is no data-modeling step and no mode selection —
this skill always builds a Q&A / chatbot-style graph from the PDFs in data/pdf/ using the
extraction schema that already exists at outputs/schemas/renewal_extraction_schema.py.
Stop after Step 7. Do not proceed to CSV ingestion, BigQuery, or Neocarta — those are a
separate, later phase of the demo.
Pre-flight: Confirm Models
Before starting, read .env and report the configured models:
EMBEDDING_MODEL — used by embed_chunks
EXTRACTION_MODEL — used by extract_entities
Never substitute, correct, or override these values. If extraction or embedding fails
with a model error, report the exact error and stop.
Progress Checklist
Copy this checklist to track progress:
- [ ] Step 1: Explain the use case, data, and target questions
- [ ] Step 2: Describe the extraction schema (report + Mermaid diagram)
- [ ] Step 3: Create the lexical graph from the PDFs (pymupdf)
- [ ] Step 4: Extract entities using the renewal extraction schema, then resolve BigQuery account IDs
- [ ] Step 5: Verify and summarize the extraction
- [ ] Step 6: Answer the target questions
- [ ] Step 7: Generate the demo report
Step 1: Use Case, Data, and Target Questions
List files in data/pdf/ using Shell: find data/pdf -name "*.pdf" | sort. (Use Shell, not
a file-picker/glob tool — data/pdf/ is gitignored, so those tools may not see the files.)
Sample 2–3 documents with Shell: pdftotext "data/pdf/FILENAME.pdf" - | head -50 (part of
Poppler; check with which pdftotext first — no extra Python deps needed).
Summarize for the user, in plain language:
- Domain: customer-success renewal intelligence for a B2B SaaS vendor — QBRs, an
account plan, a sales call note, a win-loss note, and a release-notes doc, covering
accounts Acme Corp, Globex Industries, Hooli, Stark Industries, Pied Piper, and the lost
prospect Initech.
- Why it's interesting: some documents are stale or contradict each other on purpose
(e.g. two Acme QBRs from different quarters) — this is what makes "which document is
current" a real question a graph can answer better than plain full-text search.
Then propose 3–5 target questions grounded in this data and in what the schema (Step 2) is
actually built to answer, for example:
- Which accounts have raised a competitive-threat concern, and against which competitor?
- Who is the current champion at Acme Corp — and did anyone with a champion/decision-maker
role recently leave the account?
- Why was the Initech deal lost, and to which competitor?
- Across all accounts, what unresolved concerns exist heading into renewal?
- Do the two Acme Corp QBRs disagree on account health — and if so, which one is current?
Present the list, let the user confirm/edit/add, and finalize it. This finalized list is
the validation target for Steps 6–7 — do not silently change it later.
Step 2: Describe the Extraction Schema
Read outputs/schemas/renewal_extraction_schema.py directly (do not use
neo4j-data-modeling tools — this schema was hand-authored outside that workflow and is
already in the exact format extract_entities expects).
From the file, extract:
- Each entity class:
_node_label, _key_property, and its other fields with descriptions
- Each relationship class:
_relationship_type, start label → end label
- Any alias/normalizer functions applied to key properties (e.g. account name aliases,
concern label aliases) — call these out explicitly, since they're what prevents the same
real-world entity from splitting into duplicate nodes across documents
Write outputs/reports/renewal_intelligence_schema.md:
# Renewal Intelligence — Extraction Schema
## Entities
| Label | Key Property | Notable Fields |
|-------|-------------|-----------------|
| ... | ... | ... |
## Relationships
| Type | From → To | Meaning |
|------|-----------|---------|
| ... | ... | ... |
## Normalization
<Prose: which fields are alias-normalized, and why — e.g. "Acme" and "Acme Corp" collapse
to one Account node; "SSO", "SAML", "single sign-on" collapse to one Concern node.>
## Diagram
\`\`\`mermaid
flowchart LR
Person -->|CHAMPION_OF| Account
Person -->|WORKS_AT| Account
Person -->|MOVED_FROM| Account
Account -->|EVALUATING| Competitor
Account -->|RAISED| Concern
Deal -->|LOST_TO| Competitor
Deal -->|LOST_BECAUSE| Concern
Release -->|RESOLVES| Concern
\`\`\`
Show the Mermaid diagram to the user before moving on — this is the artifact meant to be
displayed live.
Step 3: Create the Lexical Graph (pymupdf)
Follow PYMUPDF_MODE.md from the
develop-neo4j-graph skill — same server, same tool sequence:
create_lexical_graph(parse_mode="pymupdf") on data/pdf/
list_documents — confirm all PDFs listed in Step 1 were ingested
verify_lexical_graph on one representative document (optional spot-check)
generate_chunk_descriptions — skip; these PDFs are plain text QBRs/notes with no
tables or images (confirmed when the PDFs were generated)
embed_chunks with no parameters
Step 4: Extract Entities, Then Resolve BigQuery Account IDs
4a. Extract entities. Use extract_entities from neo4j-entity-graph:
schema="outputs/schemas/renewal_extraction_schema.py" — pass this path directly, there
is no convert_schema step in this skill, the file is already in the expected format
- Extraction is async — poll with
check_extraction_status until complete
- Report progress to the user: chunks processed, entities found so far
4b. Resolve BigQuery account IDs. Once extraction is complete and :Account nodes
exist, run the bridge script from the repo root:
uv run --with google-cloud-bigquery --with neo4j --with python-dotenv python3 \
.agents/skills/mistral-demo-skill/scripts/resolve_bq_link.py
This looks up each :Account node's name against BigQuery (renewal_demo.accounts) and
sets account_id + bq_resolved=true on matched nodes — purely additive, no new nodes or
relationships. It self-reports resolved/unresolved counts and a hero-account pass/fail
check (Acme Corp, Globex Industries, Hooli, Stark Industries, Pied Piper). Report this
output to the user. Requires gcloud ADC to already be authenticated and GCP_PROJECT_ID
(defaults to neo4j-field-engineering-emea) — if it fails on credentials, report the exact
error and continue to Step 5 without the BigQuery link rather than blocking the demo.
Step 5: Verify and Summarize the Extraction
Use read_neo4j_cypher from neo4j-graphrag — just two queries, covering every label and
relationship type at once:
-- Nodes per label
MATCH (n) UNWIND labels(n) as label RETURN label, count(*) as count ORDER BY count DESC
-- Relationships per type
MATCH ()-[r]->() RETURN type(r) as rel_type, count(*) as count ORDER BY count DESC
Summarize the two result tables for the user in plain language — how many of each entity
and relationship type were extracted. Do not run per-label duplicate checks, entity-chunk
link checks, or sample-value queries; if something looks off (e.g. an unexpectedly high or
low count), note it briefly rather than digging in — do not re-run extraction.
Step 6: Answer the Target Questions
For each question confirmed in Step 1, answer it using neo4j-graphrag tools. Get the
index name first with get_neo4j_schema_and_indexes.
| Question type | Preferred method | MCP tool |
|---|
| Semantic / open-ended | Vector search | vector_search |
| Keyword / name lookup | Fulltext search | fulltext_search |
| Relationship traversal / structured | Cypher | read_neo4j_cypher |
| Complex / multi-hop | Graph-grounded | search_cypher_query |
For each question, record: the method used, the actual query, the answer, and a quality
rating (Complete / Partial / Not answered).
Step 7: Generate the Demo Report
Save to outputs/reports/renewal_intelligence_demo_report.md:
# Mistral Demo Report — Renewal Intelligence
## Source Data
<List each PDF, parse mode, chunk count>
## Use Case
<From Step 1>
## Extraction Schema
<Embed or link to outputs/reports/renewal_intelligence_schema.md, including the Mermaid diagram>
## Target Questions and Answers
### Q1: <question>
- **Method**: <vector / fulltext / cypher>
- **Query**: <query used>
- **Answer**: <answer from graph>
- **Quality**: Complete / Partial / Not answered
[repeat for each question]
## Extraction Quality
<Node counts per label, relationship counts per type>
## Gaps and Limitations
<Questions not fully answered and why; anything the PDF-only graph structurally can't
resolve — e.g. quantifying dollar values requires the structured/BigQuery layer, which is
a later phase of this demo, not this skill>
## Recommended Next Steps
<e.g. add the BigQuery/Neocarta layer to answer ARR-quantified versions of these questions>
Stop here. Do not proceed to CSV/structured data, BigQuery, or Neocarta in this skill.