chatgpt api + function calling + 高德天气API + google custom search【联谷歌,联高德】

OpenAI 发布几个重磅更新

1、 开放 16k 上下文的 GPT-3.5-Turbo 模型(gpt-3.5-turbo-16k),是目前的4倍
2、 新的 GPT-4 和 3.5 Turbo 模型
3、Chat Completions API 中的新增加函数调用功能 (实时获取网络数据成为可能)
4、embeddings模型的成本降低75%
5、gpt-3.5-turbo的输入token成本降低了25% (开发者的福音)

其中最令人兴奋的功能应该当属函数调用功能,我们都知道ChatGPT训练的数据是基于2021年之前的,你要问一些实时性相关的问题就没法回答你了,而函数调用让实时获取网络数据成为可能,比如查询天气预报、查股票、推荐个近期的电影之类的。

哪些模型支持函数调用?

gpt-3.5-turbo-0613gpt-4-0613 这两个模型都支持函数调用。

函数调用的流程是什么?

1、用户发起提问时,调用一次带有函数描述的completions接口,gpt会判断是否支持调用函数,如果可以就从用户的提问信息中提取出函数所需要的参数。
2、开发者拿到gpt提取出来的参数后自行调用函数并返回结果
3、将函数的返回结果再次发给GPT做总结归纳为自然语言

需要注意的地方:

  1. 整个过程gpt会执行两次,第一次调用从问题中提取函数参数,第二次对函数返回结果做归纳总结
  2. 函数调用并不是由gpt调用,而是开发者来调用

举个真实例子

这里我以实时获取天气预报为例子演示函数调用的流程,天气预报接口我用的第三方高德API,免费,每天几千次的调用额度。申请注册流程这里省略,自行研究。

这个函数就是用来查询天气情况的,参数 city_name 是城市的名字,因为高德API只支持通过城市代码查天气,所以我这里做了一次根据城市名找到对应编码的查询

接口返回的结果中包含有温度(temperature)、风度(windpower)、风向(winddirection)、湿度(humidity)、天气(weather)等字段信息。

天气函数准备好后,用户开始提问:“深圳天气如何?“, 第一次调用`ChatCompletion`接口。

这里我们指定了一个functions 参数,该参数描述了函数的名字以及参数类型,比如我们这里定义了city_name的参数,gpt 就会从用户问题中提取出city_name。 

gpt 给我们返回的message中有function_call 字段,而且 arguments 里面提取了city_name这个字段的值。

第二步:从返回结果中提取参数后调用函数, 这个过程不是交给gpt处理,而是由开发者自己调用该函数,gpt做的事情是把函数需要的参数提取出来。

注意,这里要将函数调用返回的结果做一些json转换

第三交步:把返回结果给gpt做总结归纳

注意messages列表中最后一条消息中role角色是 function, 最后得到的结果second_response中的content内容为:

 

 

 

 

