算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
production-fast-api-template【设计与创意】Structure and execution rules for a unified, production-ready backend combining domain-oriented FastAPI, Retrieval-Augmented Generation (RAG) and Agentic AI in one codebase on AWS ECS Fargate. Use when scaffolding a new backend; adding a module, API domain, RAG component, agent, tool, workflow, LLM/provider integration or test; deciding where a file …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
These rules define where code lives and how it executes in a unified FastAPI + RAG + Agentic AI backend. Apply them whenever you generate or modify code in a project built from this template.
Companion references. Read the relevant file before writing code:
| File | Read before writing… | Summarized in |
|---|---|---|
| ASYNC_EXECUTION.md | Any route, service, client, agent tool, worker or background job | Section 5 |
| PYDANTIC_STANDARDS.md | Any schema, settings class, serialization, LLM call with structured output, citation check, agent plan or tool argument model | Section 6 |
| API_CONVENTIONS.md | Any dependency, route signature, auth/permission check, OpenAPI metadata, DB model or query, migration, API test or lint setup | Section 7 |
| GUARDRAILS.md | Any user-facing LLM call, RAG answer, ingestion of untrusted documents, agent tool execution, gateway target or content-safety check | Section 8 |
| AI_EVALUATION.md | Any evaluation contract, metric, dataset, judge, runner, report or threshold; any new RAG stage, agent, tool or conversation feature that needs quality coverage | Section 9 |
| API_SECURITY.md | Any authentication, token, permission, tenant or ownership check; input/upload/outbound-URL handling; response headers, CORS, errors; rate limits; audit events; ECS/IAM/secrets; CI security; security tests | Section 10 |
| AGENT_SECURITY.md | Any agent, tool, tool registry entry, approval flow, memory write, MCP connector or LLM-output sink | Section 10 |
Status labels:
These rules don't choose agent frameworks, LLM providers, models, vector stores or a queue implementation. If a rule doesn't fit a real need, raise it with the user instead of silently working around it.
src/ package, one FastAPI entry point (src/main.py) and one shared infrastructure layer.src/, a separate app folder (rag_app/, agents_service/, worker_app/) or an extra FastAPI() instance.src/api/v1/router.py. Domain, RAG and agent routers never mount themselves on the app.src/'s root holds only application-wide concerns.src/llm/, content-safety checks in src/guardrails/ and external-service clients in src/providers/, each exactly once. RAG and agents consume them and never define their own copies (no rag/llm_client.py, no agents/vector_db.py).llm/embeddings.py, not openai_embeddings.py). Vendor specifics stay behind src/providers/ or src/llm/.common/ is for genuinely reusable, domain-agnostic code only. Never put business logic there.agents/, thinking in cognition/ and doing, scheduling and offloading in execution/.src/. Quality evaluation (datasets, DeepEval/Ragas suites, deterministic metrics, runners, reports) lives in the top-level evaluation/ directory, separate from tests/. src/evaluation/ holds only framework-neutral contracts, the metric registry and adapters, with lazy framework imports (AI_EVALUATION.md §1).src/ has an __init__.py. Non-Python directories that must survive in Git (prompt folders, data, empty test folders) get a .gitkeep.from src.auth import constants as auth_constants.execution/background_worker.py rather than creating execution/worker2.py or rag/worker.py.<project-name>/
├── src/
│ ├── __init__.py
│ ├── main.py # the single FastAPI entry point (lifespan creates shared clients/limiters)
│ ├── config.py # application-wide configuration
│ ├── constants.py
│ ├── exceptions.py # base domain exception + handlers rendering ErrorResponse
│ ├── middleware.py
│ ├── database.py # shared metadata (naming convention), engine/session factory, get_db_session
│ ├── models.py # genuinely shared models only
│ ├── pagination.py
│ │
│ ├── api/
│ │ └── v1/
│ │ └── router.py # aggregates domain, RAG and agent routers
│ │
│ ├── auth/ # reference domain module
│ │ ├── router.py
│ │ ├── schemas.py # the domain's own Pydantic API/internal schemas
│ │ ├── models.py # persistence (ORM) models, not Pydantic
│ │ ├── dependencies.py # current_principal, require_<permission>, scope dependencies
│ │ ├── config.py # the domain's own pydantic-settings class (AUTH_ prefix: issuers, audiences, JWKS)
│ │ ├── constants.py # permission and scope names
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ ├── tokens.py # JWT/JWKS validation, token types, claims → Principal
│ │ ├── permissions.py # RBAC/ABAC policy evaluation (default deny)
│ │ ├── security.py # password hashing, only if local accounts are approved 🟡
│ │ ├── audit.py # auth audit events (login, token rejected, session revoked)
│ │ └── utils.py
│ │
│ ├── example_domain/ # copy this layout for every new business domain
│ │ ├── router.py
│ │ ├── schemas.py
│ │ ├── models.py
│ │ ├── dependencies.py # valid_<entity>_id and other I/O-backed validation
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ └── utils.py
│ │
│ ├── rag/
│ │ ├── routes.py # "routes", not "router": avoids clashing with rag/router/
│ │ ├── schemas.py # RAG API schemas + cross-stage contracts (RetrievedEvidence)
│ │ ├── service.py # container/lifecycle of long-lived RAG services
│ │ ├── dependencies.py
│ │ ├── config.py # RAG settings (RAG_ prefix)
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── extraction.py # structure-preserving document extraction
│ │ ├── ingestion.py # coordinates processing and indexing (run by background jobs)
│ │ ├── semantic/
│ │ │ ├── metadata.py
│ │ │ ├── segmentation.py
│ │ │ └── chunks.py
│ │ ├── router/
│ │ │ ├── classifier.py
│ │ │ ├── fusion.py
│ │ │ └── evaluation.py
│ │ ├── preprocessing/
│ │ │ ├── query_expansion.py
│ │ │ └── hyde.py
│ │ ├── retrieval/
│ │ │ ├── semantic_search.py
│ │ │ ├── bm25.py
│ │ │ ├── rrf.py
│ │ │ ├── mmr.py
│ │ │ ├── evidence_filter.py
│ │ │ └── hybrid_retriever.py
│ │ ├── generation/
│ │ │ ├── context_builder.py
│ │ │ ├── citations.py
│ │ │ ├── streaming.py
│ │ │ ├── schemas.py # LLM-output contracts: GeneratedAnswer, Citation
│ │ │ ├── validation.py # application-level answer/citation validation
│ │ │ └── service.py
│ │ ├── chat/
│ │ │ ├── routes.py
│ │ │ ├── schemas.py
│ │ │ ├── service.py
│ │ │ └── storage.py
│ │ └── security/
│ │ ├── access_control.py # AccessScope model + vector-store/DB filter translation
│ │ ├── document_validation.py # ingestion file checks (type, signature, size, archives)
│ │ └── retrieval_policy.py # post-retrieval ACL re-check, trust labels, provenance
│ │
│ ├── agents/
│ │ ├── router.py
│ │ ├── schemas.py # AgentTask, ToolResult, AgentExecutionState
│ │ ├── structured_output.py # LLM-produced: ExecutionPlan, ToolCall union, FinalAgentResponse
│ │ ├── dependencies.py
│ │ ├── config.py # agent settings (AGENTS_ prefix)
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ ├── base_agent.py
│ │ ├── autonomous_agent.py
│ │ ├── planner_agent.py
│ │ ├── agent_interface.py
│ │ ├── team_orchestrator.py
│ │ ├── step_handler.py
│ │ ├── task_manager.py
│ │ ├── tools/
│ │ │ ├── calculator.py
│ │ │ ├── file_manager.py
│ │ │ ├── search_tool.py
│ │ │ └── tool_registry.py
│ │ ├── workflows/
│ │ │ ├── code_review_chain.py
│ │ │ ├── research_chain.py
│ │ │ ├── multi_agent_workflow.yaml
│ │ │ └── workflow_executor.py
│ │ └── security/ # policies; enforced by execution/executor.py
│ │ ├── tool_permissions.py # default-deny tool authorization (user perms ∩ agent profile)
│ │ ├── execution_policy.py # step, tool-call, token, cost and time budgets
│ │ └── approval_policy.py # human approval bound to argument hash
│ │
│ ├── cognition/
│ │ ├── cognitive_loop.py
│ │ ├── decision_policy.py
│ │ ├── planner.py
│ │ ├── reasoner.py
│ │ ├── state_interpreter.py
│ │ └── memory/
│ │ ├── long_term_memory.py
│ │ ├── short_term_memory.py
│ │ └── memory_manager.py
│ │
│ ├── execution/
│ │ ├── action_resolver.py
│ │ ├── controller.py
│ │ ├── error_handler.py
│ │ ├── executor.py
│ │ ├── threadpool.py # thread-offload helpers and limiters (no custom executor yet)
│ │ ├── job_scheduler.py # enqueue/schedule background jobs
│ │ └── background_worker.py # queue consumer / job dispatch loop
│ │
│ ├── llm/ # shared by RAG and agents
│ │ ├── client.py
│ │ ├── embeddings.py
│ │ ├── reranker.py
│ │ ├── model_loader.py
│ │ ├── cache.py
│ │ ├── dependencies.py # accessors for lifespan-created model clients
│ │ ├── config.py # LLM settings (LLM_ prefix, SecretStr keys)
│ │ ├── schemas.py # structured-output modes, capabilities, failure kinds
│ │ ├── structured_output.py # generate → validate → bounded retry (provider-independent)
│ │ └── prompts/
│ │ ├── system/
│ │ ├── tasks/
│ │ └── templates/
│ │
│ ├── guardrails/ # shared content-safety layer, used by RAG, agents and workers
│ │ ├── schemas.py # GuardrailPhase, GuardrailOutcome, GuardrailFinding, GuardrailVerdict
│ │ ├── service.py # run a phase's checks, decide outcome, enforce / log-only, fail closed
│ │ ├── policies.py # which checks apply to which surface and phase
│ │ ├── config.py # GUARDRAILS_ settings (mode, fail mode, thresholds, policy version)
│ │ ├── dependencies.py
│ │ ├── exceptions.py # GuardrailBlocked, GuardrailUnavailable
│ │ └── validators/ # deterministic in-house checks (regex, lists, length)
│ │
│ ├── providers/ # external-service boundaries (MCP, vector DB, queues, Lambda, guardrails, …)
│ │ ├── guardrails/client.py # managed guardrail API / validator-library adapters
│ │ ├── mcp/client.py
│ │ ├── vector_store/client.py
│ │ └── external/client.py
│ │
│ ├── evaluation/ # runtime-safe evaluation contracts only (no deepeval/ragas at import)
│ │ ├── schemas.py # EvaluationSample, RetrievedContext, AgentTrajectory, EvaluationResult
│ │ ├── registry.py # metric key → framework, class, version, required fields, result kind
│ │ └── adapters/
│ │ ├── deepeval_adapter.py # contracts ↔ LLMTestCase / ConversationalTestCase (lazy import)
│ │ └── ragas_adapter.py # contracts ↔ Ragas collections inputs / samples (lazy import)
│ │
│ └── common/
│ ├── schemas/
│ │ ├── base.py # CustomModel, UtcDatetime, shared annotated types
│ │ ├── responses.py # ErrorResponse and shared response shapes
│ │ └── pagination.py # Page[T], PageParams (models only; logic in src/pagination.py)
│ ├── dependencies.py
│ ├── logging.py
│ ├── retry.py
│ ├── timers.py
│ ├── serialization.py
│ ├── validation.py
│ └── security/
│ ├── headers.py # security-headers ASGI middleware (registered in src/middleware.py)
│ ├── rate_limiting.py # distributed limiter interface + key builders (backend 🟡)
│ ├── request_validation.py # body size/depth, content type, filenames, outbound URL (SSRF) policy
│ └── audit_schemas.py # AuditEvent, AuditActor, AuditTarget
│
├── tests/
│ ├── conftest.py # async client (httpx + ASGITransport), dependency-override fixtures
│ ├── auth/
│ ├── rag/{ingestion,retrieval,generation}/
│ ├── agents/
│ ├── cognition/
│ ├── execution/
│ ├── guardrails/ # fake checkers; deny / suppress / fail-closed / log-only paths
│ ├── evaluation/ # test_schemas.py, test_adapters.py, test_deterministic.py (no LLM calls)
│ ├── security/
│ │ ├── authentication/ # JWT/JWKS validation, throttling
│ │ ├── authorization/ # BOLA, BFLA, BOPLA, cross-tenant, service-to-service
│ │ ├── api/ # headers, CORS, limits, uploads, SSRF, injection, CSRF, rate limits
│ │ ├── rag/ # unauthorized/cross-tenant retrieval, ACL revocation, citation integrity
│ │ └── agents/ # tool authorization, argument BOLA, approval bypass, budgets, injection
│ ├── integration/
│ ├── e2e/
│ └── fixtures/
├── evaluation/ # offline quality evaluation (never imported by src/)
│ ├── README.md
│ ├── config/{metrics.yaml,thresholds.yaml}
│ ├── datasets/{rag,agents,conversations,golden,guardrails}/
│ ├── deepeval/{rag,agents,conversations,custom}/
│ ├── ragas/{rag,agents,custom}/
│ ├── deterministic/{citations,retrieval,structured_output,tool_calls}.py
│ ├── tracing/{collectors,normalization}.py
│ ├── runners/{offline,regression,report}.py
│ ├── fixtures/
│ └── reports/ # generated, git-ignored 🟡
├── data/{documents,knowledge_bases,agent_state,fixtures}/
├── scripts/
├── docs/
│ ├── architecture/
│ ├── api/
│ ├── development/
│ ├── decisions/
│ └── standards/
│ ├── ASYNC_EXECUTION.md # copied from this skill
│ ├── PYDANTIC_STANDARDS.md # copied from this skill
│ ├── API_CONVENTIONS.md # copied from this skill
│ ├── GUARDRAILS.md # copied from this skill
│ ├── AI_EVALUATION.md # copied from this skill
│ ├── API_SECURITY.md # copied from this skill
│ └── AGENT_SECURITY.md # copied from this skill
├── .env.example
├── .gitignore
├── pyproject.toml
├── README.md
└── CLAUDE.md
__init__.py files are omitted above for brevity. Rule 11 still applies.
| You are adding… | Put it in | Not in |
|---|---|---|
| A new business capability (orders, billing…) | src/<domain>/ using the example_domain/ layout | src/ root, common/ |
| An HTTP endpoint | The owning module's router.py (routes.py inside rag/), mounted via api/v1/router.py | main.py |
| Request/response models | <module>/schemas.py, inheriting CustomModel | src/models.py, direct BaseModel |
Shared base model, UtcDatetime, shared annotated types | common/schemas/base.py | Per-domain base classes |
| Error, message and page response shapes | common/schemas/{responses,pagination}.py | Each domain redefining them |
| Settings for a domain or component | <module>/config.py (own prefix, cached getter) | The global src/config.py |
| LLM-output schema for answer generation | rag/generation/schemas.py | rag/schemas.py (API) |
| LLM-output schema for one RAG stage (metadata, classification, expansion) | rag/<stage>/schemas.py, created when the stage is implemented | API schemas |
| Citation and answer checks against evidence | rag/generation/validation.py | Pydantic validators |
| Agent plan, tool-call union, final agent response | agents/structured_output.py | agents/schemas.py |
| A tool's argument model | That tool's module in agents/tools/ | A central args file |
| Structured-output generation, validation and retry flow | llm/structured_output.py + llm/schemas.py | Re-implemented in RAG or agents |
Evaluation contracts (EvaluationSample, EvaluationResult, …), metric registry | src/evaluation/{schemas,registry}.py | evaluation/, per-suite copies |
| Contract ↔ DeepEval / Ragas mapping | src/evaluation/adapters/ (lazy framework imports) | Suites building test cases ad hoc |
| Metric suites, G-Eval/DAG/custom metrics, judge wrappers | evaluation/{deepeval,ragas}/ | src/, tests/ |
| Deterministic metrics (retrieval P/R/MRR/NDCG, citations, schema, tool calls) | evaluation/deterministic/ | LLM judges |
| Evaluation datasets, metric/threshold config, runners, reports | evaluation/{datasets,config,runners,reports}/ | data/, tests/fixtures/ |
| Tests of the evaluation code | tests/evaluation/ | evaluation/ |
| DB models used by one domain | <domain>/models.py | src/models.py |
| Existence, ownership or uniqueness checks that need I/O | <domain>/dependencies.py (valid_<entity>_id) | Pydantic validators, repeated in each route |
current_principal, require_<permission>, scope dependencies | auth/dependencies.py | Each domain parsing tokens |
JWT/JWKS validation, claims → Principal | auth/tokens.py | auth/service.py, routes |
| RBAC/ABAC policy evaluation | auth/permissions.py | Routes, prompts |
| Password hashing (only if local accounts are approved 🟡) | auth/security.py | common/ |
Auth audit events; shared AuditEvent schema | auth/audit.py; common/security/audit_schemas.py | Free-form log lines |
| Security headers, rate limiting, request/upload/outbound-URL validation | common/security/{headers,rate_limiting,request_validation}.py | Per-domain copies |
| RAG access scope, document validation, post-retrieval policy | rag/security/ (scope resolved in rag/dependencies.py), applied as a store filter in retrieval | Prompts, the LLM, evidence_filter.py |
| Agent tool permissions, execution budgets, approval policy | agents/security/ (context built in agents/dependencies.py), enforced by execution/executor.py | Prompts, the planner, tools themselves |
| Security tests | tests/security/{authentication,authorization,api,rag,agents}/ | evaluation/ |
| Domain exceptions | <domain>/exceptions.py, subclassing src/exceptions.py | HTTPException in services |
| SQL queries | <domain>/service.py | Routers, dependencies |
| DB naming convention, engine, session dependency | src/database.py | Per-domain engines |
| Migrations 🟡 | alembic/ + alembic.ini at the root, only after approval | src/, the app lifespan |
| Lint/format script 🟡 | scripts/lint.sh | Ad-hoc commands in docs |
| Content-safety checks (input, context, tool, output) | Call src/guardrails/service.py at the checkpoints in GUARDRAILS.md §3 | Checks embedded in rag/ or agents/, system prompts |
| Deterministic safety validators (regex, lists) | guardrails/validators/ | common/validation.py |
| Managed guardrail or validator-library client | providers/guardrails/client.py | guardrails/, llm/ |
| Gateway Cedar guardrail policies 🟡 | Infrastructure-as-code, outside src/ (location needs approval) | src/ |
| Guardrail red-team / benign datasets | evaluation/datasets/guardrails/ | tests/ |
| Long-lived clients, pools and limiters | Created in main.py's lifespan and exposed through dependencies.py | Created per request |
| Document parsing and indexing logic | rag/extraction.py, rag/ingestion.py, rag/semantic/ | providers/, workers |
| Query routing / classification | rag/router/ | agents/ |
| Query rewriting, HyDE | rag/preprocessing/ | rag/retrieval/ |
| A retrieval or ranking stage | rag/retrieval/ | rag/generation/ |
| Answer context, citations, streaming | rag/generation/ | rag/chat/ |
| Conversation API / history | rag/chat/ | rag/ root |
| A new agent type | agents/<name>_agent.py implementing agent_interface.py | cognition/ |
| A tool an agent can call | agents/tools/ + register in tool_registry.py | common/ |
| A predefined multi-step workflow | agents/workflows/ | execution/ |
| Planning, reasoning, decision logic | cognition/ | agents/ |
| Agent memory | cognition/memory/ | rag/chat/storage.py |
| Running actions, control flow, execution errors | execution/{executor,controller,action_resolver,error_handler}.py | agents/ |
| Thread-offload helpers, per-dependency limiters | execution/threadpool.py | Ad-hoc in routes |
| Enqueueing or scheduling a background job | execution/job_scheduler.py | Routes calling the queue SDK directly |
| Queue consumer / job dispatch | execution/background_worker.py | A new app or rag/worker.py |
| Lambda handler (thin adapter) 🟡 | Same codebase. The location needs approval. It calls the existing service functions. | A separate repo or app with copied logic |
| Chat/completion, embedding or rerank model access | llm/ | rag/, agents/ |
| Prompt text | llm/prompts/{system,tasks,templates}/ | Scattered prompts.py files |
| MCP, vector DB, SQS, Lambda or third-party API client | providers/<kind>/client.py | rag/, agents/, llm/ |
| Logging, retry, timing, serialization | common/ | Domain packages |
| Unit tests | tests/<mirrored module>/ | Next to source |
| RAG/agent quality metrics, benchmarks | evaluation/ (layout in section 2) | tests/ |
| Sample docs, KB files, agent state | data/ | src/ |
| One-off CLIs (bulk upload, model training) | scripts/ | src/ |
| Engineering standards | docs/standards/ | README.md |
If none of these rows fit, ask the user before inventing a new top-level package.
Use these names. The alternatives were rejected for the reasons given.
| Use | Instead of | Why |
|---|---|---|
rag/routes.py, rag/chat/routes.py | rag/router.py | rag/router.py would collide with the rag/router/ package. Keep one name inside rag/. |
rag/router/classifier.py | rag/router/router.py | Names the responsibility and avoids router/router.py |
rag/router/fusion.py (only one) | Fusion routing in both preprocessing/ and router/ | Routing fusion lives in exactly one place |
rag/retrieval/rrf.py | retrieval/fusion.py | Distinguishes rank fusion from routing fusion |
rag/retrieval/evidence_filter.py | retrieval/filtering.py | Explicit responsibility |
rag/retrieval/hybrid_retriever.py | retrieval/hybrid.py | Matches its role |
rag/retrieval/semantic_search.py | Dense search hidden inside a vector-DB client | Dense search is its own stage, and the client lives in providers/vector_store/ |
rag/semantic/chunks.py | rag/semantic/chunking.py | Contextualized chunk building, kept distinct from any passage-formatting helper |
rag/generation/context_builder.py | generation/context.py | Avoids confusion with conversational context in chat/ |
rag/generation/citations.py | Citation checks inside the context builder | Separate responsibilities |
rag/generation/service.py + llm/client.py | rag/generation/llm.py | Orchestration stays in RAG, and model access is shared |
llm/{client,embeddings,reranker}.py | rag/<vendor>/… | A vendor-neutral model layer, shared with agents |
llm/model_loader.py | Client factories in rag/embeddings.py | Model construction is shared |
providers/vector_store/client.py | src/<vendor-db>/client.py | A vendor-neutral provider boundary |
evaluation/deepeval/rag/, evaluation/ragas/rag/, evaluation/reports/ | evals/ or artifacts inside src/ | Evaluation suites and reports stay outside the app package; only contracts live in src/evaluation/ |
scripts/ for training and bulk jobs | Training scripts inside src/rag/router/ | CLIs stay outside src/ |
Trained model artifacts, datasets and domain-specific label lists are data, not structure.
Keep them under data/ or external storage 🟡, never hard-coded in src/.
Async vs sync
async def only when the whole call chain (route → service → client) awaits genuinely asynchronous libraries.def routes for work dominated by sync blocking I/O. FastAPI runs them in AnyIO's thread pool, which has 40 tokens per process by default and is shared with sync dependencies and offloaded calls.async def: sync HTTP/DB/vector clients, time.sleep, file parsing or heavy CPU.async. Never call asyncio.run() inside a request.anyio.to_thread.run_sync(..., limiter=...) or starlette.concurrency.run_in_threadpool. Use AnyIO consistently rather than asyncio.to_thread or run_in_executor(None), which use a different pool.Safeguards
CapacityLimiter, created in the lifespan.asyncio.timeout or fail_after doesn't stop a thread.503 + Retry-After, not unbounded queueing.execution/threadpool.py is reserved for that.CPU-intensive work
OMP_NUM_THREADS and similar to the vCPU count).Background processing (Path B)
BackgroundTasks. The API returns 202 with a job ID.maxReceiveCount and an alarm on DLQ depth.stopTimeout ≤ 120 s), checkpoint long jobs and use ECS task scale-in protection while working.Scaling (ECS Fargate)
Open decisions 🟡. Don't choose any of these without the user:
Models
CustomModel from src/common/schemas/base.py.CustomModel accepts field names and aliases, sets extra="forbid" and provides to_json_dict() = model_dump(mode="json"). Provider payloads override with extra="ignore", and settings use extra="ignore".Field(ge/le/min_length/max_length/pattern), Literal, StrEnum, EmailStr, HttpUrl) over custom validators. Use model_validator(mode="after") for cross-field rules. Validators do no I/O and raise ValueError with safe messages, never assert.UtcDatetime (aware, normalized to UTC, serialized as ISO 8601 Z). Naive datetimes are rejected, never assumed to be UTC. Use datetime.now(timezone.utc), never utcnow().Literal tag + Field(discriminator=...)) for multi-shape results. Build a TypeAdapter once per module for non-model types.<Entity>Create / Update / Response / internal models. ORM models aren't Pydantic. Apply PATCH with exclude_unset=True. Don't create a base class per domain.response_model re-validates returned data, model_copy(update=) and model_construct() skip validation, and model→dict→model hops are waste.model_dump(mode="json") / model_dump_json(). Use jsonable_encoder only for untyped mixed structures.Settings
BaseSettings class per domain or component, each with a unique env prefix, SecretStr secrets, required values without defaults, nested models via env_nested_delimiter="__" and an @lru_cache getter. There is no giant global class.dependency_overrides, _env_file=None and cache_clear().Structured LLM outputs
model_validate_json against the full model.description. Adapt the exported schema to the provider's capabilities. Don't assume every provider supports the same JSON Schema features, and keep enforcing stripped constraints in Pydantic.StructuredOutputError.errors(include_input=False, include_url=False, include_context=False)), never secrets, prompts, raw inputs or internal IDs.E1…) that the application maps to real chunks. Every citation must exist in this request's evidence. Self-reported confidence is a coarse enum and not a calibrated probability. Pydantic can't prove the evidence supports the claim, so faithfulness is measured in evaluation/.validate_assignment=True). Pydantic validation isn't authorization. Permissions, allowlists, sandboxing and confirmations are enforced in execution/ and in the tools.type and loc.Dependencies
valid_<entity>_id), never in Pydantic validators and never repeated in each route.Principal), not dicts. Publish reusable ones as Annotated aliases (ValidPost). Raise domain exceptions, not HTTPException.use_cache=False only with a comment explaining why.async def dependencies, but only when nothing inside them blocks. def dependencies take thread-pool tokens.yield. Never hand a request-scoped session to BackgroundTasks or a job. Pass IDs instead.execution/. Secrets come from settings (SecretStr). RAG retrieval receives an explicit access scope, applied as a store filter before ranking. Agents receive their permitted tools in the execution context. Jobs carry the principal's ID and re-check permissions in the worker.API surface
/api/v1 (set in api/v1/router.py). Use the same path-parameter name for the same entity everywhere so dependencies chain. Long operations return 202 plus a job resource.response_model. Returning a raw Response skips both and needs a profiled reason.BackgroundTasks is only for tiny best-effort side effects. If you'd page someone when a task is lost, it goes to SQS (Path B). No Celery, Arq or RQ without approval.PydanticCustomError for stable error types, and never raise HTTPException in validators. LLM-output models are never request bodies.openapi_url=None) and enabled for listed environments through settings. Every route sets response_model, status_code, summary, description, and responses using ErrorResponse.Database (engine and ORM 🟡)
MetaData with an explicit constraint naming convention. Tables are lower_snake, singular and module-prefixed (rag_chunk, agent_run). _at columns are timestamptz and map to UtcDatetime; _date columns are dates.date_slug names, and follow expand-then-contract. Review autogenerated output, and never run migrations from the app lifespan on ECS.Tests and tooling
httpx.AsyncClient + ASGITransport) from day 0. It doesn't run the lifespan, so override client accessors or use a lifespan manager 🟡. Pick one async pytest plugin 🟡.app.dependency_overrides on the exact callable, and pop only what you added. No live LLM, vector store, queue or AWS calls.ruff check --fix, ruff format). Its configuration, pre-commit hooks and a type checker are 🟡.src/guardrails/ service, and vendor clients live in providers/guardrails/. Every check returns a typed GuardrailVerdict: passed, denied, suppressed, redacted 🟡 or not_evaluated.not_evaluated refuses the request (503 + Retry-After on the API). Fail-open needs approval per surface.forbid … when guardrails for input, suppressOutput for output. Data paths (context.input.*, context.output.*) must exist in the target schema, so generate schemas from Pydantic models. Interpret results by body: an MCP denial is HTTP 200 with JSON-RPC -32002. Roll out in log-only mode, check Region availability, and alarm on "could not be evaluated".Guard.for_pydantic or guard-driven LLM calls, no embedded Flask server, and no content-rewriting on-fail actions without approval.policy_version are settings or policy, not literals. Pin a published managed-guardrail version. New policies start in log-only mode.not_evaluated spikes and block-rate shifts. Measure false positives and negatives with paired harmful and benign datasets in evaluation/datasets/guardrails/, including indirect injection.src/evaluation/ = contracts, registry and adapters only, with lazy DeepEval/Ragas imports in an optional dependency group 🟡. Importing src.main never imports deepeval or ragas. Everything else lives in evaluation/.EvaluationResult whose value is numeric, binary, categorical or unavailable. Missing fields, judge errors and timeouts are unavailable with a reason, never 0.ragas.metrics.collections API with llm_factory and await metric.ascore(...). The legacy ragas.metrics.* classes, evaluate() and RunConfig are deprecated. DeepEval: use native metrics with an explicit judge model=; DeepEval's Ragas wrapper is not a second implementation.evaluation/config/thresholds.yaml, null until baselined and approved. Never average unrelated metrics; keep raw per-sample results.system_one mode) without authorization.Open decisions 🟡: framework choice per project, pinned versions, judge provider/model/hosting, thresholds and gating, claim segmentation, dataset storage and access, tracing hook design, budgets, calibration sample sizes (full list in AI_EVALUATION.md §18).
Authentication and tokens
auth/tokens.py: algorithms pinned per issuer, signature, iss, aud, exp/nbf (small leeway 🟡), typ, scopes. Never trust alg, jku, x5u or jwk from the header. JWKS is cached, refreshed off the event loop and fails closed.localStorage. No secrets or sensitive data in JWT payloads.Access control
Input, output and processing
defusedxml, yaml.safe_load, no pickle.response_models, ErrorResponse without internals, correct status codes (403 insufficient_scope), security headers from common/security/headers.py, HSTS at the TLS edge, no Server header, explicit CORS origins.RAG and agents
AccessScope as a store filter before ranking, re-checks ACLs after retrieval, keeps provenance on every chunk and propagates deletions and ACL changes. Retrieved content is untrusted.Infrastructure, monitoring and delivery
SecretStr. Private subnets, non-root, read-only root filesystem, controlled ECS Exec, encrypted SQS with DLQs and no sensitive payloads.AuditEvents with request and correlation IDs. Never log tokens, passwords, keys, raw confidential documents or unnecessary PII.tests/security/ covers auth bypass, JWT, BOLA, BFLA, cross-tenant, rate limits, uploads, SSRF, injection, disclosure, prompt injection, agent escalation and unauthorized RAG with synthetic fixtures. No testing of external systems without authorization.Open decisions 🟡: IdP and token format, algorithms and lifetimes, BFF details, local accounts, policy engine, 404 vs 403, on-behalf-of mechanism, limits, malware scanning, egress allowlists, rate-limit backend, vector-store tenancy, approval tool list, sandboxing, audit sink, CI tools and SLAs (full lists in API_SECURITY.md §17 and AGENT_SECURITY.md §11).
When the user asks you to create a project from this template:
Ask for the target project name if they haven't given one.
Check whether the target directory already exists. Ask before modifying any existing file.
Create the structure in section 2 exactly, as one project with one src/.
Python placeholders get only a one-line module docstring stating their responsibility.
Add __init__.py to every Python directory under src/, and .gitkeep to empty directories.
Copy this skill's ASYNC_EXECUTION.md, PYDANTIC_STANDARDS.md, API_CONVENTIONS.md, GUARDRAILS.md, AI_EVALUATION.md, API_SECURITY.md and AGENT_SECURITY.md into docs/standards/. Write evaluation/README.md from AI_EVALUATION.md Appendix A. Add evaluation/reports/ to .gitignore 🟡.
.env.example: comments only. No real credentials and no speculative provider variables.
pyproject.toml: project metadata only (name, version, description, readme). No dependencies.
README.md: the project's purpose plus the generated tree.
CLAUDE.md (and the equivalent rules file for other IDEs, e.g. AGENTS.md or .cursor/rules) must contain at least:
# Project rules
- Before adding or moving modules, follow the structure rules of the `production-fast-api-template` skill (or `docs/architecture/` if present).
- Before writing any route, service, client, agent tool, worker or background job, read `docs/standards/ASYNC_EXECUTION.md` and follow its 🟢 conventions.
- Before writing any Pydantic schema, settings class, serialization, structured LLM output, citation check, agent plan or tool-argument model, read `docs/standards/PYDANTIC_STANDARDS.md` and follow its 🟢 conventions.
- Before writing any dependency, route signature, auth or permission check, OpenAPI metadata, DB model or query, migration, API test or lint setup, read `docs/standards/API_CONVENTIONS.md` and follow its 🟢 conventions.
- Before adding or changing any user-facing LLM call, RAG answer, document ingestion, agent tool execution or gateway target, read `docs/standards/GUARDRAILS.md` and place its checkpoints.
- Every new or changed RAG stage, structured output, citation format, agent, tool, workflow or conversation feature must state how it is evaluated, following `docs/standards/AI_EVALUATION.md` (deterministic checks first, then the starter judged metrics). Never call live judges from `tests/`.
- Before writing any authentication, token, permission, tenant/ownership check, input or upload handling, outbound request, response header, rate limit, audit event, secret, IAM or CI security change, read `docs/standards/API_SECURITY.md` and follow its mandatory controls. Add the matching `tests/security/` tests.
- Before adding or changing any agent, tool, approval flow, memory write or MCP connector, read `docs/standards/AGENT_SECURITY.md`: the LLM proposes, deterministic code authorizes, validates, approves, executes and audits.
- Items marked 🟡 in these documents are unapproved decisions. Ask before choosing one.
Verify that every expected path exists, that there is exactly one FastAPI() entry point, and that no logic, integrations or secrets were added.
Report the generated tree and any deviations.
Do not implement classes, functions, endpoints, algorithms, DB connections, agent workflows,
queues, workers, Lambda functions or external API calls unless the user asks. Don't generate
GitHub Actions, Docker, Kubernetes, Terraform/CDK, migrations, alembic.ini, ruff configuration or
deployment scripts without being asked. Don't install dependencies (Celery, Redis, RabbitMQ, AWS SDKs, DeepEval, Ragas, JWT or crypto libraries, etc.) without approval. Don't run evaluations against live or paid judges, create signing keys or credentials, deploy security infrastructure or test external systems without being asked and authorized.
ASYNC_EXECUTION.md §0 and §5.extra policy from PYDANTIC_STANDARDS.md §2. Treat LLM and tool output as untrusted, and apply all three validation levels.valid_* / require_* dependencies (API_CONVENTIONS.md §1–§2) instead of re-querying in the handler, and keep path-parameter names consistent with existing routes.GUARDRAILS.md §3) and its enforcement point. A new surface without input and output checkpoints is incomplete.API_SECURITY.md §0, §4, §8). A route without them is incomplete.AGENT_SECURITY.md §4.2): side-effect class, permissions, argument model, approval, limits and audit event.AI_EVALUATION.md §4–§6) and whether its baseline or thresholds need re-approval.