Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/build-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
paths:
- "dotnet-ai/provider-agnostic-chat-gateway/**"
- "dotnet-ai/hybrid-search-ef-core-pgvector/**"
- "dotnet-ai/agent-framework-incident-triage/**"
- "dotnet-aspire/orchestrate-distributed-system/**"
- "software-architecture/architecture-testing-dotnet/**"
- "distributed-systems/transactional-outbox-ef-core/**"
Expand Down Expand Up @@ -37,6 +38,7 @@ on:
paths:
- "dotnet-ai/provider-agnostic-chat-gateway/**"
- "dotnet-ai/hybrid-search-ef-core-pgvector/**"
- "dotnet-ai/agent-framework-incident-triage/**"
- "dotnet-aspire/orchestrate-distributed-system/**"
- "software-architecture/architecture-testing-dotnet/**"
- "distributed-systems/transactional-outbox-ef-core/**"
Expand Down Expand Up @@ -2012,3 +2014,35 @@ jobs:
exit 1
fi


test-agent-framework-incident-triage:
name: Test Agent Framework incident triage sample
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Install .NET 10 SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"

- name: Restore
run: >
dotnet restore
dotnet-ai/agent-framework-incident-triage/AgentFrameworkIncidentTriage.slnx

- name: Build
run: >
dotnet build
dotnet-ai/agent-framework-incident-triage/AgentFrameworkIncidentTriage.slnx
--configuration Release
--no-restore

