DeepSeek API集成:让小程序拥有AI大脑

本文基于「灶台导航」小程序集成 DeepSeek API 的实际经验,详解 HTTP 调用、JSON 模式、超时重试、错误降级等核心要点。

一、为什么选择 DeepSeek?

在为小程序接入 AI 能力时,我们对比了多个方案:

方案 优势 劣势
微信同声传译插件 官方支持 能力有限
腾讯云 AI 国内稳定 价格较高
OpenAI API 效果好 国内访问不稳定
DeepSeek API 国产、便宜、效果好 相对较新

最终选择 DeepSeek,原因:

  • 国产服务,访问稳定
  • 价格实惠(约为 GPT 的 1/10)
  • 中文效果好
  • API 兼容 OpenAI 格式

二、API 基础配置

2.1 获取 API Key

  1. 访问 DeepSeek 开放平台
  2. 注册账号并登录
  3. 创建 API Key
  4. 复制保存 Key(只显示一次)

2.2 云函数配置

javascript 复制代码
// cloudfunctions/chat/index.js
const cloud = require('wx-server-sdk')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })

// DeepSeek API 配置
const DEEPSEEK_CONFIG = {
  baseUrl: 'https://api.deepseek.com',
  apiKey: '你的apiKey',
  model: 'deepseek-chat',
  temperature: 0.7,
  maxTokens: 512  // 减少输出token加速响应
}

安全建议:API Key 应存储在云函数环境变量中,而非硬编码:

javascript 复制代码
// 从环境变量读取
const DEEPSEEK_CONFIG = {
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseUrl: 'https://api.deepseek.com',
  model: 'deepseek-chat'
}

3.3 安装 HTTP 请求库

json 复制代码
// cloudfunctions/chat/package.json
{
  "name": "chat",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "wx-server-sdk": "~2.6.3",
    "got": "^11.8.6"
  }
}

使用 got@11.x 版本(支持 CommonJS)。


三、基础调用实现

3.1 简单对话

javascript 复制代码
// cloudfunctions/chat/index.js
const cloud = require('wx-server-sdk')
const got = require('got')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })

const DEEPSEEK_CONFIG = {
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseUrl: 'https://api.deepseek.com',
  model: 'deepseek-chat'
}

