Building Customized GenAI Product Development Services in Python
Building Customized GenAI Product Development Services in Python
Generic chat interfaces are easy to demo and surprisingly hard to turn into reliable products. A useful GenAI service needs a narrow job, measurable success criteria, controlled access to data, and predictable behavior when the model is uncertain.
This tutorial designs a small product-recommendation service in Python. The interesting part is not the API call. It is the boundary around the model: structured inputs, candidate retrieval, constrained output, validation, and evaluation.
Start with a product contract
Suppose a user describes a need and expects three suitable products. Before choosing a model, define the contract:
| Input | Output | Non-negotiable rule |
|---|---|---|
| Free-text need, budget, region | Up to three product IDs with reasons | Never invent a product |
| Optional preferences | Confidence and missing information | Respect budget and availability |
| Catalog snapshot | Machine-readable JSON | Explain the evidence used |
This contract immediately suggests that the language model should not search an unlimited catalog from memory. The application should retrieve eligible candidates first and let the model rank only those records.
A minimal architecture
- Validate the request.
- Apply deterministic filters such as price, region, and inventory.
- Retrieve semantically relevant candidates.
- Ask the model to rank the candidate IDs.
- Validate the response against the catalog.
- Log the decision for evaluation without storing unnecessary personal data.
The model is one component, not the product boundary.
Define typed inputs and outputs
Pydantic makes invalid states explicit:
from pydantic import BaseModel, Field
class RecommendationRequest(BaseModel):
need: str = Field(min_length=10, max_length=1000)
budget_usd: float = Field(gt=0)
region: str
class Recommendation(BaseModel):
product_id: str
reason: str = Field(max_length=240)
confidence: float = Field(ge=0, le=1)
class RecommendationResponse(BaseModel):
items: list[Recommendation] = Field(max_length=3)
missing_information: list[str] = []
Validation does not make a model truthful, but it prevents malformed output from silently crossing into the rest of the application.
Retrieve before generating
Begin with deterministic eligibility rules:
def eligible_products(catalog, request):
return [
item for item in catalog
if item["price_usd"] <= request.budget_usd
and request.region in item["regions"]
and item["in_stock"]
]
Semantic search can then rank this smaller set. For a modest catalog, embeddings stored in PostgreSQL with pgvector may be sufficient. Large or frequently changing catalogs may need a dedicated search service, but the same rule applies: the model only sees candidates the application is allowed to recommend.
Build a constrained model request
Pass compact catalog evidence and demand IDs from that evidence:
import json
def build_prompt(request, candidates):
return f"""
You rank products for a user. Use only the candidate product IDs below.
If the evidence is insufficient, return fewer items and list what is missing.
USER REQUEST:
{request.model_dump_json()}
CANDIDATES:
{json.dumps(candidates, ensure_ascii=False)}
"""
Use the structured-output feature provided by your chosen model SDK, then parse the result into RecommendationResponse. After parsing, reject any ID that is not in the candidate set. This final check is cheap and closes an important hallucination path.
Evaluate the system, not just the prose
A recommendation can sound polished while violating the product contract. Create a small evaluation set containing normal requests and difficult cases:
- Budget below the cheapest item
- Conflicting preferences
- Empty inventory in the requested region
- Prompt-injection text inside the user request
- Two nearly identical products with one decisive specification
Track at least these metrics:
| Metric | What it catches |
|---|---|
| Catalog validity | Invented product IDs |
| Constraint compliance | Budget or region violations |
| Top-k relevance | Poor retrieval or ranking |
| Abstention quality | Confident answers without evidence |
| Latency and cost | An experience that cannot scale |
Run the evaluation whenever the prompt, model, catalog schema, or retrieval logic changes. Model upgrades should be treated like dependency upgrades, not assumed to be improvements.
Production safeguards
- Keep API keys on the server and out of client-side code.
- Rate-limit by account and protect expensive endpoints from replay.
- Separate user instructions from retrieved catalog text.
- Escape model output before rendering it as HTML.
- Store prompt and response samples only under a documented retention policy.
- Provide a deterministic fallback when the model is unavailable.
What makes the service customized
Customization is not a longer system prompt. It is the combination of domain-specific data, explicit business rules, evaluation cases, and workflow integration. Those assets remain valuable even when the underlying model changes.
The strongest first release is usually narrow: one user segment, one catalog, a visible explanation, and a human-review path for uncertain cases. Expand only after evaluation shows where the system is dependable.
Further reading
Last reviewed: August 7, 2026.
About this publication: About · Editorial Policy · Privacy · Contact
Comments
Post a Comment
Thank you for visiting AI Hub Discovery! We welcome thoughtful comments, questions, and discussions about AI, machine learning, software engineering, and cloud technologies. Please keep comments respectful, relevant, and free of spam or promotional links.