DEV Community

shashank ms
shashank ms

Posted on

Using LLM for Knowledge Graph Embedding

We are going to build a small but complete pipeline that reads raw text, extracts a knowledge graph as subject-relation-object triples, and embeds every entity so we can search the graph with natural language. If you are building a RAG system or trying to connect disjoint documents, this gives you a structured layer that vector search alone cannot provide. I will use Oxlo.ai for both the LLM extraction and the embedding generation, which keeps the code uniform and avoids token-length pricing surprises on long input texts.

What you'll need

Step 1: Configure the Oxlo.ai client and extraction prompt

First I set up the OpenAI-compatible client pointing to Oxlo.ai. I also define the system prompt that forces the model to return strict JSON triples and nothing else.

import json
from openai import OpenAI

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key="YOUR_OXLO_API_KEY"  # from https://portal.oxlo.ai
)

SYSTEM_PROMPT = """You are a precise knowledge-graph extractor.
Read the user's text and emit a JSON object with exactly one key, \"triples\".
The value must be a list of lists, where each inner list contains three strings:
[subject, relation, object].
Rules:
- Use canonical entity names (e.g., \"Amazon\" instead of \"the company\").
- Keep relations simple: one or two words, lowercase, no spaces (use underscores).
- Emit nothing outside the JSON block.
"""

Step 2: Extract triples from raw text

Next I pass a raw text block to the model and parse the JSON it returns. I use llama-3.3-70b because it handles long context reliably and follows formatting instructions.

RAW_TEXT = """
Acme Corp was founded in 1999 by Alice Johnson and Bob Smith.
The company, headquartered in Austin, Texas, manufactures electric motors.
In 2021, Acme Corp acquired Beta Industries, a battery startup based in Berlin.
Alice Johnson later became the CEO of the merged entity.
"""

response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": RAW_TEXT},
    ],
    temperature=0.1,
)

content = response.choices[0].message.content
content = content.replace("

```json", "").replace("```

", "").strip()
triples_data = json.loads(content)
triples = triples_data["triples"]

print(f"Extracted {len(triples)} triples:")
for t in triples:
    print(t)

Step 3: Parse and deduplicate the graph

Before embedding, I normalize entity names and build an adjacency list. This deduplication step prevents duplicate nodes and makes the downstream graph walk cleaner.

from collections import defaultdict

nodes = set()
edges = defaultdict(list)

for subj, rel, obj in triples:
    s = subj.strip()
    r = rel.strip().lower().replace(" ", "_")
    o = obj.strip()
    nodes.add(s)
    nodes.add(o)
    edges[s].append((r, o))

nodes = sorted(nodes)
print(f"Unique entities: {len(nodes)}")
for n in nodes:
    print(f"  - {n}")

Step 4: Generate entity embeddings

Now I embed every unique entity using Oxlo.ai's embedding endpoint. Because Oxlo.ai charges per request rather than per token, I can send the full node list in one call without worrying about character count.

import math

def cosine_similarity(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(x * x for x in b))
    return dot / (norm_a * norm_b)

emb_response = client.embeddings.create(
    model="bge-large",
    input=nodes,
)

entity_vectors = {}
for item in emb_response.data:
    idx = item.index
    entity_vectors[nodes[idx]] = item.embedding

print(f"Stored vectors for {len(entity_vectors)} entities.")

Run it: Query the knowledge graph

With the graph and vectors in memory, I embed a natural-language question and find the most similar entity by cosine similarity. I then walk one hop through the graph to surface the connected facts that answer the question.

query = "Who leads the company that bought Beta Industries?"

q_emb_resp = client.embeddings.create(
    model="bge-large",
    input=[query],
)
q_vec = q_emb_resp.data[0].embedding

scored = [
    (entity, cosine_similarity(q_vec, vec))
    for entity, vec in entity_vectors.items()
]
scored.sort(key=lambda x: x[1], reverse=True)

top_entity, top_score = scored[0]
print(f"Top entity match: '{top_entity}' (score: {top_score:.3f})\n")

print(f"Facts about '{top_entity}':")
for rel, obj in edges.get(top_entity, []):
    print(f"  - {rel} -> {obj}")

print(f"\nEntities connected to '{top_entity}':")
for src, rel_objs in edges.items():
    for rel, obj in rel_objs:
        if obj == top_entity:
            print(f"  - {src} -> {rel}")

Example output:

Top entity match: 'Acme Corp' (score: 0.812)

Facts about 'Acme Corp':
  - founded_in -> 1999
  - headquartered_in -> Austin, Texas
  - manufactures -> electric motors
  - acquired -> Beta Industries

Entities connected to 'Acme Corp':
  - Alice Johnson -> founded
  - Bob Smith -> founded

Wrap-up

That gives you a minimal but complete LLM-driven knowledge graph embedding pipeline. To take it further, persist the vectors in a vector database such as pgvector, or add relation embeddings so you can predict missing links with vector arithmetic.

Top comments (0)