const functionCalling = async ({
  isUseGoogleSearch,
  params,
  messages,
  historyMessage,
  model,
}) => {

  const searchGoogleGptFunction = {
    name: 'search_google_when_gpt_cannot_answer',
    description:
      '当 gpt 遇到无法回答的或者需要搜索引擎协助回答时从 google 搜索',
    parameters: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: '搜索句,支持中文或者英文',
        },
      },
    },
  }

  const getCurrentWeather = {
    name: 'get_current_weather',
    description: '获取指定地区的当前天气情况',
    parameters: {
      type: 'object',
      properties: {
        city: {
          type: 'string',
          description: '城市,例如:深圳',
        },
      },
      required: ['city'],
    },
  }

  params = {
    ...params,
    functions: [searchGoogleGptFunction, getCurrentWeather],
    function_call: isUseGoogleSearch ? 'auto' : 'none', //fun?.function_call, none
  }

  let completionRes = await axios.post(`${baseURL}/api/light/chat/openAi`, {
    model,
    messages,
    apiKey,
    params,
  })

  const { completion } = completionRes.data.data

  if (
    completion &&
    Array.isArray(completion.choices) &&
    completion.choices.length > 0
  ) {
    if (
      completion.choices[0].finish_reason === 'function_call' &&
      completion.choices[0]?.message?.function_call?.name ===
        'search_google_when_gpt_cannot_answer'
    ) {
      try {
        let myArguments = completion.choices[0]?.message?.function_call?.arguments
        console.log('myArguments', myArguments)
        myArguments = myArguments ? JSON.parse(myArguments) : {}
        let pageNum = 1
        searchCount = searchCount + 1
        let message = myArguments.query
        console.log('google搜索次数', searchCount)
        console.log('google搜索关键词', message, Date())

        let timer = setTimeout(() => {
          customSendEmail({
            subject: 'google搜索超时',
            html: `google搜索超时,${message},${pageNum}`,
          })
        }, 1000 * 60)

        try {
          let result = await axios.post(
            `${baseURL}/api/light/chat/googleSearchOnAzure`,
            {
              message,
              pageNum: 1,
              apiKey,
            }
          )
          clearTimeout(timer)

          const { searchResult } = result.data.data

          delete searchResult.queries

          if (searchResult && Array.isArray(searchResult.items)) {
            let googleResultList = searchResult.items.map((item) => {
              return {
                title: item.title,
                snippet: item.snippet,
              }
            })
            const googleResultForGPT = `这是我的提问:${historyMessage}\n这是我在google搜索“${message}”的结果:\n${JSON.stringify(
              googleResultList
            )}\n请结合搜索结果回答`
            console.log(googleResultForGPT)
            let messagesTemp = [
              ...messages,
              {
                role: 'function',
                name: completion.choices[0]?.message?.function_call?.name,
                content: googleResultForGPT,
              },
            ]

            completionRes = await axios.post(
              `${baseURL}/api/light/chat/openAi`,
              {
                model,
                messages: messagesTemp,
                apiKey,
                params,
              }
            )
            return completionRes
          }
        } catch (err) {
          console.log('错误1', err)

          if (global.isLocal === false) {
            customSendEmail({
              subject: 'chatgpt错误【2】',
              html: `chatgpt错误【2】<br/>${err.stack}`,
            })
          }
        }
      } catch (err) {
        console.log('chatgpt错误【1】', err)

        if (global.isLocal === false) {
          customSendEmail({
            subject: 'chatgpt错误【1】',
            html: `chatgpt错误【1】<br/>${err.stack}`,
          })
        }
      }
    } else if (
      completion.choices[0].finish_reason === 'function_call' &&
      completion.choices[0]?.message?.function_call?.name ===
        'get_current_weather'
    ) {
      try {
        let myArguments = completion.choices[0]?.message?.function_call?.arguments
        console.log('myArguments,get_current_weather', myArguments)
        myArguments = myArguments ? JSON.parse(myArguments) : {}
        let city = myArguments.city

        let resultCity = cityList.find(item => item.name.includes(city))
        let cityCode = '110000'
        if (resultCity && resultCity.adcode) {
          cityCode = resultCity.adcode
        }
        console.log('城市', city, cityCode)


        let result = await axios.get(
          `https://restapi.amap.com/v3/weather/weatherInfo?key=${weatherApiKey}&city=${cityCode}`
        )
        const searchResult = result.data

        if (searchResult && Array.isArray(searchResult.lives)) {
          const searchResultForGPT = `这是我的提问:${historyMessage}\n这是我查询到的结果:\n${JSON.stringify(
            searchResult.lives
          )}\n请结合搜索结果回答`
          console.log(searchResultForGPT)
          let messagesTemp = [
            ...messages,
            {
              role: 'function',
              name: completion.choices[0]?.message?.function_call?.name,
              content: searchResultForGPT,
            },
          ]

          completionRes = await axios.post(`${baseURL}/api/light/chat/openAi`, {
            model,
            messages: messagesTemp,
            apiKey,
            params,
          })
          return completionRes
        }
      } catch (err) {
        console.log('chatgpt错误【3】', err)

        if (global.isLocal === false) {
          customSendEmail({
            subject: 'chatgpt错误【3】',
            html: `chatgpt错误【3】<br/>${err.stack}`,
          })
        }
      }
    }
  }
}
const { google } = require('googleapis')


const chatGoogleSearchOnAzure = async (req, res) => {
  let { message = '', pageNum = 1, apiKey = 'sk-xxx' } = req.body

  if (apiKey === 'xxx') {
    let start = (pageNum - 1) * 10

    const customSearch = google.customsearch('v1')
    const customSearchRes = await customSearch.cse.list({
      cx: googleCx,
      key: googleKey,
      q: message,
      start,
      num: 10,
      hl: 'zh-CN',
      safe: 'off',
      imgColorType: 'color',
    })

    const searchResult = customSearchRes.data

    res.send({
      code: 200,
      data: {
        searchResult: searchResult,
      },
      message: '成功',
    })
  } else {
    res.send({
      code: 400,
      message: '失败:参数apiKey',
    })
  }
}

参考链接:

https://wejson.cn/excel2json/

https://lbs.amap.com/api/webservice/guide/api/weatherinfo/ 

 https://m.moji.com/weather/china/beijing/beijing

https://zhuanlan.zhihu.com/p/637425118 

http://chat.xutongbao.top/ 

猜你喜欢

转载自blog.csdn.net/xutongbao/article/details/131599916