内容
# .NET 挑战:使用 MCP 构建智能日志分析代理
[]()
[]()
[]()
[]()
> 使用 .NET 10 构建 MCP 服务器和 AI 代理客户端。代理必须选择适当的工具,检索服务日志,并以清晰易懂的语言报告生产环境错误的根本原因。
---
## 挑战结果
最终评估报告可在中国和波斯语版本中找到。
---
## 目录
- [挑战结果](#挑战结果)
- [先决条件](#先决条件)
- [问题描述](#问题描述)
- [预期架构](#预期架构)
- [第一部分:MCP 服务器](#第一部分:mcp-服务器)
- [第二部分:AI 代理客户端](#第二部分:ai-代理客户端)
- [语言模型策略](#语言模型策略)
- [规则和约束](#规则和约束)
- [验收标准](#验收标准)
- [核心场景](#核心场景)
- [示例输入和输出](#示例输入和输出)
- [建议的项目结构](#建议的项目结构)
- [如何运行和测试](#如何运行和测试)
- [如何提交](#如何提交)
- [评估标准](#评估标准)
- [时间线](#时间线)
- [联系方式](#联系方式)
---
## 先决条件
- 编程语言:**C#**
- 平台:**.NET 10**
- 应用程序类型:
- MCP 服务器
- 代理客户端的控制台应用程序
- 允许使用**官方 MCP SDK**。
- 推荐传输:**Stdio**
- 不需要连接到真正的语言模型。
- 操作系统:Windows、Linux 或 macOS
---
## 问题描述
我们的生产环境中的一个服务已失败,用户在使用它时收到 HTTP 500 错误。
本挑战的目标是构建一个智能日志分析代理,可以像高级 DevOps 工程师一样工作:
1. 从用户接收问题。
2. 确定哪种工具是回答问题的必要条件。
3. 从用户的请求中提取适当的工具参数。
4. 通过 MCP 协议连接到服务器。
5. 检索所需的日志。
6. 确定错误的根本原因。
7. 以简单、清晰、易懂的语言向用户报告结果。
要完成挑战,您必须使用 .NET 10 实现两个主要组件:
- **MCP 服务器**
- **AI 代理客户端**
---
## 预期架构
```text
用户问题
│
▼
AI 代理客户端
│
├── 代理 / 模型抽象
│ ├── 模拟或伪造模型
│ └── 真实 LLM 提供商(可选)
│
▼
工具选择和参数提取
│
▼
MCP 客户端
│
▼
MCP 服务器
│
▼
获取日志工具
│
▼
模拟 Elasticsearch 日志
```
预期执行流程:
```text
控制台应用程序
→ 代理
→ 工具选择
→ MCP 客户端
→ MCP 服务器
→ 获取日志
→ 日志分析
→ 最终报告
```
不允许直接从控制台应用程序调用 `GetLogs` 方法,而不经过代理和 MCP 层。
---
## 第一部分:MCP 服务器
创建一个 .NET 应用程序,作为 MCP 服务器,并公开以下工具供代理使用。
### `获取日志` 工具
#### 工具名称
```text
获取日志
```
#### 建议的工具描述
```text
返回指定服务在请求时间范围内的应用程序日志。
```
#### 输入
| 参数 | 类型 | 必需 | 描述 |
|---|---:|:---:|---|
| `服务名称` | `字符串` | 是 | 必须检查日志的服务名称 |
| `分钟前` | `整数` | 是 | 搜索日志的分钟数 |
示例输入:
```json
{
"服务名称": "支付服务",
"分钟前": 15
}
```
#### 预期输出
工具必须返回包含至少以下错误的模拟 Elasticsearch 数据:
```text
[错误] - 支付服务 - 数据库连接超时,30000ms 后在 SQL-Server-01
```
输出可以是纯文本或结构化模型,但必须包含以下信息:
- 日志级别:`错误`
- 服务名称:`支付服务`
- 错误类型:`数据库连接超时`
- 超时持续时间:`30000ms`
- 目标服务器:`SQL-Server-01`
### MCP 服务器要求
- `获取日志` 工具必须可通过 MCP 发现和调用。
- 允许使用官方 MCP SDK。
- 通信应优选使用 `stdio`。
- 无效输入必须在不使应用程序崩溃的情况下处理。
- 当 `服务名称` 为空或 `分钟前` 无效时,必须返回可理解的错误。
- 不需要真正的 Elasticsearch 连接。
- 日志数据可以存储在内存中、文件中或模拟提供程序后面。
- 模拟数据生成逻辑应优选放在 `ILogProvider` 等抽象后面。
---
## 第二部分:AI 代理客户端
使用 .NET 10 创建一个控制台应用程序,作为代理客户端。
用户提出以下问题:
```text
为什么支付服务在过去 15 分钟内一直返回 HTTP 500 错误?
```
代理必须分析请求并确定应使用以下参数调用 `获取日志` 工具:
```json
{
"名称": "获取日志",
"参数": {
"服务名称": "支付服务",
"分钟前": 15
}
}
```
在从 MCP 服务器接收日志后,代理必须提取根本原因并生成易懂的报告。
### 代理客户端职责
- 接收用户的问题
- 访问 MCP 服务器的可用工具列表
- 选择适当的工具
- 从用户请求中提取所需参数
- 创建工具调用
- 通过 MCP 调用工具
- 接收工具结果
- 分析日志
- 生成最终响应
- 处理通信错误和不完整的输入
---
## 语言模型策略
不需要连接到像以下这样的真实语言模型:
- OpenAI
- Azure OpenAI
- Ollama
- GitHub 模型
- 任何其他提供商
参与者可以使用 `Fake` 或 `Mock 模型` 以保持项目离线、可测试且不受强制外部依赖的约束。
然而,以下所有条件都是必需的:
1. 模型或代理提供程序必须放置在接口或其他合适的抽象后面。
2. 用真实模型替换模拟实现不需要更改应用程序的核心代理逻辑。
3. 代理的输出必须代表工具调用。
4. 代理必须负责选择工具并生成其参数。
5. 控制台应用程序不得直接决定使用固定值调用 `获取日志`。
6. 最终答案不得作为预定义的常量字符串存储在代码中。
建议的抽象:
```csharp
public interface IAgentModel
{
Task<AgentDecision> DecideAsync(
string userMessage,
IReadOnlyCollection<AvailableTool> tools,
CancellationToken cancellationToken);
}
```
接口和模型名称是可选的。参与者可以使用不同的设计,只要它满足要求。
### 可接受的实现
- 连接到真正的 LLM 并使用工具调用
- 使用生成标准工具调用的模拟模型
- 使用通用、扩展的解析器分析请求并创建工具调用
- 使用配置或元数据将人类可读的服务名称映射到其技术服务标识符
### 不可接受的实现
以下示例被视为硬编码:
```csharp
if (userMessage.Contains("支付"))
{
await GetLogs("支付服务", 15);
}
```
以下方法也是不可接受的:
- 直接从控制台应用程序调用 `获取日志`
- 将完整的工具调用作为固定值存储以适应单个示例问题
- 将最终响应作为代码中的固定字符串存储
- 绕过 MCP 直接调用 MCP 服务器类
- 将整个应用程序耦合到一个提供程序而没有替换机制
---
## 规则和约束
- 项目必须使用 **.NET 10** 实现。
- 允许使用官方 MCP SDK。
- 使用真实 LLM 是可选的。
- 项目必须可在无需 API 密钥的情况下审查和运行。
- 禁止在存储库中提交机密或 API 密钥。
- Elasticsearch 数据必须模拟,不需要真实连接。
- 代理和工具之间的通信必须通过 MCP 进行。
- 禁止硬编码工具调用或最终答案。
- 应用程序必须在不崩溃的情况下处理无效输入和通信错误。
- 所有项目必须使用标准 `dotnet build` 命令生成。
- 命名、内部结构和实现细节是开放的,但前提是满足所有挑战要求。
---
## 验收标准
只有当满足以下所有条件时,解决方案才被视为完整:
- [ ] 解决方案使用 .NET 10 成功构建。
- [ ] MCP 服务器可以启动。
- [ ] `获取日志` 工具由 MCP 服务器注册并可发现。
- [ ] 工具接受 `服务名称` 和 `分钟前` 参数。
- [ ] 代理客户端通过 MCP 连接到服务器。
- [ ] 代理为示例问题选择 `获取日志`。
- [ ] 代理提取 `支付服务` 和 `15`。
- [ ] 工具调用在日志或调试输出中可见。
- [ ] MCP 服务器返回预期的模拟日志。
- [ ] 代理识别数据库超时作为根本原因。
- [ ] 最终响应引用 `SQL-Server-01` 和 30 秒超时。
- [ ] 工具调用不在控制台应用程序中直接硬编码。
- [ ] 最终答案不作为代码中的固定字符串存储。
- [ ] 模拟/伪造模型可通过抽象替换。
- [ ] 不完整或无效输入无崩溃处理。
- [ ] 解决方案 README 解释架构和项目运行方式。
---
## 核心场景
### 场景 1:支付服务错误分析成功
**输入:**
```text
为什么支付服务在过去 15 分钟内一直返回 HTTP 500 错误?
```
**预期行为:
1. 代理选择 `获取日志` 工具。
2. 生成以下参数:
```json
{
"服务名称": "支付服务",
"分钟前": 15
}
```
3. 通过 MCP 调用工具。
4. 接收超时错误日志。
5. 在最终响应中解释根本原因。
---
### 场景 2:不完整的请求
**示例输入:
```text
为什么支付服务返回错误?
```
缺少此请求的 time 范围。
**可接受的行为:
- 代理要求用户提供时间范围;或
- 代理使用有文档记录的默认值并清楚地说明该值。
无声选择未记录的固定值是不可接受的。
---
### 场景 3:无效工具输入
示例:
```json
{
"服务名称": "",
"分钟前": 0
}
```
**预期行为:
- MCP 服务器不得崩溃。
- 它必须返回一个易于理解的结构化错误。
---
### 场景 4:MCP 服务器不可用
**预期行为:
- 代理客户端处理连接失败。
- 不得显示原始技术错误或堆栈跟踪作为唯一的面向用户的响应。
- 代理必须清楚地解释为什么无法完成调查。
---
## 示例输入和输出
### 用户输入
```text
为什么支付服务在过去 15 分钟内一直返回 HTTP 500 错误?
```
### 代理决策
```json
{
"名称": "获取日志",
"参数": {
"服务名称": "支付服务",
"分钟前": 15
}
}
```
### MCP 工具输出
```text
[错误] - 支付服务 - 数据库连接超时,30000ms 后在 SQL-Server-01
```
### 示例最终响应
```text
过去 15 分钟的日志显示,支付服务一直返回 HTTP 500 错误,因为它与 SQL-Server-01 数据库的连接超时。数据库服务器在 30 秒内没有响应。
```
措辞可能有所不同,但响应必须识别并清楚地解释正确的根本原因。
---
## 建议的项目结构
以下结构仅为建议:
```text
Dotin.DotNetForum.LogAgent/
├── src/
│ ├── Dotin.DotNetForum.LogAgent.McpServer/
│ │ ├── 工具/
│ │ ├── 服务/
│ │ └── Program.cs
│ │
│ ├── Dotin.DotNetForum.LogAgent.AgentClient/
│ │ ├── 代理/
│ │ ├── 模型/
│ │ ├── MCP/
│ │ └── Program.cs
│ │
│ └── Dotin.DotNetForum.LogAgent.Contracts/
│
├── tests/
│ ├── Dotin.DotNetForum.LogAgent.McpServer.Tests/
│ └── Dotin.DotNetForum.LogAgent.AgentClient.Tests/
│
├── README.md
└── Dotin.DotNetForum.LogAgent.slnx
```
允许使用 `.sln` 和 `.slnx` 文件。
---
## 如何运行和测试
### 克隆存储库
```bash
git clone https://github.com/<organization>/<repository>.git
cd <repository>
```
### 检查 .NET 版本
```bash
dotnet --version
```
预期版本:
```text
10.x
```
### 恢复并构建
```bash
dotnet restore
dotnet build
```
### 运行代理客户端
参与者必须在解决方案 README 中记录确切的执行命令。
示例:
```bash
dotnet run --project src/Dotin.DotNetForum.LogAgent.AgentClient
```
代理客户端可以通过 `stdio` 启动 MCP 服务器作为子进程。
示例服务器命令:
```bash
dotnet run --project src/Dotin.DotNetForum.LogAgent.McpServer
```
### 运行测试
```bash
dotnet test
```
建议的最低测试覆盖率:
- `获取日志` 输入验证
- `支付服务` 的正确日志输出
- 代理的正确工具选择
- `分钟前` 的提取
- 正确超时错误分析
- 处理不可用的 MCP 服务器
- 验证模型提供程序的可替换性
---
## 如何提交
1. 分叉挑战存储库。
2. 创建新分支:
```bash
git checkout -b solution/<github-username>
```
3. 在以下位置添加您的解决方案:
```text
solutions/csharp/<github-username>/
├── src/
├── tests/
├── README.md
└── Dotin.DotNetForum.LogAgent.slnx
```
示例:
```text
solutions/csharp/<github-username>/
├── src/
├── tests/
├── README.md
└── Dotin.DotNetForum.LogAgent.slnx
```
4. 提交并推送您的更改。
5. 打开一个拉取请求,标题如下:
```text
[解决方案] MCP 日志分析代理 - <github-username>
```
### 解决方案 README 必须包括
- 架构描述
- 运行 MCP 服务器和代理客户端的说明
- 运行测试的说明
- 使用的代理提供程序类型
- 关于如何用真实 LLM 替换模拟实现的说明
- 限制和假设
- 示例执行输出
---
## 评估标准
总分:**100 分**
| 标准 | 分数 |
|---|---:|
| 功能正确性和主要场景完成度 | 30 |
| MCP 服务器和 `获取日志` 工具实现 | 20 |
| 代理设计、工具选择和防止硬编码 | 20 |
| 架构、代码质量和可测试性 | 15 |
| 测试、验证和错误处理 | 10 |
| 文档和提交质量 | 5 |
| **总计** | **100** |
### 1. 功能正确性和主要场景完成度 — 30 分
- 用户问题场景的完整执行
- 服务名称的正确提取
- 时间范围的正确提取
- 从 MCP 服务器检索日志
- 正确识别根本原因
- 清晰易懂的最终报告
### 2. MCP 服务器和 `获取日志` 工具实现 — 20 分
- 正确的工具注册
- 适当的工具名称、描述和参数定义
- 通过 MCP 的实际通信
- 正确使用 `stdio` 或另一个有效传输
- 输入验证
- 正确的日志提供程序与 MCP 工具的分离
### 3. 代理设计、工具选择和防止硬编码 — 20 分
- 代理或模型抽象的存在
- 代理层内的工具选择
- 工具调用生成
- 替换模拟实现与真实 LLM 的能力
- 控制台应用程序不直接调用工具
- 没有硬编码的最终响应或最终参数集
> 连接到真实的 LLM 不会提供单独的奖励积分。正确的设计模型可以获得本节的满分。
### 4. 架构、代码质量和可测试性 — 15 分
- 关注点分离
- 适当的命名
- 可读且简单的代码
- 依赖注入的正确使用
- 正确使用 `async/await`
- 在适当的情况下使用取消令牌
- 组件的可扩展性和可替换性
### 5. 测试、验证和错误处理 — 10 分
- 有效的单元测试或集成测试
- 工具调用测试
- 无效输入测试
- 日志分析测试
- 服务器连接失败处理
- 不向最终用户显示原始堆栈跟踪
### 6. 文档和提交质量 — 5 分
- 完整的 README
- 正确的运行命令
- 架构解释
- 记录的假设
- 正确的目录结构
- 清洁且可审查的拉取请求
### 影响硬编码对评估的影响
- 控制台应用程序直接调用 `获取日志` 将使代理部分得分零分。
- 绕过 MCP 意味着主要场景被认为是不完整的。
- 固定的最终响应将不会获得日志分析和最终报告的分数。
- 为示例问题创建的固定参数将不会获得参数提取的分数
---
## 时间线
- **挑战开始日期:** 2026 年 6 月 21 日 - 2026 年 6 月 28 日
拉取请求创建时间用作官方提交时间。
---
## 联系方式
使用以下渠道提问或报告歧义:
- 挑战存储库中的 GitHub 问题
- .NET 社区小组