Zero code, use Dify to connect to the enterprise WeChat AI robot in two minutes

This article was contributed by Dify deep user @李昱昊. Based on the ability of Dify to create AI applications, this best practice article is produced. We welcome more outstanding developers to contribute best practices to us. Please email: [email protected], or join the Discord community (https://discord.com/invite/FngNHpbcY7) to contact official staff.

Dify allows the creation of AI applications and provides secondary development capabilities. Here I will demonstrate the creation of an AI application (robot) for a legal question-and-answer assistant, called "Zhifa". In this tutorial, I will guide you to access WeChat Enterprise for "Zhifa".

Preparation

  • Administrator authority of WeChat Work;
  • A Dify account (https://dify.ai/);
  • A Laf cloud account (https://laf.run/);
  • (Optional) An OpenAI API Key. If not, you can use the 200 call opportunities provided by Dify for free for testing;
  • (Optional) Create a new env.txt file on the computer, and copy the following content into env.txt. In the following tutorials, we will fill in the relevant information step by step into this file. Steps that require saving information are highlighted.
WXWORK_TOKEN=""
WXWORK_AESKEY=""
WXWORK_CORPID=""
WXWORK_AGENTID=""
WXWORK_CORPSECRET=""
DIFY_APPTOKEN=""

Create an app on Dify

This chapter will introduce how to create a dataset of legal knowledge and associate the dataset with the application.

Building a Legal Knowledge Dataset
In order for "Zhifa" to understand more context, we need to create a legal knowledge database.

1. Import documents: import PDF documents of legal knowledge from the computer.

3.1.png

2. Text segmentation and cleaning: The uploaded text needs to be processed twice before it can be understood by the large language model. Here we don't need to pay attention to the specific implementation logic, just select automatic segmentation, and then click "Save and Process".

3.2.png

3. Text embedding: After about 30s, the dataset is successfully created. You can always come back and add more files to the database.

3.3.png

(See more operations on building datasets in Dify official documentation: https://docs.dify.ai/v/zh-hans/advanced/datasets)

build application

1. Create an application: According to the instructions in the figure, create a conversational application and name it "Zhifa".

3.4.png

2. Associate the data set: On the "Prompt Word Arrangement" page, add and select the newly created data set in the "Context" module.

3.5.png

3. Publish the model: After completing the associated dataset, click "Publish" in the upper right corner of the page to make the model take effect.

3.6.png

4. Obtain an API access key: On the "Access API" page, create an API key and copy and save it asDIFY_APPTOKEN . Please be careful not to disclose the key to anyone, so as not to cause property damage.

3.7.png

(See more about creating an application in the Dify official documentation: https://docs.dify.ai/v/zh-hans/application/creating-an-application)

Create enterprise WeChat application

1. Record enterprise information: enter the enterprise WeChat management background - my enterprise, and record the enterprise ID here asWXWORK_CORPID。

3.8.png

2. Create an enterprise WeChat application: Enter the application management page, click [Create Application] to enter the creation page, fill in the application information and click [Create Application]. If you already have an existing application, you can skip this step.

3.9.png

3.10.png

3. Record enterprise WeChat application information: Click the newly created application on the application management page to enter the application details page. Record the AgentId and Secret here (you need to click the Get button to get it in the enterprise WeChat chat window), which are WXWORK_AGENTID and WXWORK_CORPSECRET respectively.

3.11.png

4. Enterprise WeChat application receiving information: On the application details page, click [Set API receiving] in the receiving information section.

3.12.png

On the API receiving message page, click two [Random Obtain] buttons, it will automatically generate a Token and EncodingAESKey, which we record as WXWORK_TOKEN and WXWORK_AESKEY respectively. Note, do not close this page, we will fill in the URL after the Laf side is configured.

3.13.png

Create a cloud function on Laf cloud

1. Create a new Laf cloud application: After entering Laf, click New to create a cloud application. Choose the free plan here.

3.14.png

2. Add dependencies: Enterprise WeChat application needs to be added @wecom/crypto, xml2jstwo dependencies. Once added, your dependency list should look like this.

3.15.png

3. Add environment variables: From the second line, paste all the content collected in the above steps here, and click Update.

3.16.png

4. Create a cloud function: Click to create a cloud function, pay attention to check , in "Request Method" POST, GET and click OK.

3.17.png

In the created cloud function, delete the default code, and paste all the code in the "Appendix" at the bottom of this article here.

3.18.png

5. Click Publish Cloud Function: After clicking Publish, the cloud function will take effect.

3.19.png

Now paste the URL into the blank space on the [Set API Receive] page of the enterprise WeChat background, and then click Save.

3.20.png

6. Configure the IP whitelist: find the newly created application in the enterprise WeChat, and send a message. Unsurprisingly, no messages were received. This is because Work WeChat blocks the IP of Laf Cloud by default. Click on the log, you should be able to see such an error report' not allow to access from your ip'

3.21.png

Click to view the details of this log, and record the Laf cloud IP given in the log:

3.22.png

Go back to the management background of WeChat Enterprise, click on the application just created, and configure a feasible IP for the application:

3.23.png

Just fill in the IP recorded in the log just now:

3.24.png

Verify the effect

1. Test chat: Find the newly created app in WeChat Work, and send a message. You should now be able to receive push messages.

3.25.png

appendix

Enterprise WeChat application code - (pseudo-streaming response)

import cloud from '@lafjs/cloud'
import { decrypt, getSignature } from '@wecom/crypto'
import xml2js from 'xml2js'

function genConversationKey(userName) {
  return `${process.env.WXWORK_AGENTID}:${userName}`
}

function genWxAppAccessTokenKey() {
  return `${process.env.WXWORK_AGENTID}:access-token`
}

async function getToken() {
  console.log('[getToken] called')

  const cache = cloud.shared.get(genWxAppAccessTokenKey())
  if (cache && cache.expires >= Date.now()) return cache.token

  const res = await cloud.fetch({
    url: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken',
    method: 'GET',
    params: {
      corpid: process.env.WXWORK_CORPID,
      corpsecret: process.env.WXWORK_CORPSECRET,
    }
  })

  const token = res.data.access_token
  cloud.shared.set(genWxAppAccessTokenKey(), { token, expires: Date.now() + res.data.expires_in * 1000 })
  return token
}

async function sendWxMessage(message, user) {
  console.log('[sendWxMessage] called', user, message)

  const res = await cloud.fetch({
    url: 'https://qyapi.weixin.qq.com/cgi-bin/message/send',
    method: 'POST',
    params: {
      access_token: await getToken()
    },
    data: {
      "touser": user,
      "msgtype": "text",
      "agentid": process.env.WXWORK_AGENTID,
      "text": {
        "content": message
      },
      "safe": 0,
      "enable_id_trans": 0,
      "enable_duplicate_check": 0,
      "duplicate_check_interval": 1800
    },
  })
  console.log('[sendWxMessage] received', res.data)
}

async function sendDifyMessage(message, userName, onMessage) {
  console.log('[sendDifyMessage] called', message, userName)

  const conversationId = cloud.shared.get(genConversationKey(userName)) || null
  let newConversationId = ''
  let responseText = ''

  try {
    const response = await cloud.fetch({
      url: 'https://api.dify.ai/v1/chat-messages',
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.DIFY_APPTOKEN}`
      },
      data: {
        inputs: {},
        response_mode: "streaming",
        query: message,
        user: userName,
        conversation_id: conversationId
      },
      responseType: "stream"
    })

    let firstHalfMessage = ''
    response.data.on('data', (data) => {
      let message = data.toString()
      try {
        if (firstHalfMessage) {
          message += firstHalfMessage
          firstHalfMessage = ''
        }
    
        // 检查是不是sse协议
        if (!message.startsWith('data: ')) return
    
        const parsedChunk: Record<string, any> = JSON.parse(message.substring(6))
    
        if (!newConversationId) {
          newConversationId = parsedChunk.conversation_id
          cloud.shared.set(genConversationKey(userName), newConversationId)
        }
        const { answer } = parsedChunk
        responseText += answer
    
        // 伪流式响应
        if (answer.endsWith('\n\n') || (responseText.length > 120 && /[?。;!]$/.test(responseText))) {
          onMessage(responseText.replace('\n\n', ''))
          console.log('[sendDifyMessage] received', responseText, newConversationId)
          responseText = ''
        }
      } catch (e) {
        firstHalfMessage = message
        console.error('[sendDifyMessage] error', message)
      }
    
    })
    
    // stream结束时把剩下的消息全部发出去
    response.data.on('end', () => {
      onMessage(responseText.replace('\n\n', ''))
    })
  } catch (e) {
    console.error("[sendDifyMessage] error", e)
  }
}

async function asyncSendMessage(xml) {
  console.log('[asyncSendMessage] called', xml)

  if (xml.MsgType[0] !== 'text') return

  const message = xml.Content[0]
  const userName = xml.FromUserName[0]

  if (message === '/new') {
    // 重置conversation id
    cloud.shared.set(genConversationKey(userName), null)
    sendWxMessage('新建成功,开始新的对话吧~~', userName)
    return
  }

  sendWxMessage('AI思考中, 请耐心等待~~', userName)

  try {
    sendDifyMessage(message, userName, (message) => {
      sendWxMessage(message, userName)
    })
  }
  catch (e) {
    console.error('[sendDifyMessage] error', e)
    sendWxMessage('接口请求失败,请联系管理员查看错误信息', userName)
  }
}

export default async function (ctx: FunctionContext) {
  const { query } = ctx
  const { msg_signature, timestamp, nonce, echostr } = query
  const token = process.env.WXWORK_TOKEN
  const key = process.env.WXWORK_AESKEY
  console.log('[main] called', ctx.method, ctx.request.url)

  // 签名验证专用
  if (ctx.method === 'GET') {
    const signature = getSignature(token, timestamp, nonce, echostr)
    if (signature !== msg_signature) {
      return { message: '签名验证失败', code: 401 }
    }
    const { message } = decrypt(key, echostr)
    return message
  }

  const payload = ctx.body.xml
  const encrypt = payload.encrypt[0]
  const signature = getSignature(token, timestamp, nonce, encrypt)
  if (signature !== msg_signature) {
    return { message: '签名验证失败', code: 401 }
  }

  const { message } = decrypt(key, encrypt)
  const {
    xml
  } = await xml2js.parseStringPromise(message)
  // 由于GPT API耗时较久,这里提前返回,防止企业微信超时重试,后续再手动调用发消息接口
  ctx.response.sendStatus(200)

  await asyncSendMessage(xml)

  return { message: true, code: 0 }
}

Citation: This in-depth reference to the following article, thanks to the hard work of the original author: https://forum.laf.run/d/556/2

(over)

About Dify:
Dify.AI is an LLMOps platform that helps developers build AI applications easier and faster. Its core idea is to define all aspects of AI applications, including prompts, contexts, and plug-ins, through declarative YAML files. Dify provides functions such as visual prompt orchestration, operation, and data set management. These functions enable developers to complete the development of AI applications in a few days, or quickly integrate LLM into existing applications, and carry out continuous operation and improvement to create a truly valuable AI application.

Guess you like

Origin blog.csdn.net/DifyAI/article/details/131972112