在官方例子中给出了通过chain = NebulaGraphQAChain.from_llm(ChatOpenAI(temperature=0), graph=graph, verbose=True)
来检索NebulaGraph图数据库。本文介绍了通过GPT2替换ChatOpenAI的思路和实现,暂时不考虑效果。之所以没用ChatGLM2是因为加载模型太慢,调试不方便,不过将GPT2替换为ChatGLM2也很方便。
一.通过ChatOpenAI来检索NebulaGraph
1.NebulaGraph_OpenAI.py代码实现
如果没有ChatGPT的key和proxy是没法运行的,如下所示:
"""
langchain连接NebulaGraph的例子
"""
from langchain.chat_models import ChatOpenAI
from langchain.chains import NebulaGraphQAChain
from langchain.graphs import NebulaGraph
graph = NebulaGraph(
space="basketballplayer",
username="root",
password="nebula",
address="172.21.31.166",
port=9669,
session_pool_size=30, # 设置连接池大小
)
print(graph.get_schema)
chain = NebulaGraphQAChain.from_llm( # 从语言模型创建问答链
ChatOpenAI(temperature=0), graph=graph, verbose=True
)
chain.run("Who played in The Godfather II?")
2.NebulaGraphQAChain默认prompt
基本思路是介绍、举例、图Schema和限制,如下所示:
> Entering new NebulaGraphQAChain chain...
Generated nGQL:
Task:Generate NebulaGraph Cypher statement to query a graph database.
Instructions:
First, generate cypher then convert it to NebulaGraph Cypher dialect(rather than standard):
1. it requires explicit label specification only when referring to node properties: v.`Foo`.name
2. note explicit label specification is not needed for edge properties, so it's e.name instead of e.`Bar`.name
3. it uses double equals sign for comparison: `==` rather than `=`
For instance:
diff
< MATCH (p:person)-[e:directed]->(m:movie) WHERE m.name = 'The Godfather II'
< RETURN p.name, e.year, m.name;
---
> MATCH (p:`person`)-[e:directed]->(m:`movie`) WHERE m.`movie`.`name` == 'The Godfather II'
> RETURN p.`person`.`name`, e.year, m.`movie`.`name`;
Use only the provided relationship types and properties in the schema.
Do not use any other relationship types or properties that are not provided.
Schema:
Node properties: [{'tag': 'player', 'properties': [('name','string'), ('age', 'int64')]}, {'tag': 'team', 'properties': [('name','string')]}]
Edge properties: [{'edge': 'follow', 'properties': [('degree', 'int64')]}, {'edge':'serve', 'properties': [('start_year', 'int64'), ('end_year', 'int64')]}]
Relationships: ['(:player)-[:follow]->(:player)', '(:player)-[:serve]->(:team)']
Note: Do not include any explanations or apologies in your responses.
Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.
Do not include any text except the generated Cypher statement.
The question is:
player100'age is what?
Full Context:
{}
二.通过GPT2来检索NebulaGraph
1.NebulaGraph_GPT2.py代码实现
使用自定义的GPT2()
替换ChatOpenAI(temperature=0)
即可,如下所示:
"""
langchain连接NebulaGraph的例子
"""
from langchain.chains import NebulaGraphQAChain
from langchain.graphs import NebulaGraph
from examples.GPT2 import GPT2
graph = NebulaGraph( # 连接NebulaGraph
space="basketballplayer",
username="root",
password="nebula",
address="172.24.211.214",
port=9669,
session_pool_size=30, # 设置连接池大小
)
print(graph.get_schema) # 获取图的schema
chain = NebulaGraphQAChain.from_llm( # 从语言模型创建问答链
GPT2(), graph=graph, verbose=True
)
chain.run("player100'name is what?") # 运行问答链
chain.run("player100'age is what?") # 运行问答链
2.GPT2.py代码实现
主要是继承LLM
类,并且实现def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
函数,如下所示:
import time
import logging
import requests
from typing import Optional, List, Dict, Mapping, Any
import langchain
from langchain.llms.base import LLM
from langchain.cache import InMemoryCache
logging.basicConfig(level=logging.INFO)
# 启动llm的缓存,如果同一个问题被第二次提问,模型可以快速给出答案,而不用再次调用模型,节省时间
langchain.llm_cache = InMemoryCache()
class GPT2(LLM):
# 模型服务url
url = "http://127.0.0.1:8595/chat"
@property # 这个装饰器的作用是将一个方法变成一个属性来使用
def _llm_type(self) -> str:
return "gpt2"
def _construct_query(self, prompt: str) -> Dict:
"""
构造请求体
"""
query = {
"human_input": prompt
}
return query
@classmethod # 这个装饰器的作用是将一个方法变成一个类方法来使用
def _post(cls, url: str, query: Dict) -> Any:
"""
POST请求
"""
_headers = {"Content_Type": "application/json"}
with requests.session() as sess: # 这个with语句的作用是在这个语句块中创建的对象,会在执行完语句块后自动销毁
resp = sess.post(url, json=query, headers=_headers, timeout=60)
return resp
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
"""
注释:这个方法是用来调用模型的,这个方法的参数是prompt和stop,prompt是用户输入的内容,stop是用户输入的结束标志
"""
query = self._construct_query(prompt=prompt) # 构造请求体
resp = self._post(url=self.url, query=query) # post请求
if resp.status_code == 200: # 判断请求是否成功
resp_json = resp.json() # 获取返回结果
predictions = resp_json['response'] # 获取返回结果中的response字段
return predictions # 返回模型结果
else:
return "请求模型"
@property # 这个装饰器的作用是将一个方法变成一个属性来使用
def _identifying_params(self) -> Mapping[str, Any]:
"""
这个方法的作用是获取识别参数
"""
_param_dict = {
"url": self.url
}
return _param_dict
if __name__ == "__main__":
llm = GPT2() # 实例化GPT2类
while True: # 这个while循环的作用是让用户可以一直输入
human_input = input("Human: ") # 获取用户输入
begin_time = time.time() * 1000 # 获取当前时间
response = llm(human_input, stop=["you"]) # 调用模型
end_time = time.time() * 1000 # 获取当前时间
used_time = round(end_time - begin_time, 3) # 计算模型调用时间
logging.info(f"GPT2 process time: {used_time}ms") # 打印模型调用时间
print(f"GPT2: {response}") # 打印模型返回结果
3.GPT2_Flask.py代码实现
主要是通过Flask将GPT2进行API封装,如下所示:
import os
import json
import torch
from flask import Flask
from flask import request
from transformers import GPT2LMHeadModel, GPT2Tokenizer
os.environ["CUDA_VISIBLE_DEVICES"] = "0" # 指定GPU,0表示使用第一个GPU
pretrained_model_name_or_path = "L:/20230713_HuggingFaceModel/gpt2"
tokenizer = GPT2Tokenizer.from_pretrained(pretrained_model_name_or_path, trust_remote_code=True)
model = GPT2LMHeadModel.from_pretrained(pretrained_model_name_or_path, trust_remote_code=True).half().cuda()
model.eval()
app = Flask(__name__)
@app.route("/", methods=["POST", "GET"])
def root():
return "Welcome to gpt2 model"
@app.route("/chat", methods=["POST"])
def chat():
data_seq = request.get_data() # 获取请求数据
data_dict = json.loads(data_seq) # 将请求数据转换为字典
human_input = data_dict["human_input"] # 获取请求数据中的human_input字段
# response, _ = model.chat(tokenizer, human_input, history=[]) # ChatGLM可使用这个方法
# 将输入文本编码为令牌
input_ids = tokenizer.encode(human_input, return_tensors="pt")
input_ids = input_ids.cuda()
# 进行模型推理
with torch.no_grad(): # 这个with语句的作用是在这个语句块中创建的对象,会在执行完语句块后自动销毁
output = model.generate(input_ids, max_length=50, num_return_sequences=1) # 生成模型输出,max_length表示生成的最大长度,num_return_sequences表示生成的序列数
output = output.cuda()
# 将生成的令牌解码为字符串,skip_special_tokens=True表示跳过特殊字符,clean_up_tokenization_spaces=True表示清理分词空格
response = tokenizer.decode(output[0], skip_special_tokens=True, clean_up_tokenization_spaces=True)
result_dict = { # 构造返回结果
"response": response
}
result_seq = json.dumps(result_dict, ensure_ascii=False) # 将返回结果转换为json字符串
return result_seq # 返回结果
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8595, debug=False)
因为通用LLM通过prompt将text转换为nGQL并不专业,觉得以后的发展思路应该还是专用LLM作为agent来做这个事情。
参考文献:
[1]https://huggingface.co/gpt2
[2]使用LLMs模块接入自定义大模型:https://blog.csdn.net/zhaomengsen/article/details/130585397
[3]https://github.com/ai408/Langchain-Chatchat/blob/master/examples/NebulaGraph_GPT2.py
[4]https://github.com/ai408/Langchain-Chatchat/blob/master/examples/GPT2.py
[5]https://github.com/ai408/Langchain-Chatchat/blob/master/examples/GPT2_Flask.py
路由链(RouterChain)是由LLM根据输入的Prompt去选择具体的某个链。路由链中一般会存在多个Prompt,Prompt结合LLM决定下一步选择哪个链。