exports.main = async (event, context) => {
  const { message } = event

  try {
    const response = await got.post(`${DEEPSEEK_CONFIG.baseUrl}/chat/completions`, {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${DEEPSEEK_CONFIG.apiKey}`
      },
      json: {
        model: DEEPSEEK_CONFIG.model,
        messages: [
          {
            role: 'system',
            content: '你是一个智能菜谱助手,帮助用户解答烹饪相关问题。'
          },
          {
            role: 'user',
            content: message
          }
        ]
      },
      responseType: 'json'
    })

    const reply = response.body.choices[0].message.content

    return {
      errCode: 0,
      data: {
        reply: reply
      },
      errMsg: 'success'
    }

  } catch (err) {
    console.error('DeepSeek API 调用失败:', err)
    return {
      errCode: -1,
      data: null,
      errMsg: 'AI 服务暂时不可用'
    }
  }
}

3.2 JSON 模式输出

当需要 AI 返回结构化数据时,使用 JSON 模式:

javascript 复制代码
const response = await got.post(`${DEEPSEEK_CONFIG.baseUrl}/chat/completions`, {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${DEEPSEEK_CONFIG.apiKey}`
  },
  json: {
    model: DEEPSEEK_CONFIG.model,
    messages: [
      {
        role: 'system',
        content: '你是一个菜谱推荐助手,根据用户需求推荐菜谱。返回 JSON 格式数据。'
      },
      {
        role: 'user',
        content: `请推荐适合家庭晚餐的菜谱,返回格式:
        {
          "recipes": [
            { "name": "菜名", "description": "简介", "difficulty": "难度" }
          ]
        }`
      }
    ],
    response_format: { type: 'json_object' }  // 强制 JSON 输出
  },
  responseType: 'json'
})

// 解析 JSON
const replyText = response.body.choices[0].message.content
const result = JSON.parse(replyText)

四、超时与重试

4.1 设置超时

javascript 复制代码
const response = await got.post(url, {
  // ... 其他配置
  timeout: {
    request: 30000  // 30 秒超时
  }
})

4.2 重试机制

javascript 复制代码
/**
 * 带重试的 API 调用
 */
async function callDeepSeekWithRetry(messages, maxRetries = 3) {
  let lastError

  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await got.post(`${DEEPSEEK_CONFIG.baseUrl}/chat/completions`, {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${DEEPSEEK_CONFIG.apiKey}`
        },
        json: {
          model: DEEPSEEK_CONFIG.model,
          messages: messages
        },
        responseType: 'json',
        timeout: {
          request: 30000
        }
      })

      return response.body

    } catch (err) {
      lastError = err
      console.warn(`第 ${i + 1} 次调用失败:`, err.message)

      // 指数退避
      if (i < maxRetries - 1) {
        await sleep(1000 * Math.pow(2, i))
      }
    }
  }

  throw lastError
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

五、错误降级

5.1 什么是降级?

当 AI 服务不可用时,使用本地数据库的预设回复或关键词匹配作为后备方案。

复制代码
用户请求
    │
    ▼
调用 DeepSeek API
    │
    ├── 成功 → 返回 AI 回复
    │
    └── 失败 → 降级方案
                  │
                  ▼
             关键词匹配本地数据
                  │
                  ▼
             返回预设回复

5.2 降级实现

javascript 复制代码
// cloudfunctions/chat/index.js
/**
 * DeepSeek API 调用
 */
async function callDeepSeekAPI(history, userMessage, userContext, ragContext = '', ragRecipes = []) {
  // 1. 构建 system prompt(基础 + RAG 上下文)
  let systemContent = SYSTEM_PROMPT
  if (ragContext) {
    systemContent += '\n\n' + ragContext
  }

  const messages = [
    { role: 'system', content: systemContent }
  ]

  // 添加历史对话(只取 role 和 content,去掉 timestamp 等额外字段)
  for (const item of history) {
    if (item.role === 'user' || item.role === 'assistant') {
      messages.push({ role: item.role, content: item.content })
    }
  }

  // 添加当前用户消息(拼接上下文)
  const fullMessage = userMessage + userContext
  messages.push({ role: 'user', content: fullMessage })

  // 2. 发送 HTTP 请求到 DeepSeek API
  try {
    const response = await got.post(`${DEEPSEEK_CONFIG.baseUrl}/chat/completions`, {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${DEEPSEEK_CONFIG.apiKey}`
      },
      json: {
        model: DEEPSEEK_CONFIG.model,
        messages: messages,
        temperature: DEEPSEEK_CONFIG.temperature,
        max_tokens: DEEPSEEK_CONFIG.maxTokens,
        response_format: { type: 'json_object' }  // 强制 JSON 输出
      },
      timeout: {
        request: TIMEOUT_CONFIG.deepseekApi  // 使用配置的超时时间
      }
    })

    // 3. 解析 DeepSeek 响应
    const responseBody = JSON.parse(response.body)
    const aiContent = responseBody.choices[0].message.content

    console.log('[chat] DeepSeek 响应:', aiContent.substring(0, 200))

    // 4. 解析 AI 返回的 JSON
    const parsed = parseAIReply(aiContent)

    // 5. 将 AI 推荐菜谱的 recipeId 替换为真实数据库 _id
    if (parsed.recommendations && parsed.recommendations.length > 0) {
      parsed.recommendations = await resolveRecipeIds(parsed.recommendations, ragRecipes)
    }
    // 同样处理 cookData 中的 recipeIds
    if (parsed.cookData && parsed.cookData.recipeIds && parsed.cookData.recipeIds.length > 0) {
      parsed.cookData.recipeIds = await resolveRecipeIdsToIdList(parsed.cookData.recipeIds, ragRecipes)
    }

    return parsed

  } catch (err) {
    console.error('[chat] DeepSeek API 调用失败:', err.message)

    // 超时或网络错误时使用本地兜底方案
    if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT' || err.message.includes('timeout')) {
      console.warn('[chat] API超时,降级为本地关键词匹配方案')
    } else {
      console.warn('[chat] API错误,降级为本地关键词匹配方案')
    }
    return await fallbackResponse(userMessage)
  }
}

/**
 * 降级匹配
 */
async function fallbackResponse(userMessage) {
  const msg = userMessage.toLowerCase()

  // 常见问候语直接回复
  const greetings = ['你好', 'hello', 'hi', '嗨', 'hey', '早上好', '下午好', '晚上好', '在吗']
  if (greetings.some(g => msg.includes(g))) {
    return {
      reply: '你好!我是灶台导航助手。告诉我你想做什么菜,或者说说家里有什么食材,我来帮你推荐~',
      action: 'ask',
      recommendations: [],
      cookData: null
    }
  }
 ......
 }

六、上下文管理

6.1 多轮对话

保持对话上下文,实现连续交互:

javascript 复制代码
// 存储对话历史
let conversationHistory = []

