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.
1 line
44 KiB
JSON
1 line
44 KiB
JSON
{"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}"}}}} |