DEV Community

shashank ms
shashank ms

Posted on

LLM Interpretability Explained

We are building a lightweight interpretability agent that reverse-engineers why an LLM produced a specific output. It helps engineers debug prompt failures and audit model behavior without access to internal weights. The entire pipeline runs against Oxlo.ai's API using standard OpenAI SDK calls.

What you'll need

Step 1: Configure the Oxlo.ai client

Create a single client instance pointing at Oxlo.ai. We will reuse this client to call both the target model and the interpreter model.

from openai import OpenAI

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

response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say ok"},
    ],
    max_tokens=5,
)
print(response.choices[0].message.content)

Step 2: Create the interpreter prompt

The interpreter prompt instructs the model to act as a mechanistic interpretability analyst. It must decompose a given response into latent assumptions, surface claims, and confidence markers.

INTERPRETER_PROMPT = """You are an LLM interpretability analyst. Your job is to examine a user query and a target LLM response, then produce a structured interpretability report.

For every analysis, provide:
1. Latent Assumptions: unstated premises the target model likely used.
2. Surface Claims: explicit factual assertions and their apparent sources.
3. Alternative Paths: what the target model could have answered but did not.
4. Confidence Score: a rating from 1 to 10 for each surface claim.
5. Attribution Map: mark each claim as Inferred, Retrieved, or Hallucinated.

Be concise. Use bullet points. Do not add fluff."""

Step 3: Build the analysis function

This function takes a user query and a target response, then asks the interpreter model to audit it. I use qwen-3-32b here because it handles multilingual reasoning and agentic decomposition well on Oxlo.ai.

def analyze_response(user_query: str, target_response: str) -> str:
    payload = f"""User Query:
{user_query}

Target LLM Response:
{target_response}

Produce the interpretability report now."""

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": INTERPRETER_PROMPT},
            {"role": "user", "content": payload},
        ],
        temperature=0.2,
        max_tokens=1024,
    )
    return response.choices[0].message.content

Step 4: Add contrastive probing

To surface what the target model avoided, we ask the interpreter to generate contrastive continuations. This reveals the decision boundary of the original output.

def contrastive_probe(user_query: str, target_response: str) -> str:
    probe_prompt = f"""User Query:
{user_query}

Target Response:
{target_response}

Generate exactly two alternative responses the model could have given. Label them:
- ALT A: a safer, more conservative answer.
- ALT B: a more speculative or creative answer.

Then explain in one sentence why the original response was chosen over each alternative."""

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": INTERPRETER_PROMPT},
            {"role": "user", "content": probe_prompt},
        ],
        temperature=0.7,
        max_tokens=1024,
    )
    return response.choices[0].message.content

Step 5: Score attribution and confidence

We extract structured scores by forcing the interpreter to return JSON. This lets us programmatically flag high-risk claims.

import json

def score_attribution(user_query: str, target_response: str) -> dict:
    scoring_prompt = f"""User Query:
{user_query}

Target Response:
{target_response}

Return ONLY a JSON object with this exact shape:
{{
  "overall_confidence": ,
  "claims": [
    {{"text": "...", "type": "Inferred|Retrieved|Hallucinated", "confidence": }}
  ]
}}

Do not include markdown fences or explanation."""

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": INTERPRETER_PROMPT},
            {"role": "user", "content": scoring_prompt},
        ],
        temperature=0.1,
        max_tokens=512,
        response_format={"type": "json_object"},
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

Step 6: Wrap in a CLI audit loop

We combine everything into a script that generates a target response with one model, then audits it with the interpreter.

def generate_target(query: str) -> str:
    resp = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "user", "content": query},
        ],
        temperature=0.5,
        max_tokens=512,
    )
    return resp.choices[0].message.content

if __name__ == "__main__":
    query = "Why did the Roman Empire fall?"

    print("=== TARGET RESPONSE ===")
    target = generate_target(query)
    print(target)

    print("\n=== INTERPRETABILITY REPORT ===")
    print(analyze_response(query, target))

    print("\n=== CONTRASTIVE PROBE ===")
    print(contrastive_probe(query, target))

    print("\n=== STRUCTURED SCORES ===")
    scores = score_attribution(query, target)
    print(json.dumps(scores, indent=2))

Run it

Set your key and run the script.

export OXLO_API_KEY="your-key-here"
python interpretability_agent.py

Example output (abbreviated for brevity):

=== TARGET RESPONSE ===
The Roman Empire fell due to a combination of internal instability...
=== INTERPRETABILITY REPORT ===
- Latent Assumptions: the model assumes "fall" refers to the Western Empire in 476 CE.
- Surface Claims: economic overextension (confidence 7), barbarian invasions (confidence 8).
- Alternative Paths: could have focused on the Eastern Empire's continuity.
=== CONTRASTIVE PROBE ===
- ALT A: list only military causes.
- ALT B: argue climate change was primary.
=== STRUCTURED SCORES ===
{
  "overall_confidence": 7,
  "claims": [
    {"text": "economic overextension", "type": "Inferred", "confidence": 7}
  ]
}

Next steps

Hook the scoring output into a pytest suite to regress confidence scores across prompt versions. Or swap in deepseek-r1-671b as the interpreter on Oxlo.ai to test whether a reasoning-heavy model surfaces different latent assumptions than qwen-3-32b.

Top comments (0)