如何为chatGPT增加网络访问功能

释放双眼,带上耳机,听听看~!
本文章介绍了如何通过使用搜索引擎API为chatGPT增加网络访问功能,以便获取最新信息。

问题背景

我们都知道chatGPT无法访问较新的数据,比如我希望询问硅谷银行的事情时,得到的回答是这样的:

如何为chatGPT增加网络访问功能

当然如果使用newbing 可以达到获得我们想要的结果,那么除了newbing,我们能否自己为chatGPT增加网络访问功能呢?

解决方案

要解决这个问题,我们很容易想到需要搜索引擎这个工具,那么现在我们有2个工具1个是chatGPT,1个是搜索引擎, 接下来我们看什么样的流程可以完成我们的目标。

提供Context

如果我们可以在给chatGPT 提问时带上这些GPT所不知道的信息,chatGPT是可以给出不错的结果的。比如:

如何为chatGPT增加网络访问功能

这些信息可以通过我们的工具搜索引擎获得,但这里的问题是如何得到搜索的关键字,因为我们并不知道GPT需要知道哪些信息。

让GPT自动生成要搜索的关键字

这里很好的1个思路是使用GPT产生要搜索关键字,这里应该是有借助GPT的推理能力,这也是最为关键的prompt

prompt:

'''Answer the following questions as best you can. 
You have access to the following tools:nnBing Search: 
A wrapper around Bing Search. Useful for when you need to answer questions 
about current events. Input should be a search query.nnUse the following 
format:nnQuestion: the input question you must answernThought: 
you should always think about what to donAction: the action to take, 
should be one of [Bing Search]nAction Input: the input to the actionn
Observation: the result of the actionn... (this Thought/Action/Action 
Input/Observation can repeat N times)nThought: 
I now know the final answernFinal Answer: 
the final answer to the original input questionnnBegin!nnQuestion: 
${问题}nThought:'''

当我们使用这个prompt时,GPT就会输出它需要知道的搜索关键字,也就是prompt中的 Action Input

然后我们将GPT产生的搜索关键字,使用搜索引擎API,查到最新的信息,最后将搜索引擎返回的最新结果和查询结果一同输入到GPT里面,得到最终结果。

这里注意,我们只是叙述1个最简模型,当我们询问的问题中有较多信息GPT不知道时,GPT可能会查询多次。

完整流程

画出我们的流程图:

如何为chatGPT增加网络访问功能

实现

从0实现

按照这个思路我们使用cursor 写了1个nodejs express应用实现这个功能, 以下是服务端部分(此代码部分由cursor完成)。

const express = require('express');
const app = express();
const cors = require('cors');
const axios = require('axios');
app.use(cors());
app.use(express.json());

app.get('/test', (req, res) => {
 console.log('hello ai!');
  res.send('hello ai!');
});

app.post('/chat_ai_with_internal', async (req, res) => {
  //处理post请求
  const { query_message, pre_message } = req.body;
  if(!query_message || query_message === '') {
    console.log('error:', "queryMessage cannot be empty ");
    res.status(400).send({ error: "queryMessage cannot be empty" });
    return;
  }
  console.log('queryMessage:', query_message);
  let result = await getFinalAnswer(query_message, pre_message);
  result = await getAnswerFromGPT("Please translate the following statements into Chinese: " + result);
  console.log('result:', result);
  res.send({ role: "assistant", content: result});
});

async function getFinalAnswer(question, preMessage) {
  let answer = '';
  let searchResult = '';
  let prompt = '';
  while (!isFinalAnswer(answer)) {
    if(prompt === '') {
        prompt = questionToPrompt(question, searchResult, answer, preMessage);
    } else {
        prompt = handlePrompt(prompt, searchResult, answer);
    }
    answer = await getAnswerFromGPT(prompt);
    if (!isFinalAnswer(answer)) {
      const processedAnswer = await gptResToQuestion(answer);
      searchResult = await searchAPI(processedAnswer);
      console.log('searchResult:', searchResult);
    }
  }
  return answerHandler(answer);
}

function answerHandler(answer) {
  const regex = /Final Answer: (.*)/;
  const match = answer.match(regex);
  let result = '';
  if (match) {
    result = match[1];
  }
  return result;
}

async function gptResToQuestion(answer) {
  console.log('gptResToQuestion input:' +  answer);
  const regex = /Action Input: (.*)/;
  const match = answer.match(regex);
  let result = '';
  if (match) {
    result = match[1];
  }
  console.log('gptResToQuestion output:' + result);
  return result;
}

function handlePrompt(prompt, searchResult, lastAnswer) {
  if(searchResult && searchResult !== "" && lastAnswer && lastAnswer !== "") {
      prompt += lastAnswer + "nObservation: "  + searchResult + "nThought: ";
  }
  return prompt;
}

function questionToPrompt(question, searchResult, lastAnswer, preMessage) {
  if(preMessage && preMessage.length > 0) {
      question = "histroy message: " + JSON.stringify(preMessage) + "nnew question: " + question;
  }
  let prompt = "Answer the following questions as best you can. You have access to the following tools:nnBing Search: A wrapper around Bing Search. Useful for when you need to answer questions about current events. Input should be a search query.nnUse the following format:nnQuestion: the input question you must answernThought: you should always think about what to donAction: the action to take, should be one of [Bing Search]nAction Input: the input to the actionnObservation: the result of the actionn... (this Thought/Action/Action Input/Observation can repeat N times)nThought: I now know the final answernFinal Answer: the final answer to the original input questionnnBegin!nnQuestion: " + question+ "nThought: ";
  if(searchResult && searchResult !== "" && lastAnswer && lastAnswer !== "") {
      prompt += lastAnswer + "nObservation: "  + searchResult + "nThought: ";
  }
  return prompt;
}

