基于AI插件快速构建钉钉机器人

本文介绍如何通过Higress提供的AI插件构建一个钉钉答疑机器人。

前提条件

已经创建云原生API网关实例,具体操作请参见创建网关实例

实现钉钉机器人基础对话

方案概览

image

步骤一:创建HTTP API

  1. 登录云原生API网关控制台

  2. 在左侧导航栏,选择API,并在顶部菜单栏选择地域。

  3. API页面单击右上角创建API

  4. 选择HTTP API,单击创建,在创建HTTP API页面填写API名称描述

步骤二:创建服务

创建dashscope服务,用于提供对话功能以及embedding功能,该服务由通义千问提供。

  1. 实例页面,单击目标网关实例名称。

  2. 在左侧导航栏,选择服务,并单击服务页签。

  3. 单击左上角创建服务,在创建服务面板中参考如下配置填写相关参数。

    image

步骤三:创建并发布路由

  1. 单击目标API名称,进入API详情页,单击创建路由,在创建路由面板,配置相关参数。

  2. 配置路由基本信息。

    image

    • 域名:单击右侧的添加域名,在添加域名面板中创建“*“域名,用于暂无域名的场景,支持IP访问,存在安全风险,请谨慎使用。

    • 路径:配置OpenAI的访问路径/v1/chat/completions

  3. 配置所属环境&后端服务。

    image

    • 环境/网关/VPC:您可根据实际需求单击右侧创建环境,在创建环境面板中配置环境名称,关联所属的网关实例,本文以默认环境为例。

    • 后端服务:在后端服务的下拉列表中选择步骤二:创建服务中创建的dashscope服务。

步骤四:配置相关插件

1. 配置钉钉转OpenAI自定义插件

本文以Go语言开发自定义插件,提供了一个实现钉钉消息与OpenAI协议转换的插件,目的是实现钉钉消息与OpenAI请求之间的互转。

  1. 开发自定义插件:建议您使用云原生API网关提供的WebIDE一站式完成自定义插件的开发和编译,详细信息请参考WebIDE编写插件

    自定义插件示例代码

    const defaultRequestFormat = `{"model": "gpt-3.5-turbo","messages": [{"role": "user","content": "%s"}]}`
    const defaultResponseFormat = `{"msgtype":"markdown","markdown": {"title": "answer", "text": "%s"}}`
    
    
    func onHttpRequestHeaders(ctx wrapper.HttpContext, config DingConfig, log wrapper.Log) types.Action {
    	proxywasm.RemoveHttpRequestHeader("content-length")
    	return types.ActionContinue
    }
    
    func onHttpRequestBody(ctx wrapper.HttpContext, config DingConfig, body []byte, log wrapper.Log) types.Action {
    	question := gjson.GetBytes(body, "text.content").String()
    	if question == "" {
    		log.Error("Parse request body error")
    	}
    	newBody := fmt.Sprintf(defaultRequestFormat, question)
    	proxywasm.ReplaceHttpRequestBody([]byte(newBody))
    	return types.ActionContinue
    }
    
    func onHttpResponseHeaders(ctx wrapper.HttpContext, config DingConfig, log wrapper.Log) types.Action {
    	proxywasm.RemoveHttpResponseHeader("content-length")
    	return types.ActionContinue
    }
    
    func onHttpResponseBody(ctx wrapper.HttpContext, config DingConfig, body []byte, log wrapper.Log) types.Action {
    	answer := gjson.GetBytes(body, "choices.0.message.content").String()
    	if answer == "" {
    		log.Error("Parse response body error")
    	}
    	newBody := fmt.Sprintf(defaultResponseFormat, answer)
    	proxywasm.ReplaceHttpResponseBody([]byte(newBody))
    	return types.ActionContinue
    }
    • 在处理请求时,解析用户的问题,然后转换为OpenAI协议的请求。例如原请求如下:

      {
        "msgtype": "text",
        "text": {
          "content": "你是谁?"
        }
      }

      自定义插件处理后,请求为:

      {
        "model": "gpt-3.5-turbo",
        "messages": [
          {
            "role": "user",
            "content": "你是谁?"
          }
        ]
      }
    • 处理响应时,解析大模型的返回结果,转换为钉钉的响应。例如原始响应如下:

      {
        "output": {
          "choices": [
            {
              "finish_reason": "stop",
              "message": {
                "role": "assistant",
                "content": "我是来自阿里云的超大规模语言模型,我叫通义千问。"
              }
            }
          ]
        }
      }

      自定义插件处理后,响应为:

      {
        "msgtype": "markdown",
        "markdown": {
          "title": "answer",
          "text": "我是来自阿里云的超大规模语言模型,我叫通义千问。"
        }
      }
  2. 发布插件:开发完自定义插件之后,您可参考上传插件将插件上传发布。

    重要

    本示例中,自定义插件的执行优先级要高于AI 代理、AI 检索增强生成插件,AI代理插件的优先级默认为100,AI检索增强生成的优先级默认为400,请您将自定义插件的优先级定义大于400,数字越大,优先级越高,否则将会报错。

  3. 安装并启用插件

    1. 在左侧导航栏,选择实例,并在顶部菜单栏选择地域。

    2. 实例页面,单击目标网关实例名称。

    3. 在左侧导航栏,单击插件,在页面左上角单击安装插件。

    4. 在安装插件的控制面板中,选择插件类型自定义,单击上传的自定义插件卡片上的安装,在安装插件对话框中单击安装并配置

    5. 单击自定义插件操作列的规则配置,单击实例级插件规则,打开启用开关,插件规则无需配置,单击保存

      image

