AI support agent order data access separates a scripted FAQ bot from a system that actually clears a backlog. You grant that access by exposing order records through a tightly scoped tool interface, not by pasting SQL results into the context window.
Step 1: Define the data boundary
Before writing code, decide exactly which fields the agent may read. Order data includes PII and payment metadata you should never expose to a model unless absolutely required.
List the safe fields: order_id, status, placed_at, items (sku, name, qty), shipping_address (city, state, zip, no street), carrier, tracking_number, refund_state. Exclude card_last_four, raw auth tokens, internal fraud flags.
Treat the model as an untrusted caller. The backend enforces scope; the prompt does not. When designing AI support agent order data access, this boundary is the only thing that prevents a prompt injection from leaking another customer’s record.
Step 2: Build a read-only orders API
For solid AI support agent order data access, stand up a minimal service that accepts an order ID and a customer-scoped token, then returns only the whitelisted fields. Use a separate DB role with SELECT granted solely on the orders and line_items views.
from flask import Flask, request, jsonify
from db import read_order_for_customer # your internal accessor
app = Flask(__name__)
@app.route("/v1/orders/<order_id>", methods=["GET"])
def get_order(order_id):
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer "):
return jsonify({"error": "missing token"}), 401
token = auth.split(" ", 1)[1]
customer_id = validate_token(token) # raises if invalid
if not customer_id:
return jsonify({"error": "unauthorized"}), 403
order = read_order_for_customer(order_id, customer_id)
if not order:
return jsonify({"error": "not_found"}), 404
return jsonify({
"order_id": order["id"],
"status": order["status"],
"placed_at": order["created_at"].isoformat(),
"items": [{"sku": i["sku"], "name": i["name"], "qty": i["qty"]} for i in order["items"]],
"shipping": {"city": order["city"], "state": order["state"], "zip": order["zip"]},
"tracking": order["tracking_number"],
})
Run it behind TLS, rate-limit per token, and log every access. This service is the only path the agent has to order data.
Step 3: Describe the tool to the model
LLM tool-calling works off a JSON schema. Keep the parameters minimal: just the order identifier. Do not let the model supply customer_id; that comes from your session layer.
{
"name": "get_order_details",
"description": "Fetch status, items, and shipping progress for a customer order. Use only when the user references an order ID.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order identifier, e.g. 'ORD-10293'"
}
},
"required": ["order_id"]
}
}
The description steers the model on when to call. Vague descriptions cause premature or missed calls. Test the schema against a few sample utterances before shipping.
Step 4: Implement the agent loop
Use an OpenAI-compatible client. Point it at your inference gateway. If you want one endpoint that fronts 240+ models with automatic fallback when a provider is rate-limited, n4n.ai fits that pattern and keeps the client code unchanged during outages.
import json
import requests
from openai import OpenAI
client = OpenAI(base_url="https://api.n4n.ai/v1", api_key="YOUR_KEY")
tools = [{
"type": "function",
"function": {
"name": "get_order_details",
"description": "Fetch status, items, and shipping progress for a customer order.",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"]
}
}
}]
def run_agent(user_msg, customer_token):
messages = [{"role": "system", "content": "You are a support agent. Only use get_order_details for the authenticated customer."},
{"role": "user", "content": user_msg}]
resp = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = resp.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
if call.function.name == "get_order_details":
args = json.loads(call.function.arguments)
order = requests.get(
f"https://internal.example.com/v1/orders/{args['order_id']}",
headers={"Authorization": f"Bearer {customer_token}"}
).json()
messages.append(msg)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(order)
})
resp = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=messages,
tools=tools
)
msg = resp.choices[0].message
return msg.content
The second call lets the model synthesize the answer from tool output. Never let the model see the raw token or your internal schema.
Step 5: Enforce customer isolation
Customer isolation is the core of safe AI support agent order data access. The agent must not fetch orders for a different user. The tool backend already keys on customer_id from the token, but add defense in depth: reject any order_id that doesn’t pass a format check, and log mismatches.
import re
ORDER_RE = re.compile(r"^ORD-\d{4,10}$")
def safe_order_id(raw):
if not ORDER_RE.match(raw):
raise ValueError("bad order id")
return raw
Call this before hitting the API. If the model hallucinates an ID, the regex fails and you return a clean error to the loop.
Step 6: Verify with scripted conversations
Write a test that simulates a customer asking about a known order. Use a fixed customer token and a seeded database row.
def test_agent_order_lookup():
token = "test-token-cust-1"
seed_order("ORD-1001", customer_id=1, status="shipped")
answer = run_agent("Where is my order ORD-1001?", token)
assert "shipped" in answer.lower()
assert "ORD-1001" in answer
Run this in CI against a stub of the orders API. If the agent calls the tool with a wrong ID or omits the call, the assertion fails. That catches regressions when you swap models.
Add a negative test: a token for customer 2 asking about ORD-1001 (owned by 1) must return not_found and the agent must not leak data.
Step 7: Cache and meter
Order status changes slowly. Forward provider cache-control hints so repeated lookups for the same order within a session hit cache instead of re‑querying the DB. At the gateway level, per-token usage metering shows which customer sessions burn the most inference.
Set a short TTL on the orders service response:
@app.after_request
def add_cache_header(resp):
resp.headers["Cache-Control"] = "max-age=30"
return resp
Combine that with a rate limit of 10 requests/minute per token. The agent stays responsive without overloading your fulfillment system.
Verify success
Success means three things: the agent invokes get_order_details only when given a valid order ID, the returned answer contains fields from the whitelisted set, and no request to the orders API escapes the customer scope. Watch latency on the tool call path; a p95 under 200ms keeps the conversation feeling synchronous. If you see the model apologizing for missing data, your schema description or field whitelist is too tight.
That is the full pipeline. Build the scoped API first, treat the model as a caller with zero trust, and verify with deterministic tests before any customer sees it.