Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/bingbing-gui_dotnet-agent-playbook.json
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
44 KiB
JSON
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "bingbing-gui/dotnet-agent-playbook", "files": {"src/ai-agent/Agent-Framework/30-Agent-Providers-DeepSeek/30-Agent-Providers-DeepSeek/Program.cs": {"a31c73181bf18d800f26815b82723752a0c83cd0": {"t": 1785746188.9513361, "n": 1540, "h": "afc9578bfe4a5c93", "c": "using Anthropic;\nusing Anthropic.Core;\nusing Anthropic.Models.Beta.Sessions;\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing OpenAI.Chat;\nusing OpenAI.Responses;\nusing System.ClientModel;\nusing System.Text;\n\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n\nConsole.WriteLine(\"## 基于 OpenAI 协议接入 DeepSeek V4 Pro\");\n#pragma warning disable OPENAI001\n\nvar client = new OpenAIClient(\n new ApiKeyCredential(\"sk-\"),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(\"https://api.deepseek.com\")\n });\n\nvar chatClient = client.GetChatClient(\"deepseek-v4-pro\");\n\nvar result = await chatClient.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n\nConsole.WriteLine(new string('-', 60));\n\n#region 不支持Response方式\n//AIAgent agent = client\n// .GetResponsesClient()\n// .AsAIAgent(\n// model: \"deepseek-v4-pro\", // ⭐DeepSeek模型名\n// instructions: \"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\",\n// name: \"Joker\"\n// );\n\n// 调用\n//var result = await agent.RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n#endregion\n\n\nConsole.WriteLine(\"## 基于 Anthropic 协议接入 DeepSeek V4 Pro\");\n\n#pragma warning restore OPENAI001\n\nvar client2 = new AnthropicClient(new ClientOptions\n{\n ApiKey = \"sk-\",\n BaseUrl = \"https://api.deepseek.com/anthropic\"\n});\n\nvar agent = client2.AsIChatClient(\"deepseek-v4-pro\");\n\nresult = await agent.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n"}, "9d5fbfd66a054b33d8fc33a0aebdefc3fed7972d": {"t": 1785760456.0630755, "n": 1540, "h": "afc9578bfe4a5c93", "c": "using Anthropic;\nusing Anthropic.Core;\nusing Anthropic.Models.Beta.Sessions;\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing OpenAI.Chat;\nusing OpenAI.Responses;\nusing System.ClientModel;\nusing System.Text;\n\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n\nConsole.WriteLine(\"## 基于 OpenAI 协议接入 DeepSeek V4 Pro\");\n#pragma warning disable OPENAI001\n\nvar client = new OpenAIClient(\n new ApiKeyCredential(\"sk-\"),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(\"https://api.deepseek.com\")\n });\n\nvar chatClient = client.GetChatClient(\"deepseek-v4-pro\");\n\nvar result = await chatClient.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n\nConsole.WriteLine(new string('-', 60));\n\n#region 不支持Response方式\n//AIAgent agent = client\n// .GetResponsesClient()\n// .AsAIAgent(\n// model: \"deepseek-v4-pro\", // ⭐DeepSeek模型名\n// instructions: \"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\",\n// name: \"Joker\"\n// );\n\n// 调用\n//var result = await agent.RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n#endregion\n\n\nConsole.WriteLine(\"## 基于 Anthropic 协议接入 DeepSeek V4 Pro\");\n\n#pragma warning restore OPENAI001\n\nvar client2 = new AnthropicClient(new ClientOptions\n{\n ApiKey = \"sk-\",\n BaseUrl = \"https://api.deepseek.com/anthropic\"\n});\n\nvar agent = client2.AsIChatClient(\"deepseek-v4-pro\");\n\nresult = await agent.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n"}, "80d6eb027a347d1983cc2416e84f086923bc634a": {"t": 1786067625.2949677, "n": 1540, "h": "afc9578bfe4a5c93", "c": "using Anthropic;\nusing Anthropic.Core;\nusing Anthropic.Models.Beta.Sessions;\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing OpenAI.Chat;\nusing OpenAI.Responses;\nusing System.ClientModel;\nusing System.Text;\n\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n\nConsole.WriteLine(\"## 基于 OpenAI 协议接入 DeepSeek V4 Pro\");\n#pragma warning disable OPENAI001\n\nvar client = new OpenAIClient(\n new ApiKeyCredential(\"sk-\"),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(\"https://api.deepseek.com\")\n });\n\nvar chatClient = client.GetChatClient(\"deepseek-v4-pro\");\n\nvar result = await chatClient.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n\nConsole.WriteLine(new string('-', 60));\n\n#region 不支持Response方式\n//AIAgent agent = client\n// .GetResponsesClient()\n// .AsAIAgent(\n// model: \"deepseek-v4-pro\", // ⭐DeepSeek模型名\n// instructions: \"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\",\n// name: \"Joker\"\n// );\n\n// 调用\n//var result = await agent.RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n#endregion\n\n\nConsole.WriteLine(\"## 基于 Anthropic 协议接入 DeepSeek V4 Pro\");\n\n#pragma warning restore OPENAI001\n\nvar client2 = new AnthropicClient(new ClientOptions\n{\n ApiKey = \"sk-\",\n BaseUrl = \"https://api.deepseek.com/anthropic\"\n});\n\nvar agent = client2.AsIChatClient(\"deepseek-v4-pro\");\n\nresult = await agent.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n"}, "4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068387.797448, "n": 1540, "h": "afc9578bfe4a5c93", "c": "using Anthropic;\nusing Anthropic.Core;\nusing Anthropic.Models.Beta.Sessions;\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing OpenAI.Chat;\nusing OpenAI.Responses;\nusing System.ClientModel;\nusing System.Text;\n\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n\nConsole.WriteLine(\"## 基于 OpenAI 协议接入 DeepSeek V4 Pro\");\n#pragma warning disable OPENAI001\n\nvar client = new OpenAIClient(\n new ApiKeyCredential(\"sk-\"),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(\"https://api.deepseek.com\")\n });\n\nvar chatClient = client.GetChatClient(\"deepseek-v4-pro\");\n\nvar result = await chatClient.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n\nConsole.WriteLine(new string('-', 60));\n\n#region 不支持Response方式\n//AIAgent agent = client\n// .GetResponsesClient()\n// .AsAIAgent(\n// model: \"deepseek-v4-pro\", // ⭐DeepSeek模型名\n// instructions: \"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\",\n// name: \"Joker\"\n// );\n\n// 调用\n//var result = await agent.RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n#endregion\n\n\nConsole.WriteLine(\"## 基于 Anthropic 协议接入 DeepSeek V4 Pro\");\n\n#pragma warning restore OPENAI001\n\nvar client2 = new AnthropicClient(new ClientOptions\n{\n ApiKey = \"sk-\",\n BaseUrl = \"https://api.deepseek.com/anthropic\"\n});\n\nvar agent = client2.AsIChatClient(\"deepseek-v4-pro\");\n\nresult = await agent.AsAIAgent().RunAsync(\"你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。\");\n\nConsole.WriteLine(result);\n"}}, "src/ai-agent/Agent-Framework/26-CodeDefined-Agent-Skills/26-CodeDefined-Agent-Skills-DeepSeek/Program.cs": {"4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068321.4621305, "n": 3255, "h": "9bfcd17654cf7d50", "c": "// Copyright (c) Microsoft. All rights reserved.\n\n// 此示例演示如何使用 AgentInlineSkill 完全通过代码定义 Agent Skills。\n// 无需 SKILL.md 文件——技能、资源和脚本都以编程方式定义。\n//\n// 这里使用单位转换技能展示三种方式:\n// 1. 静态资源——通过 AddResource 提供的内联内容\n// 2. 动态资源——通过工厂委托在运行时计算\n// 3. 代码脚本——可由代理直接调用的可执行委托\n\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing System.ClientModel;\nusing System.Text;\nusing System.Text.Json;\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n// --- DeepSeek configuration ---\nvar apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\") ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY 未设置。\");\nvar endpoint = Environment.GetEnvironmentVariable(\"DEEPSEEK_ENDPOINT\") ?? \"https://api.deepseek.com\";\nvar model = Environment.GetEnvironmentVariable(\"DEEPSEEK_MODEL\") ?? \"deepseek-v4-pro\";\n\n// --- 构建代码定义的技能 ---\n#pragma warning disable MAAI001\nvar unitConverterSkill = new AgentInlineSkill(\n name: \"unit-converter\",\n description: \"使用乘法系数在常见单位之间进行转换。当被要求在英里、公里、磅或千克之间转换时使用此技能。\",\n instructions: \"\"\"\n 当用户要求进行单位转换时,使用此技能。\n\n 1. 查看 conversion-table 资源,找到所请求转换对应的系数。\n 2. 查看 conversion-policy 资源,确认舍入和格式化规则。\n 3. 使用 convert 脚本,并传入表格中的数值和系数。\n \"\"\")\n // 1. 静态资源:转换表\n .AddResource(\n \"conversion-table\",\n \"\"\"\n # 转换表\n\n 公式:**result = value × factor**\n\n | 从 | 到 | 系数 |\n |-------------|-------------|----------|\n | 英里 | 公里 | 1.60934 |\n | 公里 | 英里 | 0.621371 |\n | 磅 | 千克 | 0.453592 |\n | 千克 | 磅 | 2.20462 |\n \"\"\")\n // 2. 动态资源:转换策略(运行时计算)\n .AddResource(\"conversion-policy\", () =>\n {\n const int Precision = 4;\n return $\"\"\"\n # 转换策略\n **小数位数:** {Precision}\n **格式:** 始终显示带单位的原始值和转换后值\n **生成时间:** {DateTime.UtcNow:O}\n \"\"\";\n })\n // 3. 代码脚本:convert\n .AddScript(\"convert\", (double value, double factor) =>\n {\n double result = Math.Round(value * factor, 4);\n return JsonSerializer.Serialize(new { value, factor, result });\n });\n#pragma warning restore MAAI001\n\n// --- 技能提供程序 ---\n#pragma warning disable MAAI001\nvar skillsProvider = new AgentSkillsProvider(unitConverterSkill);\n#pragma warning restore MAAI001\n\n// --- DeepSeek 代理设置(OpenAI 兼容 API)---\nvar client = new OpenAIClient(\n new ApiKeyCredential(apiKey),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(endpoint),\n });\n\nAIAgent agent = new ChatClientAgent(\n client.GetChatClient(model).AsIChatClient(),\n new ChatClientAgentOptions\n {\n Name = \"DeepSeekUnitConverterAgent\",\n ChatOptions = new()\n {\n Instructions = \"你是一个乐于助人的助手,能够进行单位转换。\",\n },\n AIContextProviders = [skillsProvider],\n });\n\n// --- 示例:单位转换 ---\nConsole.WriteLine(\"使用 DeepSeek 和代码定义的技能进行单位转换\");\nConsole.WriteLine(new string('-', 60));\n\nvar stringBuilder = new StringBuilder();\nawait foreach (var response in agent.RunStreamingAsync(\n \"请严格用脚本计算。马拉松(26.2 英里)等于多少公里?另外,75 千克等于多少磅?\"))\n{\n stringBuilder.Append(response.Text);\n}\n\nConsole.WriteLine(stringBuilder.ToString());\nConsole.ReadLine();\n"}}, "src/ai-agent/Agent-Framework/27-ClassBased-Agent-Skills/27-ClassBased-Agent-Skills-DeepSeek/Program.cs": {"4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068321.5986173, "n": 3773, "h": "6c8eaa7c6261f1cc", "c": "// Copyright (c) Microsoft. All rights reserved.\n\n// 本示例演示如何通过 DeepSeek 的 OpenAI 兼容 API,使用 AgentClassSkill\n// 将 Agent 技能定义为 C# 类,并通过特性自动发现脚本和资源。\n\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing System.ClientModel;\nusing System.ComponentModel;\nusing System.Text;\nusing System.Text.Json;\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n// --- DeepSeek configuration ---\nvar apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\") ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY 未设置。\");\nvar endpoint = Environment.GetEnvironmentVariable(\"DEEPSEEK_ENDPOINT\") ?? \"https://api.deepseek.com\";\nvar modelName = Environment.GetEnvironmentVariable(\"DEEPSEEK_MODEL\") ?? \"deepseek-chat\";\n\n// --- 基于类的技能 ---\n// 实例化技能类。\nvar unitConverter = new UnitConverterSkill();\n\n// --- 技能提供程序 ---\n#pragma warning disable MAAI001 // 此类型仅用于评估,未来更新中可能更改或移除。抑制此诊断以继续。\nvar skillsProvider = new AgentSkillsProvider(unitConverter);\n#pragma warning restore MAAI001 // 此类型仅用于评估,未来更新中可能更改或移除。抑制此诊断以继续。\n\n//--- DeepSeek 代理设置(OpenAI 兼容 API)---\nvar openAIClient = new OpenAIClient(\n new ApiKeyCredential(apiKey),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(endpoint),\n });\n\nAIAgent agent = openAIClient\n .GetChatClient(modelName)\n .AsIChatClient()\n .AsAIAgent(new ChatClientAgentOptions\n {\n Name = \"UnitConverterAgent\",\n ChatOptions = new()\n {\n Instructions = \"你是一个可以进行单位换算的助手。\",\n },\n AIContextProviders = [skillsProvider],\n });\n\n// --- 示例:单位换算 ---\nConsole.WriteLine(\"使用 DeepSeek 和基于类的技能进行单位换算\");\nConsole.WriteLine(new string('-', 60));\n\nAgentResponse response = await agent.RunAsync(\n \"马拉松(26.2 英里)是多少公里?75 千克又是多少磅?\");\n\nConsole.WriteLine($\"助手:{response.Text}\");\n\nConsole.ReadLine();\n\n#pragma warning disable MAAI001\npublic class UnitConverterSkill : AgentClassSkill<UnitConverterSkill>\n#pragma warning restore MAAI001\n{\n /// <inheritdoc/>\n#pragma warning disable MAAI001\n public override AgentSkillFrontmatter Frontmatter { get; } = new(\n#pragma warning restore MAAI001\n \"unit-converter\",\n \"使用乘法因子在常见单位之间进行转换。当被要求在英里、公里、磅或千克之间换算时使用。\");\n\n /// <inheritdoc/>\n protected override string Instructions => \"\"\"\n 当用户请求进行单位换算时,使用此技能。\n\n 1. 查看 conversion-table 资源,找到目标换算所需的因子。\n 2. 使用 convert 脚本,并传入数值和表中的因子。\n 3. 清晰地给出结果,并同时标明两种单位。\n \"\"\";\n\n /// <summary>\n /// 获取用于脚本和资源参数及返回值编组的 <see cref=\"JsonSerializerOptions\"/>。\n /// </summary>\n /// <remarks>\n /// 在本示例中不一定需要重写该属性,但可用于提供自定义序列化选项,\n /// 例如为 Native AOT 兼容性提供源生成的 <c>JsonTypeInfoResolver</c>。\n /// </remarks>\n protected override JsonSerializerOptions? SerializerOptions => null;\n\n /// <summary>\n /// 一个提供乘法因子的换算表资源。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillResource(\"conversion-table\")]\n#pragma warning restore MAAI001\n [Description(\"常见单位换算的乘法因子查询表。\")]\n public string ConversionTable => \"\"\"\n # 换算表\n\n 公式:**result = value × factor**\n\n | 从 | 到 | 因子 |\n |-------------|-------------|----------|\n | 英里 | 公里 | 1.60934 |\n | 公里 | 英里 | 0.621371 |\n | 磅 | 千克 | 0.453592 |\n | 千克 | 磅 | 2.20462 |\n \"\"\";\n\n /// <summary>\n /// 按给定因子换算数值。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillScript(\"convert\")]\n#pragma warning restore MAAI001\n [Description(\"将数值与换算因子相乘,并以 JSON 返回结果。\")]\n private static string ConvertUnits(double value, double factor)\n {\n double result = Math.Round(value * factor, 4);\n return JsonSerializer.Serialize(new { value, factor, result });\n }\n}\n"}}, "src/ai-agent/Agent-Framework/28-Mixed-Agent-Skills/28-Mixed-Agent-Skills-DeepSeek/Program.cs": {"4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068321.7575903, "n": 7321, "h": "d0e670dd19774463", "c": "// 版权所有 (c) Microsoft。保留所有权利。\n\n// 此示例演示了一个高级场景:在单个智能体中使用 AgentSkillsProviderBuilder\n// 组合多种技能类型。该构建器适用于简单的 AgentSkillsProvider\n// 构造函数不足以满足需求的情况——例如你需要混合技能来源、应用筛选,\n// 或在同一处配置跨领域选项。\n//\n// 这里注册了三种不同的技能来源:\n// 1. 基于文件:unit-converter(英里↔公里、磅↔千克),来自磁盘上的 SKILL.md\n// 2. 代码定义:volume-converter(加仑↔升),使用 AgentInlineSkill\n// 3. 基于类:temperature-converter(°F↔°C↔K),使用带特性的 AgentClassSkill\n//\n// 对于更简单的单一来源场景,请参阅本示例系列前面的步骤\n// (例如:Step01 对应基于文件,Step02 对应代码定义,Step03 对应基于类)。\n\nusing Microsoft.Agents.AI;\nusing OpenAI;\nusing OpenAI.Chat;\nusing System.ClientModel;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Text;\nusing System.Text.Json;\n\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n// --- 配置 ---\nstring apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\") ?? throw new InvalidOperationException(\"未设置 DEEPSEEK_API_KEY。\");\nstring endpoint = Environment.GetEnvironmentVariable(\"DEEPSEEK_ENDPOINT\") ?? \"https://api.deepseek.com\";\nstring model = Environment.GetEnvironmentVariable(\"DEEPSEEK_MODEL\") ?? \"deepseek-chat\";\n\n// --- 1. 代码定义技能:volume-converter ---\n#pragma warning disable MAAI001 // 该类型仅用于评估用途,未来更新中可能会变更或移除。抑制此诊断以继续。\nvar volumeConverterSkill = new AgentInlineSkill(\n name: \"volume-converter\",\n description: \"使用乘法系数在加仑和升之间进行转换。\",\n instructions: \"\"\"\n 当用户请求在加仑和升之间转换时,使用此技能。\n\n 1. 查看 volume-conversion-table 资源,找到正确的系数。\n 2. 使用 convert-volume 脚本,并传入数值与系数。\n \"\"\")\n .AddResource(\"volume-conversion-table\",\n \"\"\"\n # 体积换算表\n\n 公式:**result = value × factor**\n\n | 从 | 到 | 系数 |\n |---------|---------|---------|\n | gallons | liters | 3.78541 |\n | liters | gallons | 0.264172|\n \"\"\")\n .AddScript(\"convert-volume\", (double value, double factor) =>\n {\n double result = Math.Round(value * factor, 4);\n return JsonSerializer.Serialize(new { value, factor, result });\n });\n#pragma warning restore MAAI001\n\n// --- 2. 基于类的技能:temperature-converter ---\nvar temperatureConverter = new TemperatureConverterSkill();\n// --- 技能提供程序 ---\n// 从 'skills' 目录中发现技能,这些技能以 SKILL.md 文件的形式存在。\n// 脚本执行器会以本地子进程的方式运行基于文件的脚本(例如 Python)。\n#pragma warning disable MAAI001\nAgentFileSkillScriptRunner myRunner = async (skill, script, args, ct) =>\n{\n\n try\n {\n Console.WriteLine($\"运行脚本: {script.Name}\");\n // 你在这个方法里面:\n // ✔ 调 Python\n // ✔ 调 HTTP API\n // ✔ 调数据库\n // ✔ 调你自己的 C# 方法\n var psi = new ProcessStartInfo(\"python\")\n {\n RedirectStandardOutput = true,\n RedirectStandardError = true,\n UseShellExecute = false,\n };\n psi.ArgumentList.Add(Path.Combine(skill.Path, script.FullPath));\n if (args != null)\n {\n foreach (var (key, value) in args)\n {\n if (value is not null && !string.IsNullOrWhiteSpace(value.ToString()))\n {\n psi.ArgumentList.Add(key);\n psi.ArgumentList.Add(value.ToString()!);\n }\n }\n }\n using var process = Process.Start(psi)!;\n string output = await process.StandardOutput.ReadToEndAsync();\n var error = await process.StandardError.ReadToEndAsync();\n\n Console.WriteLine(\"STDOUT: \" + output);\n //Console.WriteLine(\"STDERR: \" + error);\n await process.WaitForExitAsync();\n\n return output.Trim();\n }\n catch (Exception)\n {\n throw;\n }\n};\n\n\n// --- 3. 构建同时组合三种来源类型的提供程序 ---\n#pragma warning disable MAAI001 // 该类型仅用于评估用途,未来更新中可能会变更或移除。抑制此诊断以继续。\nvar skillsProvider = new AgentSkillsProviderBuilder()\n .UseFileSkill(Path.Combine(AppContext.BaseDirectory, \"skills\")) // 基于文件:unit-converter\n .UseSkill(volumeConverterSkill) // 代码定义:volume-converter\n .UseSkill(temperatureConverter) // 基于类:temperature-converter\n .UseFileScriptRunner(myRunner)\n .Build();\n#pragma warning restore MAAI001 // 该类型仅用于评估用途,未来更新中可能会变更或移除。抑制此诊断以继续。\n\n//--- DeepSeek 代理设置(OpenAI 兼容 API)---\nAIAgent agent = new OpenAIClient(\n new ApiKeyCredential(apiKey),\n new OpenAIClientOptions\n {\n Endpoint = new Uri(endpoint),\n })\n .GetChatClient(model)\n .AsAIAgent(new ChatClientAgentOptions\n {\n Name = \"MultiConverterAgent\",\n ChatOptions = new()\n {\n Instructions = \"你是一个乐于助人的助手,可以进行单位、体积和温度转换。\",\n },\n AIContextProviders = [skillsProvider],\n });\n\n// --- 示例:使用三种技能 ---\nConsole.WriteLine(\"使用混合技能进行转换(文件 + 代码 + 类)\");\nConsole.WriteLine(new string('-', 60));\n\nAgentResponse response = await agent.RunAsync(\n \"我需要三个换算:\" +\n \"1)马拉松(26.2 英里)是多少公里?\" +\n \"2)5 加仑桶是多少升?\" +\n \"3)98.6°F 换算成摄氏度是多少?\");\nConsole.WriteLine($\"智能体:{response.Text}\");\n\nConsole.ReadLine();\n\n/// <summary>\n/// 一个使用特性进行发现、以 C# 类定义的 temperature-converter 技能。\n/// </summary>\n/// <remarks>\n/// 使用 <see cref=\"AgentSkillResourceAttribute\"/> 标注的属性会被自动发现为技能资源,\n/// 使用 <see cref=\"AgentSkillScriptAttribute\"/> 标注的方法会被自动发现为技能脚本。\n/// </remarks>\n#pragma warning disable MAAI001 \ninternal sealed class TemperatureConverterSkill : AgentClassSkill<TemperatureConverterSkill>\n#pragma warning restore MAAI001\n{\n /// <inheritdoc/>\n#pragma warning disable MAAI001\n public override AgentSkillFrontmatter Frontmatter { get; } = new(\n#pragma warning restore MAAI001\n \"temperature-converter\",\n \"在不同温标之间进行转换(华氏、摄氏、开尔文)。\");\n\n /// <inheritdoc/>\n protected override string Instructions => \"\"\"\n 当用户请求进行温度换算时,使用此技能。\n\n 1. 查看 temperature-conversion-formulas 资源,找到正确的公式。\n 2. 使用 convert-temperature 脚本,并传入数值、源温标和目标温标。\n 3. 清晰展示换算结果,并标注两种温标。\n \"\"\";\n\n /// <summary>\n /// 温度换算公式参考表。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillResource(\"temperature-conversion-formulas\")]\n#pragma warning restore MAAI001 \n [Description(\"华氏、摄氏与开尔文之间的换算公式。\")]\n public string ConversionFormulas => \"\"\"\n # 温度换算公式\n\n | 从 | 到 | 公式 |\n |-------------|-------------|---------------------------|\n | Fahrenheit | Celsius | °C = (°F − 32) × 5/9 |\n | Celsius | Fahrenheit | °F = (°C × 9/5) + 32 |\n | Celsius | Kelvin | K = °C + 273.15 |\n | Kelvin | Celsius | °C = K − 273.15 |\n \"\"\";\n\n /// <summary>\n /// 在不同温标之间转换温度数值。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillScript(\"convert-temperature\")]\n#pragma warning restore MAAI001\n [Description(\"将温度值从一种温标转换到另一种温标。\")]\n private static string ConvertTemperature(double value, string from, string to)\n {\n double result = (from.ToUpperInvariant(), to.ToUpperInvariant()) switch\n {\n (\"FAHRENHEIT\", \"CELSIUS\") => Math.Round((value - 32) * 5.0 / 9.0, 2),\n (\"CELSIUS\", \"FAHRENHEIT\") => Math.Round(value * 9.0 / 5.0 + 32, 2),\n (\"CELSIUS\", \"KELVIN\") => Math.Round(value + 273.15, 2),\n (\"KELVIN\", \"CELSIUS\") => Math.Round(value - 273.15, 2),\n _ => throw new ArgumentException($\"不支持的换算:{from} → {to}\")\n };\n\n return JsonSerializer.Serialize(new { value, from, to, result });\n }\n}"}}, "src/ai-agent/Agent-Framework/25-FileBased-Agent-Skills/25-FileBased-Agent-Skills-DeepSeek/Program.cs": {"4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068321.9377959, "n": 7341, "h": "7d3f53173f7a44cd", "c": "// Copyright (c) Microsoft. All rights reserved.\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.AI;\nusing OpenAI;\nusing System.ClientModel;\nusing System.Diagnostics;\nusing System.Text;\nusing System.Text.Json;\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n// --- DeepSeek configuration ---\nvar apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\") ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY 未设置。\");\nvar endpoint = Environment.GetEnvironmentVariable(\"DEEPSEEK_ENDPOINT\") ?? \"https://api.deepseek.com/v1\";\nvar model = Environment.GetEnvironmentVariable(\"DEEPSEEK_MODEL\")?? \"deepseek-v4-pro\";\n// --- File-based Skills provider ---\nstring skillsPath = Path.Combine(AppContext.BaseDirectory, \"skills\");\nif (!Directory.Exists(skillsPath))\n{\n throw new DirectoryNotFoundException($\"Skills directory was not copied to the output: {skillsPath}\");\n}\nvar skillsProvider = new AgentSkillsProvider(\n skillsPath,\n RunAsync);\n\nvar chatClient = new OpenAIClient(\n new ApiKeyCredential(apiKey),\n new OpenAIClientOptions { Endpoint = new Uri(endpoint) })\n .GetChatClient(model)\n .AsIChatClient();\n\n#pragma warning disable MAAI001\nAIAgent agent = chatClient\n .AsAIAgent(new ChatClientAgentOptions\n {\n\n Name = \"DeepSeekUnitConverterAgent\",\n ChatOptions = new()\n {\n Temperature = 0,\n Instructions = \"你是一个可以进行单位转换的助手。\",\n },\n AIContextProviders = [skillsProvider],\n })\n .AsBuilder()\n .UseToolApproval(new ToolApprovalAgentOptions\n {\n // 仅用于演示。生产环境应在运行 Skill 脚本之前请求用户授权。\n AutoApprovalRules = [AgentSkillsProvider.AllToolsAutoApprovalRule],\n })\n .Build();\n#pragma warning restore MAAI001\n\nConsole.WriteLine($\"正在使用 DeepSeek 模型 '{model}' 调用基于文件的 Skill\");\nConsole.WriteLine(new string('-', 60));\n\nAgentResponse response = await agent.RunAsync(\"对如下单位进行转换:马拉松(26.2 英里)等于多少公里?另外,75 千克等于多少磅?\");\n\n\nConsole.WriteLine($\"Agent: {response.Text}\");\n\nConsole.ReadLine();\n\nstatic async Task<object?> RunAsync(\n AgentFileSkill skill,\n AgentFileSkillScript script,\n JsonElement? arguments,\n IServiceProvider? serviceProvider,\n CancellationToken cancellationToken)\n{\n \n if (!File.Exists(script.FullPath))\n {\n return $\"错误: 找不到脚本文件: {script.FullPath}\";\n }\n\n string extension = Path.GetExtension(script.FullPath);\n string? interpreter = extension switch\n {\n \".py\" => OperatingSystem.IsWindows() ? \"python\" : \"python3\",\n \".js\" => \"node\",\n \".sh\" => \"bash\",\n \".ps1\" => \"pwsh\",\n _ => null,\n };\n\n var startInfo = new ProcessStartInfo\n {\n RedirectStandardOutput = true,\n RedirectStandardError = true,\n UseShellExecute = false,\n CreateNoWindow = true,\n WorkingDirectory = Path.GetDirectoryName(script.FullPath) ?? \".\",\n };\n\n if (interpreter is not null)\n {\n startInfo.FileName = interpreter;\n startInfo.ArgumentList.Add(script.FullPath);\n }\n else\n {\n startInfo.FileName = script.FullPath;\n }\n\n if (arguments is { ValueKind: JsonValueKind.Array } jsonArray)\n {\n foreach (JsonElement element in jsonArray.EnumerateArray())\n {\n if (element.ValueKind != JsonValueKind.String)\n {\n throw new InvalidOperationException(\n $\"数组参数只能包含字符串,收到:{element.ValueKind}\");\n }\n\n startInfo.ArgumentList.Add(element.GetString()!);\n }\n }\n else if (arguments is { ValueKind: JsonValueKind.String } commandLine)\n {\n foreach (string argument in TokenizeCommandLine(commandLine.GetString()!))\n {\n startInfo.ArgumentList.Add(argument);\n }\n }\n else if (arguments is { ValueKind: JsonValueKind.Object } jsonObject)\n {\n if (!jsonObject.TryGetProperty(\"value\", out JsonElement value) ||\n !jsonObject.TryGetProperty(\"factor\", out JsonElement factor))\n {\n throw new InvalidOperationException(\n $\"脚本参数必须包含 value 和 factor。实际参数:{jsonObject}\");\n }\n\n startInfo.ArgumentList.Add(\"--value\");\n startInfo.ArgumentList.Add(value.ToString());\n\n startInfo.ArgumentList.Add(\"--factor\");\n startInfo.ArgumentList.Add(factor.ToString());\n }\n else if (arguments is not null &&\n arguments.Value.ValueKind is not JsonValueKind.Null\n and not JsonValueKind.Undefined)\n {\n throw new InvalidOperationException(\n $\"不支持的脚本参数类型:{arguments.Value.ValueKind}\");\n }\n\n Process? process = null;\n try\n {\n process = Process.Start(startInfo);\n if (process is null)\n {\n return $\"错误: 无法启动脚本 '{script.Name}'。\";\n }\n\n Task<string> outputTask = process.StandardOutput.ReadToEndAsync(cancellationToken);\n Task<string> errorTask = process.StandardError.ReadToEndAsync(cancellationToken);\n\n await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);\n\n string output = await outputTask.ConfigureAwait(false);\n string error = await errorTask.ConfigureAwait(false);\n\n if (!string.IsNullOrEmpty(error))\n {\n output += $\"\\n标准错误输出:\\n{error}\";\n }\n\n if (process.ExitCode != 0)\n {\n output += $\"\\n脚本以代码 {process.ExitCode} 退出\";\n }\n\n return string.IsNullOrEmpty(output) ? \"(无输出)\" : output.Trim();\n }\n catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)\n {\n process?.Kill(entireProcessTree: true);\n throw;\n }\n catch (Exception ex)\n {\n return $\"错误: 执行脚本 '{script.Name}' 失败: {ex.Message}\";\n }\n finally\n {\n process?.Dispose();\n }\n}\n\nstatic IReadOnlyList<string> TokenizeCommandLine(string commandLine)\n{\n var arguments = new List<string>();\n var current = new StringBuilder();\n char? quote = null;\n bool tokenStarted = false;\n\n for (int index = 0; index < commandLine.Length; index++)\n {\n char character = commandLine[index];\n\n if (character == '\\\\' &&\n index + 1 < commandLine.Length &&\n commandLine[index + 1] is '\\'' or '\"')\n {\n current.Append(commandLine[++index]);\n tokenStarted = true;\n }\n else if (character is '\\'' or '\"')\n {\n if (quote is null)\n {\n quote = character;\n tokenStarted = true;\n }\n else if (quote == character)\n {\n quote = null;\n }\n else\n {\n current.Append(character);\n }\n }\n else if (char.IsWhiteSpace(character) && quote is null)\n {\n if (tokenStarted)\n {\n arguments.Add(current.ToString());\n current.Clear();\n tokenStarted = false;\n }\n }\n else\n {\n current.Append(character);\n tokenStarted = true;\n }\n }\n\n if (quote is not null)\n {\n throw new InvalidOperationException(\"脚本参数包含未闭合的引号。\");\n }\n\n if (tokenStarted)\n {\n arguments.Add(current.ToString());\n }\n\n return arguments;\n}\n"}}, "src/ai-agent/Agent-Framework/29-AgentSkill-Integration-DI/29-AgentSkill-Integration-DI-DeepSeek/Program.cs": {"4420500bbb3e76a890cba4cf864406efbe811684": {"t": 1786068322.017265, "n": 7204, "h": "87ddc665f8e9d496", "c": "// Copyright (c) Microsoft. 保留所有权利。\n\n// 此示例演示如何将依赖注入 (DI) 与 Agent Skills 结合使用。\n// 它并排展示两种方法,每种方法处理不同的转换领域:\n//\n// 1. 代码定义的技能 (AgentInlineSkill) — 转换距离(英里 ↔ 千米)。\n// 资源和脚本是内联委托,会从 IServiceProvider 解析服务。\n//\n// 2. 基于类的技能 (AgentClassSkill) — 转换重量(磅 ↔ 千克)。\n// 资源和脚本封装在类中,同样会从 IServiceProvider 解析服务。\n//\n// 这两个技能共享同一个注册到 DI 容器中的 ConversionService,\n// 这表明无论技能如何定义,DI 的工作方式都是一致的。\n// 当提示中包含跨越两个领域的问题时,智能体会同时使用这两个技能。\n\nusing Microsoft.Agents.AI;\nusing Microsoft.Extensions.DependencyInjection;\nusing OpenAI;\nusing OpenAI.Chat;\nusing System.ClientModel;\nusing System.ComponentModel;\nusing System.Text;\nusing System.Text.Json;\n\nConsole.InputEncoding = Encoding.UTF8;\nConsole.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);\n\n\n// --- 配置 ---\nstring apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\") ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY is not set.\");\nstring endpoint = Environment.GetEnvironmentVariable(\"DEEPSEEK_ENDPOINT\") ?? \"https://api.deepseek.com\";\nstring model = Environment.GetEnvironmentVariable(\"DEEPSEEK_MODEL\") ?? \"deepseek-v4-pro\";\n// --- DI 容器 ---\n// 注册应用程序服务,以便技能资源和脚本在执行时可以解析这些服务。\nServiceCollection services = new();\nservices.AddSingleton<ConversionService>();\n\nIServiceProvider serviceProvider = services.BuildServiceProvider();\n\n// =====================================================================\n// 方法 1:使用 DI 的代码定义技能 (AgentInlineSkill)\n// =====================================================================\n// 处理距离转换(英里 ↔ 千米)。\n// 资源和脚本是内联委托。每个委托都可以声明一个 IServiceProvider 参数,\n// 框架会自动注入该参数。\n\n#pragma warning disable MAAI001\nvar distanceSkill = new AgentInlineSkill(\n name: \"distance-converter\",\n description: \"在距离单位之间转换。当要求将英里转换为千米或将千米转换为英里时使用。\",\n instructions: \"\"\"\n 当用户要求在距离单位(英里和千米)之间转换时,请使用此技能。\n 1. 查看 distance-table 资源,找到所请求转换的系数。\n 2. 使用 convert 脚本,传入值和表中的系数。\n \"\"\")\n .AddResource(\"distance-table\", (IServiceProvider serviceProvider) =>\n {\n var service = serviceProvider.GetRequiredService<ConversionService>();\n return service.GetDistanceTable();\n })\n .AddScript(\"convert\", (double value, double factor, IServiceProvider serviceProvider) =>\n {\n var service = serviceProvider.GetRequiredService<ConversionService>();\n return service.Convert(value, factor);\n });\n#pragma warning restore MAAI001 \n\n// =====================================================================\n// 方法 2:使用 DI 的基于类的技能 (AgentClassSkill)\n// =====================================================================\n// 处理重量转换(磅 ↔ 千克)。\n// 资源和脚本通过特性使用反射发现。\n// 带有 IServiceProvider 参数的方法会自动接收 DI。\n//\n// 或者,基于类的技能也可以通过构造函数接收依赖项。\n// 将技能类本身注册到 ServiceCollection 中,\n// 然后从容器中解析它:\n//\n// services.AddSingleton<WeightConverterSkill>();\n// var weightSkill = serviceProvider.GetRequiredService<WeightConverterSkill>();\n\nvar weightSkill = new WeightConverterSkill();\n// --- 技能提供程序 ---\n// 这两个技能注册到同一个提供程序中,因此智能体可以使用其中任意一个。\n#pragma warning disable MAAI001\nvar skillsProvider = new AgentSkillsProvider(distanceSkill, weightSkill);\n#pragma warning restore MAAI001\n\n//--- DeepSeek 代理设置(OpenAI 兼容 API)---\nAIAgent agent = new OpenAIClient(\n new ApiKeyCredential(apiKey),\n new OpenAIClientOptions { Endpoint = new Uri(endpoint) })\n .GetChatClient(model)\n .AsAIAgent(\n options: new ChatClientAgentOptions\n {\n Name = \"UnitConverterAgent\",\n ChatOptions = new()\n {\n Instructions = \"你是一个可以转换单位的实用助手。\",\n },\n AIContextProviders = [skillsProvider],\n },\n services: serviceProvider);\n\n// --- 示例:单位转换 ---\n// 此提示跨越两个领域,因此智能体会使用这两个技能。\nConsole.WriteLine($\"使用 DeepSeek ({model}) 和 DI 驱动的技能转换单位\");\nConsole.WriteLine(new string('-', 60));\n\nAgentResponse response = await agent.RunAsync(\n \"一场马拉松(26.2 英里)是多少千米?75 千克是多少磅?\");\n\nConsole.WriteLine($\"智能体:{response.Text}\");\n\nConsole.ReadLine();\n\n// ---------------------------------------------------------------------------\n// 基于类的技能\n// ---------------------------------------------------------------------------\n\n/// <summary>\n/// 一个定义为 C# 类并使用依赖注入的重量转换技能。\n/// </summary>\n/// <remarks>\n/// 此技能会在其资源方法和脚本方法中从 DI 容器解析 <see cref=\"ConversionService\"/>。\n/// 带有 <see cref=\"IServiceProvider\"/> 参数的方法会由框架自动注入。\n/// 使用 <see cref=\"AgentSkillResourceAttribute\"/> 和 <see cref=\"AgentSkillScriptAttribute\"/>\n/// 标注的属性和方法会通过反射自动发现。\n/// </remarks>\n#pragma warning disable MAAI001\ninternal sealed class WeightConverterSkill : AgentClassSkill<WeightConverterSkill>\n#pragma warning restore MAAI001\n{\n /// <inheritdoc/>\n#pragma warning disable MAAI001\n public override AgentSkillFrontmatter Frontmatter { get; } = new(\n#pragma warning restore MAAI001 \n \"weight-converter\",\n \"在重量单位之间转换。当要求将磅转换为千克或将千克转换为磅时使用。\");\n\n /// <inheritdoc/>\n protected override string Instructions => \"\"\"\n 当用户要求在重量单位(磅和千克)之间转换时,请使用此技能。\n\n 1. 查看 weight-table 资源,找到所请求转换的系数。\n 2. 使用 convert 脚本,传入值和表中的系数。\n 3. 使用两个单位清晰呈现结果。\n \"\"\";\n\n /// <summary>\n /// 从 DI 注册的 <see cref=\"ConversionService\"/> 返回重量转换表。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillResource(\"weight-table\")]\n#pragma warning restore MAAI001\n [Description(\"重量转换乘法系数的查找表。\")]\n private static string GetWeightTable(IServiceProvider serviceProvider)\n {\n var service = serviceProvider.GetRequiredService<ConversionService>();\n return service.GetWeightTable();\n }\n /// <summary>\n /// 使用 DI 注册的 <see cref=\"ConversionService\"/> 按给定系数转换值。\n /// </summary>\n#pragma warning disable MAAI001\n [AgentSkillScript(\"convert\")]\n#pragma warning restore MAAI001\n [Description(\"将值乘以转换系数,并以 JSON 形式返回结果。\")]\n private static string Convert(double value, double factor, IServiceProvider serviceProvider)\n {\n var service = serviceProvider.GetRequiredService<ConversionService>();\n return service.Convert(value, factor);\n }\n}\n\n// ---------------------------------------------------------------------------\n// 服务\n// ---------------------------------------------------------------------------\n\n/// <summary>\n/// 提供单位之间的转换率。\n/// 在实际应用程序中,这可以调用外部 API、从数据库读取,\n/// 或应用随时间变化的汇率。\n/// </summary>\ninternal sealed class ConversionService\n{\n /// <summary>\n /// 返回受支持距离转换的 Markdown 表。\n /// </summary>\n public string GetDistanceTable() =>\n \"\"\"\n # 距离转换\n\n 公式:**结果 = 值 × 系数**\n\n | 从 | 到 | 系数 |\n |-------------|-------------|----------|\n | 英里 | 千米 | 1.60934 |\n | 千米 | 英里 | 0.621371 |\n \"\"\";\n /// <summary>\n /// 返回受支持重量转换的 Markdown 表。\n /// </summary>\n public string GetWeightTable() =>\n \"\"\"\n # 重量转换\n\n 公式:**结果 = 值 × 系数**\n\n | 从 | 到 | 系数 |\n |-------------|-------------|----------|\n | 磅 | 千克 | 0.453592 |\n | 千克 | 磅 | 2.20462 |\n \"\"\";\n /// <summary>\n /// 按给定系数转换值,并返回 JSON 结果。\n /// </summary>\n public string Convert(double value, double factor)\n {\n double result = Math.Round(value * factor, 4);\n return JsonSerializer.Serialize(new { value, factor, result });\n }\n}"}}}}