Meeting minutes to action-item graph
Objective
Project managers re-read meeting minutes to figure out who-promised-what-by-when. Half the
follow-through fails because actions live in prose. /v2/graph/extract turns minutes into a
structured graph of People → Action → DueDate edges; LLM_SCORE then grades urgency so the
PM-bot knows which Slack ping to send first. The wow moment: paste minutes from this morning's
standup, immediately query "everything Devon owes by Friday, ordered by urgency."
Step 1: Extract from minutes prose
curl -X POST https://localhost:8443/v2/graph/extract \
-H "Authorization: Bearer $AIDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Engineering standup, May 1 2026. Sarah Chen will finalise the GraphRAG benchmark report by Friday May 3. Devon Park owns the Q2 budget reconciliation, due May 5; Devon flagged a risk that the ERP export is blocked on legal review. Raj Patel will reach out to the Tessera Robotics integration team this week to confirm the May 14 demo. The team agreed to defer the v1.5 API redesign to next quarter. Mia Rossi volunteered to run a knowledge-sharing session on HNSW tuning on May 7.",
"default_node_label": "MeetingEntity",
"node_provenance": {"meeting": "ENG-STANDUP-2026-05-01"},
"edge_provenance": {"meeting": "ENG-STANDUP-2026-05-01"},
"min_confidence": 0.55
}'
Step 2: Pre-seed the action graph
MERGE (sarah:Person {name: "Sarah Chen", team: "ML"})
MERGE (devon:Person {name: "Devon Park", team: "Finance"})
MERGE (raj:Person {name: "Raj Patel", team: "Partnerships"})
MERGE (mia:Person {name: "Mia Rossi", team: "ML"})
MERGE (a1:Action {id: "ACT-1",
text: "Finalise the GraphRAG benchmark report",
due: "2026-05-03"})
MERGE (a2:Action {id: "ACT-2",
text: "Reconcile Q2 budget; ERP export blocked on legal review",
due: "2026-05-05"})
MERGE (a3:Action {id: "ACT-3",
text: "Reach out to Tessera Robotics to confirm May 14 demo",
due: "2026-05-08"})
MERGE (a4:Action {id: "ACT-4",
text: "Run a knowledge-sharing session on HNSW tuning",
due: "2026-05-07"})
MERGE (a5:Action {id: "ACT-5",
text: "Defer v1.5 API redesign to next quarter (decision, not action)",
due: null})
MERGE (sarah)-[:OWNS]->(a1)
MERGE (devon)-[:OWNS]->(a2)
MERGE (raj)-[:OWNS]->(a3)
MERGE (mia)-[:OWNS]->(a4);
Step 3: Rank actions by LLM-judged urgency
// Urgency blends the due date with content (blocked items, exec promises, etc.)
MATCH (p:Person)-[:OWNS]->(a:Action)
WHERE a.due IS NOT NULL
WITH p, a,
llm_score(
"Rate this action's urgency 0..1 for a project manager's pings list. " +
"Items blocked on external parties, near-term due dates, and exec-owned items score high.",
a
) AS urgency
RETURN p.name AS owner,
a.id AS action,
a.text AS description,
a.due AS due,
urgency
ORDER BY urgency DESC;
What's happening
/v2/graph/extractlifts owner/action/due-date relationships out of prose. Provenance (meeting: "ENG-STANDUP-2026-05-01") survives every edge so audits can reconstruct exactly which meeting created which obligation.LLM_SCOREreads the action text and the prompt's definition of urgency to produce a calibrated score. "Blocked on legal review" with a Tuesday deadline scores high; "knowledge-sharing session" without a hard external dep scores moderate.- The pattern
(p:Person)-[:OWNS]->(a:Action)plus aduefilter answers "what does Devon owe this week" in one MATCH — a query the PM-bot can run on every Slack tag. - Re-running on the same meeting is idempotent; re-running on tomorrow's standup adds new meeting-tagged actions next to today's.
- Compose with recipe 020 (agentic memory): the agent recalls past standups when summarising
today's by walking
:FOLLOWED_BYbetween meeting episode nodes.
Try this next
// Owner workload by team.
MATCH (p:Person)-[:OWNS]->(a:Action)
WHERE a.due IS NOT NULL
RETURN p.team AS team, p.name AS owner, count(a) AS open_actions
ORDER BY open_actions DESC;
// Anything due in the next 5 days.
MATCH (p:Person)-[:OWNS]->(a:Action)
WHERE a.due IS NOT NULL AND a.due <= "2026-05-06"
RETURN p.name, a.text, a.due
ORDER BY a.due;
// Dropped decisions vs taken actions.
MATCH (a:Action)
OPTIONAL MATCH (p:Person)-[:OWNS]->(a)
WITH a, p
RETURN a.text,
CASE WHEN p IS NULL THEN "decision/no owner" ELSE p.name END AS status;