用 C# 扩展 Dynamics 365 Copilot:自定义插件与场景

Dynamics 365 Copilot 作为基于 AI 的智能助手,为企业用户提供了自动化流程、智能分析和自然语言交互的能力,但通用功能往往无法满足特定行业或企业的定制化需求。本文将详细介绍如何通过 C# 编写自定义插件,扩展 Dynamics 365 Copilot 的能力,并结合实际业务场景实现定制化 AI 交互。

一、核心基础:Dynamics 365 Copilot 扩展架构

Dynamics 365 Copilot 的扩展主要依赖于 Power Platform 插件框架Copilot Studio 的自定义连接器,核心技术栈包括:

  • C# (.NET Framework 4.8 或 .NET 6+):编写业务逻辑插件
  • Dynamics 365 SDK:与 Dataverse 数据交互
  • Azure OpenAI / 自定义 LLM 接口:扩展 AI 生成能力
  • Power Platform 注册工具:部署插件到 Dynamics 365 环境

扩展的核心逻辑是:通过 C# 插件拦截 Copilot 的交互请求,注入自定义业务规则,调用外部 AI 服务或 Dataverse 数据,最终返回定制化的 AI 响应。

二、环境准备

  1. 安装必备工具:

    • Visual Studio 2022(带 .NET 桌面开发和 Azure 开发工作负载)
    • Dynamics 365 SDK(最新版本)
    • Power Platform CLI(用于插件注册)
    • 有效的 Dynamics 365 环境(带 Copilot 启用权限)
  2. 创建 Dynamics 365 插件项目:

    bash 复制代码
    # 通过 Power Platform CLI 创建插件项目
    pac plugin init --name CopilotCustomPlugin --template ClassLibrary

三、核心示例:自定义客户服务 Copilot 插件

场景描述

某零售企业需要定制 Copilot 能力:当用户通过 Copilot 查询客户订单状态时,Copilot 能自动从 Dataverse 中读取该客户的订单数据,并结合 AI 生成结构化、个性化的回复(而非通用话术)。

核心源代码实现

1. 插件核心类(CustomerOrderCopilotPlugin.cs)
cs 复制代码
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Linq;

// 插件入口类,继承 IPlugin 接口
public class CustomerOrderCopilotPlugin : IPlugin
{
    // 插件执行方法(核心入口)
    public void Execute(IServiceProvider serviceProvider)
    {
        // 1. 获取插件执行上下文
        IPluginExecutionContext context = (IPluginExecutionContext)
            serviceProvider.GetService(typeof(IPluginExecutionContext));
        
        // 2. 获取组织服务(用于操作 Dataverse 数据)
        IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)
            serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

