Social network analysis usually requires specialized graph libraries and manual data labeling, but an LLM can extract entities and relationships from raw text in a single pass. In this guide, we will build a Python agent that reads a conversation transcript, builds a directed graph of interactions, and scores influence using degree centrality, all powered by Oxlo.ai. The finished script fits in one file and runs against any long-form text you provide.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai - NetworkX and Matplotlib:
pip install networkx matplotlib
Step 1: Configure the Oxlo.ai client
I initialize the OpenAI-compatible client pointing at Oxlo.ai. I picked llama-3.3-70b because it follows structured instructions reliably, and Oxlo.ai's flat per-request pricing keeps the cost predictable even when I feed it much longer transcripts later.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY" # get yours at https://portal.oxlo.ai
)
MODEL = "llama-3.3-70b"
Step 2: Define the agent's system prompt
The system prompt forces the model to behave like a strict extraction engine. It returns only normalized JSON with nodes and edges, which keeps downstream parsing simple and deterministic.
SYSTEM_PROMPT = """You are a social network analysis extractor.
Read the provided conversation transcript and identify every person or organization mentioned.
Return a single JSON object with no markdown formatting.
The JSON must contain:
- "nodes": a list of unique entities, each with "id" and "type" (person or organization).
- "edges": a list of directed interactions, each with "source", "target", and "relationship" (e.g., replies_to, mentions, reports_to).
If the same name appears with variations, normalize it to one id.
Do not include any text outside the JSON object."""
Step 3: Extract the graph from raw text
I wrote a small sample transcript that mimics a cross-team thread. The extract_graph function sends the text to Oxlo.ai and sanitizes the response so we get clean JSON back.
TRANSCRIPT = """
Alice (Product): Hey team, the new API docs are live. Bob, can you review the authentication section?
Bob (Engineering): Sure Alice, I will look at it today. Charlie, you wrote the OAuth flow, can you double-check the examples?
Charlie (Engineering): On it. Alice, do we need to notify the marketing team?
Alice (Product): Yes. Dana, can you handle the announcement?
Dana (Marketing): Already drafting it. I will loop in Evan from PR.
Evan (PR): Thanks Dana. I will reach out to Alice for final approval.
"""
def extract_graph(text: str):
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
],
temperature=0.1,
)
raw = response.choices[0].message.content.strip()
# Strip accidental markdown fences if the model emits them
if raw.startswith("
```json"):
raw = raw.split("```
json")[1]
if raw.endswith("
```"):
raw = raw.rsplit("```
", 1)[0]
return json.loads(raw.strip())
Step 4: Build the graph and compute centrality
With the extracted nodes and edges, we load a directed graph in NetworkX and calculate in-degree centrality. This surfaces the people who receive the most attention in the network.
import networkx as nx
def analyze_graph(graph_data: dict):
G = nx.DiGraph()
for node in graph_data["nodes"]:
G.add_node(node["id"], type=node["type"])
for edge in graph_data["edges"]:
G.add_edge(edge["source"], edge["target"], relationship=edge["relationship"])
centrality = nx.in_degree_centrality(G)
return G, centrality
Step 5: Render the network
A quick matplotlib plot gives an immediate sanity check. We color nodes by type and annotate each edge with its relationship label.
import matplotlib.pyplot as plt
def draw_graph(G):
color_map = []
for n in G.nodes():
t = G.nodes[n].get("type", "person")
color_map.append("skyblue" if t == "person" else "lightgreen")
pos = nx.spring_layout(G, seed=42, k=1.5)
plt.figure(figsize=(8, 6))
nx.draw_networkx_nodes(G, pos, node_color=color_map, node_size=1200, alpha=0.9)
nx.draw_networkx_labels(G, pos, font_size=10)
nx.draw_networkx_edges(G, pos, arrowstyle="->", arrowsize=20, edge_color="gray")
edge_labels = nx.get_edge_attributes(G, "relationship")
nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=8)
plt.title("Social Network Extracted from Transcript")
plt.axis("off")
plt.tight_layout()
plt.show()
Run it
Tie the pieces together in a single entrypoint. When I run this, the agent extracts the graph, prints centrality scores, and opens the visualization.
if __name__ == "__main__":
graph_data = extract_graph(TRANSCRIPT)
G, centrality = analyze_graph(graph_data)
print("Extracted graph JSON:")
print(json.dumps(graph_data, indent=2))
print("\nIn-degree centrality:")
for node, score in sorted(centrality.items(), key=lambda x: x[1], reverse=True):
print(f" {node}: {score:.2f}")
print(f"\nTotal nodes: {G.number_of_nodes()}, Total edges: {G.number_of_edges()}")
draw_graph(G)
Example output:
Extracted graph JSON:
{
"nodes": [
{"id": "Alice", "type": "person"},
{"id": "Bob", "type": "person"},
{"id": "Charlie", "type": "person"},
{"id": "Dana", "type": "person"},
{"id": "Evan", "type": "person"}
],
"edges": [
{"source": "Alice", "target": "Bob", "relationship": "requests_review"},
{"source": "Bob", "target": "Charlie", "relationship": "requests_review"},
{"source": "Charlie", "target": "Alice", "relationship": "asks"},
{"source": "Alice", "target": "Dana", "relationship": "requests"},
{"source": "Dana", "target": "Evan", "relationship": "loops_in"},
{"source": "Evan", "target": "Alice", "relationship": "requests_approval"}
]
}
In-degree centrality:
Alice: 0.50
Bob: 0.25
Charlie: 0.25
Dana: 0.25
Evan: 0.25
Total nodes: 5, Total edges: 6
Wrap-up and next steps
Replace the hard-coded transcript with a Slack export or email mbox file to analyze real organizational communication. If you want to experiment before scaling, swap llama-3.3-70b for deepseek-v3.2 on Oxlo.ai's free tier to compare extraction quality on your own data.
Top comments (0)