Sphera AI Platform
PoC β Production Blueprint
Full system blueprint to move the Sphera AI PoC to production on Microsoft Azure β component mapping, target architecture, code-issue remediation playbook, decision matrices, security & governance, and a 12-week migration roadmap.
- Company: Sphera β enterprise ESG, sustainability & operational-risk software (data-heavy, compliance-sensitive).
- Platform: Microsoft Azure is the approved cloud. AI PoC is built but cannot move to production.
- Current components: Claude (Anthropic), nginx, AI-DLC (AI-Driven Development Life Cycle), Hermes agents, Git, RAG, Pendo, Juno*, Jina (embeddings/rerankers), Databricks, MS Teams.
- Symptom: "Currently having code issue" β PoC code does not meet production bar (see Section 04 triage).
- *Juno assumed to be an internal AI assistant / orchestration tool β confirm vendor & role in the Assumptions Register (Β§02).
β Operable
Deploys repeatably (IaC), runs on containers/serverless, zero hardcoded secrets, health checks, autoscaling.
β Observable
Every prompt, token, cost, latency, and eval trace is logged; alerts fire on drift & error rate.
β Governed
Entra ID + RBAC, private networking, data residency, AIDLC stage gates, Responsible-AI guardrails.
β Tested
Unit + integration + golden-set evals in CI; prompt & model versions pinned; rollback in minutes.
β Cost-Controlled
Budget alerts, token caps, caching, model tiering (haiku/sonnet/opus by task).
β User-Loved
Pendo in-app guidance + feedback loops; Teams human-in-the-loop approvals for high-stakes actions.
| Component | Role in PoC | Production Target on Azure | Action |
|---|---|---|---|
| Claude / Anthropic | LLM inference via direct API | Claude via Azure AI Foundry (enterprise control) β direct API as fallback | Migrate |
| nginx | Reverse proxy | Azure Front Door + WAF at edge; Azure API Management (APIM) as gateway; nginx kept only for internal sidecars | Replace/split |
| AI-DLC | AI Development Life Cycle process | Formalize as stage-gate pipeline in Azure DevOps (defineβdataβbuildβverifyβdeployβmonitor) | Formalize |
| Hermes agents | Agent orchestration & automation | Containerized agent runtime on Azure Container Apps with Managed Identity + MCP tool registry | Containerize |
| Git | Source control | Azure DevOps / GitHub Enterprise β branch policy, PR gates, semantic releases | Harden |
| RAG | Retrieval-augmented generation | Azure AI Search (hybrid + semantic) with Jina embeddings/reranker; chunking & eval pipeline | Rebuild |
| Pendo | Product analytics & in-app guidance | Keep β feed AI feature telemetry, feedback, and NPS into eval & improvement loop | Keep |
| Juno* | Assumed internal assistant/orchestrator | Confirm vendor; fold into agent layer as an internal copilot surface | Verify |
| Jina (jini) | Embeddings / rerankers | Keep for retrieval quality (or swap to Azure OpenAI text-embedding-3) β decision matrix Β§06 | Evaluate |
| Databricks | Lakehouse, pipelines, ML | Keep as data source of truth; Unity Catalog for lineage/ACL; sync to Azure AI Search | Integrate |
| MS Teams (msteai) | Collaboration | Teams app with Adaptive Cards for human-in-the-loop approvals + bot notifications | Extend |
| Azure | Cloud platform | Everything above lands on Azure: Entra ID, Key Vault, ACA/AKS, Monitor, AI Foundry | Core |
| Layer | Key Azure services | Why | Replaces / upgrades |
|---|---|---|---|
| L1 Edge | Front Door + WAF, CDN | Global TLS, DDoS, bot protection; single public entry | Bare nginx exposure |
| L2 Gateway | APIM, Entra ID | OAuth2/JWT, rate limits, quotas, policy-based routing, per-key usage | nginx as API gateway |
| L3 Orchestration | Azure Container Apps, Dapr, Service Bus | Agent runtime w/ managed identity, event-driven scale-to-zero, durable workflows | Ad-hoc scripts & notebooks |
| L4 AI Services | Azure AI Foundry (Claude), AI Search, Jina/embeddings, Azure OpenAI | Enterprise LLM control plane: private networking, entitlements, content filters, evals | Direct Anthropic API calls |
| L5 Data | Databricks + Unity Catalog, Data Lake, Key Vault, Cosmos/Postgres | Single source of truth; lineage; secrets never in code | Scattered CSVs / hardcoded keys |
| L6 Observability | App Insights, Log Analytics, Grafana (optional), Pendo, cost alerts | Prompt/token/cost/latency traces, eval dashboards, drift alarms | print() debugging |
| CI/CD | Azure DevOps / GitHub Actions, Bicep | Everything as code: infra, prompts, models, data pipelines | Manual deploys |
- Symptom: API keys in
.py/.envcommitted to Git; keys rotate and break the PoC. - Root cause: Fast PoC iteration, no secret hygiene.
- Fix: Azure Key Vault + Managed Identity (
DefaultAzureCredential) β zero keys in code.
# β PoC: key in code / .env
ANTHROPIC_API_KEY = "sk-ant-..."
# β
Prod: identity-based, no secret in code
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
cred = DefaultAzureCredential() # Managed Identity in prod, dev login locally
client = SecretClient(vault_url="https://sphera-ai.vault.azure.net/", credential=cred)
ANTHROPIC_API_KEY = client.get_secret("anthropic-api-key").value
git filter-repo to purge history + deny-list file patterns in branch policy.- Symptom: Random 429/5xx from Anthropic under load; jobs die mid-run; users see errors.
- Root cause: Raw
client.messages.create()with no retry, no backoff, no fallback model. - Fix: Retry with exponential backoff + jitter, circuit breaker, and a model/route fallback chain.
from tenacity import retry, stop_after_attempt, wait_exponential_jitter, retry_if_exception_type
import httpx
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential_jitter(initial=1, max=60),
retry=retry_if_exception_type((httpx.HTTPStatusError, ConnectionError)),
reraise=True,
)
def call_llm(model: str, messages: list[dict]) -> str:
# ... invoke via AI Foundry gateway, fallback to Anthropic direct on timeout
return response
# Circuit breaker: if error_rate > 10% in 60s β route to fallback model for 5 min
- Symptom: One giant notebook or
main.py; nobody can test it; imports are a tangle. - Root cause: PoC written as a linear experiment, never refactored.
- Fix: Package by layer:
ingest/ Β· retrieval/ Β· llm/ Β· agents/ Β· api/ Β· eval/, typed interfaces, no business logic in notebooks.
sphera-ai/
βββ pyproject.toml # pinned deps (uv/poetry)
βββ src/sphera_ai/
β βββ config.py # pydantic-settings (env-driven, validated)
β βββ retrieval/ # chunking, embeddings, search
β βββ llm/ # gateway client, prompt templates, fallback
β βββ agents/ # Hermes/tool orchestration, MCP registry
β βββ api/ # FastAPI routes (thin)
β βββ eval/ # golden sets, scorers
βββ tests/ # unit + integration + eval (run in CI)
- Symptom: LLM returns malformed JSON β
KeyErrorcrashes; garbage in β hallucinated garbage out. - Root cause: Free-form
json.loads()on model output. - Fix: Structured output (tool-use / Pydantic) + retry-on-parse-failure.
from pydantic import BaseModel, Field
from typing import Literal
class TradeSignal(BaseModel):
action: Literal["buy", "sell", "hold"]
confidence: float = Field(ge=0.0, le=1.0)
reasoning: str
# model called with response_format / tool schema β parse with .model_validate()
# on ValidationError β one retry with "fix your output" prompt, else degrade to hold
- Symptom: RAG documents or user fields containing "ignore previous instructions"; model exfiltrates data.
- Root cause: No isolation between instructions, data, and user input; no output filtering.
- Fix: Delimiters + instruction hierarchy, input sanitization, output guardrails (PII/secret regex), least-privilege tool access, human approval for high-impact tool calls.
- Symptom: "It worked yesterday" β no logs, no traces, no token/cost accounting.
- Root cause:
print()debugging in a serverless world. - Fix: Structured logging + OpenTelemetry spans per request: prompt hash, model, tokens, latency, cost, retrieval ids.
import logging, time, opentelemetry.trace as trace
tracer = trace.get_tracer("sphera-ai")
logger = logging.getLogger("sphera_ai.llm")
with tracer.start_as_current_span("llm.call") as span:
t0 = time.perf_counter()
resp = call_llm(model, messages)
span.set_attributes({
"llm.model": model, "llm.prompt_hash": sha256(str(messages)),
"llm.tokens_in": resp.usage.input_tokens, "llm.tokens_out": resp.usage.output_tokens,
"llm.latency_ms": int((time.perf_counter()-t0)*1000),
"llm.cost_usd": estimate_cost(model, resp.usage),
})
logger.info("llm_call", extra={"model": model, "tokens": resp.usage})
- Symptom: Results change between runs; can't roll back a bad prompt; "which model was this?"
- Root cause: Prompts inline in code, model names hardcoded, no registry.
- Fix: Prompt templates as versioned artifacts in Git (or a prompt registry); model versions pinned in config; prompt/model deploy = release with rollback.
# config: pinned, environment-specific
model:
primary: "claude-sonnet-4-6@2026-06-01" # pinned via AI Foundry deployment
fallback: "claude-haiku-4-5@2026-06-01"
embedding: "jina-embeddings-v3@latest-validated"
prompt_version: "summary-v2-2026-08-01"
- Symptom: Every change is a gamble; regressions discovered by users.
- Root cause: "Evals are hard" β skipped.
- Fix: Golden-set evals in CI (50β200 curated Q/A with rubric scorers), LLM-as-judge with guardrail, regression gates on answer quality, latency, cost, hallucination rate. Start small; grow the set with Pendo-flagged bad answers.
- Symptom: Bill spikes; slow responses; users complain.
- Root cause: Huge contexts, no caching, no model tiering, no budgets.
- Fix: Prompt caching (Anthropic + AI Foundry support), model tiering by task (haiku for classification, sonnet for generation), token caps, response caching for identical queries, Azure budget alerts at 50/80/100%.
- Symptom: Anybody with the URL can query the API; internal data exposed.
- Root cause: PoC had no identity layer.
- Fix: Entra ID SSO (OAuth2 PKCE for SPA), APIM validates JWT, role-based authorization per capability (viewer/analyst/admin), per-tenant data isolation checks.
| Decision | Recommendation | Rationale |
|---|---|---|
| Chunking | Semantic chunking (150β400 tokens), metadata-rich; late chunking for long docs | Better retrieval than fixed-size; keeps citations intact |
| Search | Hybrid (BM25 + vector + semantic ranker) via Azure AI Search | ESG docs mix terminology & exact IDs β lexical+semantic beats either alone |
| Embeddings | Jina embeddings-v3 (multilingual, Matryoshka) or Azure OpenAI text-embedding-3-large | Multilingual ESG content; evaluate on your golden set (Β§06 matrix) |
| Rerank | jina-reranker-v1 on top-20 β top-5 | +10β20% answer quality for a few ms; cheap insurance |
| Tenancy | Index per tenant + filterable tenant_id field; RBAC enforced in app | Compliance β Sphera data is per-customer sensitive |
| Freshness | Incremental indexer from Databricks (Change Data Capture); daily + on-demand | Emissions/safety data must not go stale |
| Evals | Golden set 50β200 Q/A; metrics: faithfulness, answer relevance, context precision/recall, citation accuracy | Every RAG change is a PR with an eval delta |
| Factor | AI Foundry (win) | Direct Anthropic API |
|---|---|---|
| Enterprise controls | Private endpoints, entitlements, audit | Vendor-only controls |
| Compliance (ESG data) | Azure compliance + data residency story | Separate DPA/compliance scope |
| Managed identity / networking | First-class Azure integration | Keys + egress needed |
| Feature velocity | Slight lag behind Anthropic releases | Same-day new models |
| Cost | Azure commitments/EA discounts | List price |
| Verdict | Primary = AI Foundry; keep direct API as failover behind abstraction layer | |
| Factor | APIM (win) | nginx only |
|---|---|---|
| OAuth2/JWT validation | Native Entra integration | Manual/openresty work |
| Rate limits & quotas | Per-key, per-plan, built-in | Custom Lua/limits |
| Analytics per consumer | Built-in + export to App Insights | Custom access-log parsing |
| Operational simplicity | Another managed service | Already running |
| Verdict | APIM for external/internal API surface; nginx stays only as an ingress sidecar (e.g., local dashboards) | |
| Factor | Container Apps (win) | AKS | Functions |
|---|---|---|---|
| Team size / K8s expertise | No cluster management | Full cluster ops | Zero infra |
| Long-running agents / workflows | Durable, scale-to-zero, Dapr | Full control | Execution limits |
| Managed identity + VNet | Built-in | Via workload identity | Built-in |
| Cost at low volume | Scale-to-zero | Node minimums | Per-execution |
| Verdict | ACA for the agent/API tier; Functions for event-driven ingestion triggers; AKS only if the team grows a platform team | ||
| Factor | AI Search (win for serving) | Databricks Vector Search |
|---|---|---|
| Hybrid + semantic ranking | BM25 + vector + semantic out of the box | Vector only (v recent) |
| Serving SLA / latency | Managed search SLA, low latency | Compute-dependent |
| Ecosystem (indexers, skills) | Azure-native indexers, enrichments | Delta Lake integration great |
| Verdict | AI Search = serving index; Databricks = curation & feature plane feeding it | |
| Decision | Recommendation | Why |
|---|---|---|
| M5 Β· Embeddings: Jina vs Azure OpenAI | Evaluate both on your golden set; Jina if multilingual/late-chunking matters | Quality delta is domain-specific β let evals decide, keep provider behind an interface |
| M6 Β· Secrets: Key Vault vs .env | Key Vault + Managed Identity | Non-negotiable for compliance (also feeds Β§04 blocker #1) |
| M7 Β· Observability: App Insights + Langfuse-style tracing vs DIY | Managed tracing (Langfuse Cloud/OSS or Azure Monitor GenAI tracing) | Prompt/token/cost trace out of the box; DIY logs drift and die |
π Identity & Access
Entra ID SSO (PKCE) for apps; APIM JWT validation; RBAC roles: AI-Viewer / AI-Analyst / AI-Admin; Managed Identity for all service-to-service calls; Conditional Access for admin.
π Network
Hub-spoke VNet; Private Endpoints for AI Foundry, AI Search, Key Vault, Databricks; Front Door + WAF (OWASP rules, geo-filter); no public egress except allowlisted providers.
ποΈ Data Protection
Data classification (public/internal/confidential/PII); PII redaction before LLM context; per-tenant isolation; retention & deletion policies; Azure Purview for lineage.
π§ AI-Specific Controls
Prompt-injection filters; output guardrails (PII, secrets, prohibited content); tool-call allowlists; human approval for write actions; audit log of every model call & tool invocation.
| Gate | Exit criteria | Owner |
|---|---|---|
| G1 Β· Define | Use case, success metrics, risk tier, data inventory signed off | Product + Architect |
| G2 Β· Data | Data sources catalogued; PII flagged; lineage in Unity Catalog; consent/residency verified | Data Eng |
| G3 Β· Build | Code passes lint/tests/secrets scan; prompts versioned; abstraction layer in place | Engineering |
| G4 Β· Verify | Golden-set evals green; security review; load test; cost estimate approved | QA + Security |
| G5 Β· Deploy | Blue/green or canary; rollback runbook; runbook tested in dry-run | Platform |
| G6 Β· Monitor | Dashboards live; drift & cost alerts; feedback loop (Pendo) feeding eval set; monthly review | Ops + Product |
- β Data residency β region pin (e.g., EU/US) enforced at storage & inference
- β DPA with Anthropic (and Azure) covering sub-processors
- β Zero-retention or short-retention model settings where applicable
- β Audit log: who asked what, which model answered, what was retrieved
- β Model cards & system cards for each deployed use case (Responsible AI)
- β Incident response runbook for prompt-injection / data-exposure events
- β Pen test + threat model before GA; quarterly red team of the RAG path
- Latency: per-model, per-feature; alert on P95 breach.
- Quality: online evals (LLM-as-judge on sampled traffic) + offline golden set each deploy.
- Cost: per use case, per tenant; budgets with 50/80/100% alerts; token caps per key.
- Drift: input distribution, retrieval hit-rate, "I don't know" rate, hallucination proxy (groundedness score).
- Feedback: Pendo thumbs up/down + comments β weekly triage β eval set growth β model/prompt improvement (AIDLC loop).
- Adaptive Cards for human-in-the-loop approvals (e.g., agent-proposed actions, sensitive queries).
- Bot notifications: eval regressions, cost spikes, drift alarms, weekly AI report.
- Prompt-injection or PII incident β Teams channel with severity + runbook link.
- Users ask in Teams β same governed pipeline (APIM β agents β AI Foundry). One platform, one audit trail.
- Detect: error-rate / cost / groundedness alert fires (App Insights β Teams).
- Triage: trace lookup by request id; classify (model, prompt, retrieval, infra).
- Mitigate: feature flag off / rollback to previous prompt+model release (registry = instant).
- Post-mortem: add regression to golden set; AIDLC gate re-run before re-promotion.
π Phase 1 Β· Foundation (W1β2)
IaC landing zone (Bicep): VNet, Key Vault, Entra app regs, APIM, AI Foundry workspace, budgets. Secrets scan + git history purge. Exit: empty environment deploys from one command.
π΅ Phase 2 Β· Harden Code (W3β6)
Refactor into sphera-ai package; P0 fixes (#1,#2,#5,#10); abstraction layer; tests + golden-set evals in CI; prompt registry. Exit: CI green on every PR; eval baseline recorded.
π£ Phase 3 Β· Migrate (W7β10)
Move LLM calls to AI Foundry (fallback chain); rebuild RAG on AI Search + Jina; Databricks β incremental indexer; teams approvals; tracing live. Exit: parallel-run parity (95% answer match, no PII leaks).
π’ Phase 4 Β· GA & Scale (W11β12)
Canary β 100% traffic; SLOs + dashboards + runbooks; Pendo feedback loop live; security review + pen test sign-off; cost monitoring in place. Exit: production approval (G1βG6 all green).
Azure AI Foundry
Azure's unified AI platform β model catalog (incl. Claude), deployments, entitlements, evals, content safety.
AI-DLC
AI-Driven Development Life Cycle β stage-gate framework (DefineβDataβBuildβVerifyβDeployβMonitor) for governing AI releases.
APIM
Azure API Management β gateway with auth, rate limits, quotas, policies, analytics.
RAG
Retrieval-Augmented Generation β ground model answers in your data via retrieval, reducing hallucination.
Managed Identity
Azure service identity with automatic credential rotation β replaces API keys in code.
Golden Set
Curated Q/A test set with rubric scoring β the acceptance test for prompt/model changes.
Groundedness
Eval metric: is the answer supported by retrieved context? (hallucination inverse)
MCP
Model Context Protocol β standard for connecting agents to tools/data (Hermes uses MCP tool registry).
Hybrid Search
Keyword (BM25) + vector search combined β best for mixed terminology/data (ESG docs).
Human-in-the-Loop
Approval step for high-stakes agent actions β e.g., Teams Adaptive Card before a write operation.
Unity Catalog
Databricks governance β lineage, ACLs, and data discovery across the lakehouse.
Scale-to-Zero
Serverless containers that shut down when idle β cuts PoC-stage cost to near zero.
The printable quick reference (also standalone at /sphera-cheatsheet β download PDF, or use the print button):
| Rule | One-liner |
|---|---|
| #1 Secrets | Never in code β Key Vault + Managed Identity |
| #2 Calls | Always via gateway wrapper: retry Β· fallback Β· budget Β· trace |
| #3 Models | Pin versions; registry; deploy = release with rollback |
| #4 Prompts | Versioned artifacts; treat retrieved data as untrusted |
| #5 Output | Pydantic/structured; validate; retry-on-parse; degrade safely |
| #6 Eval | Golden set in CI; no eval improvement β no deploy |
| #7 Trace | Prompt hash, tokens, cost, latency per call β App Insights |
| #8 Tenancy | Per-tenant isolation at index + RBAC + query filter |
| #9 Cost | Model tiering + caching + budgets at 50/80/100% |
| #10 Gate | Nothing to prod without AIDLC G1βG6 sign-off |