Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Deploy, evaluate, and manage AI agents end-to-end on Microsoft Azure AI Foundry
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