Real-world use cases by industry
Nine complete, working catalogues - healthcare, banking, legal, retail support, engineering, HR, marketing, travel, analytics - with the requests users send, where each one is routed and why; every decision is verified by the test suite.
Nine industry scenarios, each a complete, working routing catalogue you can copy into a file and run as it stands: a hospital, a bank, a law firm, an online shop, an engineering team, an HR department, a marketing team, a hotel group and an analytics team. Every scenario shows the requests real users send, the target each one is routed to and the reason - and every one of those decisions is checked by the test suite on every commit.
Every decision on this page is verified, not illustrated
tests/test_use_cases.py reads this very page, writes each YAML block to a file, loads it into a router,
sends every request in every What happens table and asserts the target named in the Routed to
column - in process with the SDK's default objective and the command line's default (--cost-weight 0.3),
through osr route, and over HTTP through the server osr serve starts. platform/api/tests/test_use_cases_platform.py
mounts each catalogue as a hosted-platform deployment and sends the same rows through POST /api/v1/route
with a workspace key (guard middleware, workspace policy and metering included). Every Python block is
executed too. If any surface ever decided differently, the build would fail before the change shipped.
Nothing here needs an API key or a network connection.
How to run a scenario
Save a scenario's YAML block under the file name in its first line (for example healthcare.yaml); the
same file carries the targets: and the rules:. Then either use the command line
pip install 'opensmartroute[yaml]'
osr -t healthcare.yaml -r healthcare.yaml route "What time does the clinic open on Saturdays?"
osr -t healthcare.yaml -r healthcare.yaml route "Summarize this patient intake note." --constraint data_boundary=on_prem
osr -t banking.yaml -r banking.yaml route --event payment.failed
osr -t legal.yaml -r legal.yaml route "Summarize the key points of this NDA in three bullets." --planor the Python SDK - this is exactly how the verifying test builds each router:
from opensmartroute import Router, load_rules, load_targets
from opensmartroute.strategies import default_strategies
router = Router(load_targets("healthcare.yaml"), strategies=[load_rules("healthcare.yaml"), *default_strategies(seed=0)])
d = router.route("What time does the clinic open on Saturdays?")
assert d.target.id == "front-desk-fast"
print(d.trace.explain()) # the signals, the policy verdicts and every strategy's scoreEach scenario follows the same shape:
| Part | What it tells you |
|---|---|
| Situation | Who the users are, what they ask and what must never happen |
| Catalogue | The targets (models, agents, skills, tools, workflows, people) and the rules, as one YAML file |
| What happens | Real requests, the target each one reaches, and why - the verified table |
| Look closer | A short Python block that inspects the trace or adds a hard constraint - also executed by the test |
Three mechanisms recur in every industry:
- Data that must stay inside stays inside. A target declares
pii_allowed: falseand the policy stage removes it the moment a request carries an e-mail address, phone number, IBAN, card or national id. This is a hard stop, never a score - a rule then prefers the on-premises target among what is left. - People are one target among the others. A
humantarget withactions: [escalate]and a pinned rule means "I want to speak to a manager" or apayment.failedevent reaches a person, not a model. - Events route without any text.
when: { event: order.* }matchescontext.event, so webhooks and queue messages are routed the same way chat is.
1. Healthcare - a hospital group
Situation. Front-desk staff and patients ask practical questions all day (opening hours, preparation for tests). Clinicians paste discharge notes and intake forms that name patients. Complex medication questions deserve the best model available. Anything with patient identifiers must never leave the hospital network, and an emergency has to reach the on-call clinician, not a chatbot.
Catalogue.
# healthcare.yaml
targets:
- id: clinic-notes-onprem
kind: llm
name: Clinic model (inside the hospital network)
capabilities:
domains: [medical, general]
actions: [summarize, extract, qa, classify, generation]
max_complexity: 0.7
constraints: { data_boundary: on_prem, pii_allowed: true, regions: [eu] }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 1200
quality_prior: 0.72
examples:
- "Summarize this discharge note."
- "Extract the medications and dosages from this intake form."
- id: front-desk-fast
kind: llm
name: Front-desk assistant (public cloud, cheap)
capabilities:
domains: [general, general_chat, customer_support]
actions: [qa, classify, summarize, generation]
max_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0002 }
latency_ms: 300
quality_prior: 0.55
examples:
- "What time does the clinic open on Saturdays?"
- "Do I need to fast before a blood test?"
- id: clinical-reasoner
kind: llm
name: Clinical reasoning model (public cloud, frontier)
capabilities:
domains: [medical, general]
actions: [reasoning, qa, summarize, generation]
min_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.015 }
latency_ms: 2500
quality_prior: 0.93
examples:
- "Explain the interactions between these three medications and how to adjust the dosage."
- id: on-call-clinician
kind: human
name: On-call clinician
capabilities: { actions: [escalate], tags: [safety] }
cost: { usd_per_1k_tokens: 0.5 }
latency_ms: 300000
rules:
- name: patient-data-stays-inside
when: { contains_pii: true }
prefer: [clinic-notes-onprem]
weight: 1.0
- name: clinical-emergency-to-human
when: { actions: [escalate] }
prefer: [on-call-clinician]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Summarize this discharge note for patient John Doe, DOB 03/04/1961, phone +44 7700 900123: admitted with pneumonia, treated with IV antibiotics, discharged on oral amoxicillin. | clinic-notes-onprem | The phone number is PII: both public-cloud models are rejected by policy before any scoring; the rule prefers the on-premises model |
Explain step by step how metformin dosage should be adjusted for a patient with reduced kidney function and why. | clinical-reasoner | Medical domain, reasoning intent, complexity above the frontier model's floor; no identifiers, so the public model is allowed |
What time does the clinic open on Saturdays? | front-desk-fast | A simple question: the cheapest capable model wins on utility |
The patient reports acute chest pain right now - escalate to the on-call doctor. | on-call-clinician | The escalation intent triggers the pinned rule; every model is excluded |
Look closer. A department can insist on the boundary even when a note happens to contain no identifier - the constraint is enforced by policy, and the trace tells you exactly which target was refused and why:
from opensmartroute import RequestConstraints, RouteRequest, Router, load_rules, load_targets
from opensmartroute.strategies import default_strategies
router = Router(load_targets("healthcare.yaml"), strategies=[load_rules("healthcare.yaml"), *default_strategies(seed=0)])
d = router.route("Summarize this discharge note for patient John Doe, phone +44 7700 900123.")
assert d.target.id == "clinic-notes-onprem"
assert d.trace.signals.contains_pii is True
assert d.trace.policy_rejections["front-desk-fast"] == "PII not allowed on this target"
assert d.trace.policy_rejections["clinical-reasoner"] == "PII not allowed on this target"
req = RouteRequest("Summarize this patient intake note.", constraints=RequestConstraints(region="eu", data_boundary="on_prem"))
d = router.route(req)
assert d.target.id == "clinic-notes-onprem"
assert d.trace.policy_rejections["clinical-reasoner"] == "data boundary public < required on_prem"2. Banking and financial services
Situation. Customers ask about products in the chat widget. Back-office teams extract fields from invoices and loan files that contain account numbers. Analysts want deep comparisons. A failed payment or a suspected fraud is an event from the core banking system, not a sentence, and it must land on the fraud desk.
Catalogue.
# banking.yaml
targets:
- id: ledger-onprem
kind: llm
name: Back-office model (bank data centre)
capabilities:
domains: [finance, customer_support, general]
actions: [extract, summarize, classify, qa]
max_complexity: 0.7
constraints: { data_boundary: on_prem, pii_allowed: true, regions: [eu] }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 1200
quality_prior: 0.72
examples:
- "Extract the IBAN, amount and due date from this invoice."
- id: banker-fast
kind: llm
name: Customer chat model (cheap)
capabilities:
domains: [general, general_chat, customer_support, finance]
actions: [qa, classify, generation, summarize]
max_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0002 }
latency_ms: 300
quality_prior: 0.55
examples:
- "What is the interest rate on the savings account?"
- id: analyst-frontier
kind: llm
name: Analyst model (frontier)
capabilities:
domains: [finance, math, data_analysis, general]
actions: [reasoning, planning, generation, qa]
min_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.015 }
latency_ms: 2500
quality_prior: 0.93
examples:
- "Compare the risk-adjusted returns of two portfolios and explain the trade-offs."
- id: fraud-desk
kind: human
name: Fraud desk
capabilities: { actions: [escalate], tags: [safety] }
cost: { usd_per_1k_tokens: 0.5 }
latency_ms: 600000
rules:
- name: account-data-stays-inside
when: { contains_pii: true }
prefer: [ledger-onprem]
weight: 1.0
- name: failed-payments-to-fraud-desk
when: { event: ["payment.failed", "fraud.*"] }
prefer: [fraud-desk]
pin: true
weight: 1.0
- name: escalation
when: { actions: [escalate] }
prefer: [fraud-desk]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Extract the IBAN and amount from this invoice: IBAN DE89370400440532013000, EUR 4,200 due in 30 days. | ledger-onprem | An IBAN is PII: the public models are rejected, the rule prefers the data-centre model |
Compare the risk-adjusted return of a 60/40 portfolio against an all-equity portfolio over 30 years and explain the trade-offs step by step. | analyst-frontier | Finance and mathematics, reasoning intent, high complexity: the frontier model is worth its price |
What is the interest rate on the savings account? | banker-fast | Simple product question: cheapest capable model |
event payment.failed | fraud-desk | No text at all: the event name carries the intent and the pinned rule sends it to people |
Look closer. Events are plain context; a webhook handler passes them through without composing a prompt:
from opensmartroute import RequestConstraints, RouteRequest, Router, load_rules, load_targets
from opensmartroute.strategies import default_strategies
router = Router(load_targets("banking.yaml"), strategies=[load_rules("banking.yaml"), *default_strategies(seed=0)])
d = router.route(RouteRequest("", context={"event": "payment.failed", "account": "****1234"}))
assert d.target.id == "fraud-desk"
assert "escalate" in d.trace.signals.actions
d = router.route(RouteRequest("Summarize this loan file.", constraints=RequestConstraints(region="eu", data_boundary="on_prem")))
assert d.target.id == "ledger-onprem"3. Legal - a law firm
Situation. Associates summarise NDAs a dozen times a day - a cheap model does that well. Partners want a careful analysis of liability clauses from the strongest model, framed by the firm's own counsel persona. Any document naming a client stays on the firm's servers.
Catalogue.
# legal.yaml
targets:
- id: counsel-frontier
kind: llm
name: Counsel model (frontier)
capabilities:
domains: [legal, general]
actions: [reasoning, summarize, qa, generation]
min_complexity: 0.4
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.015 }
latency_ms: 2500
quality_prior: 0.92
examples:
- "Review this indemnification clause and explain our liability exposure."
- id: paralegal-fast
kind: llm
name: Paralegal model (cheap)
capabilities:
domains: [legal, general]
actions: [summarize, qa, classify, extract]
max_complexity: 0.5
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0004 }
latency_ms: 400
quality_prior: 0.6
examples:
- "Summarize the key points of this NDA in three bullets."
- id: matter-files-onprem
kind: llm
name: Matter-files model (firm servers)
capabilities:
domains: [legal, general]
actions: [summarize, extract, qa, generation]
max_complexity: 0.7
constraints: { data_boundary: on_prem, pii_allowed: true }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 1200
quality_prior: 0.7
- id: persona-inhouse-counsel
kind: persona
primary: false
capabilities: { domains: [legal], actions: [reasoning, summarize, qa] }
instructions: "You are cautious in-house counsel. Cite the clause, flag jurisdiction risk, never give a final legal opinion."
rules:
- name: client-data-stays-inside
when: { contains_pii: true }
prefer: [matter-files-onprem]
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Review this indemnification clause and explain our liability exposure under GDPR if a sub-processor breaches - compare the two drafting options step by step. | counsel-frontier | Legal reasoning at high complexity: the frontier model, above the cheap model's ceiling |
Summarize the key points of this NDA in three bullets. | paralegal-fast | A routine summary below the frontier model's floor: the cheap model wins |
Draft a contract amendment for client Maria Rossi (maria.rossi@example.com) extending the term by 12 months. | matter-files-onprem | The e-mail address is PII: only the firm's own model is admissible |
Look closer. Ask for a plan and the persona is layered on whichever model wins; its instructions become the
system prompt when the plan is executed:
from opensmartroute import Router, load_rules, load_targets
from opensmartroute.strategies import default_strategies
router = Router(load_targets("legal.yaml"), strategies=[load_rules("legal.yaml"), *default_strategies(seed=0)])
d = router.route("Review this indemnification clause and explain our liability exposure step by step.", plan=True)
assert d.target.id == "counsel-frontier"
assert {s.role: s.target.id for s in d.plan.slots}["persona"] == "persona-inhouse-counsel"4. Retail and e-commerce customer support
Situation. Shoppers ask about shipping and sizes, report damaged orders and demand refunds. Order events from the fulfilment system trigger customer notifications. A customer who asks for a manager gets one. The support agent has tools (order lookup, refund) and runs inside the company, so it may see customer details.
Catalogue.
# retail.yaml
targets:
- id: support-agent
kind: agent
name: Support agent (order lookup, refunds)
capabilities:
domains: [customer_support]
actions: [qa, action, classify, summarize]
supports_tools: true
tags: [autonomous]
constraints: { data_boundary: private, pii_allowed: true }
cost: { usd_per_1k_tokens: 0.004 }
latency_ms: 3000
quality_prior: 0.8
examples:
- "I want a refund for order #4821, it arrived damaged."
- "Where is my order? It was due yesterday."
- id: shop-chat-fast
kind: llm
name: Shop assistant (cheap)
capabilities:
domains: [general, general_chat]
actions: [qa, generation, classify]
max_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0002 }
latency_ms: 300
quality_prior: 0.55
examples:
- "Do you ship to Canada?"
- "What sizes does the jacket come in?"
- id: workflow-notify-customer
kind: workflow
name: Customer notification workflow
capabilities: { domains: [customer_support], actions: [action] }
cost: { usd_per_1k_tokens: 0.0001 }
latency_ms: 200
quality_prior: 0.7
- id: support-team
kind: human
name: Support team
capabilities: { actions: [escalate], tags: [safety] }
cost: { usd_per_1k_tokens: 0.5 }
latency_ms: 900000
rules:
- name: support-questions-to-agent
when: { domains: [customer_support] }
prefer: [support-agent]
weight: 0.8
- name: order-events-to-notification-workflow
when: { event: ["order.*", "shipment.*"] }
prefer: [workflow-notify-customer]
pin: true
weight: 1.0
- name: angry-customers-to-people
when: { actions: [escalate] }
prefer: [support-team]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
I want a refund for order #4821, it arrived damaged. | support-agent | Customer-support domain and an action to perform: the rule prefers the agent with tools |
Do you ship to Canada? | shop-chat-fast | A general question: the cheap model answers |
event order.shipped | workflow-notify-customer | The fulfilment event is pinned to the notification workflow - no prompt involved |
This is the third time I am asking. I want to speak to a manager now. | support-team | Escalation intent: pinned to people |
Look closer. Close the loop: when the agent resolves the refund, report the outcome and the learners shift future support traffic toward what actually works.
from opensmartroute import Outcome, Router, load_rules, load_targets
from opensmartroute.strategies import default_strategies
router = Router(load_targets("retail.yaml"), strategies=[load_rules("retail.yaml"), *default_strategies(seed=0)])
d = router.route("I want a refund for order #4821, it arrived damaged.")
assert d.target.id == "support-agent"
router.learn(
Outcome(
request_id=d.request_id,
target_id=d.target.id,
success=True,
quality=0.9,
cost_usd=0.003,
latency_ms=2400,
domains=d.trace.signals.domains,
)
)
assert router.feedback.stats()["support-agent"]["n"] == 15. Software engineering
Situation. Developers ask quick language questions, request SQL, and hand over multi-step repository work (refactor, run tests, open a pull request). CI and deployment failures arrive as events and should go straight to the coding agent.
Catalogue.
# engineering.yaml
targets:
- id: coding-agent
kind: agent
name: Coding agent (repository access, tests, pull requests)
capabilities:
domains: [coding]
actions: [code_generation, code_review, action, reasoning]
min_complexity: 0.4
supports_tools: true
tags: [autonomous]
cost: { usd_per_1k_tokens: 0.008 }
latency_ms: 6000
quality_prior: 0.85
examples:
- "Refactor this module to remove the circular import and run the test suite."
- "Investigate why the deploy failed and open a pull request with the fix."
- id: code-fast
kind: llm
name: Code model (cheap)
capabilities:
domains: [coding, general]
actions: [qa, code_generation, summarize, reasoning]
max_complexity: 0.5
cost: { usd_per_1k_tokens: 0.0004 }
latency_ms: 400
quality_prior: 0.6
examples:
- "What does the Python zip function do?"
- "Write a one-line list comprehension that squares a list."
- id: skill-sql
kind: skill
name: SQL skill
capabilities: { domains: [data_analysis, coding], actions: [code_generation, extract] }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 200
quality_prior: 0.8
instructions: "Always qualify table names with the schema; never SELECT *."
examples:
- "Write a SQL query that returns monthly active users per region."
rules:
- name: sql-to-skill
when: { domains: [data_analysis], actions: [code_generation] }
prefer: [skill-sql]
pin: true
weight: 1.0
- name: broken-builds-to-agent
when: { event: ["deploy.failed", "build.failed", "test.failed"] }
prefer: [coding-agent]
pin: true
weight: 1.0
- name: repo-work-to-agent
when: { domains: [coding], actions: [action, code_review] }
prefer: [coding-agent]
weight: 0.9
- name: hard-coding-to-agent
when: { domains: [coding], min_complexity: 0.4 }
prefer: [coding-agent]
weight: 0.7What happens.
| Request | Routed to | Why |
|---|---|---|
Refactor the payment module to remove the circular import, run the test suite, fix the failures and open a pull request. | coding-agent | Repository work with actions to perform: the agent with tools |
Review this pull request and find the bug in the retry loop. | coding-agent | Code review is agent work in this catalogue |
What does the Python zip function do? | code-fast | A quick language question: the cheap model |
Write a SQL query that returns monthly active users per region from the events table. | skill-sql | Data-analysis code generation is pinned to the SQL skill |
event deploy.failed | coding-agent | The failure event is pinned to the agent |
6. Human resources
Situation. Employees ask about policies. Recruiters screen CVs full of personal data. Managers draft performance reviews. Sensitive cases go to a human business partner.
Catalogue.
# hr.yaml
targets:
- id: people-onprem
kind: llm
name: People-data model (inside the company)
capabilities:
domains: [hr, general]
actions: [extract, classify, summarize, qa, generation]
max_complexity: 0.7
constraints: { data_boundary: on_prem, pii_allowed: true }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 1200
quality_prior: 0.72
examples:
- "Screen this resume against the job description."
- id: hr-helpdesk-fast
kind: llm
name: HR helpdesk model (cheap)
capabilities:
domains: [hr, general, general_chat]
actions: [qa, classify, summarize]
max_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0002 }
latency_ms: 300
quality_prior: 0.55
examples:
- "How many days of parental leave does the leave policy give?"
- id: hr-writer
kind: llm
name: HR writing model
capabilities:
domains: [hr, general]
actions: [generation, summarize, reasoning]
min_complexity: 0.3
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.003 }
latency_ms: 900
quality_prior: 0.78
examples:
- "Draft a constructive performance review for an engineer."
- id: hr-business-partner
kind: human
name: HR business partner
capabilities: { actions: [escalate], tags: [safety] }
cost: { usd_per_1k_tokens: 0.5 }
latency_ms: 3600000
rules:
- name: candidate-and-employee-data-stays-inside
when: { contains_pii: true }
prefer: [people-onprem]
weight: 1.0
- name: sensitive-cases-to-people
when: { actions: [escalate] }
prefer: [hr-business-partner]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Screen this resume for the data engineer role: Priya Natarajan, priya.n@example.com, +91 98765 43210, 6 years of Spark and Airflow. | people-onprem | E-mail and phone number: only the in-house model may see the CV |
How many days of parental leave does the leave policy give? | hr-helpdesk-fast | Policy question: the cheap model |
Draft a constructive performance review for an engineer who ships fast but skips code reviews, and explain how to phrase the feedback. | hr-writer | Drafting with reasoning at moderate complexity: the writing model |
An employee reported harassment on the team - escalate this to the HR business partner. | hr-business-partner | Escalation intent: pinned to a person |
7. Marketing
Situation. Copy is written all day and should cost next to nothing - a small self-hosted model does it. Campaign planning needs a stronger model. Translations go to a dedicated translation skill so terminology stays consistent.
Catalogue.
# marketing.yaml
targets:
- id: copywriter-small
kind: llm
name: Copywriter (small, self-hosted)
capabilities:
domains: [marketing, creative, general]
actions: [generation, summarize, translate, qa]
max_complexity: 0.5
languages: ["*"]
constraints: { data_boundary: on_prem, pii_allowed: true }
cost: { usd_per_1k_tokens: 0.00005 }
latency_ms: 700
quality_prior: 0.6
examples:
- "Write three subject lines for our spring newsletter."
- id: strategist-mid
kind: llm
name: Campaign strategist model
capabilities:
domains: [marketing, data_analysis, general]
actions: [planning, reasoning, generation, summarize]
min_complexity: 0.3
cost: { usd_per_1k_tokens: 0.003 }
latency_ms: 900
quality_prior: 0.78
examples:
- "Plan a multi-channel campaign with budget allocation and KPIs per channel."
- id: skill-translate
kind: skill
name: Translation skill
capabilities: { actions: [translate], languages: ["*"] }
cost: { usd_per_1k_tokens: 0.0005 }
latency_ms: 300
quality_prior: 0.85
examples:
- "Translate this headline into Spanish."
rules:
- name: translations-to-skill
when: { actions: [translate] }
prefer: [skill-translate]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Write three subject lines for our spring newsletter. | copywriter-small | Simple copy: the near-free self-hosted model |
Plan a 12-week multi-channel campaign for a B2B SaaS launch with budget allocation and KPIs per channel, and explain the trade-offs. | strategist-mid | Planning and reasoning above the copywriter's ceiling |
Translate this landing page headline into Spanish: 'Route every request to the right model.' | skill-translate | Translation intent: pinned to the skill |
8. Travel and hospitality
Situation. Guests ask quick questions at any hour. Trip planning with bookings is agent work with tools. Anything naming a guest stays on the group's own systems.
Catalogue.
# travel.yaml
targets:
- id: concierge-fast
kind: llm
name: Concierge model (cheap)
capabilities:
domains: [travel, general, general_chat, customer_support]
actions: [qa, generation, summarize]
max_complexity: 0.45
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.0002 }
latency_ms: 300
quality_prior: 0.55
examples:
- "What time is checkout?"
- id: booking-agent
kind: agent
name: Booking agent (search and reservations)
capabilities:
domains: [travel]
actions: [planning, action, reasoning, generation]
supports_tools: true
tags: [autonomous]
min_complexity: 0.3
constraints: { pii_allowed: false }
cost: { usd_per_1k_tokens: 0.006 }
latency_ms: 5000
quality_prior: 0.82
examples:
- "Plan a five-day itinerary with train bookings."
- id: guest-records-onprem
kind: llm
name: Guest-records model (own systems)
capabilities:
domains: [travel, customer_support, general]
actions: [extract, summarize, qa, classify]
max_complexity: 0.7
constraints: { data_boundary: on_prem, pii_allowed: true }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 1200
quality_prior: 0.7
rules:
- name: guest-data-stays-inside
when: { contains_pii: true }
prefer: [guest-records-onprem]
weight: 1.0
- name: trip-planning-to-agent
when: { domains: [travel], actions: [planning, action] }
prefer: [booking-agent]
weight: 0.8What happens.
| Request | Routed to | Why |
|---|---|---|
Plan a five-day itinerary in Kyoto for a family with two kids and book the train from Tokyo. | booking-agent | Travel planning with a booking to make: the agent with tools |
What time is checkout? | concierge-fast | A trivial question: the cheap model |
Change the booking for guest anna.k@example.com to a late arrival on Friday. | guest-records-onprem | The guest's e-mail address is PII: only the group's own model is admissible |
9. Data and analytics
Situation. Analysts ask for SQL, quick arithmetic and narrative analysis of datasets. SQL goes to a deterministic skill, arithmetic to a calculator tool that costs nothing and never hallucinates, analysis to a mid-tier model.
Catalogue.
# analytics.yaml
targets:
- id: skill-sql
kind: skill
name: SQL skill
capabilities: { domains: [data_analysis], actions: [code_generation, extract] }
cost: { usd_per_1k_tokens: 0.001 }
latency_ms: 200
quality_prior: 0.8
instructions: "Always qualify table names with the schema; never SELECT *."
examples:
- "Write a SQL query that returns revenue per month."
- id: tool-calculator
kind: tool
name: Calculator
capabilities: { domains: [math], actions: [qa], max_complexity: 0.3 }
cost: { usd_per_1k_tokens: 0.0 }
latency_ms: 5
quality_prior: 0.99
examples:
- "What is 17 percent of 2340?"
- id: analyst-mid
kind: llm
name: Analyst model
capabilities:
domains: [data_analysis, math, general]
actions: [reasoning, summarize, qa, generation, planning]
min_complexity: 0.25
cost: { usd_per_1k_tokens: 0.003 }
latency_ms: 900
quality_prior: 0.78
examples:
- "Analyze this CSV of monthly sales and describe the trend."
rules:
- name: sql-to-skill
when: { domains: [data_analysis], actions: [code_generation] }
prefer: [skill-sql]
pin: true
weight: 1.0
- name: arithmetic-to-calculator
when: { domains: [math], max_complexity: 0.4 }
prefer: [tool-calculator]
pin: true
weight: 1.0What happens.
| Request | Routed to | Why |
|---|---|---|
Write a SQL query that returns revenue per month and region from the orders table. | skill-sql | Data-analysis code generation: pinned to the SQL skill |
What is 17% of 2340? | tool-calculator | Simple arithmetic: pinned to the free, exact tool |
Analyze this CSV of monthly sales and explain the trend and the likely drivers. | analyst-mid | Reasoning over data: the analyst model |
The patterns behind the scenarios
| You need | Declare | What the router does |
|---|---|---|
| Personal or regulated data never leaves your boundary | constraints: { pii_allowed: false } on every external target; a rule when: { contains_pii: true } preferring the internal one | Detects e-mail, phone, IBAN, card, SSN and Aadhaar numbers in the request; policy removes every pii_allowed: false target before scoring |
| A whole department must stay on-premises regardless of content | RequestConstraints(data_boundary="on_prem") (or --constraint data_boundary=on_prem) | Rejects every target with a weaker boundary and records the reason in the trace |
| Only targets in a region | constraints: { regions: [eu] } on the target and RequestConstraints(region="eu") on the request | Same hard filter |
| A person handles emergencies, complaints and sensitive cases | A human target with actions: [escalate] and a pinned rule when: { actions: [escalate] } | Every model is excluded when the escalation intent is detected |
| System events are routed like chat | when: { event: ["order.*", "payment.failed"] }; send RouteRequest("", context={"event": ...}) or osr route --event ... | The event name supplies domain and intent; pinned rules pick the workflow, agent or person |
| Cheap things stay cheap, hard things get the best model | max_complexity on cheap targets, min_complexity on expensive ones | The utility trades quality against cost and latency inside the admissible set |
| Deterministic work never goes to a model | A skill or tool target and a pinned rule on its action | SQL, translation, arithmetic reach the skill or tool directly |
| A house style on top of any model | A persona with primary: false | Fills the persona slot of every plan whose domain matches; its instructions become the system prompt |
From the page to production
- Same file, served over HTTP.
osr -t retail.yaml -r retail.yaml serveexposes/route,/feedback, the OpenAI-compatible/v1endpoints and/mcpfor the catalogue above; the deploy guide covers the container image, Helm chart and the hardening flags. - Real providers. Attach handlers to the targets and call
router.run()instead ofrouter.route(); the user guide shows OpenAI-compatible clients, MCP tools, A2A agents and agent harnesses. - Hosted platform. The same decisions, traces and feedback loop behind an API key, with plans, tenants and a
dashboard: platform guide. Mount a catalogue with
OSR_TARGETS/OSR_RULESwhen you run the platform yourself (install guide);POST /api/v1/routetakes the sametext,context(includingcontext.event) andconstraintsas the examples above. - Hardening for multi-tenant traffic. Health circuit breakers, budgets, input and output guards, audit and persistent learner state come from the enterprise builder.
- Measuring your own traffic. Turn the What happens tables into a JSONL dataset and run
osr evalto gate routing accuracy in CI: evaluation.
How this page is verified
tests/test_use_cases.py parses this Markdown file. For every scenario it writes the YAML block to the file named
in its first line, builds Router(load_targets(path), strategies=[load_rules(path), *default_strategies(seed=0)]),
routes every row of the What happens table (text rows as plain requests, event rows as
RouteRequest("", context={"event": ...})) twice - once with the SDK's default Objective() and once with the
command line's Objective(cost=0.3) - and asserts the Routed to target. The same rows then go through
osr -t <file> -r <file> route --json and through POST /route on the FastAPI app osr serve runs, and
platform/api/tests/test_use_cases_platform.py repeats them against the hosted platform application with the
catalogue mounted as the deployment's OSR_TARGETS / OSR_RULES (POST /api/v1/route with a workspace key).
Every Python block on the page is executed in the directory holding the YAML files, so its assert lines are the
test's assertions. Change a request, a target or a rule here and the tests tell you whether every surface still
agrees.
Install the platform
Run the whole platform yourself: the Azure Marketplace offer, azd, Docker Compose on your own servers or air-gapped; first sign-in, custom domain, upgrade and backup.
Platform guide
Authentication, POST /api/v1/route, the OpenAI-compatible endpoint, feedback, plans, tenants, dashboard and errors.