问题布景

咱们都知道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:\n\nBing Search: 
A wrapper around Bing Search. Useful for when you need to answer questions 
about current events. Input should be a search query.\n\nUse the following 
format:\n\nQuestion: the input question you must answer\nThought: 
you should always think about what to do\nAction: the action to take, 
should be one of [Bing Search]\nAction Input: the input to the action\n
Observation: the result of the action\n... (this Thought/Action/Action 
Input/Observation can repeat N times)\nThought: 
I now know the final answer\nFinal Answer: 
the final answer to the original input question\n\nBegin!\n\nQuestion: 
${问题}\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:\n\nBing Search: A wrapper around Bing Search. Useful for when you need to answer questions about current events. Input should be a search query.\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [Bing Search]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\n\nQuestion: " + 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("\n\nprompt 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('ages?.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就为咱们在运用大模型上供给了很好的启示。