We are going to build a domain-specific support agent that applies transfer learning through retrieval-augmented generation. Instead of fine-tuning a foundation model, we will adapt Llama 3.3 70B to answer questions about a fictional SaaS API by injecting relevant documentation into the context window. This pattern keeps costs predictable on Oxlo.ai, where pricing is per request rather than per token. See the pricing page for current rates.
What you'll need
- Python 3.10+
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai. We also usenumpyfor similarity math.
Step 1: Configure the Oxlo.ai client
Initialize the OpenAI SDK pointing at Oxlo.ai. We will reuse this client for both embeddings and chat completions.
from openai import OpenAI
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
Step 2: Create a knowledge base
We need domain data to adapt the general model. Here is a small JSON-like list representing fake API documentation for a payment gateway.
import json
KB = [
{
"id": "auth-001",
"text": "Authenticate by sending a POST to /v1/auth with your API key in the X-API-Key header. Keys are scoped to sandbox or production environments."
},
{
"id": "charge-002",
"text": "To create a charge, POST to /v1/charges with amount (integer, cents), currency (ISO-4217), and source_token. The API returns a charge object with status pending, succeeded, or failed."
},
{
"id": "webhook-003",
"text": "Webhooks are delivered as POST requests to your configured endpoint. Each webhook includes an event_type and a data payload. Verify signatures using your webhook secret and SHA-256 HMAC."
},
{
"id": "rate-limit-004",
"text": "The default rate limit is 100 requests per minute for sandbox and 1000 requests per minute for production. Exceeding the limit returns HTTP 429 with a Retry-After header."
}
]
Step 3: Build a retriever with Oxlo.ai embeddings
We embed the knowledge base and the user query using Oxlo.ai's embedding model, then return the top match by cosine similarity. This is the transfer mechanism: the general BGE embedding model is applied to our specific domain.
import numpy as np
def get_embedding(text):
response = client.embeddings.create(
model="bge-large",
input=[text]
)
return np.array(response.data[0].embedding)
kb_embeddings = np.array([get_embedding(item["text"]) for item in KB])
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def retrieve(query, top_k=1):
q_emb = get_embedding(query)
scores = [cosine_similarity(q_emb, doc_emb) for doc_emb in kb_embeddings]
best_idx = int(np.argmax(scores))
return KB[best_idx]["text"], scores[best_idx]
Step 4: Write the system prompt
The system prompt constrains the general LLM to behave like a domain expert. It instructs the model to use only the retrieved context and to refuse off-topic questions.
SYSTEM_PROMPT = """You are PayAssist, a technical support agent for the Acme Payments API.
You answer questions using ONLY the provided API documentation context.
If the answer is not in the context, say: "I do not have that information in the current documentation."
Do not make up endpoints, parameters, or behavior.
Base your tone on the following rules: be concise, use bullet points for steps, and always include the relevant endpoint path."""
print(SYSTEM_PROMPT)
Step 5: Assemble the agent pipeline
Now we wire retrieval to generation. The function fetches relevant docs, formats a message with the context, and calls Llama 3.3 70B on Oxlo.ai.
def ask_agent(question):
context, score = retrieve(question)
user_content = f"""Documentation context:
{context}
User question: {question}"""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_content},
],
)
return {
"answer": response.choices[0].message.content,
"retrieved_context": context,
"similarity_score": float(score)
}
Run it
Test the agent with a question that requires domain transfer. The model has never seen Acme Payments docs during pre-training, yet it answers accurately because we retrieved the right context.
if __name__ == "__main__":
question = "What happens if I exceed the sandbox rate limit?"
result = ask_agent(question)
print(f"Question: {question}")
print(f"Retrieved: {result['retrieved_context']}")
print(f"Score: {result['similarity_score']:.4f}")
print(f"Answer:\n{result['answer']}")
Example output:
Question: What happens if I exceed the sandbox rate limit?
Retrieved: The default rate limit is 100 requests per minute for sandbox and 1000 requests per minute for production. Exceeding the limit returns HTTP 429 with a Retry-After header.
Score: 0.8912
Answer:
- Exceeding the sandbox rate limit returns HTTP 429.
- The response includes a Retry-After header.
- Sandbox limits: 100 requests per minute.
Wrap-up and next steps
That is the core of a transfer-learning support agent. You are taking a general model hosted on Oxlo.ai and specializing it to a domain without touching any weights.
Two concrete next steps: replace the static JSON knowledge base with a vector database such as Chroma or pgvector so the agent scales to thousands of documents, or swap the chat model to DeepSeek V3.2 on Oxlo.ai if your domain involves heavy reasoning or code generation.
Top comments (0)