Lab 2 · GitHub Copilot + Agent Framework Vibe Coding (30 min)
Lab 1 has already deployed the built-in research-agent to Foundry. In Lab 2, you iterate on business capability locally: first understand the default market and competitive research scenario, then optionally ask Copilot to generate or modify persona, skill, and tool files. Lab 3 publishes the updated version.
2.1 Goals
Section titled “2.1 Goals”- Understand the Soul / Persona · Skills · Tools trio.
- Use Copilot to generate or modify the trio.
- Run
/responseslocally withagentdev run. - Optionally replace the default research assistant with your own business agent.
2.2 Agent-driven Workflow
Section titled “2.2 Agent-driven Workflow”Lab 2 is the core vibe-coding part of the workshop. Do not ask Copilot to “rewrite the whole agent” in one shot. Split the work into persona, skill, tool, local run, and reflection milestones. Use the same rhythm for each milestone:
| Stage | You do | Copilot coding agent does |
|---|---|---|
| Brief | State the business goal, non-goals, and expected output | Restate the goal and point to relevant files |
| Ask | Use /persona, /skill, /tool, or an @workspace prompt | Generate or modify the smallest necessary files |
| Inspect | Review the diff and decide whether it makes business sense | Explain how the change maps to the persona contract or tool schema |
| Verify | Run lint, unit tests, agentdev run, and HTTP POST | Summarize results and attribute failures |
| Reflect | Decide whether to move to the next milestone | Suggest next steps and rollback options |
Starter prompt:
@workspace I am working on Lab 2. First read #file:Lab-2-vibe-coding/README.md, #file:personas/research-agent.md, #file:skills/market-research/SKILL.md, #file:tools/web_search.py, and #file:src/research_agent/main.py.This round should do only one mini-milestone. Tell me the goal, relevant files, verification command, and completion signal.2.3 Working with Copilot
Section titled “2.3 Working with Copilot”| Task | VS Code path (main) | Copilot TUI path (optional) |
|---|---|---|
| Generate / edit persona | /persona agentName=… role=… boundaries=… | Run copilot, then paste .github/prompts/persona.prompt.md plus parameters into chat |
| Generate / edit SKILL.md | /skill skillName=… purpose=… | Paste skill.prompt.md plus parameters |
| Generate / edit tool | /tool toolName=… inputs=… outputs=… | Paste tool.prompt.md plus parameters |
| Explain SDK / hosted tools | Ask chat directly; keywords will activate .agents/skills/agent-framework-azure-ai-py/ | Paste the relevant SKILL.md content into TUI chat |
2.4 Default Scenario: Market / Competitive Research Assistant
Section titled “2.4 Default Scenario: Market / Competitive Research Assistant”Input: a product / category / company │ ▼[ResearchAgent] ├─ Break down 3-7 sub-questions ├─ web_search Multi-keyword, multi-source search ├─ web_fetch Fetch body text + remove HTML └─ report_builder Validate citations + output structured JSON ▼Output: markdown report with footnotes + sources array + confidence ratingCore files:
personas/research-agent.md: role, boundaries, output contract.personas/shared/guardrails.md: shared refusal and safety boundaries.skills/market-research/SKILL.md: research workflow.tools/*.py: Agent Framework@toolfunctions.src/research_agent/main.py: wires persona, skills, and tools into the agent.
2.5 Code Directory Overview
Section titled “2.5 Code Directory Overview”Lab-2-vibe-coding/├── azure.yaml # azd deploy entrypoint├── hooks/postdeploy-grant-roles.ps1 # azd postdeploy wrapper├── personas/│ ├── shared/guardrails.md│ └── research-agent.md├── skills/│ ├── market-research/SKILL.md│ └── citation-format/SKILL.md├── tools/│ ├── web_search.py│ ├── web_fetch.py│ └── report_builder.py├── src/research_agent/│ ├── main.py│ ├── agent.yaml│ ├── agent.manifest.yaml│ ├── Dockerfile│ └── requirements.txt├── tests/unit/├── .github/ # VS Code Copilot customization├── requirements.txt└── pyproject.toml2.6 Mini-milestones
Section titled “2.6 Mini-milestones”M1 · Persona / Soul (5 min)
Section titled “M1 · Persona / Soul (5 min)”Brief: Confirm the agent role, refusal boundaries, tool-use rules, and JSON output contract. The default scenario does not require edits; only bring-your-own scenarios generate a new persona.
Ask Copilot:
@workspace Check #file:personas/research-agent.md and #file:personas/shared/guardrails.md.Explain this persona's role, refusal boundaries, tool order, and output contract. Do not modify files.The default scenario is already written. Run lint first:
python ..\scripts\lint-persona.py personas\research-agent.mdpython3 ../scripts/lint-persona.py personas/research-agent.mdFor a bring-your-own scenario, ask Copilot to generate a new persona, for example:
/persona agentName=invoice-explainer role="invoice explanation assistant" boundaries="do not fetch live exchange rates; do not provide tax advice; extract facts only from the invoice text supplied by the user" tools="ocr_extract, classify_charges, currency_normalize" contract="{lineItems, totalsByCategory, suspiciousFlags}"Inspect: Confirm frontmatter has name, version, owner, and extends; the body references {{include: shared/guardrails.md}}; and tool names match the planned tools.
Verify: Run lint-persona.py again after generation.
Reflect: Ask Copilot:
Map each persona boundary to a testable prompt, and indicate which prompts should be refused and which should call tools.M2 · Skill (5 min)
Section titled “M2 · Skill (5 min)”Brief: A skill is a workflow instruction, not code. It tells the model when to trigger, what order to follow, and how to degrade on failure.
Ask Copilot:
@workspace Read #file:skills/market-research/SKILL.md and #file:personas/research-agent.md.Check whether the skill steps support the persona output contract. If you find conflicts, propose only minimal changes.For the default scenario, read skills/market-research/SKILL.md. For your own business scenario, use:
/skill skillName=invoice-explain purpose="explain an uploaded invoice step by step" triggers="the user uploads invoice image or text; the user asks where each charge went" tools="ocr_extract, classify_charges, currency_normalize" relatedSkills="citation-format"Inspect: A good SKILL.md includes triggers, steps, tool usage, and failure/refusal boundaries.
Verify: Ask Copilot to walk through 2 success prompts and 1 refusal prompt, confirming it does not skip required citations or schema checks.
Reflect: If the skill and persona disagree, prefer editing the skill. Persona should express durable identity and boundaries.
M3 · Client-side Tools (8 min)
Section titled “M3 · Client-side Tools (8 min)”Brief: A tool is the callable boundary for the model. In the classroom, tools must be mockable, testable, and observable; external API failure should not block the entire lab.
Ask Copilot:
@workspace Reference #file:tools/web_search.py, #file:tools/web_fetch.py, #file:tools/report_builder.py, and #file:.github/instructions/maf-tools.instructions.md.Check whether existing tools meet classroom requirements for input/output, mock fallback, timeout, and OTel spans.Default tools already live under tools/. When generating a new tool, use:
/tool toolName=ocr_extract purpose="extract text from image or PDF" inputs="image_url: HttpUrl, lang: str='auto'" outputs="text: str, blocks: list[dict], pages: int" liveBackend="Azure Computer Vision Read API" envKey="AZURE_VISION_KEY"Requirements:
- Define inputs and outputs clearly with Pydantic.
- Use
@tool(description=...)to explain when the model should call it. - Include mock fallback so classroom network or external APIs do not block progress.
- Add reasonable timeouts and do not swallow exceptions.
Inspect: Confirm the new tool has no hard-coded keys, returns diagnosable errors, and has a concrete enough description for the model to choose it.
Verify:
pytest tests/unit/test_tools.pyIf you only changed a new tool and there is no unit test yet, ask Copilot to generate a mock-path test and run the relevant test.
Reflect: Ask Copilot which failures come from external services and which come from schema or prompt design, so you do not misdiagnose network problems as agent logic problems.
M4 · Local Assembly + Run (8 min)
Section titled “M4 · Local Assembly + Run (8 min)”Brief: Wire persona, skills, and tools into src/research_agent/main.py, then use agentdev run to verify the full /responses protocol locally.
Ask Copilot:
@workspace Explain how #file:src/research_agent/main.py loads persona, SkillsProvider, and tools.If I switch to my own business agent, tell me the minimum imports, tool list, and persona filename changes.Windows (PowerShell)
cd Lab-2-vibe-codingpip install -r requirements.txt. ..\scripts\Windows\load-env.ps1agentdev run src\research_agent\main.py --port 8087In a second terminal:
$body = @{ input = "Research the consumer AI note-taking app category and compare five key players in 2025." } | ConvertTo-JsonInvoke-RestMethod -Method POST -Uri "http://localhost:8087/responses" -ContentType "application/json" -Body $bodymacOS / Linux (bash)
cd Lab-2-vibe-codingpip install -r requirements.txtsource ../scripts/macOSLinux/load-env.shagentdev run src/research_agent/main.py --port 8087In a second terminal:
curl -s -X POST http://localhost:8087/responses \ -H "Content-Type: application/json" \ -d '{"input":"Research the consumer AI note-taking app category and compare five key players in 2025."}' | jq .Expected output is JSON matching the persona output contract, at least including report, sources, and confidence.
Inspect: If the response runs but has the wrong structure, paste it into Copilot and ask it to compare against personas/research-agent.md.
Verify: In addition to the success sample, run one refusal sample to confirm guardrails still apply locally.
M5 · Inspector + Reflection (4 min)
Section titled “M5 · Inspector + Reflection (4 min)”Brief: Inspector is not a demo toy; it verifies whether the model followed persona/skill/tool agreements.
agentdev inspectIn the browser, send a prompt that should be refused:
Is Company X worth investing in? Buy or sell?Expected result: the persona refuses investment advice according to guardrails. Give the Inspector span or screenshot to Copilot and ask why it did not continue calling tools.
Reflect prompt:
Here is the Inspector span / screenshot text. Explain:1. Which step called the model2. Which tools were called or skipped3. Whether the result satisfies the persona contract4. What still needs fixing before Lab 32.7 Mock Mode
Section titled “2.7 Mock Mode”When BING_SEARCH_API_KEY / GOOGLE_CSE_* are missing, web_search automatically falls back to local mock data. Set WORKSHOP_WEB_FETCH_FORCE_MOCK=1 to force web_fetch into mock mode. Enable both to do fully offline vibe coding.
Ask Copilot to confirm the offline path:
@workspace Check the mock fallback for web_search and web_fetch. Tell me whether local Lab 2 can still complete an end-to-end demo without external search keys.2.8 Minimal Bring-your-own-business Changes
Section titled “2.8 Minimal Bring-your-own-business Changes”- Copy or generate
personas/<agent>.md. - Describe the workflow in
skills/<skill>/SKILL.md. - Write an
@toolintools/<tool>.py. - Create
src/<agent>/main.pyusingsrc/research_agent/main.pyas a reference. - Use
/deployto generate matchingagent.yamlandagent.manifest.yaml.
Do not hard-code keys. All configuration must come from .env / environment variables.
2.9 Deploy to Foundry (Lab 3)
Section titled “2.9 Deploy to Foundry (Lab 3)”Lab 1 has already initialized and synced azd env. After Lab 2 changes, you only need:
Windows (PowerShell)
azd env set AGENT_NAME "research-agent-$env:STUDENT_SUFFIX"azd deploy research-agent..\scripts\Windows\invoke-hosted.ps1 -Prompt "ping"macOS / Linux (bash)
azd env set AGENT_NAME "research-agent-${STUDENT_SUFFIX}"azd deploy research-agent../scripts/macOSLinux/invoke-hosted.sh --prompt "ping"See ../Lab-3-update-hosted-agent/README.en.md.
2.10 Exit Checkpoint
Section titled “2.10 Exit Checkpoint”- Persona lint passes.
agentdev runstarts without errors.- Local POST
/responsesreturns business JSON. - If you changed a tool, there is a unit test or at least one local mock/live call verification.
- Copilot can explain how local behavior differs from the Lab 1 hosted version.
2.11 Troubleshooting
Section titled “2.11 Troubleshooting”| Symptom | Fix |
|---|---|
FoundryChatClient 401 | Confirm . ..\scripts\Windows\load-env.ps1 or source ../scripts/macOSLinux/load-env.sh has been run; the SP needs Azure AI User on the shared project |
agentdev run port conflict | Use --port 8088 |
| Copilot output does not follow conventions | Explicitly reference .github/instructions/maf-*.instructions.md in the prompt |
| Model does not call the tool | Check whether @tool(description=...) is specific enough and the input schema is clear |
| Skill not loaded | SKILL.md must live at skills/<skill-name>/SKILL.md |
Before leaving this directory, ask Copilot for a summary:
Summarize which persona/skill/tool/runtime files I changed in Lab 2, which verifications passed, and the 3 most likely failure points for Lab 3 deployment.