2. 配置AI 代理插件

AI-Proxy实现OpenAI消息格式与各种大模型消息格式之间的转换,目前已支持国内外主流模型,本文以通义千问为例,关于插件的更多信息请参考AI 代理

  1. 在左侧导航栏,选择实例,并在顶部菜单栏选择地域。

  2. 实例页面,单击目标网关实例名称。

  3. 在左侧导航栏,单击插件,在页面左上角单击安装插件。

  4. 在安装插件的控制面板中,选择插件类型AI,单击AI 代理卡片上的安装,在安装插件对话框中单击安装并配置

  5. 在规则配置面板中,添加实例级插件规则,本文以通义千问模型为例,配置完成后单击保存

    image

    apiTokens:用于在访问 AI 服务时进行认证的令牌。本示例配置通义千问的API Key,支持单个或多个,当配置多个时自动轮询API Key。

步骤五:钉钉群中添加webhook机器人

在钉钉群中添加webhook机器人,配置IP地址(段)、选择开启Outgoing机制、POST地址设置为网关的地址:

image

开始对话

当完成上述步骤后,您可在钉钉群内与机器人展开对话了。

image

添加FAQ知识库实现钉钉机器人答疑功能

方案概览

image

接下来,我们在钉钉机器人基础对话方案的基础上,添加FAQ知识库,使机器人能够根据FAQ知识库回答用户的问题。

知识库文件示例

{
  "docs": [
    {
      "id": 1,
      "question": "MSE云原生网关上传自定义插件的个数限制是多少",
      "answer": "5个,且不支持调整"
    },
    {
      "id": 2,
      "question": "MSE云原生网关上传文件时报 413 Payload Too Large",
      "answer": "请求大小超过网关的链接缓存大小,需要调大“参数配置”中的DownstreamConnectionBufferLimits。"
    },
    {
      "id": 3,
      "question": "MSE云原生网关投递到用户侧的SLS Logstore是否可以修改索引配置?",
      "answer": "可以,用户可以自行根据需求调整,需要在mse_gw_access_log的“属性”中关闭索引自动更新。"
    }
  ]
}

