Branching on the Result
Explore how to transform validated AI feedback into actionable responses in backend systems. This lesson guides you through building a deterministic routing function that decides when to create support tickets, notify teams, or archive feedback silently based on confidence and severity levels. You will learn the importance of check order for reliable business rule enforcement without additional model calls, ensuring predictable and auditable backend behavior.
The endpoint from the previous lesson returns a validated Feedback object and stops there. A real feature needs to do something with that object, open a support ticket for something urgent, post a notification for something worth a human glance, or file something routine away without bothering anyone. This lesson builds that decision layer. It is worth noticing upfront that nothing in it calls a model again. Every branch below runs on plain Python, checking fields that were already validated by the schema from the previous chapter.
Already configured setup code
The block below is carried over so this file runs entirely on its own, the schema a valid result must match, the prompt builder, the code fence stripper, a call_llm that runs with no API key using a mock response by default, and the pipeline tying it all together. None of this is new. It exists here purely so this lesson does not depend on any other file.
import jsonfrom enum import Enumfrom pydantic import BaseModel, Field, ValidationErrorclass Sentiment(str, Enum):POSITIVE = "positive"NEUTRAL = "neutral"NEGATIVE = "negative"class FeedbackCategory(str, Enum):PRODUCT_QUALITY = "product_quality"CUSTOMER_SERVICE = "customer_service"PRICING = "pricing"OTHER = "other"class Confidence(str, Enum):HIGH = "high"LOW = "low"class Feedback(BaseModel):sentiment: Sentimentcategory: FeedbackCategoryseverity: int = Field(ge=1, le=5, default=1)confidence: Confidencesummary: strdef build_feedback_prompt(feedback_text: str) -> str:return ("Analyze the customer feedback below. Respond with a JSON object ""in exactly this shape, and nothing else:\n\n""{\n"' "sentiment": <one of: "positive", "neutral", "negative">,\n'' "category": <one of: "product_quality", "customer_service", ''"pricing", "other">,\n'' "severity": <integer from 1 to 5>,\n'' "confidence": <one of: "high", "low">,\n'' "summary": <string, one short sentence>\n'"}\n\n"f'Feedback: "{feedback_text}"')def build_repair_prompt(original_prompt: str, bad_response: str, error: ValidationError) -> str:return (f"{original_prompt}\n\nYour previous response did not match the "f"required format. Previous response:\n{bad_response}\n\n"f"Validation error:\n{error}\n\nRespond again with only the ""corrected JSON object.")def strip_code_fence(text: str) -> str:stripped = text.strip()if stripped.startswith("```"):lines = stripped.split("\n")[1:]if lines and lines[-1].strip() == "```":lines = lines[:-1]return "\n".join(lines).strip()return stripped# USE_MOCK is True by default, so this file runs immediately with no# API key. Set it to False and fill in a real provider block below to# use an actual model.USE_MOCK = Truedef call_llm(prompt: str) -> str:if USE_MOCK:return ('{"sentiment": "negative", "category": "product_quality", ''"severity": 5, "confidence": "low", ''"summary": "Checkout appears broken but the ticket text was vague."}')# from openai import OpenAI# client = OpenAI(api_key="your-openai-api-key")# response = client.chat.completions.create(# model="your-chosen-openai-model",# messages=[{"role": "user", "content": prompt}],# )# return response.choices[0].message.contentraise NotImplementedError("Fill in a real provider block, or set USE_MOCK back to True.")def get_validated_feedback(feedback_text: str, max_attempts: int = 2) -> Feedback:prompt = build_feedback_prompt(feedback_text)current_prompt = promptfor attempt in range(1, max_attempts + 1):raw_response = call_llm(current_prompt)cleaned = strip_code_fence(raw_response)try:parsed = json.loads(cleaned)except json.JSONDecodeError:continuetry:return Feedback.model_validate(parsed)except ValidationError as error:current_prompt = build_repair_prompt(prompt, cleaned, error)return Feedback(sentiment=Sentiment.NEUTRAL,category=FeedbackCategory.OTHER,severity=3,confidence=Confidence.LOW,summary="Could not be automatically assessed.",)
Turning a result into an action
This is what the lesson is actually about. Everything above produces a validated Feedback object and stops. This is the part that decides what happens next.
It would be tempting to ask the model itself which action to take, the same way Lesson 2 warned ...