1---2name: ai-engineering-standards3description: Enforces production-grade Python and AI engineering standards for FastAPI, LangChain/LangGraph, RAG pipelines, and LLM integrations, covering type safety, error handling, testing, and security.4---5 6# Production Python & AI Engineering Standards7 8## Code quality baseline9- Type hints on every function signature. Pydantic v2 models at all I/O boundaries (API requests/responses, LLM outputs, file parsing results).10- Code must pass ruff and mypy. No print() — use the logging module with structured context.11- Config via pydantic-settings and environment variables. Never hardcode API keys, model names, or URLs. Magic numbers (chunk sizes, top_k, thresholds) live in a config object, not inline.12## FastAPI13- async def for I/O-bound routes; never call blocking/sync I/O inside them.14- Shared clients (DB, HTTP, LLM SDKs) created once at startup and injected via Depends — never instantiated per request.15- response_model on every route. Correct status codes. Routes stay thin: validation in, service call, response out — business logic lives in service functions.16- Raise HTTPException with clear detail; map internal exceptions to safe client messages (never leak stack traces or keys).17## LLM calls (any provider)18- Every call gets: explicit timeout, retry with exponential backoff on transient errors, and a max-retry cap.19- NEVER trust raw model output. Parse into a Pydantic schema; on validation failure, retry with the error fed back or fall through to an explicit failure path — never .get() blindly on un-validated JSON.20- Pin model versions in config. Prompts are versioned constants/templates in the repo, not inline f-strings scattered through code.21- Log per call: model, latency, input/output tokens, and a request/trace ID. Propagate the trace ID through every pipeline stage.22## LangChain / LangGraph / RAG23- Prefer explicit LangGraph state (TypedDict/Pydantic) over implicit chains; every node validates what it reads from state.24- Handle tool errors inside the graph — a failed tool returns a structured error message to the model, it does not crash the run.25- RAG: chunking params, embedding model, and top_k come from config; embedding model version is pinned (changing it invalidates the index — say so in code comments).26- Use asyncio.gather for independent parallel LLM/tool calls; cap concurrency with a semaphore.27## Errors & logging28- Catch specific exceptions only — no bare except. Either handle meaningfully or let it propagate; never swallow silently.29- Fail loud and early in pipelines: validate inputs at the start, not three stages deep.30- Logs are structured (key=value or JSON): event, trace_id, duration_ms, and outcome.31## Testing32- pytest. Unit tests NEVER hit live LLM APIs — mock the client and test the parsing/validation/retry logic hard, including malformed model output.33- Bug fix = first a failing test that reproduces it, then the fix.34- For prompts: keep golden input/output examples and assert the parser handles them.35## Before declaring done36- Run the code or tests — never claim it works without verification.37- Check: secrets out of code, types clean, errors handled, LLM outputs validated, logs in place.s