function isFinalAnswer(answer) {
  return answer && answer.includes('Final Answer: ');
}

async function getAnswerFromGPT(prompt) {
  console.log("nnprompt is: " + prompt)
  const message = {role:"user", "content": prompt};
  const messageList = [message];
  console.log('messageList:', JSON.stringify(messageList));
  const OPEN_AI_KEY = "填入openaikey";
  const response = await axios.post('https://api.openai.com/v1/chat/completions', {
    messages: messageList,
    stop: ["nObservation:", "ntObservation:"],
    model: 'gpt-3.5-turbo',
  }, {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${OPEN_AI_KEY}`,
    },
  });
  console.log('getAnswerFromGPT result:', response.data.choices[0].message.content.trim());
  return response.data.choices[0].message.content.trim();
}

async function searchAPI(question) {
  if(!question || question === '') {
    throw new Error('Question cannot be empty');
  }
  const BING_SEARCH_API_KEY = "填入bing search key"
  const response = await axios.get('https://api.bing.microsoft.com/v7.0/search', {
    headers: {
      'Ocp-Apim-Subscription-Key': BING_SEARCH_API_KEY,
    },
    params: {
      q: question,
      count: 10,
      textDecorations: true,
      textFormat: "HTML",
    },
  });
  const snippets = response.data.webPages?.value.map(page => page.snippet);
  return snippets.join('');
}

app.listen(6666, () => {
  console.log('服务器已启动,本地地址:http://localhost:6666');
});

代码解释

cursor对以上代码自动生成的解释:

这段代码是一个基于express框架的node.js后端服务,主要提供了两个接口:/test和/chat_ai_with_internal。其中/test接口是一个get请求,返回”hello ai!”字符串;/chat_ai_with_internal接口是一个post请求,接收一个json格式的请求体,包含query_message和pre_message两个字段。其中query_message是一个字符串类型的参数,表示用户的问题;pre_message是一个数组类型的参数,表示之前的对话记录。接口的主要逻辑在getFinalAnswer函数中,该函数通过调用getAnswerFromGPT函数获取AI的回答,然后根据回答是否为最终答案来决定是否需要调用searchAPI函数进行搜索。如果回答不是最终答案,则需要将回答转化为问题,然后调用searchAPI函数进行搜索,获取搜索结果。最终答案的判断是通过判断回答中是否包含”Final Answer: “字符串来实现的。

效果展示

下面是效果,可以看到已经可以比较好的回答了

如何为chatGPT增加网络访问功能

可以在命令行中看到GPT如何将自己不知道的信息转化未搜索关键字:

如何为chatGPT增加网络访问功能

借助LangChain实现

我们是参考langchain来解析如何为chatGPT增加网络访问功能,其实我们如果直接使用langchain可以使用更加简洁的代码实现这个应用,以下是实现:

from flask import Flask,request
from flask_cors import CORS
import search
import json

app = Flask(__name__)
CORS(app)

@app.route('/test')
def index():
    return 'Hello, ai!'

@app.route('/chat_ai_with_internal', methods=["POST"])
def searchPOST():
    data = json.loads(request.data);
    queryMessage = data['query_message'];
    preMessage = data['pre_message'];
    if len(queryMessage):
        res = {'role': 'assistant', 'content': search.query(queryMessage, preMessage)};
        return json.dumps(res);
    return "search error"

if __name__ == '__main__':
    app.run(port=8115)
import os
os.environ["BING_SUBSCRIPTION_KEY"] = "填入 search key"
os.environ["BING_SEARCH_URL"] = "https://api.bing.microsoft.com/v7.0/search"
os.environ['OPENAI_API_KEY'] = "填入openai key"

from langchain.utilities import BingSearchAPIWrapper
search = BingSearchAPIWrapper(k=5)

from langchain.agents import load_tools
from langchain.agents import initialize_agent

from langchain.llms import OpenAIChat
llm = OpenAIChat()

tool_names = ["bing-search"]
tools = load_tools(tool_names)
agent = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True)

def query(queryKey:str, preMessage: str) -> str:
    print("queryKey is %s", queryKey);
    print("preMessage is %s", preMessage)
    if len(preMessage):
        llm.prefix_messages = preMessage
    res = agent.run(queryKey);
    print(res);
    translationRes = llm.generate(["请将下面语句翻译为中文:" + res]).generations[0][0].text
    return  translationRes;

效果基本是一致的:

如何为chatGPT增加网络访问功能

注:

  1. 本文参考LangChain实现,prompt也是参考LangChain。
  2. 本文大部分代码是借助cursor自动生成。

结尾

AI大模型时代已来,一定会对我们的生活带来极大的改变,无论是底层的模型层,还是如何使用这些底层模型的应用层都有很多事情值得我们研究,LangChain就为我们在使用大模型上提供了很好的启示。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

Niji-journey V5:AI绘画神器的新版本发布

2023-12-6 15:06:14

AI教程

PyTorch深度学习常见错误及解决方法

2023-12-6 15:13:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索