async function chatWithHistory(message) {
  // 添加用户消息
  conversationHistory.push({
    role: 'user',
    content: message
  })

  // 限制历史长度(避免 token 过多)
  const maxHistory = 10
  if (conversationHistory.length > maxHistory) {
    conversationHistory = conversationHistory.slice(-maxHistory)
  }

  const response = await got.post('https://api.deepseek.com/chat/completions', {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`
    },
    json: {
      model: 'deepseek-chat',
      messages: [
        { role: 'system', content: '你是智能菜谱助手' },
        ...conversationHistory
      ]
    },
    responseType: 'json'
  })

  const reply = response.body.choices[0].message.content

  // 添加 AI 回复到历史
  conversationHistory.push({
    role: 'assistant',
    content: reply
  })

  return reply
}

6.2 使用数据库存储历史

javascript 复制代码
// cloudfunctions/chat/index.js

exports.main = async (event, context) => {
  const { message, sessionId } = event
  const wxContext = cloud.getWXContext()
  const openid = wxContext.OPENID

  // 获取历史对话
  let history = []
  if (sessionId) {
    const sessionRes = await db.collection('chat_sessions')
      .where({
        _id: sessionId,
        _openid: openid
      })
      .get()

    if (sessionRes.data.length > 0) {
      history = sessionRes.data[0].messages || []
    }
  }

  // 构建消息列表
  const messages = [
    { role: 'system', content: '你是智能菜谱助手' },
    ...history.slice(-10),  // 最近 10 条
    { role: 'user', content: message }
  ]

  // 调用 AI
  const response = await callDeepSeek(messages)

  // 保存历史
  const newHistory = [
    ...history,
    { role: 'user', content: message },
    { role: 'assistant', content: response }
  ]

  if (sessionId) {
    await db.collection('chat_sessions').doc(sessionId).update({
      data: { messages: newHistory, updateTime: db.serverDate() }
    })
  } else {
    const createRes = await db.collection('chat_sessions').add({
      data: {
        _openid: openid,
        messages: newHistory,
        createTime: db.serverDate()
      }
    })
  }

  return {
    errCode: 0,
    data: {
      reply: response,
      sessionId: sessionId || createRes._id
    }
  }
}

七、流式响应(小程序端)

由于小程序云函数不支持流式返回,可以采用轮询或分段返回的方式。

7.1 分段返回方案

javascript 复制代码
// 云函数中生成完整回复后返回
// 前端逐字显示

// 前端实现打字机效果
function typeWriter(text, element, speed = 50) {
  let index = 0
  element.text = ''

  const timer = setInterval(() => {
    if (index < text.length) {
      element.text += text[index]
      index++
    } else {
      clearInterval(timer)
    }
  }, speed)
}

八、总结

DeepSeek API 集成要点:

要点 说明
配置管理 API Key 存环境变量
超时重试 设置超时 + 指数退避重试
错误降级 AI 失败时使用关键词匹配
上下文管理 数据库存储对话历史
成本控制 缓存 + 限制历史长度

通过合理的架构设计,可以让小程序稳定地接入 AI 能力。

后续还可以添加RAG检索生成功能,将RAG检索生成的context加入提问词中优化回答效果

javascript 复制代码
if (ragContext) {
    systemContent += '\n\n' + ragContext
  }

作者:「倒灶了队」

项目:灶台导航 - 微信小程序

更新时间:2026-04-18

相关推荐
hqyjzsb2 小时前
AI培训课程怎么设计才有效?
人工智能·职场和发展·aigc·产品经理·学习方法·业界资讯·设计语言
深海鱼在掘金2 小时前
AI时代的魔法咒语:那些被吹爆了的价值百万的AI提示词(二)
人工智能
深海鱼在掘金2 小时前
AI时代的魔法咒语:那些被吹爆了的价值百万的AI提示词(一)
人工智能
小汪说干货2 小时前
2026年4月最新|公众号文章插入文档附件3种技术方案
javascript·小程序
Cisyam^2 小时前
Bright Data Web Scraping 指南:用 MCP + Dify 自动采集 TikTok 与 LinkedIn数据
大数据·前端·人工智能
人工智能AI技术3 小时前
聚类算法基础:K-Means 到底如何工作
人工智能
captain_AIouo3 小时前
Captain AI功能全景解析——从选品到物流的智能闭环
大数据·人工智能·经验分享·aigc
深海鱼在掘金3 小时前
从图灵测试到Openclaw:一部80年AI“智慧觉醒”史诗
人工智能
TLeung653673 小时前
国家怕你失业没技能,亲自出手2万多门免费课彻底砸了培训机构的饭碗
人工智能