        try
        {
            // 3. 从 Copilot 请求中提取参数(客户名称/ID、查询类型)
            string customerId = context.InputParameters["CustomerId"]?.ToString();
            string queryType = context.InputParameters["QueryType"]?.ToString();

            // 校验必填参数
            if (string.IsNullOrEmpty(customerId) || queryType != "OrderStatus")
            {
                throw new InvalidPluginExecutionException("无效的查询参数:请提供客户ID并选择订单状态查询");
            }

            // 4. 从 Dataverse 查询客户订单数据
            var orderData = GetCustomerOrders(service, customerId);

            // 5. 构建定制化的 Copilot 响应内容
            string copilotResponse = BuildCopilotResponse(orderData);

            // 6. 将响应写入输出参数,返回给 Copilot
            context.OutputParameters["CopilotResponse"] = copilotResponse;
        }
        catch (Exception ex)
        {
            // 插件异常处理(Dynamics 365 标准规范)
            throw new InvalidPluginExecutionException("客户订单查询插件执行失败:" + ex.Message, ex);
        }
    }

    /// <summary>
    /// 从 Dataverse 查询指定客户的订单数据
    /// </summary>
    /// <param name="service">Dynamics 365 组织服务</param>
    /// <param name="customerId">客户GUID</param>
    /// <returns>订单数据集合</returns>
    private EntityCollection GetCustomerOrders(IOrganizationService service, string customerId)
    {
        // 构建查询表达式(查询 salesorder 实体)
        QueryExpression query = new QueryExpression("salesorder")
        {
            ColumnSet = new ColumnSet("name", "salesorderid", "statuscode", "totalamount", "createdon"),
            Criteria = new FilterExpression
            {
                Conditions =
                {
                    new ConditionExpression("customerid", ConditionOperator.Equal, new Guid(customerId))
                }
            },
            // 按创建时间降序排列
            Orders = { new OrderExpression("createdon", OrderType.Descending) }
        };

        // 执行查询并返回结果
        return service.RetrieveMultiple(query);
    }

    /// <summary>
    /// 构建 Copilot 定制化响应内容
    /// </summary>
    /// <param name="orders">订单数据</param>
    /// <returns>结构化的 AI 回复文本</returns>
    private string BuildCopilotResponse(EntityCollection orders)
    {
        if (orders.Entities.Count == 0)
        {
            return "未查询到该客户的任何订单记录。";
        }

        // 构建结构化回复(结合业务规则定制话术)
        var responseBuilder = new System.Text.StringBuilder();
        responseBuilder.AppendLine($"为您查询到 {orders.Entities.Count} 条订单记录:\n");

        foreach (var order in orders.Entities)
        {
            string orderName = order["name"].ToString();
            string orderStatus = GetOrderStatusText(order.GetAttributeValue<OptionSetValue>("statuscode").Value);
            decimal totalAmount = order.GetAttributeValue<Money>("totalamount").Value;
            DateTime createDate = order.GetAttributeValue<DateTime>("createdon");

            responseBuilder.AppendLine($"订单编号:{orderName}");
            responseBuilder.AppendLine($"状态:{orderStatus}");
            responseBuilder.AppendLine($"金额:{totalAmount:C}");
            responseBuilder.AppendLine($"创建时间:{createDate:yyyy-MM-dd HH:mm}\n");
        }

        responseBuilder.AppendLine("如需了解某笔订单的详细进度,可直接说出订单编号,我会为您进一步查询。");
        return responseBuilder.ToString();
    }

    /// <summary>
    /// 将订单状态码转换为易读文本(适配中文场景)
    /// </summary>
    /// <param name="statusCode">订单状态码</param>
    /// <returns>状态文本</returns>
    private string GetOrderStatusText(int statusCode)
    {
        return statusCode switch
        {
            1 => "草稿",
            2 => "已提交",
            3 => "已批准",
            4 => "已发货",
            5 => "已完成",
            6 => "已取消",
            _ => "未知状态"
        };
    }
}
2. 插件注册配置(PluginRegistration.cs)
cs 复制代码
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System;
using System.ServiceModel;

// 插件注册辅助类(用于Power Platform CLI部署)
public class PluginRegistrationHelper
{
    /// <summary>
    /// 注册插件到 Dynamics 365 环境
    /// </summary>
    /// <param name="serviceUri">Dynamics 365 服务地址</param>
    /// <param name="username">管理员账号</param>
    /// <param name="password">密码</param>
    /// <param name="domain">域名(如适用)</param>
    public static void RegisterPlugin(Uri serviceUri, string username, string password, string domain)
    {
        // 构建认证凭据
        ClientCredentials credentials = new ClientCredentials();
        credentials.UserName.UserName = username;
        credentials.UserName.Password = password;

        // 创建组织服务代理
        using (OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(
            serviceUri, null, credentials, null))
        {
            serviceProxy.EnableProxyTypes();

            // 注册插件步骤(绑定到 Copilot 自定义消息)
            CreatePluginStep(serviceProxy, "new_CopilotCustomerOrderQuery", 
                "CustomerOrderCopilotPlugin", "Microsoft.Crm.Sdk.Messages.ExecutePluginRequest");
        }
    }

    private static void CreatePluginStep(IOrganizationService service, string messageName, 
        string pluginTypeName, string requestType)
    {
        // 插件步骤创建逻辑(简化版,实际部署建议使用Power Platform CLI)
        Entity pluginStep = new Entity("sdkmessageprocessingstep")
        {
            ["name"] = $"Copilot_{messageName}_Step",
            ["sdkmessageid"] = new EntityReference("sdkmessage", new Guid("替换为实际消息ID")),
            ["pluginassemblyid"] = new EntityReference("pluginassembly", new Guid("替换为程序集ID")),
            ["plugintypeid"] = new EntityReference("plugintype", new Guid("替换为插件类型ID")),
            ["stage"] = 40, // 操作后执行
            ["mode"] = 0,   // 同步模式
            ["rank"] = 1
        };

        service.Create(pluginStep);
    }
}

四、插件部署与集成到 Copilot

