⚠️ This blog post was created with the help of AI tools. Yes, I used a bit of magic from language models to organize my thoughts and automate the boring parts, but the geeky fun and the 🤖 in C# are 100% mine.
Hi!
Ok so I spend the full morning with this, so let’s better write it down for Future Bruno:
The deployment was green. The hosted agent was active. The Microsoft Foundry Playground could reach it. There were no authentication failures, container crashes, or model errors.
There was just one problem: when I asked the C# agent, “Who are you?”, it answered:
I’m ChatGPT, an AI assistant created by OpenAI.
That was not the behavior defined in the source code. The C# sample was explicitly instructed to identify itself as the C# hosted agent. The same problem appeared in the Go and C++ hosted agents.
My first suspicion was stale build output or an aggressive container cache. The real cause was simpler and more useful: I had deployed from the wrong Git revision.
The setup
You can find the working samples in the microsoft-foundry-hosted-agents repository. For a focused walkthrough of the C# sample, see From dotnet run to Foundry Hosted Agent in 3 Lines of C# on the .NET Blog.
The repository contains three Microsoft Foundry hosted-agent samples:
| Sample | Runtime | Protocol | Deployment style |
|---|---|---|---|
| C# | .NET 10 and Microsoft Agent Framework | Responses | Direct code deployment |
| Go | Go 1.26 and Microsoft Agent Framework | Invocations | Container deployment |
| C++ | C++20 and a repository-owned hosting adapter | Invocations | Container deployment |
Each agent has language-specific instructions. For example, the C# agent is configured with:
instructions: "You are the C# hosted agent sample, running on .NET 10 with Microsoft Agent Framework. " + "When greeted or asked who you are, identify yourself as the C# hosted agent. Keep your answers brief."
The Go and C++ samples have equivalent instructions for their own runtimes.
Despite that, all three deployed agents returned the generic model identity.
Why the obvious evidence was misleading
Several facts initially suggested a packaging problem:
- The source files contained the correct language-specific instructions.
- All deployed agent versions were active.
- Each endpoint returned a successful model response.
- The issue reproduced in a completely new session.
- For the C# sample, inspecting the deployed build output revealed the older prompt:
You are a friendly assistant.
That last detail looked like proof of a stale compiler artifact. It was proof that the deployed artifact was old, but not proof that the compiler or deployment service had incorrectly cached it.
The Git reflog and timestamps completed the picture.
The timeline that exposed the cause
The original versions were deployed between 11:30 and 11:54:
| Time | Event |
|---|---|
| 11:30 | Go hosted agent version 1 deployed |
| 11:52 | C++ hosted agent version 1 deployed |
| 11:54 | C# hosted agent version 2 deployed |
| 12:04 | The workspace switched from a feature branch to main |
| 12:04 | main was fast-forwarded from origin/main |
The language-specific instructions arrived in the working tree with that 12:04 branch switch and pull. The deployments had already happened.
The local source looked correct during the investigation because I was looking at main. Microsoft Foundry was correctly running the artifacts produced earlier from the feature branch.
There was no mysterious cloud cache. I had successfully deployed exactly the wrong code.
Confirming the diagnosis
Before changing anything, I reproduced the behavior outside the Playground. This ruled out old browser conversation history.
For the C# Responses agent:
azd ai agent invoke --new-session --new-conversation "who are you?"
For the Go and C++ Invocations agents:
azd ai agent invoke --new-session "who are you?"
All three returned the generic identity.
Next, I checked the deployed version:
azd ai agent show --output json
Then I compared the repository state:
git branch --show-currentgit status --shortgit rev-parse HEADgit rev-parse origin/maingit reflog --date=iso
The reflog was the decisive evidence. It showed that the deployments preceded the switch to the revision containing the new instructions.
The fix
I verified that:
- the active branch was
main; HEADmatchedorigin/main;- the source contained the intended instructions; and
- the local builds contained the intended prompt.
I then redeployed all three samples. Microsoft Foundry created new immutable agent versions:
| Sample | Corrected version | Verified response |
|---|---|---|
| C# | 3 | “I am the C# hosted agent sample…” |
| Go | 2 | “I am the Go hosted agent.” |
| C++ | 2 | “I am the C++ hosted agent.” |
Each corrected deployment was tested with a new session. The C# Responses agent also used a new conversation to guarantee that no previous conversation history influenced the result.
A safer deployment gate
The key lesson is that a successful build and a successful deployment answer different questions:
- Build succeeded: this revision can be compiled.
- Deployment succeeded: these packaged bytes can run.
- Behavioral smoke test succeeded: the active deployment behaves like the revision I intended to release.
All three checks are necessary.
1. Verify the Git revision
Run this before packaging or deploying:
git fetch origin$branch = git branch --show-current$head = git rev-parse HEAD$remote = git rev-parse origin/mainWrite-Host "Branch: $branch"Write-Host "HEAD: $head"Write-Host "Remote: $remote"git status --shortif ($branch -ne "main") { throw "Deployment must run from main."}if ($head -ne $remote) { throw "Local HEAD does not match origin/main."}
Whether deployments must come from main, a release branch, or a tag is a team decision. The important part is to make the rule explicit and fail before deployment when it is not satisfied.
For production releases, a tag or immutable commit SHA is safer than relying only on a branch name:
$expectedCommit = "<approved-commit-sha>"$actualCommit = git rev-parse HEADif ($actualCommit -ne $expectedCommit) { throw "Workspace is not at the approved release commit."}
2. Build and test from that revision
Use the repository’s normal validation commands before deployment:
dotnet build .\MAF-Agents-Samples.slnxPush-Location .\04-MAF-Agent-GO-Hostedgo test ./...Pop-LocationPush-Location .\06-Foundry-Agent-CPP-Hostedcmake --preset debugcmake --build --preset debugctest --preset debugPop-Location
This catches source and dependency problems, but it still does not prove that the intended revision reached Microsoft Foundry.
3. Record the commit with the deployment
Capture the SHA in deployment logs:
$commit = git rev-parse HEADWrite-Host "Deploying commit $commit"azd deploy --no-prompt
In a CI/CD pipeline, preserve this value as release metadata or an artifact. Being able to answer “Which commit produced agent version 7?” turns a long investigation into a quick comparison.
4. Verify the active immutable version
After deployment:
azd ai agent show --output json
Confirm that:
- the new version is active;
- the protocol is correct;
- the model deployment and environment variables are correct; and
- the endpoint points to the expected agent.
Do not assume that a completed azd deploy means the endpoint has already switched to the version you intended.
5. Test a distinguishing behavior
Hello is a weak smoke test. Almost any functioning model can answer it.
Choose a prompt whose expected response distinguishes the new release from the old one. In this case, identity was ideal:
azd ai agent invoke --new-session "who are you?"
For a Responses agent, reset both forms of state:
azd ai agent invoke --new-session --new-conversation "who are you?"
Other useful distinguishing checks include:
- a newly added tool;
- a changed refusal or safety rule;
- a version-specific response marker;
- a newly supported data source; or
- a corrected edge case.
The smoke test should fail when the previous release is still active.
6. Evaluate the behavior you actually care about
The original evaluation descriptions called these agents friendly, general-purpose assistants. That criterion would pass for both the incorrect and corrected deployments.
The evaluation criteria now explicitly require each agent to identify itself as the C#, Go, or C++ hosted sample. This turns identity from an informal expectation into a regression check.
Evaluations should describe the behavior that makes the release distinct—not only generic qualities such as helpfulness, relevance, and brevity.
CI/CD is the long-term solution
Manual deployment from a developer workstation makes the active branch, uncommitted changes, local build outputs, and shell environment part of the release process.
A CI/CD deployment should instead:
- start from an explicit commit or signed tag;
- require a clean checkout;
- run builds and tests;
- log the commit SHA;
- deploy through
azd; - wait for the new immutable agent version to become active;
- invoke a release-specific smoke test in fresh state; and
- run a small regression evaluation suite.
That process does not merely automate deployment. It makes the deployed source traceable and reproducible.
What I would check next time
If deployed behavior does not match local source, I would investigate in this order:
- Reproduce with a new session and, for Responses, a new conversation.
- Check the active agent version.
- Print the current branch and commit SHA.
- Compare
HEADwith the approved release SHA. - Check
git statusfor uncommitted changes. - Inspect the packaged artifact or container image.
- Only then investigate build and container caching.
This order starts with the cheapest and most common explanations. It also separates three different sources of stale behavior:
- stale conversation state;
- stale deployed version; and
- stale or unintended source revision.
Final takeaway
The cloud platform did exactly what I asked it to do. The mistake was assuming that the source visible in my editor was the source I had deployed ten minutes earlier.
The most important deployment question is not:
Did the deployment succeed?
It is:
Can I prove which revision is active, and can I prove that it behaves like that revision?
An immutable version number, a recorded Git SHA, and one distinguishing smoke test provide that proof.
Happy coding!
Greetings
El Bruno
More posts in my blog ElBruno.com.
More info in https://beacons.ai/elbruno
Leave a comment