- name: Test deterministic layers
run: >
dotnet test
dotnet-ai/agent-framework-incident-triage/AgentFrameworkIncidentTriage.slnx
--configuration Release
--no-build
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| --- | --- | --- |
| [`dotnet-ai/provider-agnostic-chat-gateway`](dotnet-ai/provider-agnostic-chat-gateway/) | One HTTP chat endpoint using Ollama and optional OpenAI providers through `Microsoft.Extensions.AI.IChatClient` | [Build a switchable multi-provider AI gateway with IChatClient](https://www.dotnet-guide.com/tutorials/dotnet-ai/provider-agnostic-chat-gateway/) |
| [`dotnet-ai/hybrid-search-ef-core-pgvector`](dotnet-ai/hybrid-search-ef-core-pgvector/) | Minimal Reciprocal Rank Fusion demo combining pre-ranked keyword and vector results | [Hybrid Search in .NET with EF Core 10 and pgvector](https://www.dotnet-guide.com/tutorials/dotnet-ai/hybrid-search-ef-core-pgvector/) |
| [`dotnet-ai/agent-framework-incident-triage`](dotnet-ai/agent-framework-incident-triage/) | Evidence-first Microsoft Agent Framework sample with read-only tools, deterministic incident evidence, `AgentSession` reuse, and correlation-vs-causation boundaries | [Microsoft Agent Framework Tutorial in C#: Build a Tool-Using AI Agent](https://www.dotnet-guide.com/tutorials/dotnet-ai/agent-framework-incident-triage/) |
| [`dotnet-aspire/orchestrate-distributed-system`](dotnet-aspire/orchestrate-distributed-system/) | Minimal Aspire AppHost coordinating a web project and API with service discovery and startup ordering | [Aspire in .NET: Orchestrate, Run, and Deploy a Distributed System from One App Host](https://www.dotnet-guide.com/tutorials/dotnet-aspire/orchestrate-distributed-system/) |
| [`software-architecture/architecture-testing-dotnet`](software-architecture/architecture-testing-dotnet/) | Minimal NetArchTest.eNhancedEdition rule that prevents Domain from depending on outer layers | [Architecture Testing in .NET: Enforce Layer and Module Boundaries with NetArchTest and ArchUnitNET](https://www.dotnet-guide.com/tutorials/software-architecture/architecture-testing-dotnet/) |
| [`distributed-systems/transactional-outbox-ef-core`](distributed-systems/transactional-outbox-ef-core/) | Minimal EF Core and SQLite demonstration that saves business state and an outbox message atomically, then publishes it through a one-shot relay | [Transactional Outbox Pattern in .NET with EF Core (.NET 10): Fix the Dual-Write Problem](https://www.dotnet-guide.com/tutorials/distributed-systems/transactional-outbox-ef-core/) |
Expand Down Expand Up @@ -68,6 +69,14 @@ dotnet restore
dotnet run
```

### Agent Framework incident triage

```powershell
cd dotnet-ai\agent-framework-incident-triage
dotnet restore
dotnet test
```

## Repository structure

```text
Expand All @@ -81,6 +90,14 @@ tutorials/
| |-- HybridSearchMinimal.csproj
| |-- Program.cs
| `-- README.md
| `-- agent-framework-incident-triage/
| |-- AgentFrameworkIncidentTriage.slnx
| |-- README.md
| |-- global.json
| |-- verified-environment.json
| |-- docs/
| |-- src/
| `-- tests/
|-- dotnet-aspire/
| `-- orchestrate-distributed-system/
| |-- AspireOrchestrationMinimal.slnx
Expand Down
87 changes: 87 additions & 0 deletions dotnet-ai/agent-framework-incident-triage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# ============================================================
# .NET build output
# ============================================================

bin/
obj/

# Test output
TestResults/
*.trx

# ============================================================
# Visual Studio / IDE files
# ============================================================

.vs/
.vscode/
.idea/

*.user
*.suo
*.userosscache
*.sln.docstates

# ============================================================
# Build / package artifacts
# ============================================================

artifacts/
packages/

*.nupkg
*.snupkg

# ============================================================
# Local environment files and secrets
# ============================================================

.env
.env.*
!.env.example

# Local secret/config variants
appsettings.Local.json
appsettings.local.json
appsettings.Development.local.json

# ============================================================
# API keys / secret files
# ============================================================

secrets.json
*.secrets.json
credentials.json
*.credentials.json

# ============================================================
# OS-generated files
# ============================================================

.DS_Store
Thumbs.db
Desktop.ini

# ============================================================
# Temporary/editor files
# ============================================================

*.tmp
*.temp
*.bak
*.swp
*~

# ============================================================
# Logs
# ============================================================

*.log
logs/

# ============================================================
# Local live-run captures
# ============================================================

live-output/
live-run.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/IncidentTriageAgent/IncidentTriageAgent.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/IncidentTriageAgent.Tests/IncidentTriageAgent.Tests.csproj" />
</Folder>
</Solution>
149 changes: 149 additions & 0 deletions dotnet-ai/agent-framework-incident-triage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Microsoft Agent Framework in .NET &mdash; Evidence-First Incident Triage Agent

Full tutorial: [Microsoft Agent Framework Tutorial in C#: Build a Tool-Using AI Agent](https://www.dotnet-guide.com/tutorials/dotnet-ai/agent-framework-incident-triage/)

A minimal .NET 10 console companion sample for the Microsoft Agent Framework tutorial. It builds a tool-using AI agent that triages a sample incident using deterministic operational evidence, read-only function tools, and a shared agent session &mdash; with no live model required to build or test it.

> This sample is an independent educational reference and is not an official Microsoft sample or specification.

## What this sample demonstrates

- **`IChatClient` provider boundary** &mdash; provider-specific OpenRouter configuration stays in `Program.cs`; everything else works against the neutral `IChatClient` abstraction.
- **`AIAgent`** &mdash; agent creation, instructions, and tool registration are centralized in a factory.
- **`AgentSession`** &mdash; one session is created for the incident and reused for the same-session follow-up question.
- **Streaming** &mdash; model responses are produced with `RunStreamingAsync` and consumed as `AgentResponseUpdate`.
- **Three read-only function tools** &mdash; `get_service_health`, `get_recent_deployment`, and `get_runbook`, created with `AIFunctionFactory.Create`.
- **Deterministic evidence** &mdash; observed operational facts are loaded from embedded JSON (`services.json`, `deployments.json`).
- **Separate runbook guidance** &mdash; reference investigation guidance is loaded independently from evidence (`runbooks.json`).
- **Missing-field behavior** &mdash; absent evidence stays unknown; the agent is instructed never to invent missing facts.
- **Correlation vs causation** &mdash; the sample shows that temporal correlation does not establish root cause.
- **Deterministic tests without an API key** &mdash; the evidence, guidance, and tool layers are fully unit-tested offline.

## Architecture

```text
Program.cs → IChatClient → TriageAgentFactory → AIAgent → AgentSession
│
┌───────────────┬───────────────┬────────┘
▼ ▼ ▼
ServiceHealthTool DeploymentTool RunbookTool
│ │ │
EvidenceStore EvidenceStore RunbookStore
│ │ │
services.json deployments.json runbooks.json
```

See [docs/architecture.md](docs/architecture.md) for the full design discussion.

## Project structure

```text
AgentFrameworkIncidentTriage.slnx
global.json
verified-environment.json
docs/
├── architecture.md
├── evidence-first-contract.md
├── sample-run.md
├── tool-risk-ladder.md
└── verified-environment.md
src/
└── IncidentTriageAgent/
├── Program.cs
├── Agents/TriageAgentFactory.cs
├── Data/ (embedded JSON evidence + runbooks)
├── Evidence/ (EvidenceItem, EvidenceStore)
├── Guidance/ (RunbookItem, RunbookStore)
└── Tools/ (ServiceHealthTool, DeploymentTool, RunbookTool)
tests/
└── IncidentTriageAgent.Tests/
├── EvidenceStoreTests.cs
├── RunbookStoreTests.cs
└── ToolTests.cs
```

## Prerequisites

- .NET 10 SDK
- An API key is required **only** for live execution. Building and testing require no API key and no internet connection to a model provider.

## Restore, build, and test

```bash
dotnet restore AgentFrameworkIncidentTriage.slnx
dotnet build AgentFrameworkIncidentTriage.slnx --configuration Release --no-restore
dotnet test AgentFrameworkIncidentTriage.slnx --configuration Release --no-build
```

Expected: build with 0 errors, and **14/14 deterministic tests passing**.

## Run without an API key

With no `OPENROUTER_API_KEY` set, the application prints a short notice and exits safely &mdash; it makes no live model call:

```bash
dotnet run --project src/IncidentTriageAgent
```

```
OPENROUTER_API_KEY is not configured.

The project can build and test without an API key.
Run 'dotnet test' to verify the deterministic evidence and tool layers.

Set OPENROUTER_API_KEY only when you want to run the live agent.
```

## Run the live sample

The live path uses OpenRouter's OpenAI-compatible endpoint with model `openai/gpt-4o-mini` (override with `OPENROUTER_MODEL`).

PowerShell:

```powershell
$env:OPENROUTER_API_KEY="YOUR_KEY"
dotnet run --project src/IncidentTriageAgent
```

Bash:

```bash
export OPENROUTER_API_KEY="YOUR_KEY"
dotnet run --project src/IncidentTriageAgent
```

`.env` files are not automatically loaded. Replace `YOUR_KEY` with a real key only when you intend to make a live model request &mdash; never commit it.

## Evidence-first behavior

The agent retrieves operational evidence (for example `E-101`, `E-201`) and separate runbook guidance (`RB-CHECKOUT-03`), and distinguishes what is *known* from what is *inferred*. Missing fields remain unknown, guidance is not evidence, and correlation is not treated as proof of causation. This is the repository-specific **Evidence-First Agent Contract** &mdash; see [docs/evidence-first-contract.md](docs/evidence-first-contract.md).

## Tool risk boundary

All three tools in this sample are **read-only** Level 0 / Level 1 tools: they query deterministic stores and perform no operational write. The agent can recommend investigation steps but cannot restart services, roll back deployments, or modify configuration. See [docs/tool-risk-ladder.md](docs/tool-risk-ladder.md).

## Verified sample run

A controlled live run was captured while the deterministic tests were passing &mdash; see [docs/sample-run.md](docs/sample-run.md). Note that LLM wording is probabilistic and may vary between runs; the deterministic layer is stable and fully tested.

## Important boundary / deliberate exclusions

This first Agent Framework sample deliberately does **not** implement: MCP, RAG, vector search, persistent long-term memory, multi-agent workflows, automatic remediation, write-capable tools, service restart/rollback tools, configuration mutation, human approval workflows, durable orchestration, production hosting, A2A, or AG-UI. The tutorial focuses on provider abstraction, tool calling, session state, deterministic evidence, and evidence-aware reasoning. See [docs/architecture.md](docs/architecture.md) and the full tutorial for the follow-on topics.

## Verification table

| Item | Value |
| --- | --- |
| Target framework | net10.0 |
| SDK baseline | 10.0.401 |
| Microsoft.Agents.AI.OpenAI | 1.22.0 |
| Deterministic tests | 14/14 passing |
| Live provider used for verification | OpenRouter |
| Live model used for verification | openai/gpt-4o-mini |
| Live run verification date | 26 September 2026 |

Environment details: [docs/verified-environment.md](docs/verified-environment.md) &middot; [verified-environment.json](verified-environment.json)

## License

Repository-owned sample code is available under the [MIT License](../../LICENSE).
Loading
Loading