头图

前言

随着AI越来越火热,越来越多的产品都接入了DeepSeek,今天我们就来尝试在微信小程序中集成国产顶尖大模型DeepSeek,解锁智能对话|实时翻译|长文本生成三大核心场景

准备工作

  • 注册一个微信小程序账号,并且创建本地小程序工程项目
  • 小程序基础库需要在 3.7.1 及以上版本,具备 wx.cloud.extend.AI 对象
  • 小程序需要开通「云开发」,可在小程序开发工具中点击工具栏里的「云开发」按钮进行开通,并创建环境(PS:对于首次使用云开发的用户,第一个月套餐免费)

开发,调用大模型

以上工作完成后,我们会得到一个云开发环境ID,接下来我们可以正式接入 DeepSeek API。这里我使用的是Taro框架,开发过程中会遇到一些坑点,一起往下看看都是如何解决的。

初始化云开发环境

if(process.env.TARO_ENV === 'weapp') {
  Taro.cloud.init({
    env: 'cloud1-6gxxxxxxxxxxxx'
  })
}

创建AI模型

const init = async () => {
   model = Taro.cloud.extend.AI.createModel("deepseek");
  // 创建模型实例,这里使用 DeepSeek 大模型
}

调用生成文本

const userInput = ref('')  // 用户输入内容
const messageList = ref<ChatMessage[]>([])  // 对话列表

const send = async () => {
  const res = await model.streamText({
      data: {
        model: "deepseek-r1", // 指定具体的模型
        messages: [
          { role: "user", content: userInput.value },
        ],
      },
  })
  
  // push 用户输入的消息到消息列表
  messageList.value.push({
    role: 'user',
    content: userInput.value,
  })
  userInput.value = ''  // 清空输入框
  
  if(res.textStream) {
     // 先push一条空内容,表示ai返回内容
    messageList.value.push({
      role: 'ai',
      content: '',
    })
     // 接收大模型的响应
    // 由于大模型的返回结果是流式的,所以我们这里需要循环接收完整的响应文本。
    for await (let str of res.textStream) {
      messageList.value[messageList.value.length - 1].content += str;
    }
  }
}

整个基本对话流程就好了,我们来看看效果,UI部分代码就不贴了,比较简单(一个输入框一个列表)

可以看到,这样我们就完成了AI对话的基本交互,但是有一个问题,AI思考过程时间比较长,用户对这个过程无感知,容易产生无响应的错觉,所以我们需要优化一下大模型思考等待过程的提示交互。

model.streamText除了可以传入data数据,也可以传入一些事件监听,需要吐槽一下微信开发文档,写的太粗糙了,事件里面的参数及数据一个也没介绍,完全需要自己调试摸索...

const isGenerating = ref(false)
const send = async () => {
  console.log('发送消息', userInput.value)
  if(isGenerating.value) {
    Taro.showToast({
      title: '正在生成中,请稍后再试',
      icon: 'none',
    })
    return
  }
  isGenerating.value = true

  // 将系统提示词和用户输入,传入大模型
  const res = await model.streamText({
    data: {
      model: "deepseek-r1", // 指定具体的模型
      messages: [
        { role: "user", content: userInput.value },
      ],
    },
    onEvent: (event: any) => {
      console.log('event', event);
      // 处理事件流
      if(event.delta) {
        // 开始返回内容
        console.log('开始返回内容', event.delta);
        messageList.value[messageList.value.length - 1].status = 2;
      }else if(event.data === '[DONE]') {
        // 结束返回内容
        console.log('结束返回内容');
        messageList.value[messageList.value.length - 1].status = 3;
      }
    },
    onFinish: (event: any) => {
      console.log('finish', event);
      // 处理完成事件
      messageList.value[messageList.value.length - 1].status = 3;
    },
  });
  // push 用户输入的消息到消息列表
  messageList.value.push({
    role: 'user',
    content: userInput.value,
    status: 3
  })
  userInput.value = ''

  if(res.textStream) {
    messageList.value.push({
      role: 'ai',
      content: '',
      status: 0,
    })
    // 由于大模型的返回结果是流式的,所以我们这里需要循环接收完整的响应文本。
    for await (let str of res.textStream) {
      messageList.value[messageList.value.length - 1].content += str;
    }
  }
  isGenerating.value = false
}

通过事件监听为每条消息加上了一个status状态

status: number // 0: 发送中 1: 思考中 2: 回答中 3: 回答完成
<view v-else>
  <view v-if="item.status < 2">思考中💭...</view>
  <view class="ai_message" v-else v-html="item.content"></view>
</view>

再来看看此时的效果:

2-4

处理markdown

由于大模型返回的内容可能会含有markdown语法,所以我们不值简单的直接将大模型返回的内容直接进行渲染,这样就无法还原大模型吐出的排版

比如:

2-5

这些markdown语法都没有按预期排版进行渲染,为了排版更加好看,我们还需要处理markdown语法。

小程序虽然支持渲染富文本,但是如何渲染 Markdown 是一个问题。经过调研,我找到了 towxml第三方组件,它除了支持基本的 Markdown 语法,还支持下面三种格式的渲染:

  1. 支持 echarts 图表(3.0+)
  2. 支持 LaTex 数学公式(3.0+)
  3. 支持 yuml 流程图(3.0+)

⚠️需要注意的是:Towxml 是纯微信小程序组件,所以想要在taro项目中使用,还是有点麻烦的。

构建 Towxml

  1. 下载Towxml源码
git clone https://github.com/sbfkcel/towxml.git
  1. 安装依赖,进入项目根目录找到config.js,根据配置文件构建自己需要的功能
npm install

由于我只需要markdown部分的处理,所以对于echarts、laTex、yuml我都注释掉了,可以减小产物体积

2-6

  1. 打包,并且将产物放入taro项目中,修改decode.json组件引入路径
npm run build

2-7

使用Towxml组件

产物挪到项目后,就可以使用了,由于是小程序原生组件,所以需要在配置文件中进行引入

export default definePageConfig({
  usingComponents: {
    towxml: "./components/towxml/towxml"
  }
})

调用towxmlParse方法处理markdown

import towxmlParse from './components/towxml/index'

const send = async () => {
   // ...
    let content = ''
    for await (let str of res.textStream) {
      // messageList.value[messageList.value.length - 1].content += str;
      content += str;
      messageList.value[messageList.value.length - 1].content = towxmlParse(content, 'markdown');
    }
  // ...

}

打开微信开发者工具,你会发现控制台有警告,组件也不能正常渲染

2-8

原因是 Vue 把它当做 Vue 组件来解析,我们可以修改 VueLoader 的编译配置 compilerOptions.isCustomElement,把此组件声明为原生组件。

plugins: [
    [
      '@tarojs/plugin-framework-vue3',
      {
        vueLoaderOption: {
          compilerOptions: {
            isCustomElement: (tag) => tag.includes('towxml'),
            whitespace: 'preserve',
            // ...
          },
          reactivityTransform: true, // 开启vue3响应性语法糖
        },
      },
    ],
  ],

再重启项目来看看效果:

image.png

可以看到此时的markdown渲染就正常了。


南玖
1.2k 声望111 粉丝