MSAZ-301 · Lesson 12 of 12 · 20 min
An intelligent app case study
▶ Video coming soon A video version of this lesson is on the way.
On November 11, the last day of the prototype, Hana presented Ask Finch to Grace Oduya, Elena, Sunil, Carmen and Nikolai, with Dana Whitcombe from the AI review group joining online. The meeting had one purpose: decide what the February 2027 pilot at two stores, Store 12 in Winnipeg and Store 15 in Brandon, would look like. This lesson walks through the design they approved, each decision and the lesson of this course it came from, so that it can serve as a template for an exam case study and for real work.
The requirements
Grace's requirements were short. Store associates must be able to ask a product or how-to question in their own words and get, within half a second, a short list of matching products with stock at their store, and within three seconds, a written answer of no more than 80 words that cites its sources. Contractors must be able to search by SKU or brand. Nothing customers say may be stored. Answers must never invent a product. And the monthly running cost must be known before launch.
| Requirement | Design response | Lesson |
|---|---|---|
| Plain-language questions and codes | Hybrid search with RRF over products and guide chunks | 7 |
| Results in 0.5 s, answer in 3 s | Parallel searches, results first, answer after; timeouts and fallback | 4, 11 |
| Stock at the user's store only | Row-level security with the store passed in SESSION_CONTEXT | 10 |
| Cited, grounded answers | RAG with JSON packaging, citation check and allowed-value checks | 8 |
| No customer details stored | Product question only; redaction safety net; no embedding of notes | 10 |
| Known monthly cost | Token model and cache; database tier sized separately | 11 |
The architecture
Everything runs in the catalogue database, which moves for the pilot from the serverless test database to a provisioned General Purpose database in Canada Central, with a failover group to Canada East as MSAZ-282 set up for other databases. Products and guide chunks have half-precision embeddings from text-embedding-3-small in narrow tables. Full-text indexes cover product names, descriptions, usage notes and SKUs, and guide titles and text. Two external models point at two deployments of the embedding model: one for questions during the day and one for the nightly maintenance job. A chat model deployment answers questions. All three are called with the server's managed identity, which holds the Cognitive Services OpenAI User role, and the server's outbound firewall allows only the company's model host.
Two stored procedures form the whole interface. askfinch.Search takes a question and returns ranked products with local stock and ranked guide passages; askfinch.Answer takes the same question and the store, checks the cache, retrieves, builds the prompt, calls the chat model, checks the reply and returns the answer and its sources, or a no-answer status. Both run WITH EXECUTE AS ai_proc_owner. The application's identity has EXECUTE on those two procedures and nothing else.
CREATE OR ALTER PROCEDURE askfinch.Answer
@question nvarchar(400), @answer nvarchar(1000) OUTPUT, @sources nvarchar(max) OUTPUT, @status varchar(20) OUTPUT
WITH EXECUTE AS 'ai_proc_owner'
AS
BEGIN
SET NOCOUNT ON;
DECLARE @storeId int = CAST(SESSION_CONTEXT(N'StoreId') AS int); -- set by the app from the user's token
-- 1. Cache (lesson 11)
EXEC askfinch.TryCache @question, @storeId, @answer OUTPUT, @sources OUTPUT, @status OUTPUT;
IF @status = 'cached' RETURN;
-- 2. Retrieve with hybrid search (lesson 7) into #productHits and #chunkHits
EXEC askfinch.Retrieve @question, @storeId;
-- 3. Package, 4. generate, 5. check (lesson 8)
EXEC askfinch.Generate @question, @storeId, @answer OUTPUT, @sources OUTPUT, @status OUTPUT;
-- Log every request without customer details (lesson 10)
INSERT askfinch.RequestLog (AskedAt, StoreId, Question, Status, SourcesJson)
VALUES (SYSUTCDATETIME(), @storeId, @question, @status, @sources);
END;Keeping it current and deployable
Embeddings are maintained by change tracking and a stored hash, as lesson 3 described: an elastic job every 15 minutes re-embeds products and guide sections whose descriptive text changed, and a nightly job checks for any gaps. The full-text indexes update automatically. When the team changes embedding model in future, the plan is a new vector column, a full re-embedding on the nightly deployment, a side-by-side test on the evaluation questions, and a switch of the procedures to the new column, with the old column kept for a week as a fallback.
Deployment follows the database project practices from MSAZ-300. External models and credentials are part of the project, with the credential's identity but never a secret. One limit needed special handling: because the pilot uses exact search, there is no vector index, but if one is added later, it must be scripted outside the database project, since databases with vector indexes cannot be exported to a BACPAC or deployed with the index in a DACPAC, as lesson 6 explained.
Testing and evaluation
The 60 prototype questions were too few to support a go-live decision, so Grace's managers wrote 150 more, including 20 deliberately awkward ones: misspellings, French product names, questions about products Harrow & Finch does not sell, and two attempts to make the assistant reveal cost prices. Hana reran the whole set after the improvements from lessons 7, 8 and 11.
results = { # judged by two store managers, disagreements settled by Grace
"correct and useful": 191,
"correct, missed a better product": 11,
"said it could not answer (correctly)": 4,
"wrong or unhelpful": 4,
}
total = sum(results.values())
good = results["correct and useful"] + results["said it could not answer (correctly)"]
for k, v in results.items():
print(f"{k:38s} {v:4d} {v * 100 / total:5.1f} in 100")
print(f"total {total}; acceptable answers {good} = {good * 100 / total:.1f} in 100")
print("target met (90 in 100 or more):", good * 100 / total >= 90)correct and useful 191 91.0 in 100 correct, missed a better product 11 5.2 in 100 said it could not answer (correctly) 4 1.9 in 100 wrong or unhelpful 4 1.9 in 100 total 210; acceptable answers 195 = 92.9 in 100 target met (90 in 100 or more): True
What this code does
The program adds up the judgements for all 210 evaluation questions, the 60 original ones plus 150 new ones, and shows each group as a number out of 100. It counts correct answers and correct refusals as acceptable, because saying it cannot answer a question about a product the company does not sell is the right result. The output shows about 93 acceptable answers in every 100, above the 90 target, with 4 wrong answers out of 210.
Correct refusals count as acceptable because the right answer to which deck stain should I buy from a competitor's range is that Ask Finch can only recommend Harrow & Finch products. Both attempts to extract cost prices failed, because cost prices are never selected into the package, the design principle lesson 10 described: what is not sent cannot leak. Median time to the written answer was 1.9 seconds and the 95th percentile 3.4 seconds; search results appeared in a median of 0.19 seconds.
The 95th percentile missed the three-second target. The meeting decided not to block the pilot on it, because the slow answers were all chat calls during a short throttling period on the shared test deployment, and the pilot deployment has its own quota. The pilot success criteria include the 95th percentile, measured from the request log, and the timeout and fallback mean that, at worst, an associate sees results without an answer.
Governance
The AI review group, Carmen, Sunil and Dana, approved the pilot with conditions. A privacy impact assessment covers the model provider's data commitments and the regional deployment. A weekly review samples 30 answers and every answer flagged by staff with the not helpful button. An incident procedure says what to do if the assistant gives a dangerous answer, such as unsafe advice about electrical work: disable the answer procedure with one setting while search keeps working, review the log, fix the cause and record the outcome. And the assistant states in the app that its answers are generated and that staff should check the product label, a small line Grace asked for so associates treat it as a helper rather than an authority.
The pilot timeline
| Date | Step |
|---|---|
| December 2026 | Provision the pilot database and model deployments; load embeddings; security review of the deployed design |
| January 2027 | Staff training at Stores 12 and 15; dry runs with the store managers |
| February 1 to 28, 2027 | Pilot: both stores, all associates, weekly answer reviews |
| March 2027 | Decision on rollout to all 23 stores, based on acceptance, latency and cost |
| June 2027 | Hana sits exam DP-800 |
Monitoring after launch
The pilot dashboard brings together the signals the earlier lessons introduced. From the request log: requests per store per day, the share answered from cache, the share of no-answer results, the median and 95th percentile response times, and the not helpful flags. From the database: CPU, log write use, the HTTP_EXTERNAL_CONNECTION wait, and the search procedures' durations from Query Store. From Azure Monitor for the model resource: token use per deployment against quota and any 429 responses. From Cost Management: model charges to date against the budget. Each signal has an owner and a threshold, and the weekly review starts from the dashboard rather than from anecdotes.
Grace added one measure of her own: how often an associate who received an answer then searched again within a minute. A quick second search suggests the first answer did not help, even if nobody pressed not helpful, and it is cheap to compute from the log.
The meeting ended with a list of what would make the team stop the pilot early: any answer that gave unsafe advice and was acted on, any exposure of one store's data to another, or a not helpful rate above 25 in 100 for two consecutive weeks. Writing the stop conditions down before launch, Dana observed, is what makes it possible to launch at all: everyone knows in advance what failure looks like and who decides. Hana added the list to the runbook, and to her DP-800 notes as a reminder that case-study questions about pilots often hinge on exactly these criteria.
Worked problem
Reading an exam-style case study
A DP-800 case study describes a company like Harrow & Finch and asks: the company needs semantic search over 3 million support tickets with filters by product line, results in under a second, and the ability to update tickets throughout the day. Which design should you recommend?
Show solution
- 3 million vectors is too many for exact search at interactive speed, so approximate search with a vector index is needed.
- Tickets change all day, so the index must support DML: the latest vector index version, used with SELECT TOP (N) WITH APPROXIMATE.
- Filters by product line apply during the search with the latest version; add a B-tree index on the product line column.
- Embeddings are maintained asynchronously, for example with an Azure Function and a SQL trigger binding, not a table trigger.
- Check availability: if the platform is SQL Server 2025, vector indexes need PREVIEW_FEATURES; on Managed Instance, the right update policy.
Answer: A latest-version DiskANN vector index with filtered VECTOR_SEARCH, asynchronous embedding maintenance and an availability check for the platform.
Case studies on the exam reward the habit this course has followed: read each requirement, map it to a feature, and check the constraint that rules alternatives out. Nikolai's question, who reads the data and how fast they need it, turned out to be a good way to find the constraint that matters most.
Common mistakes
MistakeA pilot can launch once accuracy looks good on a few dozen questions.
InsteadEvaluate on a larger, deliberately awkward set, including refusals and attempts to extract protected data.
MistakeA refusal to answer is always a failure.
InsteadRefusing a question the data cannot answer is the correct outcome and should count as acceptable.
MistakeVector indexes deploy with the rest of the database project.
InsteadDatabases with vector indexes cannot use BACPAC export or deploy the index in a DACPAC; script them separately.
MistakeThe model's output decides what the application does.
InsteadProcedures decide; model output is checked text, and the answer feature can be switched off independently of search.
Summary and key terms
- Map each requirement to a feature: hybrid search, RAG, row-level security, caching and checks.
- Keep the interface to a few stored procedures running under a dedicated owner, with EXECUTE only for the application.
- Maintain embeddings asynchronously, plan model changes as side-by-side columns, and handle vector index deployment limits.
- Evaluate on a large, awkward question set; count correct refusals as acceptable.
- Govern with a privacy assessment, regular answer reviews, an incident procedure and clear labelling in the app.
- In case studies, find the constraint that rules out alternatives, such as table size, update frequency or platform availability.
- Pilot
- A limited first deployment used to test a system with real users before wider rollout.
- Evaluation set
- A fixed collection of test questions with judged answers used to measure quality over time.
- Correct refusal
- An assistant saying it cannot answer when the supplied data does not support an answer.
- Fallback
- A reduced response, such as search results without an answer, used when a step fails or times out.
- Incident procedure
- Agreed steps for responding when an AI feature produces a harmful or wrong result.
- Case study
- An exam format that describes a business scenario and asks several questions about it.
Practice problems
Work each problem on paper first, then reveal the answer to check.
Why does the pilot use exact search instead of a vector index?
Show answer
Exact float16 search meets the time target on 18,400 products without the preview status and export limits of vector indexes.
Why were correct refusals counted as acceptable?
Show answer
When the data cannot support an answer, saying so is the right result.
Why did the attempts to reveal cost prices fail?
Show answer
Cost prices are never selected into the prompt package.
What happens if the chat call times out in the pilot?
Show answer
The associate sees search results without a written answer.
What is the plan for changing embedding models later?
Show answer
Add a new vector column, re-embed overnight, test side by side, switch the procedures, keep the old column briefly as a fallback.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.