Bigit是一款基于BigDl-LLM和LangChain的Web服务。结合区块链技术,其可以实现针对知识图谱的问答,实现让个人拥有自己的“专用大模型”。
1.后端设计思路
- ChatGo的后端主要功能是实现基于BigDl-LLM和LangChain的大模型问答服务,为ChatGo的核心组件。
- 知识图谱采用Neo4j aura云部署的形式,方便个人用户使用
- ChatGo的后端主要使用了LangChain的Agent模型来实现知识图谱的问答
- 问答步骤为:根据用户的问题生成Cypher语句——Cypher语句查询知识图谱——根据知识图谱的查询结果返回最终的问答结结果
2.后端实现
2.1 根据问题生成Cypher语句
# 示例生成查询的语句
from env import getEnv
from database import Neo4jDatabase
from pydantic import BaseModel, Extra
from langchain.prompts.base import BasePromptTemplate
from langchain.chains.llm import LLMChain
from langchain.chains.base import Chain
from langchain.memory import ReadOnlySharedMemory, ConversationBufferMemory
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from typing import Dict, List, Any
from logger import logger
with open('examples.txt', 'r') as file:
examples = file.read()
SYSTEM_TEMPLATE = """
您是一名助手,能够根据示例Cypher查询生成Cypher查询。
示例Cypher查询是:\n""" + examples + """\n
不要回复除Cypher查询以外的任何解释或任何其他信息。
您永远不要为你的不准确回复感到抱歉,并严格根据提供的Cypher示例生成Cypher语句。
不要提供任何无法从密码示例中推断出的Cypher语句。
"""
SYSTEM_CYPHER_PROMPT = SystemMessagePromptTemplate.from_template(SYSTEM_TEMPLATE)
HUMAN_TEMPLATE = "{question}"
HUMAN_PROMPT = HumanMessagePromptTemplate.from_template(HUMAN_TEMPLATE)
class LLMCypherGraphChain(Chain, BaseModel):
"""Chain that interprets a prompt and executes python code to do math.
"""
llm: Any
"""LLM wrapper to use."""
system_prompt: BasePromptTemplate = SYSTEM_CYPHER_PROMPT
human_prompt: BasePromptTemplate = HUMAN_PROMPT
input_key: str = "question" #: :meta private:
output_key: str = "answer" #: :meta private:
graph: Neo4jDatabase
memory: ReadOnlySharedMemory
class Config:
"""Configuration for this pydantic object."""
extra = Extra.forbid
arbitrary_types_allowed = True
@property
def input_keys(self) -> List[str]:
"""Expect input key.
:meta private:
"""
return [self.input_key]
@property
def output_keys(self) -> List[str]:
"""Expect output key.
:meta private:
"""
return [self.output_key]
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
logger.debug(f"Cypher generator inputs: {inputs}")
chat_prompt = ChatPromptTemplate.from_messages(
[self.system_prompt] + inputs['chat_history'] + [self.human_prompt]
)
cypher_executor = LLMChain(
prompt=chat_prompt, llm=self.llm, callback_manager=self.callback_manager
)
cypher_statement = cypher_executor.predict(
question=inputs[self.input_key], stop=["Output:"])
self.callback_manager.on_text(
"Generated Cypher statement:", color="green", end="\n", verbose=self.verbose
)
self.callback_manager.on_text(
cypher_statement, color="blue", end="\n", verbose=self.verbose
)
print(cypher_statement)
# If Cypher statement was not generated due to lack of context
if not "MATCH" in cypher_statement:
return {'answer': 'Missing context to create a Cypher statement'}
try:
context = self.graph.query(cypher_statement)
return {'answer': context}
except:
logger.debug('Cypher generator context:')
return {'answer': 'No match Cypher statement'}
if __name__ == "__main__":
from langchain.chat_models import ChatOpenAI
from bigdl.llm.langchain.llms import TransformersLLM
llm = TransformersLLM.from_model_id(
model_id="lmsys/vicuna-7b-v1.5",
model_kwargs={"temperature": 0, "max_length": 1024, "trust_remote_code": True},
)
database = Neo4jDatabase(host="neo4j://localhost:7687",
user="neo4j", password="aowang")
memory = ConversationBufferMemory(
memory_key="chat_history", return_messages=True)
readonlymemory = ReadOnlySharedMemory(memory=memory)
print('query scuess')
chain = LLMCypherGraphChain(llm=llm, verbose=True, graph=database, memory=readonlymemory)
output = chain.run(
"演唱兰亭序的歌手是"
)
print(output)
2.2 根据Cpyher语句进行本地图谱的查询
from typing import List, Optional, Dict
from neo4j import GraphDatabase
from logger import logger
class Neo4jDatabase:
def __init__(self, host: str = "neo4j://localhost:7687",
user: str = "neo4j",
password: str = "aowang"):
"""Initialize the movie database"""
self.driver = GraphDatabase.driver(host, auth=(user, password))
print('driver sucess'+host)
def query(
self,
cypher_query: str,
params: Optional[Dict] = {}
) -> List[Dict[str, str]]:
logger.debug(cypher_query)
with self.driver.session() as session:
result = session.run(cypher_query, params)
# Limit to at most 50 results
return [r.values()[0] for r in result][:50]
if __name__ == "__main__":
database = Neo4jDatabase(host="neo4j://localhost:7687",
user="neo4j", password="aowang")
a = database.query("""
MATCH (n) RETURN {count: count(*)} AS count
""")
print(a)
2.3 生成最后的答案,前端调用
# 路由处理
import logging
from agent import GraphAgent
from env import getEnv
from database import Neo4jDatabase
from fastapi import APIRouter, HTTPException, Query
from run import get_result_and_thought_using_graph
neo4j_host = getEnv("NEO4J_URL")
neo4j_user = getEnv("NEO4J_USER")
neo4j_password = getEnv("NEO4J_PASS")
# build router
router = APIRouter()
logger = logging.getLogger(__name__)
graph = Neo4jDatabase(
host=neo4j_host, user=neo4j_user, password=neo4j_password)
print(neo4j_host)
# 不使用记忆组件功能,每次接口初始化
# agent_graph = GraphAgent.initialize(
# graph=graph, model_name=model_name)
@router.get("/predict")
def get_load(message: str = Query(...)):
try:
agent_graph = GraphAgent.initialize(
graph=graph, model_name=model_name)
print(neo4j_host)
return get_result_and_thought_using_graph(agent_graph, message)
except Exception as e:
# Log stack trace
logger.exception(e)
raise HTTPException(status_code=500, detail=str(e)) from e
3.后端使用
1. 后端在项目的/backend文件夹中,首先安装项目的依赖库
pip install langchain==0.0.2
pip install openai==0.27.4
pip install neo4j-driver==1.7.6
pip install neo4j
pip install fastapi
pip install uvicorn
2. 其次完成环境配置,设置node4j数据库等信息,我们提供.env的实例,可以照此填写
cp .env.example .env
3. 环境变量设置完后,即可开启后端,进入backend/src/中运行
python3 main.py