Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build and deploy AI applications on Azure AI Foundry using Microsoft's model catalog and AI services
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
foundry-agent/create/quick-start-hosted.md
1# Quick Start: Hosted Foundry Agent23Opinionated happy-path for first-time users creating their first hosted Foundry agent. Safe defaults, minimal decisions.45> **Scope:** Defaults below are applied automatically when the user is silent. The user may override the language or sample explicitly; new-vs-existing Foundry project is handled inline. For anything not covered here, stop and read [create-hosted.md](create-hosted.md).67## When to Use This Skill89Use this when the request is to create a new hosted Foundry agent end-to-end — scaffold, provision, deploy, and smoke-test. Common overrides (language, region, sample, topic, existing project, existing model) are fine; bounce to [create-hosted.md](create-hosted.md) for anything else.1011## Quick Reference1213| Property | Default (when user is silent) | Override |14|----------|-------------------------------|----------|15| Language / runtime | Python 3.13 (`python_3_13`) | Any of `python_3_13`, `python_3_14`, `dotnet_10`, `node_22` |16| Sample | Featured basic starter for the chosen language (`azd ai agent sample list --featured-only --language <lang> --output json`) | User may name a different featured sample |17| Subscription | `az account show` | User may supply |18| Region | `northcentralus` | Ask user to confirm or pick another |19| Foundry project | Ask if the user doesn't mention one | create new → no `--project-id`; existing → pass `--project-id` (ARM ID / endpoint); no mention → stop and ask (existing vs new) |20| Model deployment | Whatever the sample's manifest declares | If user supplies a deployment name, `azd env set AZURE_AI_MODEL_DEPLOYMENT_NAME` after init |21| Deploy mode | `code` (no Docker, no ACR build) | — |22| Stops at | Deployed agent + remote smoke invoke + eval generation submitted | — |2324## Workflow2526Walk through every step in order. **Before Step 2**, scan the user's original prompt for any of these values: project name, language, subscription, region, existing Foundry project endpoint or ARM ID, existing model deployment name, agent topic/purpose. **Do not ask** for anything already supplied.2728### Step 1 — Verify the environment2930Run the bundled script:3132```bash33./scripts/verify-environment.sh # macOS / Linux34./scripts/verify-environment.ps1 # Windows (pwsh)35```3637Act on the summary prefixes:3839- `[OK]` -- nothing to do.40- `[WARN]` -- non-blocking; continue.41- `[ACTION]` -- resolve first, then rerun the script. If `az` or `azd` is missing, ask before installing in interactive mode; install directly in non-interactive mode. For how to install `azd`, see <https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd>. In any mode, never run `az login` or `azd auth login`; stop and ask the user to log in manually before any init, provision, or deploy command. Missing `azure.ai.agents` / `azure.ai.projects` extensions may be resolved with `azd extension install <name>`.4243### Step 2 — Collect remaining inputs (one batch)4445For any values **not** already in the prompt, ask the rest in a single `AskUserQuestion` round:4647| Value | Default | Notes |48|-------|---------|-------|49| Project / agent name | `ai-agent-<random6>` (6 lowercase alphanumeric chars) | Used as agent name, service key, and project directory. |50| Language | `python_3_13` | One of `python_3_13`, `python_3_14`, `dotnet_10`, `node_22`. |51| Subscription | `az account show --query id -o tsv` | Must be a GUID. |52| Region | `northcentralus` | Confirm or override. |53| Foundry project | Ask if the user doesn't mention one | User said create new → create a new one (no `--project-id`). User gave an existing project → use its ARM resource ID *or* Foundry project endpoint URL. User didn't mention a project at all → stop and ask, offering existing vs new. |54| Existing model deployment? | No (use sample manifest's model) | If Yes: collect the deployment name. |5556If the user supplied only a **Foundry project endpoint** (not an ARM ID), resolve the ARM ID before Step 6:5758```bash59./scripts/resolve-project-id.sh --endpoint "<foundry-project-endpoint>" # macOS / Linux60./scripts/resolve-project-id.ps1 -Endpoint "<foundry-project-endpoint>" # Windows (pwsh)61```6263Use the returned `id` value. Never guess or construct the ARM ID from the endpoint.6465### Step 3 — Pick the sample6667```bash68azd ai agent sample list --featured-only --language <lang> --output json69```7071> `--language` here takes the short form (`python`, `dotnetCsharp`) — not the runtime token (`python_3_13` fails with `unknown language`). The runtime tokens are only used in Step 6's `azd ai agent init --runtime ...`.7273Pick the basic starter (e.g. `azd-ai-starter-basic` for Python — avoid samples with `parameters:` blocks requiring secrets). Capture the `manifestUrl`.7475Step 6 needs `--runtime` and `--entry-point` values. These are CLI args, **not** fields in the manifest — use these standard defaults for the chosen language:7677| Language | `--runtime` | `--entry-point` |78|----------|-------------|-----------------|79| Python | `python_3_13` | `main.py` |80| .NET | `dotnet_10` | `MyAgent.dll` |81| Node | `node_22` | `index.js` |8283### Step 4 — Create the project directory8485```bash86mkdir <project-name>87cd <project-name>88```8990### Step 5 — Pre-bootstrap with core `azd init`9192This step writes `AZURE_SUBSCRIPTION_ID` + `AZURE_LOCATION` into the azd env *before* `azd ai agent init` runs, which prevents init from deferring model resolution and leaving the `{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}` placeholder in `agent.yaml`.9394```bash95azd init -t Azure-Samples/azd-ai-starter-basic . \96-e <project>-<random6> \97--subscription <id> \98-l <region> \99--no-prompt100```101102Use env name `<project>-<random6>` as the **default** to avoid collisions with stuck "Deleting"-state resource groups from prior runs. Use bare `<project>` only when you're confident the name has never been used in this subscription.103104### Step 6 — Scaffold the agent105106```bash107azd ai agent init --no-prompt \108-m "<manifestUrl>" \109--deploy-mode code \110--runtime python_3_13 \111--entry-point main.py \112--agent-name <project>113```114115Values you **must** substitute from Step 3 — do not pass placeholders or guesses:116117- `--runtime`: exactly one of `python_3_13`, `python_3_14`, `dotnet_10` (the bare value `python` fails with `--runtime must be one of: python_3_13, python_3_14, dotnet_10`).118- `--entry-point`: the file name from the manifest's `code_configuration.entry_point` (e.g. `main.py`, not `app.py` — a wrong value scaffolds correctly but breaks local run and deploy).119120If using an existing Foundry project, add `--project-id "<arm-id>"`.121122⏳ May take time — init resolves the model catalog server-side. Wait for the prompt to return; do not interrupt.123124`init` writes `azure.yaml` (appending the service), `src/<project>/agent.yaml`, `src/<project>/.agentignore`, and the sample source files under `src/<project>/`.125126### Step 7 — Customize the scaffolded sample (per user's original intent)127128The scaffold is a generic working sample. Edit only what the user's original prompt asked for — touch tools, dependencies, or model config only when the user explicitly asked for external actions, APIs, tools, connectors, data lookup, or a specific model.129130Typical changes:131132- `src/<project>/agent.yaml` — update `description:` to match the user's intent (this also feeds Step 15 eval generation).133- `src/<project>/<entry-point>` — update the system prompt / instructions to match the user's intent.134135Only when the user explicitly asked for it:136137- Add or modify tool / function-calling code in `<entry-point>`.138- Add dependencies to `pyproject.toml` / `requirements.txt` (Python), `*.csproj` (.NET), or `package.json` (Node).139- Change the model in `azure.yaml services.<project>.config.deployments[]` before Step 10 provision.140141If the user's original prompt was generic (no specific agent purpose described), skip customization and ship the sample as-is.142143### Step 8 — Write the agent instruction file (required)144145Do **not** skip this. Add a one-line marker to the project's `AGENTS.md` (or `CLAUDE.md` in Claude Code) so future prompts reload this skill. If the file already exists, append the line; don't overwrite it or add a second file.146147```148This project was built with the microsoft-foundry skill. Before working on or answering questions about foundry agents, read the microsoft-foundry skill first.149```150151### Step 9 — Sanity-check the scaffold152153Verify all four before continuing. If any check fails, pick **one** recovery path, then re-verify:154155| Check | Expected | If failed |156|-------|----------|-----------|157| `azure.yaml services.<project>.config.deployments[]` | Non-empty array with `name`, `model.{name,format,version}`, `sku.{name,capacity}` | Model resolution deferred — use recovery |158| `src/<project>/agent.yaml` `model_deployment_name:` | Literal name **or** `${AZURE_AI_MODEL_DEPLOYMENT_NAME}` substitution | If literal `{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}` (double braces): use recovery |159| `src/<project>/agent.yaml` `code_configuration.entry_point:` | Matches a real file in `src/<project>/` (e.g. `main.py` and `main.py` exists) | If mismatch (e.g. `entry_point: app.py` but only `main.py` exists): edit `agent.yaml` to the real filename, then re-verify. Most often caused by passing a wrong `--entry-point` in Step 6. |160| `azure.yaml services:` keys | Only one `<project>` entry | If `<project>-2` exists: init was re-run; use recovery |161162**Recovery paths** (pick based on whether Step 7 has already customized `src/<project>/`):1631641. **Hand-fix in place** *(use when Step 7 customization is already done — preserves user code)* — edit `azure.yaml services.<project>.config.deployments[]` to add the model block, replace `{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}` in `agent.yaml` with `${AZURE_AI_MODEL_DEPLOYMENT_NAME}`, then `azd env set AZURE_AI_MODEL_DEPLOYMENT_NAME <deployment-name>`.1652. **Clean re-init** *(use only when Step 7 has not run yet — destructive: deletes `src/<project>/`)* — delete `src/<project>/`, remove the `services.<project>:` block from `azure.yaml`, re-run Step 6.1663. **Interactive overwrite** *(loses Step 7 edits — re-resolves the model from the original manifest)* — re-run Step 6 *without* `--no-prompt`. When the collision prompt appears, **arrow-up to "Overwrite existing"** (default is *not* overwrite).167168Never `azd env set AI_PROJECT_DEPLOYMENTS '[...]'` (single-escaped JSON breaks Bicep parse). Never `az cognitiveservices account deployment create` against this account (creates the deployment outside the azd lifecycle).169170If recovery still fails → escape to [create-hosted.md](create-hosted.md).171172### Step 10 — Provision Azure resources173174> 🚦 **Project-selection gate (align with Step 2).** Only `azd provision` a new project when the user asked to create one. If the user gave an existing project, skip provision and use it. If the user didn't mention a project at all, stop and ask first — don't silently provision a new one.175176```bash177azd provision --no-state --no-prompt178```179180`--no-state` skips the existing-deployment check; safe here because the golden path starts from a fresh environment (Step 5). Keep it for this quickstart; you can omit it later when re-provisioning the same environment.181182⏳ May take time — creates the resource group, Foundry account + project, model deployment, App Insights, Log Analytics. Wait for the prompt to return; do not interrupt.183184### Step 11 — Wire local env vars185186```bash187azd env get-values188```189190Capture `FOUNDRY_PROJECT_ENDPOINT` and `AZURE_AI_MODEL_DEPLOYMENT_NAME`. Write `src/<project>/.env`:191192```env193FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>194AZURE_AI_MODEL_DEPLOYMENT_NAME=<deployment-name>195```196197Also mirror them into the azd env (so `azd ai agent run` injects the right values — it reads azd env *before* `.env`):198199```bash200azd env set AZURE_AI_PROJECT_ENDPOINT "<endpoint>"201azd env set AZURE_AI_MODEL_DEPLOYMENT_NAME "<deployment-name>"202```203204### Step 12 — Local smoke test205206Set up a venv with `uv` installed first. `azd ai agent run` installs Python dependencies on first start; with an activated venv that has `uv` available, it uses `uv` (seconds) instead of plain `pip` (minutes).207208> **Important:** the venv must live in `src/<project>/` (next to `requirements.txt`). `azd ai agent run` resolves the venv relative to the service source directory; a venv at the project root is ignored and azd silently creates a second one without `uv`, wasting the speedup.209210**Python:**211```bash212cd src/<project>213python -m venv .venv214# Activate the venv — pick the line for your shell:215.\.venv\Scripts\Activate.ps1 # Windows pwsh216source .venv/bin/activate # macOS / Linux217python -m pip install uv218cd - # back to project root for the azd commands below219```220221**.NET / Node:** no pre-install step — `azd ai agent run` runs `dotnet restore` / `npm install` itself on first start.222223Run the agent locally. For Python, do this **with the service-dir venv still activated** — activation is what lets `azd ai agent run` find `uv` for the fast dependency install. `azd ai agent run` **is** the local server — a foreground process holding port 8088 that must stay alive from start, through every `invoke --local`, until you explicitly stop it.224225Start it in a **managed** background session your shell tool can poll and stop (most tools detect a long-running foreground process and return a session/shell id — use that id). Do **not** use job operators (`bash &`, `nohup`, `start /B`, popped windows): on Linux/macOS the child gets `SIGHUP` and **dies when its parent bash exits**, so the next command sees `could not connect` even though `ss` from inside the *same* bash just showed `:8088` bound.226227> ⚠️ **Readiness gate — do not skip.** After starting `azd ai agent run`, **watch the server log for the ready line, something like `Running` (e.g. `Running on http://0.0.0.0:8088`) — not just `Starting …`**, which azd prints as a banner before the Python process has bound the socket. Invoking before the socket is bound fails with `could not connect`.228> - **Never invoke before the most recent log read shows the ready line.** Premature invokes waste a poll cycle and return a misleading `could not connect`.229> - **Poll short — 2–5s per read.** Boot time is unbounded; long sleeps cost wall-clock directly. No 15s+ blocks or `sleep N` waits.230> - **Don't substitute log polling** with `sleep N && curl`, `netstat` / `ss` / `lsof`, or `ps aux` probes — only the log tells you readiness.231> - **If `invoke --local` fails,** re-read the server log. Error before the ready line (missing env var, auth, port in use) → fix the cause and restart `azd ai agent run` in the managed session. Ready line present but request still fails → the issue is in the request, not the server. Either way, do **not** bypass with `python main.py` or raw `curl POST /responses` — those skip the wiring the deployed agent uses.232> - **If `invoke --local` returns `could not connect` after you saw the ready line in a previous shell,** the server died when that shell exited (classic `&` symptom). Restart in the managed session — do not retry with another `&`.233234```bash235azd ai agent run --no-inspector236```237238Smoke-invoke (local):239240```bash241azd ai agent invoke --local "<short representative prompt for the agent's purpose>"242```243244Stop the local server via the managed session's stop primitive before continuing — a lingering process holds files in the project and breaks later cleanup.245246### Step 13 — Deploy247248```bash249azd deploy --no-prompt250```251252⏳ May take time — zips `src/<project>/` (respecting `.agentignore`), uploads to Foundry, builds runtime remotely, registers agent version. Wait for the prompt to return; do not interrupt.253254### Step 14 — Verify + remote smoke255256```bash257azd ai agent show --output json258```259260Expect `"status": "active"` (or `"deployed"`) and an `agent_endpoints` map.261262Remote invoke (billed):263264```bash265azd ai agent invoke "<short representative prompt>"266```267268`azd ai agent invoke` has **no `--force` flag**. If the command succeeds, read the response. If it surfaces a confirmation prompt or message, summarize the cost implication for the user (*"this will call the deployed agent and incur model usage charges"*), get explicit consent, and re-run — do **not** invent flags.269270### Step 15 — Submit eval suite generation (async, fire-and-forget)271272> ⚠️ **Pre-summary gate.** Do not write the Step 16 final summary until this step has been submitted. The eval suite is part of the deployment artifact; skipping it ships an incomplete result.273274Read the `description:` from `src/<project>/agent.yaml` (the value you set in Step 7) and pass it as `--gen-instruction`:275276```bash277azd ai agent eval generate --gen-instruction "<agent.yaml description>" --no-wait --no-prompt278```279280Expected output:281282```283Eval generate submitted (async)284dataset generation: datagen-<id> (queued)285evaluator generation: evaluatorgen-<id> (in_progress)286Config written to: src/<project>/eval.yaml287When ready, run:288azd ai agent eval run289```290291Generation runs server-side and takes several minutes. Tell the user:292293> *"Eval suite generation submitted. Run `azd ai agent eval run` whenever you're ready — it'll wait for generation to finish and execute the eval in one step."*294295### Step 16 — Final summary296297Produce a concise summary covering: agent name/version/status/endpoints, a Playground link, the resources created, and the three follow-up commands below. Construct the Playground URL from `azd env get-values` (or read `playground_url` directly from `azd ai agent show --output json` if present):298299```300https://ai.azure.com/nextgen/r/{encodedSubId},{resourceGroup},,{accountName},{projectName}/build/agents/{agentName}/build?version={agentVersion}301```302303`encodedSubId` = URL-safe base64 of the subscription GUID, padding stripped:304305```bash306python -c "import base64,uuid;print(base64.urlsafe_b64encode(uuid.UUID('<SUBSCRIPTION_ID>').bytes).rstrip(b'=').decode())"307```308309Three follow-up commands to include:310311```bash312azd ai agent invoke "<follow-up message>" # chat with the deployed agent (billed)313azd ai agent eval run # finalize + run the eval suite (Step 15)314azd down # tear down all resources when done315```316317## Error Handling318319| Symptom | Fix |320|---------|-----|321| `azd ai agent init` fails with `--runtime must be one of: python_3_13, python_3_14, dotnet_10` | You passed a bare value like `python`. Use the full runtime token (e.g. `python_3_13`). |322| `azd ai agent init` fails with `--entry-point is required when using --deploy-mode code with --no-prompt` | Pass `--entry-point <filename>` matching the manifest's `code_configuration.entry_point` from Step 3. |323| `agent.yaml` `entry_point` doesn't match any file in `src/<project>/` | You guessed the entry-point in Step 6. Edit `agent.yaml` to the real filename (verify with `ls src/<project>/`). No re-init needed. |324| `azd deploy` postdeploy hook fails with missing `AZURE_TENANT_ID` | Run `az account show --query tenantId -o tsv` and `azd env set AZURE_TENANT_ID <tenant-id>`, then re-run `azd deploy --no-prompt`. The deployed agent version from the first deploy is still valid; the postdeploy hook just registers env vars. |325| Scaffold sanity check fails (Step 9) | Pick a recovery path from Step 9. If still failing → [create-hosted.md](create-hosted.md). |326| Local invoke returns model `404` / wrong deployment | Stale `AZURE_AI_MODEL_DEPLOYMENT_NAME` in azd env overrides `.env`. Re-run Step 11 to sync both. |327| `azd ai agent invoke ... --force` returns `unknown flag: --force` | `--force` is not a valid flag for invoke. Re-run without it. |328| Anything else | Escape to [create-hosted.md](create-hosted.md). |329330## Escape Hatch331332If any step fails in a way not covered above, the output looks unexpected, or the user's request drifts outside what this quickstart covers → **stop improvising**. Read [create-hosted.md](create-hosted.md) and follow its full workflow.333334335