1 Agent Skill中的执行脚本根据上一篇我们知道Agent Skill就是大模型随时翻阅的说明文档它还可以包含一些资源文件而脚本就是其中的一种重要资源。skill/ ├── SKILL.md # 必需指令 元数据 ├── scripts/ # 可选可执行代码 ├── references/ # 可选文档资料 └── assets/ # 可选模板、资源文件scripts子目录这些脚本会被放在其所属skill目录下的scripts子目录下不管它是BASH脚本PowerShell脚本又或者是Python/C#代码只要是Agent所在客户/服务端能够执行的都可以。总结Skills不仅让Agent知道处理规则SKILL.md references还能让Agent直接上手处理scripts。工具 vs 技能工具Tool是一种能力而技能Skill是一种知识。比如自己开发一个PowerShellTools给模型提供一些run shell的能力我们通过注册到 ChatOptions.Tools 完成加载。又如自己定义一个系统运维Skill给模型提供一些系统运维的知识我们通过注册到AIContextProviders完成加载。知识指导行为而非工具限定行为。2 快速开始MAF中集成Shell执行能力假设我们有这样一个需求某企业开发了一个IT助手Agent它运行在每个员工的笔记本电脑上可以回答用户关于系统方面的一些问题并加以分析给出建议。例如很多员工发现自己的笔记本电脑最近越来越慢了又或者发现内存一直高负载 等等在以前员工会直接联系IT部的某位员工而现在IT部将自身的运维经验封装为了一个Skill并通过Agent直接回复员工。以终为始我们的Agent应用的解决方案长这样子这是一个.NET控制台应用程序其中有一个skills目录存放了系统运维领域的知识这是IT部呕心沥血整理的“宝典”。系统运维Skill首先我们创建这个SKILL.md内容如下--- name: system-ops description: 系统运维诊断技能。适用于系统健康检查、磁盘空间分析、进程资源监控、故障排查等系统运维场景。包含可执行的诊断脚本。 --- # 系统运维System Operations ## 可用诊断脚本 以下脚本位于本技能的 scripts/ 目录可通过 run_shell 工具执行 | 脚本 | 用途 | 执行命令 | |------|------|--------| | check-system-info.ps1 | 获取系统基本信息OS、CPU、内存 | | check-disk-usage.ps1 | 检查磁盘使用情况和剩余空间 | | check-top-processes.ps1 | 查看 CPU/内存占用 Top 进程 | ## 运维检查流程 1. **基础检查**先执行 check-system-info.ps1 获取系统概况 2. **针对性诊断**根据用户问题选择性执行磁盘或进程检查脚本 3. **分析报告**综合脚本输出和故障排查指南给出诊断结论和建议 4. **故障排查**如需深入排查参考 [references/troubleshooting-guide.md](references/troubleshooting-guide.md) ## 告警阈值 | 指标 | 正常 | 警告 | 严重 | |------|------|------|------| | CPU 使用率 | 70% | 70-90% | 90% | | 内存使用率 | 80% | 80-95% | 95% | | 磁盘使用率 | 70% | 70-90% | 90% | | 单进程 CPU | 30% | 30-60% | 60% |由于该Skill还定义了引用的资源所以我们还需要添加1参考文件故障自查指南说明这是一个markdown文件。# 故障排查指南 ## CPU 持续高负载 1. 通过 check-top-processes.ps1 识别高占用进程 2. 判断是否为预期行为如编译、数据处理 3. 如为异常进程建议 - 记录进程信息PID、启动时间、命令行 - 联系应用负责人确认 - 必要时可终止进程Stop-Process -Id PID ## 内存不足 1. 检查物理内存使用率通过 check-system-info.ps1 2. 识别内存大户通过 check-top-processes.ps1 3. 常见原因 - 内存泄漏进程内存持续增长 - 缓存过多数据库或应用缓存未限制 4. 缓解措施 - 重启内存泄漏进程 - 调整应用缓存配置 - 考虑扩容 ## 磁盘空间不足 1. 通过 check-disk-usage.ps1 确认各盘使用率 2. 清理建议按优先级 - 清理临时文件 - 清理日志文件保留近 7 天 - 清理 NuGet 包缓存dotnet nuget locals all --clear 3. 长期方案配置日志轮转、扩容磁盘2脚本文件PowerShell脚本说明这里主要在Windows系统中执行脚本所以选择了PowerShell。脚本1获取系统基本信息# check-system-info.ps1 — 获取系统基本信息 Write-Host 系统基本信息 Write-Host 计算机名: $env:COMPUTERNAME Write-Host 操作系统: $([System.Runtime.InteropServices.RuntimeInformation]::OSDescription) Write-Host 处理器架构: $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture) Write-Host 逻辑处理器数: $([Environment]::ProcessorCount) $os Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue if ($os) { $totalMemGB [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) $freeMemGB [math]::Round($os.FreePhysicalMemory / 1MB, 2) $usedMemGB [math]::Round($totalMemGB - $freeMemGB, 2) $memUsagePercent [math]::Round(($usedMemGB / $totalMemGB) * 100, 1) Write-Host Write-Host 内存信息 Write-Host 总内存: ${totalMemGB} GB Write-Host 已使用: ${usedMemGB} GB ($memUsagePercent%) Write-Host 可用: ${freeMemGB} GB } Write-Host Write-Host 系统运行时间 $uptime (Get-Date) - (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime Write-Host 已运行: $($uptime.Days) 天 $($uptime.Hours) 小时 $($uptime.Minutes) 分钟脚本2检查系统磁盘使用信息# check-disk-usage.ps1 — 检查磁盘使用情况 Write-Host 磁盘使用情况 Get-CimInstance -ClassName Win32_LogicalDisk -Filter DriveType3 | ForEach-Object { $totalGB [math]::Round($_.Size / 1GB, 2) $freeGB [math]::Round($_.FreeSpace / 1GB, 2) $usedGB [math]::Round($totalGB - $freeGB, 2) $usagePercent if ($totalGB -gt 0) { [math]::Round(($usedGB / $totalGB) * 100, 1) } else { 0 } $status if ($usagePercent -gt 90) { 严重 } elseif ($usagePercent -gt 70) { 警告 } else { 正常 } Write-Host Write-Host 驱动器 $($_.DeviceID) Write-Host 总容量: ${totalGB} GB Write-Host 已使用: ${usedGB} GB ($usagePercent%) Write-Host 可用: ${freeGB} GB Write-Host 状态: $status }脚本3查看资源占用Top10信息# check-top-processes.ps1 — 查看资源占用 Top 进程 param( [int]$Top 10 ) Write-Host CPU 占用 Top $Top 进程 Get-Process | Sort-Object CPU -Descending | Select-Object -First $Top | Format-Table -Property {N进程名;E{$_.ProcessName}}, {NPID;E{$_.Id}}, {NCPU(s);E{[math]::Round($_.CPU, 2)}}, {N内存(MB);E{[math]::Round($_.WorkingSet64/1MB, 1)}} -AutoSize | Out-String | Write-Host Write-Host Write-Host 内存占用 Top $Top 进程 Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First $Top | Format-Table -Property {N进程名;E{$_.ProcessName}}, {NPID;E{$_.Id}}, {N内存(MB);E{[math]::Round($_.WorkingSet64/1MB, 1)}}, {NCPU(s);E{[math]::Round($_.CPU, 2)}} -AutoSize | Out-String | Write-Host自定义ShellTools最近MAF发布了1.0.0-rc2支持了Agent Skill但其尚未开放原生的脚本执行能力虽然在GitHub的demo中我已经看到了脚本执行的demo。但貌似已经revert了因此我们再静静等候一阵。这里我们通过自定义一个ShellTools来实现PowerShell的脚本执行能力public class ShellTools { // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // run_shell — 一个 Shell 工具做一切含安全护栏 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ️ 安全护栏 1危险命令黑名单 private static string[] dangerousPatterns [ rm -rf /, rm -rf /*, // 删除根目录 sudo , // 提权 shutdown, reboot, // 系统操作 /dev/, // 设备写入 :(){ :|: };:, // Fork bomb mkfs., // 格式化 dd if, // 磁盘覆写 format , // Windows 格式化 del /f /s /q, // Windows 递归删除 ]; [Description(执行 Shell 命令。通过操作系统原生 Shell 执行命令Windows 用 cmdLinux/Mac 用 bash。包含安全护栏危险命令阻止、输出截断50KB、超时控制60秒。)] public static string RunShell( [Description(要执行的 Shell 命令。例如pwsh -File /path/to/script.ps1 或 dir)] string command, [Description(命令执行的工作目录可选。如果不指定使用当前目录。)] string? workingDirectory null) { try { // ️ 安全护栏 1危险命令检查 if (dangerousPatterns.Any(d command.Contains(d, StringComparison.OrdinalIgnoreCase))) { return ❌ 安全拦截检测到危险命令已阻止执行。; } // 跨平台 Shell 适配 // Windows → cmd /c command 原生命令提示符 // Linux/Mac → bash -c command 原生 Bash // // 为什么不直接用 pwsh // SKILL.md 中的命令已经是 pwsh -File ... // 如果 RunShell 也用 pwsh -Command 包裹 → pwsh 套 pwsh冗余嵌套 // 用原生 shell 分发 → pwsh -File 直接执行零嵌套 var isWindows OperatingSystem.IsWindows(); var processInfo new ProcessStartInfo { FileName isWindows ? cmd : bash, Arguments isWindows ? $/c {command} : $-c \{command.Replace(\, \\\)}\, RedirectStandardOutput true, RedirectStandardError true, UseShellExecute false, CreateNoWindow true }; // 设置工作目录 if (!string.IsNullOrWhiteSpace(workingDirectory) Directory.Exists(workingDirectory)) { processInfo.WorkingDirectory workingDirectory; } using var process Process.Start(processInfo); if (process null) { return ❌ 无法启动 Shell 进程; } var stdout process.StandardOutput.ReadToEnd(); var stderr process.StandardError.ReadToEnd(); // ️ 安全护栏 3超时控制60秒 if (!process.WaitForExit(60_000)) { process.Kill(entireProcessTree: true); return ❌ 命令执行超时60秒已强制终止。; } var result new StringBuilder(); if (!string.IsNullOrWhiteSpace(stdout)) { result.AppendLine(stdout.Trim()); } if (!string.IsNullOrWhiteSpace(stderr)) { result.AppendLine($⚠️ stderr: {stderr.Trim()}); } if (process.ExitCode ! 0) { result.AppendLine($⚠️ 退出码: {process.ExitCode}); } var output result.Length 0 ? result.ToString() : (命令执行成功无输出); // ️ 安全护栏 2输出截断50KB const int maxOutputLength 50_000; if (output.Length maxOutputLength) { output output[..maxOutputLength] \n... (输出已截断超过 50KB 上限); } return output; } catch (Exception ex) { return $❌ 执行失败: {ex.Message}; } } }这个ShellTools还考虑了必要的安全性对于一些危险命令会主动进行阻止还对超时60s的操作进行了命令挂起。智能体调用Agent Skill这里让我们一步一步来实现在MAF中让智能体调用集成脚本执行的Skill。1创建SkillsProvider从文件中发现和加载Skillsvar skillsProvider new FileAgentSkillsProvider( skillPath: Path.Combine(Directory.GetCurrentDirectory(), skills), options: new FileAgentSkillsProviderOptions { // 自定义提示词引导模型加载技能后使用 run_shell 执行脚本 SkillsInstructionPrompt 你可以使用以下技能获取领域知识和操作指引。 每个技能提供专业指令、参考文档和可执行脚本。 available_skills {0} /available_skills 工作流程 1. 当用户任务匹配技能描述时使用 load_skill 加载该技能的完整指令 2. 技能指令中会标明可用脚本及其执行命令 3. 使用 run_shell 工具执行技能中标注的命令 4. 需要时使用 read_skill_resource 读取参考资料 重要原则先加载知识再执行操作。 } ); Console.WriteLine(✅ FileAgentSkillsProvider 创建成功知识层); Console.WriteLine( 自动注册工具: load_skill, read_skill_resource);2创建Agent注入SkillsProvider让其有Skill调用能力AIAgent agent chatClient.AsAIAgent(new ChatClientAgentOptions { Name SkillsBashAgent, ChatOptions new() { Instructions 你是一个专业的系统运维助手。请用中文回答所有问题。, // 能力层仅注册一个 run_shell 工具 Tools [AIFunctionFactory.Create(ShellTools.RunShell)], }, // 知识层通过 AIContextProviders 注入 Skills AIContextProviders [skillsProvider], }); Console.WriteLine(✅ 技能 Agent 创建成功);下面我们就来测试一下这个Agent测试用例1用户帮我检查一下当前系统的整体健康状态包括 CPU、内存和磁盘使用情况。Agent 会 1. 识别属于 system-ops 领域 → load_skill(system-ops) 2. 从 SKILL.md 获取脚本执行命令 3. 依次调用 run_shell 执行诊断脚本 4. 根据告警阈值分析结果测试代码如下var session await agent.CreateSessionAsync(); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine( 测试 1系统健康检查); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); var question1 帮我检查一下当前系统的整体健康状态包括 CPU、内存和磁盘使用情况。; Console.WriteLine($ 用户: {question1}); Console.WriteLine(); var response1 await agent.RunAsync(question1, session); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine($ Agent: {response1.Text}); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); Console.WriteLine();测试结果如下图所示测试用例2危险脚本操作命令验证Agent不会执行这些命令。var dangerousTestCases new[] { (rm -rf /, 删除根目录), (sudo apt-get install malware, 提权操作), (shutdown -s -t 0, 关机命令), }; foreach (var (cmd, desc) in dangerousTestCases) { var result ShellTools.RunShell(cmd); Console.WriteLine($❌ 测试: {desc}); Console.WriteLine($ 命令: {cmd}); Console.WriteLine($ 结果: {result}); Console.WriteLine(); } // 测试正常命令 var normalResult ShellTools.RunShell(Get-Date); Console.WriteLine($✅ 测试: 正常命令); Console.WriteLine($ 命令: Get-Date); Console.WriteLine($ 结果: {normalResult.Trim()}); Console.WriteLine(); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(️ 安全护栏验证完成危险命令被正确拦截正常命令正常执行。);测试结果如下图所示