1. 插件编译与签名

  • 在 Visual Studio 中编译项目,生成强名称签名的 DLL(右键项目 → 属性 → 签名 → 选择强名称密钥文件)。

  • 使用 Power Platform CLI 将 DLL 注册到 Dynamics 365 环境:

    bash 复制代码
    # 登录 Dynamics 365 环境
    pac auth create --url https://your-org.crm.dynamics.com
    
    # 注册插件程序集
    pac plugin register --assembly "bin\Debug\CopilotCustomPlugin.dll" --serviceprincipalid "你的SPN ID" --serviceprincipalsecret "你的SPN密钥"

2. Copilot Studio 集成

  1. 打开 Copilot Studio,创建新的自定义主题 "客户订单查询";
  2. 添加 "调用自定义插件" 操作,绑定上述 C# 插件;
  3. 配置输入参数(客户 ID)和输出参数(Copilot 响应);
  4. 发布自定义主题,关联到 Dynamics 365 应用。

五、进阶场景:集成外部 LLM 增强 Copilot 能力

如果需要进一步增强 AI 生成能力,可以在插件中调用 Azure OpenAI API,结合业务数据生成更自然的回复:

cs 复制代码
// 新增方法:调用 Azure OpenAI 生成定制化回复
private string GenerateAiResponseWithOpenAI(string orderData)
{
    var openAIClient = new Azure.AI.OpenAI.OpenAIClient(
        new Uri("https://your-openai-resource.openai.azure.com/"),
        new Azure.AzureKeyCredential("你的 OpenAI API 密钥"));

    var chatCompletionsOptions = new Azure.AI.OpenAI.ChatCompletionsOptions()
    {
        DeploymentName = "gpt-35-turbo",
        Messages =
        {
            new Azure.AI.OpenAI.ChatMessage(Azure.AI.OpenAI.ChatRole.System, 
                "你是零售企业的客户服务助手,需基于提供的订单数据生成友好、简洁的中文回复,避免技术术语。"),
            new Azure.AI.OpenAI.ChatMessage(Azure.AI.OpenAI.ChatRole.User, 
                $"基于以下订单数据生成回复:{orderData}")
        },
        Temperature = 0.7f,
        MaxTokens = 500
    };

    var response = openAIClient.GetChatCompletions(chatCompletionsOptions);
    return response.Value.Choices[0].Message.Content;
}

六、关键注意事项

  1. 性能优化:插件执行超时时间默认 2 分钟,需优化 Dataverse 查询(使用分页、索引);
  2. 权限控制:插件以当前用户身份执行,需为用户分配 salesorder 实体的读取权限;
  3. 异常处理 :必须捕获并抛出 InvalidPluginExecutionException,确保错误信息能返回给 Copilot;
  4. 版本管理:插件更新需通过 Power Platform CLI 重新注册,建议保留历史版本。

总结

  1. Dynamics 365 Copilot 可通过 C# 插件扩展,核心是实现 IPlugin 接口并拦截 Copilot 交互请求,结合 Dataverse 数据定制 AI 响应;
  2. 自定义插件的核心流程为:提取请求参数 → 查询业务数据 → 构建定制化响应 → 返回给 Copilot;
  3. 进阶场景可集成 Azure OpenAI 等外部 LLM,进一步增强 Copilot 的自然语言生成能力,同时需注意插件的性能、权限和异常处理。
相关推荐
lhxcc_fly21 天前
Coze实战案例分享
ai·sdk·coze·实战案例
lhxcc_fly21 天前
Coze开发平台
ai·api·sdk·提示词·应用·智能体·coze
logocode_li22 天前
跑通第一个LLM应用:调用DeepSeek模型
ai·llm·sdk·deepseek
查士丁尼·绵25 天前
通过sdk获取ecs指标
python·sdk
明哥说编程1 个月前
Power Automate 与Dynamics 365 插件结合实现复杂业务逻辑
dynamics 365
devmoon1 个月前
使用 Zombienet 运行平行链网络
web3·区块链·sdk·polkadot·测试网·跨链
王解1 个月前
SDK 与 API: 从概念到应用的深度解析
sdk
jfqqqqq1 个月前
minIO分页请求maxKeys无效的问题
java·sdk·minio·分页
明哥说编程1 个月前
从代码陷阱到敏捷配置:Dynamics 365 避免过度定制的架构设计原则
硬编码·dynamics 365·插件开发规范·过度定制·架构设计原则·原生功能优先·低代码扩展边界