Coreference resolution is the task of linking pronouns and noun phrases to the entities they represent. In this tutorial, I will build a small Python utility that passes text to an LLM and returns structured coreference chains. This is useful for preprocessing pipelines in knowledge extraction, legal document analysis, or conversational context tracking.
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
Oxlo.ai uses flat per-request pricing, so running a full document through this tool costs the same whether the text is one paragraph or fifty. You can explore plans at https://oxlo.ai/pricing.
Step 1: Configure the Oxlo.ai client
I initialize the OpenAI SDK to point at Oxlo.ai and select llama-3.3-70b for its strong instruction following on structured NLP tasks.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
MODEL = "llama-3.3-70b"
Step 2: Define the system prompt
The prompt must force the model to return only valid JSON and to include exact character offsets for each mention. I keep the instructions short to reduce token overhead.
SYSTEM_PROMPT = """You are a coreference resolution engine.
Given the user's text, identify all coreference chains.
A chain contains every mention that refers to the same entity.
Output strictly valid JSON in this format:
{
"chains": [
{
"entity_id": 1,
"best_name": "Descriptive Name",
"mentions": [
{"text": "Alice", "start": 0, "end": 5}
]
}
]
}
Rules:
- Use zero-based character indices relative to the full input text.
- Include pronouns, names, and common noun phrases.
- Return an empty chains array if no coreferences exist.
- Do not wrap the JSON in markdown code fences."""
Step 3: Build the extraction function
I wrap the API call in a small function that sends the raw text and parses the JSON response. I set temperature low to keep outputs deterministic.
def resolve_coreferences(text: str) -> dict:
user_message = f"Text:\n{text}"
response = client.chat.completions.create(
model=MODEL,
temperature=0.1,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
content = response.choices[0].message.content.strip()
if content.startswith("
```"):
content = content.split("```
")[1].replace("json", "").strip()
return json.loads(content)
Step 4: Add validation and formatting
To make the output useful, I add a small post-processor that sorts chains by the first mention's start index and prints a readable table.
def print_chains(result: dict, original_text: str):
if not result.get("chains"):
print("No coreference chains found.")
return
for chain in result["chains"]:
cid = chain.get("entity_id", "?")
name = chain.get("best_name", "Unknown")
print(f"\nEntity {cid}: {name}")
for mention in chain["mentions"]:
start = mention["start"]
end = mention["end"]
snippet = original_text[start:end]
print(f" - [{start}:{end}] '{snippet}'")
Run it
I run the complete script against a short news paragraph that contains multiple pronouns and names.
if __name__ == "__main__":
sample = (
"Maria Garcia announced that she would run for mayor. "
"Her campaign manager said Garcia is committed to the race. "
"If elected, she plans to focus on public transit."
)
output = resolve_coreferences(sample)
print(json.dumps(output, indent=2))
print_chains(output, sample)
Example output:
{
"chains": [
{
"entity_id": 1,
"best_name": "Maria Garcia",
"mentions": [
{"text": "Maria Garcia", "start": 0, "end": 12},
{"text": "she", "start": 37, "end": 40},
{"text": "Her", "start": 57, "end": 60},
{"text": "Garcia", "start": 79, "end": 85},
{"text": "she", "start": 121, "end": 124}
]
}
]
}
Entity 1: Maria Garcia
- [0:12] 'Maria Garcia'
- [37:40] 'she'
- [57:60] 'Her'
- [79:85] 'Garcia'
- [121:124] 'she'
Next steps
Try swapping in kimi-k2.6 or deepseek-v3.2 if you need stronger reasoning on ambiguous antecedents. For production use, cache repeated documents in a local SQLite table and batch process PDFs by first extracting text with a parser like PyMuPDF. Oxlo.ai's request-based pricing makes it practical to iterate on long documents without watching token meters.
Top comments (0)