Objective
ATS keyword matching gives recruiters too many false positives ("React" hits anyone who put it in a buzzword soup) and too many misses ("LLMOps" rejects the candidate who wrote "production GenAI infra"). A graph of jobs → required skills, candidates → demonstrated skills, plus embedding-based similarity on candidate summaries fixes both. The wow moment: one query returns candidates with the structural skill hits AND a semantic similarity to the job AND an LLM-judged "would they thrive in this role" score, ranked together.
Step 1: Set up jobs, skills, and candidates
MERGE (j1:Job {id: "JOB-101", title: "Senior ML Engineer",
summary: "Train and deploy production LLM systems with vector DBs and observability",
embedding: [0.41, 0.13, -0.22, 0.55, 0.07]})
MERGE (j2:Job {id: "JOB-102", title: "Data Platform Engineer",
summary: "Build streaming ETL pipelines on Kafka and Spark, manage Snowflake warehouse",
embedding: [-0.10, 0.62, 0.31, -0.18, 0.20]})
MERGE (s1:Skill {name: "Python"})
MERGE (s2:Skill {name: "PyTorch"})
MERGE (s3:Skill {name: "Vector Databases"})
MERGE (s4:Skill {name: "LLM Inference"})
MERGE (s5:Skill {name: "Kafka"})
MERGE (s6:Skill {name: "Spark"})
MERGE (s7:Skill {name: "Snowflake"})
MERGE (s8:Skill {name: "Observability"})
MERGE (j1)-[:REQUIRES {weight: 1.0}]->(s1)
MERGE (j1)-[:REQUIRES {weight: 1.0}]->(s2)
MERGE (j1)-[:REQUIRES {weight: 1.0}]->(s4)
MERGE (j1)-[:REQUIRES {weight: 0.7}]->(s3)
MERGE (j1)-[:REQUIRES {weight: 0.5}]->(s8)
MERGE (j2)-[:REQUIRES {weight: 1.0}]->(s5)
MERGE (j2)-[:REQUIRES {weight: 1.0}]->(s6)
MERGE (j2)-[:REQUIRES {weight: 1.0}]->(s7)
MERGE (j2)-[:REQUIRES {weight: 0.5}]->(s1)
MERGE (c1:Candidate {id: "CAND-9001", name: "Sarah Chen",
summary: "Built and deployed production GPT-style assistants on Kubernetes, owns vector retrieval stack",
embedding: [0.40, 0.14, -0.21, 0.56, 0.06]})
MERGE (c2:Candidate {id: "CAND-9002", name: "Raj Patel",
summary: "10 years ETL, recently migrated petabyte warehouse from Hadoop to Snowflake",
embedding: [-0.09, 0.63, 0.30, -0.19, 0.21]})
MERGE (c3:Candidate {id: "CAND-9003", name: "Mia Rossi",
summary: "Computer vision research, papers on diffusion models and robotic manipulation",
embedding: [0.18, -0.30, 0.41, 0.07, -0.22]})
MERGE (c4:Candidate {id: "CAND-9004", name: "Leo Park",
summary: "Built the realtime fraud platform at a fintech, Kafka and Spark heavy",
embedding: [-0.08, 0.61, 0.32, -0.17, 0.19]})
MERGE (c1)-[:HAS_SKILL]->(s1)
MERGE (c1)-[:HAS_SKILL]->(s2)
MERGE (c1)-[:HAS_SKILL]->(s3)
MERGE (c1)-[:HAS_SKILL]->(s4)
MERGE (c1)-[:HAS_SKILL]->(s8)
MERGE (c2)-[:HAS_SKILL]->(s1)
MERGE (c2)-[:HAS_SKILL]->(s5)
MERGE (c2)-[:HAS_SKILL]->(s6)
MERGE (c2)-[:HAS_SKILL]->(s7)
MERGE (c3)-[:HAS_SKILL]->(s1)
MERGE (c3)-[:HAS_SKILL]->(s2)
MERGE (c4)-[:HAS_SKILL]->(s1)
MERGE (c4)-[:HAS_SKILL]->(s5)
MERGE (c4)-[:HAS_SKILL]->(s6);