步骤六:上传FAQ知识库至DashVector

  1. 登录向量检索服务控制台

  2. 创建API-KEY:在左侧导航栏,单击API-KEY管理,单击页面左上角创建新的API-KEYimage

  3. 创建Cluster:在左侧导航栏,单击Cluster列表,单击页面左上角创建Cluster

  4. 创建Collection:单击目标Cluster名称,选择Collection列表页签,单击创建Collection,如图所示配置相关参数。

    image

  5. 新增向量数据:可以使用如下脚本进行上传(根据实际情况进行修改),实现文档的向量化以及向量入库:

    上传脚本

    import requests
    from dashvector import Client, Doc
    import json
    
    DASHSCOPE_URL = "https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding/text-embedding"
    DASHSCOPE_APIKEY = ""
    DASHVECTOR_URL = "vrs-cn-xxxxxxxxxx.dashvector.cn-hangzhou.aliyuncs.com"
    DASHVECTOR_APIKEY = ""
    DASHVECTOR_COLLECTION = "qa_docs"
    
    
    # 将文本内容转换为向量
    def get_embedding(message):
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {DASHSCOPE_APIKEY}"
        }
        json_body = {
            "model": "text-embedding-v2",
            "input": {
                "texts": [
                    message,
                ]
            },
            "parameters": {
                "text_type": "document",
            }
        }
        resp = requests.post(DASHSCOPE_URL, headers=headers, json=json_body)
        return resp.json()["output"]["embeddings"][0]["embedding"]
    
    
    # 将向量及文档内容上传到阿里云向量检索服务中
    def upload_file(file_name):
        client = Client(
            api_key=DASHVECTOR_APIKEY,
            endpoint=DASHVECTOR_URL
        )
        collection = client.get(DASHVECTOR_COLLECTION)
        with open(file_name) as f:
            json_data = json.load(f)
        docs = []
        for item in json_data["docs"]:
            index = item["id"]
            question = item["question"]
            answer = item["answer"]
            raw = f'Q: {question}\n\nA: {answer}'
            vec = get_embedding(raw)
            # vec = get_embedding(question)
            docs.append(Doc(id=str(index), vector=vec, fields={"raw": raw}))
        rsp = collection.upsert(docs)
        assert rsp
    
    
    upload_file("faqs.json")
    

    执行成功后,到控制台查看文档上传结果:

    image

步骤七:创建向量服务

该服务用于词向量检索,由阿里云向量服务提供。

参考上文创建通义千问服务的操作步骤,创建向量服务,相关配置参数如下:

image

步骤八:配置增强插件

1. 配置AI 检索增强生成插件

参考上文配置AI 代理插件的操作步骤,安装AI 检索增强生成插件,并配置实例级插件规则,并启用规则。

插件规则参考示例

dashscope:
  serviceFQDN: dashscope.dns
  servicePort: 443
  serviceHost: dashscope.aliyuncs.com
  apiKey: xxxxxxxx
dashvector:
  serviceFQDN: dashvector.dns
  servicePort: 443
  serviceHost: vrs-cn-xxxxxxxxxx.dashvector.cn-hangzhou.aliyuncs.com
  apiKey: xxxxxxxx
  collection: qa_docs
  topk: 2 # 选择从知识库中检索多少篇文档
  threshold: 0.4 # 设置文档被采用的距离阈值,只有小于该距离的文档才会被采用
  field: raw # dashvector中字段

RAG插件的工作流程如下图所示:

yuque_diagram

2. 配置AI 提示词修饰插件

为了使机器人具有更好的答疑效果,您还可以使用AI 提示词修饰插件,用于修改llm请求的prompt,您可以参考如下配置规则进行配置:

配置实例

prepend:
- role: system
  content: 你是一个网关领域的专家,你的目的是简洁的回答我的问题。我在提问问题时,可能会提供多条参考资料,但是并非所有参考资料都是有效的,你在解答问题时,应该根据参考资料与问题是否相关对参考资料进行筛选。如果我没有提供参考资料,你应该根据自己的知识解答。

开始答疑

image

进阶

云原生API网关还提供了安全防护、可观测、非法内容检测、流量控制等插件,使机器人的功能更强大,您可参考如下架构图进行增强配置:

image

相关文档

  • 云原生API网关提供了全面的AI插件集,包含安全防护、多模型适配、可观测、缓存等多个开箱即用的插件,详细信息请参见概览

  • 云原生API网关提供插件市场,内置多种功能扩展插件,支持自定义插件,详细信息请参考插件市场