问题布景
咱们都知道chatGPT无法拜访较新的数据,比如我希望问询硅谷银行的工作时,得到的答复是这样的:
当然假如运用newbing 能够达到获得咱们想要的成果,那么除了newbing,咱们能否自己为chatGPT添加网络拜访功用呢?
处理方案
要处理这个问题,咱们很容易想到需求查找引擎这个东西,那么现在咱们有2个东西1个是chatGPT,1个是查找引擎, 接下来咱们看什么样的流程能够完结咱们的方针。
供给Context
假如咱们能够在给chatGPT 提问时带上这些GPT所不知道的信息,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可能会查询多次。
完整流程
画出咱们的流程图:
完成
从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: “字符串来完成的。
效果展示
下面是效果,能够看到已经能够比较好的答复了
能够在命令行中看到GPT怎么将自己不知道的信息转化未查找要害字:
凭借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;
效果基本是一致的:
注:
- 本文参阅LangChain完成,prompt也是参阅LangChain。
- 本文大部分代码是凭借cursor主动生成。
结束
AI大模型时代已来,一定会对咱们的生活带来极大的改动,无论是底层的模型层,仍是怎么运用这些底层模型的运用层都有许多工作值得咱们研究,LangChain就为咱们在运用大模型上供给了很好的启示。