实现langchain-ChatGLM API调用客户端(及未解决的问题)

langchain-ChatGLM是一个基于本地知识库的LLM对话库。其基于text2vec-large-Chinese为Embedding模型,ChatGLM-6B为对话大模型。原项目地址:https://github.com/chatchat-space/langchain-ChatGLM

对于如何本地部署ChatGLM模型,可以参考我之前的文章http://t.csdn.cn/16STJ

在本项目中,我们编写了langchai-ChatGLM API调用的客户端代码。经过测试虽然客户端可以正常调用服务器的API,但是对于删除知识库的指令服务器无法正常执行

1 langchain-ChatGLM API服务器端程序
下面程序段为langchain-ChatGLM项目中的api.py文件

import argparse
import json
import os
import shutil
from typing import List, Optional
import urllibimport nltk
import pydantic
import uvicorn
from fastapi import Body, FastAPI, File, Form, Query, UploadFile, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing_extensions import Annotated
from starlette.responses import RedirectResponsefrom chains.local_doc_qa import LocalDocQA
from configs.model_config import (KB_ROOT_PATH, EMBEDDING_DEVICE,EMBEDDING_MODEL, NLTK_DATA_PATH,VECTOR_SEARCH_TOP_K, LLM_HISTORY_LEN, OPEN_CROSS_DOMAIN)
import models.shared as shared
from models.loader.args import parser
from models.loader import LoaderCheckPointnltk.data.path = [NLTK_DATA_PATH] + nltk.data.pathclass BaseResponse(BaseModel):code: int = pydantic.Field(200, description="HTTP status code")msg: str = pydantic.Field("success", description="HTTP status message")class Config:schema_extra = {"example": {"code": 200,"msg": "success",}}class ListDocsResponse(BaseResponse):data: List[str] = pydantic.Field(..., description="List of document names")class Config:schema_extra = {"example": {"code": 200,"msg": "success","data": ["doc1.docx", "doc2.pdf", "doc3.txt"],}}class ChatMessage(BaseModel):question: str = pydantic.Field(..., description="Question text")response: str = pydantic.Field(..., description="Response text")history: List[List[str]] = pydantic.Field(..., description="History text")source_documents: List[str] = pydantic.Field(..., description="List of source documents and their scores")class Config:schema_extra = {"example": {"question": "工伤保险如何办理?","response": "根据已知信息,可以总结如下:\n\n1. 参保单位为员工缴纳工伤保险费,以保障员工在发生工伤时能够获得相应的待遇。\n2. 不同地区的工伤保险缴费规定可能有所不同,需要向当地社保部门咨询以了解具体的缴费标准和规定。\n3. 工伤从业人员及其近亲属需要申请工伤认定,确认享受的待遇资格,并按时缴纳工伤保险费。\n4. 工伤保险待遇包括工伤医疗、康复、辅助器具配置费用、伤残待遇、工亡待遇、一次性工亡补助金等。\n5. 工伤保险待遇领取资格认证包括长期待遇领取人员认证和一次性待遇领取人员认证。\n6. 工伤保险基金支付的待遇项目包括工伤医疗待遇、康复待遇、辅助器具配置费用、一次性工亡补助金、丧葬补助金等。","history": [["工伤保险是什么?","工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。",]],"source_documents": ["出处 [1] 广州市单位从业的特定人员参加工伤保险办事指引.docx:\n\n\t( 一)  从业单位  (组织)  按“自愿参保”原则,  为未建 立劳动关系的特定从业人员单项参加工伤保险 、缴纳工伤保 险费。","出处 [2] ...","出处 [3] ...",],}}def get_folder_path(local_doc_id: str):return os.path.join(KB_ROOT_PATH, local_doc_id, "content")def get_vs_path(local_doc_id: str):return os.path.join(KB_ROOT_PATH, local_doc_id, "vector_store")def get_file_path(local_doc_id: str, doc_name: str):return os.path.join(KB_ROOT_PATH, local_doc_id, "content", doc_name)async def upload_file(file: UploadFile = File(description="A single binary file"),knowledge_base_id: str = Form(..., description="Knowledge Base Name", example="kb1"),
):saved_path = get_folder_path(knowledge_base_id)if not os.path.exists(saved_path):os.makedirs(saved_path)file_content = await file.read()  # 读取上传文件的内容file_path = os.path.join(saved_path, file.filename)if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content):file_status = f"文件 {file.filename} 已存在。"return BaseResponse(code=200, msg=file_status)with open(file_path, "wb") as f:f.write(file_content)vs_path = get_vs_path(knowledge_base_id)vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store([file_path], vs_path)if len(loaded_files) > 0:file_status = f"文件 {file.filename} 已上传至新的知识库,并已加载知识库,请开始提问。"return BaseResponse(code=200, msg=file_status)else:file_status = "文件上传失败,请重新上传"return BaseResponse(code=500, msg=file_status)async def upload_files(files: Annotated[List[UploadFile], File(description="Multiple files as UploadFile")],knowledge_base_id: str = Form(..., description="Knowledge Base Name", example="kb1"),
):saved_path = get_folder_path(knowledge_base_id)if not os.path.exists(saved_path):os.makedirs(saved_path)filelist = []for file in files:file_content = ''file_path = os.path.join(saved_path, file.filename)file_content = file.file.read()if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content):continuewith open(file_path, "ab+") as f:f.write(file_content)filelist.append(file_path)if filelist:vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store(filelist, get_vs_path(knowledge_base_id))if len(loaded_files):file_status = f"documents {', '.join([os.path.split(i)[-1] for i in loaded_files])} upload success"return BaseResponse(code=200, msg=file_status)file_status = f"documents {', '.join([os.path.split(i)[-1] for i in loaded_files])} upload fail"return BaseResponse(code=500, msg=file_status)async def list_kbs():# Get List of Knowledge Baseif not os.path.exists(KB_ROOT_PATH):all_doc_ids = []else:all_doc_ids = [folderfor folder in os.listdir(KB_ROOT_PATH)if os.path.isdir(os.path.join(KB_ROOT_PATH, folder))and os.path.exists(os.path.join(KB_ROOT_PATH, folder, "vector_store", "index.faiss"))]return ListDocsResponse(data=all_doc_ids)async def list_docs(knowledge_base_id: Optional[str] = Query(default=None, description="Knowledge Base Name", example="kb1")
):local_doc_folder = get_folder_path(knowledge_base_id)if not os.path.exists(local_doc_folder):return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"}all_doc_names = [docfor doc in os.listdir(local_doc_folder)if os.path.isfile(os.path.join(local_doc_folder, doc))]return ListDocsResponse(data=all_doc_names)async def delete_kb(knowledge_base_id: str = Query(...,description="Knowledge Base Name",example="kb1"),
):# TODO: 确认是否支持批量删除知识库knowledge_base_id = urllib.parse.unquote(knowledge_base_id)if not os.path.exists(get_folder_path(knowledge_base_id)):return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"}shutil.rmtree(get_folder_path(knowledge_base_id))# self-added code#shutil.rmtree(get_vs_path(knowledge_base_id))# /self-added codereturn BaseResponse(code=200, msg=f"Knowledge Base {knowledge_base_id} delete success")async def delete_doc(knowledge_base_id: str = Query(...,description="Knowledge Base Name",example="kb1"),doc_name: str = Query(None, description="doc name", example="doc_name_1.pdf"),
):knowledge_base_id = urllib.parse.unquote(knowledge_base_id)if not os.path.exists(get_folder_path(knowledge_base_id)):return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"}doc_path = get_file_path(knowledge_base_id, doc_name)if os.path.exists(doc_path):os.remove(doc_path)remain_docs = await list_docs(knowledge_base_id)if len(remain_docs.data) == 0:shutil.rmtree(get_folder_path(knowledge_base_id), ignore_errors=True)return BaseResponse(code=200, msg=f"document {doc_name} delete success along with the whole knowledge base")else:status = local_doc_qa.delete_file_from_vector_store(doc_path, get_vs_path(knowledge_base_id))if "success" in status:return BaseResponse(code=200, msg=f"document {doc_name} delete success")else:return BaseResponse(code=1, msg=f"document {doc_name} delete fail")else:return BaseResponse(code=1, msg=f"document {doc_name} not found")async def update_doc(knowledge_base_id: str = Query(...,description="知识库名",example="kb1"),old_doc: str = Query(None, description="待删除文件名,已存储在知识库中", example="doc_name_1.pdf"),new_doc: UploadFile = File(description="待上传文件"),
):knowledge_base_id = urllib.parse.unquote(knowledge_base_id)if not os.path.exists(get_folder_path(knowledge_base_id)):return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"}doc_path = get_file_path(knowledge_base_id, old_doc)if not os.path.exists(doc_path):return BaseResponse(code=1, msg=f"document {old_doc} not found")else:os.remove(doc_path)delete_status = local_doc_qa.delete_file_from_vector_store(doc_path, get_vs_path(knowledge_base_id))if "fail" in delete_status:return BaseResponse(code=1, msg=f"document {old_doc} delete failed")else:saved_path = get_folder_path(knowledge_base_id)if not os.path.exists(saved_path):os.makedirs(saved_path)file_content = await new_doc.read()  # 读取上传文件的内容file_path = os.path.join(saved_path, new_doc.filename)if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content):file_status = f"document {new_doc.filename} already exists"return BaseResponse(code=200, msg=file_status)with open(file_path, "wb") as f:f.write(file_content)vs_path = get_vs_path(knowledge_base_id)vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store([file_path], vs_path)if len(loaded_files) > 0:file_status = f"document {old_doc} delete and document {new_doc.filename} upload success"return BaseResponse(code=200, msg=file_status)else:file_status = f"document {old_doc} success but document {new_doc.filename} upload fail"return BaseResponse(code=500, msg=file_status)async def local_doc_chat(knowledge_base_id: str = Body(..., description="Knowledge Base Name", example="kb1"),question: str = Body(..., description="Question", example="工伤保险是什么?"),history: List[List[str]] = Body([],description="History of previous questions and answers",example=[["工伤保险是什么?","工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。",]],),
):vs_path = get_vs_path(knowledge_base_id)if not os.path.exists(vs_path):# return BaseResponse(code=1, msg=f"Knowledge base {knowledge_base_id} not found")return ChatMessage(question=question,response=f"Knowledge base {knowledge_base_id} not found",history=history,source_documents=[],)else:for resp, history in local_doc_qa.get_knowledge_based_answer(query=question, vs_path=vs_path, chat_history=history, streaming=True):passsource_documents = [f"""出处 [{inum + 1}] {os.path.split(doc.metadata['source'])[-1]}:\n\n{doc.page_content}\n\n"""f"""相关度:{doc.metadata['score']}\n\n"""for inum, doc in enumerate(resp["source_documents"])]return ChatMessage(question=question,response=resp["result"],history=history,source_documents=source_documents,)async def bing_search_chat(question: str = Body(..., description="Question", example="工伤保险是什么?"),history: Optional[List[List[str]]] = Body([],description="History of previous questions and answers",example=[["工伤保险是什么?","工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。",]],),
):for resp, history in local_doc_qa.get_search_result_based_answer(query=question, chat_history=history, streaming=True):passsource_documents = [f"""出处 [{inum + 1}] [{doc.metadata["source"]}]({doc.metadata["source"]}) \n\n{doc.page_content}\n\n"""for inum, doc in enumerate(resp["source_documents"])]return ChatMessage(question=question,response=resp["result"],history=history,source_documents=source_documents,)async def chat(question: str = Body(..., description="Question", example="工伤保险是什么?"),history: List[List[str]] = Body([],description="History of previous questions and answers",example=[["工伤保险是什么?","工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。",]],),
):for answer_result in local_doc_qa.llm.generatorAnswer(prompt=question, history=history,streaming=True):resp = answer_result.llm_output["answer"]history = answer_result.historypassreturn ChatMessage(question=question,response=resp,history=history,source_documents=[],)async def stream_chat(websocket: WebSocket, knowledge_base_id: str):await websocket.accept()turn = 1while True:input_json = await websocket.receive_json()question, history, knowledge_base_id = input_json["question"], input_json["history"], input_json["knowledge_base_id"]vs_path = get_vs_path(knowledge_base_id)if not os.path.exists(vs_path):await websocket.send_json({"error": f"Knowledge base {knowledge_base_id} not found"})await websocket.close()returnawait websocket.send_json({"question": question, "turn": turn, "flag": "start"})last_print_len = 0for resp, history in local_doc_qa.get_knowledge_based_answer(query=question, vs_path=vs_path, chat_history=history, streaming=True):await websocket.send_text(resp["result"][last_print_len:])last_print_len = len(resp["result"])source_documents = [f"""出处 [{inum + 1}] {os.path.split(doc.metadata['source'])[-1]}:\n\n{doc.page_content}\n\n"""f"""相关度:{doc.metadata['score']}\n\n"""for inum, doc in enumerate(resp["source_documents"])]await websocket.send_text(json.dumps({"question": question,"turn": turn,"flag": "end","sources_documents": source_documents,},ensure_ascii=False,))turn += 1async def document():return RedirectResponse(url="/docs")def api_start(host, port):global appglobal local_doc_qallm_model_ins = shared.loaderLLM()llm_model_ins.set_history_len(LLM_HISTORY_LEN)app = FastAPI()# Add CORS middleware to allow all origins# 在config.py中设置OPEN_DOMAIN=True,允许跨域# set OPEN_DOMAIN=True in config.py to allow cross-domainif OPEN_CROSS_DOMAIN:app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)app.websocket("/local_doc_qa/stream-chat/{knowledge_base_id}")(stream_chat)app.get("/", response_model=BaseResponse)(document)app.post("/chat", response_model=ChatMessage)(chat)app.post("/local_doc_qa/upload_file", response_model=BaseResponse)(upload_file)app.post("/local_doc_qa/upload_files", response_model=BaseResponse)(upload_files)app.post("/local_doc_qa/local_doc_chat", response_model=ChatMessage)(local_doc_chat)app.post("/local_doc_qa/bing_search_chat", response_model=ChatMessage)(bing_search_chat)app.get("/local_doc_qa/list_knowledge_base", response_model=ListDocsResponse)(list_kbs)app.get("/local_doc_qa/list_files", response_model=ListDocsResponse)(list_docs)app.delete("/local_doc_qa/delete_knowledge_base", response_model=BaseResponse)(delete_kb)app.delete("/local_doc_qa/delete_file", response_model=BaseResponse)(delete_doc)app.post("/local_doc_qa/update_file", response_model=BaseResponse)(update_doc)local_doc_qa = LocalDocQA()local_doc_qa.init_cfg(llm_model=llm_model_ins,embedding_model=EMBEDDING_MODEL,embedding_device=EMBEDDING_DEVICE,top_k=VECTOR_SEARCH_TOP_K,)uvicorn.run(app, host=host, port=port)if __name__ == "__main__":parser.add_argument("--host", type=str, default="0.0.0.0")parser.add_argument("--port", type=int, default=7861)# 初始化消息args = Noneargs = parser.parse_args()args_dict = vars(args)shared.loaderCheckPoint = LoaderCheckPoint(args_dict)api_start(args.host, args.port)

我们可以在api_start方法中看到服务器程序开放了以下的API:
1 upload_file 上传一个文件
2 upload_files 上传多个文件
3 local_doc_chat 基于本地知识库进行对话
4 bing_search_chat 基于bing搜索进行对话
5 list_knowledge_base 列出所有的知识库
6 list_files 列出一个知识库下面所有文件
7 delete_knowledge_base 删除一个知识库
8 delete_file 删除某一个知识库下面一个文件
9 update_file 将某一知识库里一个文件替换为另一个问题

另外,在文件最上方定义了三种服务器返回的消息类型BaseResponse,ListDocsResponse,ChatMessage。在api_start方法中可以找到每一个API对于的返回消息类型。各个消息均为json文件

基于以上内容,我对除了bing_search_chat之外所有的API编写了对应的客户端。使用python的requests类发送HTTP请求并获取回应。下面是完整代码:

import requestsAPI_BASE_URL = "http://localhost:7861"  # the server's url
API_KB_URL = API_BASE_URL + "/local_doc_qa" # the url for knowledge base answer# upload local file for knowledge base
def upload_file(knowledge_base_id, file_path):with open(file_path, "rb") as file:files = {"file": file}data = {"knowledge_base_id": knowledge_base_id}try:response = requests.post(API_KB_URL + "/upload_file", data=data, files=files)response.raise_for_status()#print(f"File '{file_path}' uploaded successfully to knowledge base '{knowledge_base_id}'.")print_msg(response)except requests.exceptions.RequestException as e:print(f"Failed to upload '{file_path}' knowledge base: {e}")# upload multiple files for knowledge base
def upload_files(knowledge_base_id, file_paths):files = [(f"files", open(file_path, "rb")) for file_path in file_paths]data = {"knowledge_base_id": knowledge_base_id}try:response = requests.post(API_KB_URL + "/upload_files", data=data, files=files)response.raise_for_status()#print(f"File '{file_paths}' uploaded successfully to knowledge base '{knowledge_base_id}'.")print_msg(response)except requests.exceptions.RequestException as e:print(f"Failed to upload '{file_paths}' knowledge base: {e}")# replace an existing file with another one
def update_file(knowledge_base_id, old_file, new_file):files = {"new_doc": open(new_file, "rb")}params = {"knowledge_base_id": knowledge_base_id, "old_doc": old_file}try:response = requests.post(API_KB_URL + "/update_file", params=params, files=files)response.raise_for_status()#print(f"Replace '{old_file}' with '{new_file}' in knowledge base '{knowledge_base_id}'")print_msg(response)except requests.exceptions.RequestException as e:print(f"Fail to update file {new_file} to knowledge base")# chat with chatglm
def chat_with_llm(question, knowledge_base_id=None, history=None):# use chat with knowledge base (if knowledge base is available), or chat with LLMurl = API_KB_URL + "/local_doc_chat" if knowledge_base_id else API_BASE_URL + "/chat"data = {"question": question,"history": history or [],}if knowledge_base_id:data["knowledge_base_id"] = knowledge_base_idtry:# send request to LLMresponse = requests.post(url, json=data)response.raise_for_status()chat_response = response.json()print("LLM Response:", chat_response.get("response"))print("Reference:", chat_response.get("source_documents"))return chat_responseexcept requests.exceptions.RequestException as e:print(f"Error while chatting with LLM: {e}")return None# list knowledge bases
def list_kbs():try:response = requests.get(API_KB_URL + "/list_knowledge_base")response.raise_for_status()kbs = response.json()print("List of Knowledge Bases:")for kb in kbs.get("data"):print(kb)return kbsexcept requests.exceptions.RequestException as e:print(f"Error while listing knowledge bases: {e}")return None# list documents in a knowledge base
def list_files(knowledge_base_id):try:response = requests.get(API_KB_URL + "/list_files", params={"knowledge_base_id":knowledge_base_id})response.raise_for_status()docs = response.json()print(f"List of Documents in Knowledge Base '{knowledge_base_id}':")for doc in docs.get("data"):print(doc)return docsexcept requests.exceptions.RequestException as e:print(f"Error while listing documents: {e}")return None# delete a knowledge base
def delete_knowledge_base(knowledge_base_id):param = {"knowledge_base_id": knowledge_base_id}try:response = requests.delete(API_KB_URL + "/delete_knowledge_base", params=param)response.raise_for_status()print_msg(response)except requests.exceptions.RequestException as e:print(f"Error while deleting knowledge base: {e}")# delete a single file from a selected knowledge base
def delete_file(knowledge_base_id, file_path):params = {"knowledge_base_id": knowledge_base_id,"doc_name": file_path}try:response = requests.delete(API_KB_URL + "/delete_file", params=params)response.raise_for_status()print_msg(response)except requests.exceptions.RequestException as e:print(f"Failed to delete file: {e}")# print the status information returned by the server (for base and listdoc response only)
def print_msg(response):print(response.json().get("msg"))

程序说明:
1

def print_msg(response):print(response.json().get("msg"))

该方法用于打出服务器回复信息中msg部分

2

API_BASE_URL = "http://localhost:7861"  # the server's url
API_KB_URL = API_BASE_URL + "/local_doc_qa" # the url for knowledge base answer

这里本地服务器默认的url为http://localhost:7861(可以在服务器配置文件中看到),所有和知识库相关的API的endpoint均为/local_doc_qa/(API名称),而不使用知识库直接和模型对话的时候endpoint直接为/chat

3
程序主体内容就是对各个API客户端方法的实现。其实现原理都差不多,均为传入参数并封装为json文件,然后在服务器请求的参数中将json文件传入服务器,再等待服务器的回复。其中要注意的是upload_file, upload_files, local_doc_chat, chat, update_file使用POST指令。list_knowledge_base,list_files使用GET指令。delete_knowledge_base和delete_file使用DELETE指令

在程序完成后,我对各个指令的实现进行测试,在测试中出现了一定问题

1 测试使用的数据
为了测试模型是否可以参考知识库内容进行回答,我找了3篇关于番茄种植的文段,并提出了一个需要结合三篇文段内容的知识型问题

文段

文本1:种植番茄的方法
番茄是家庭菜园中广受欢迎的水果(没错,它们是水果!),因其多功能和美味而备受喜爱。要成功种植番茄,请按照以下步骤进行:

选择合适的位置:番茄需要每天至少6-8小时的直接阳光。在花园中选一个能充分接受阳光的地方。

准备土壤:番茄喜欢排水良好、富含有机物质的土壤。在种植前,将堆肥或腐熟的粪肥混入土壤中,以改善其肥力。

种植:在您所在地区的最后霜日期后种植番茄苗。挖一个略深于苗木根系的洞,将苗木放入洞中,用土填充并轻轻压实。

浇水:在种植后立即给新种植的苗木浇水。一旦苗木生根,要定期浇水,每周浇水量约为1-1.5英寸。

支撑:番茄是藤本植物,需要支撑才能向上生长。用桩或笼子支撑植物,防止其蔓延在地面上。

修剪:定期修剪掉主干和侧枝之间生长的小枝,以便将能量集中在结果上。

施肥:在种植后几周,以及出现第一批果实时,施用均衡肥料。

文本2:常见的番茄害虫和疾病 种植番茄虽然令人满足,但也可能面临与害虫和疾病有关的挑战。您可能会遇到以下一些常见问题:

蚜虫:这些微小的昆虫会吸取植物的汁液,导致植物生长受阻,叶子变形。

枯萎病:早疫病和晚疫病都可能影响番茄,导致叶子和果实上出现黑斑,最终导致植物死亡。

烟粉虱:这些小型飞虫会吸食植物汁液并排泄粘性的蜜露,吸引霉菌并导致叶片发黄。

枯萎蔓枝菌:这是一种土壤传播的真菌,会导致植株下部叶片枯萎和发黄,最终导致植株死亡。

大黄螟:这些大型的绿色毛虫如果不加以控制,可能会迅速让番茄植株叶片凋萎。

文本3:值得一试的番茄品种 番茄有各种不同的形状、大小和颜色,每种都有独特的口味和用途。考虑种植这些受欢迎的番茄品种:

罗马番茄:由于其肉质的质地和较少的种子,非常适合制作酱汁和罐装食品。

小番茄:小巧、甜美,非常适合作为零食,用于沙拉或装饰菜肴。

牛腿番茄:个头大而多汁,非常适合制作三明治和切片。

传统品种:这些是口味和外观独特的老品种,通常代代相传。

葡萄番茄:椭圆形且甜美,非常适合用于沙拉和烤制。

绿色斑纹番茄:一种味道略带酸味的绿色条纹番茄,非常适合用于沙拉和莎莎酱。

问题:如何防止番茄植株蔓延在地面上,枯萎蔓枝菌的常见症状是什么,以及哪种番茄品种最适合制作酱汁和罐装食品?

在使用upload_file上传单个文件,upload_files上传多个文件,list_knowledge_base列出知识库,list_files列出知识库内文件,和chat_with_llm和模型对话这几项都成功。模型也可以结合知识库内容回答并给出对应索引位置

但是在使用所有和删除相关的方法时(包括delete_knowledge_base delete_file update_file),会出现以下问题:

1 有些时候即使删除了某个文件,模型依然会引用已删除的文件。我一开始以为是历史对话造成的结果,但是在重启模型后该问题依然出现。

在知识库本地储存的位置中,知识库content文件夹(储存文本的文件夹)下要删除的文件的确消失了,但是有可能该文件对应的向量库并没有成功被移除。(根据api.py文件里内容在向量库里移除文件的方法应该是local_doc_qa.delete_file_from_vector_store,但是我目前还没有深入去研究local_doc_qa里的内容)

我也尝试了在删除文件时同时删除该文件夹下面的vector_store文件夹shutil.rmtree(get_vs_path(knowledge_base_id, doc_name))
这么做会导致LLM的知识库引用直接为空。向量库只有一个文件,应该包含了该知识库所有文件转化的向量,所有没法从中删除单个文件内容

2 在删除文件后进行提问会出现以下报错

INFO: 127.0.0.1:56826 - “POST /local_doc_qa/local_doc_chat HTTP/1.1” 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File “/home/pai/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py”, line 429, in run_asgi
result = await app( # type: ignore[func-returns-value]
File “/home/pai/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py”, line 78, in call
return await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/fastapi/applications.py”, line 276, in call
await super().call(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/applications.py”, line 122, in call
await self.middleware_stack(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/errors.py”, line 184, in call
raise exc
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/errors.py”, line 162, in call
await self.app(scope, receive, _send)
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/exceptions.py”, line 79, in call
raise exc
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/exceptions.py”, line 68, in call
await self.app(scope, receive, sender)
File “/home/pai/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py”, line 21, in call
raise e
File “/home/pai/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py”, line 18, in call
await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 718, in call
await route.handle(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 276, in handle
await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 66, in app
response = await func(request)
File “/home/pai/lib/python3.9/site-packages/fastapi/routing.py”, line 237, in app
raw_response = await run_endpoint_function(
File “/home/pai/lib/python3.9/site-packages/fastapi/routing.py”, line 163, in run_endpoint_function
return await dependant.call(**values)
File “/mnt/workspace/langchain-ChatGLM/api.py”, line 293, in local_doc_chat
for resp, history in local_doc_qa.get_knowledge_based_answer(
File “/mnt/workspace/langchain-ChatGLM/chains/local_doc_qa.py”, line 231, in get_knowledge_based_answer
related_docs_with_score = vector_store.similarity_search_with_score(query, k=self.top_k)
File “/home/pai/lib/python3.9/site-packages/langchain/vectorstores/faiss.py”, line 221, in similarity_search_with_score
docs = self.similarity_search_with_score_by_vector(embedding, k)
File “/mnt/workspace/langchain-ChatGLM/vectorstores/MyFAISS.py”, line 86, in similarity_search_with_score_by_vector
_id0 = self.index_to_docstore_id[l]
KeyError: 23
INFO: 127.0.0.1:57038 - “GET /local_doc_qa/list_knowledge_base HTTP/1.1” 200 OK
INFO: 127.0.0.1:57042 - “GET /local_doc_qa/list_files?knowledge_base_id=pizza HTTP/1.1” 200 OK
INFO: 127.0.0.1:57046 - “POST /local_doc_qa/local_doc_chat HTTP/1.1” 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File “/home/pai/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py”, line 429, in run_asgi
result = await app( # type: ignore[func-returns-value]
File “/home/pai/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py”, line 78, in call
return await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/fastapi/applications.py”, line 276, in call
await super().call(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/applications.py”, line 122, in call
await self.middleware_stack(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/errors.py”, line 184, in call
raise exc
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/errors.py”, line 162, in call
await self.app(scope, receive, _send)
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/exceptions.py”, line 79, in call
raise exc
File “/home/pai/lib/python3.9/site-packages/starlette/middleware/exceptions.py”, line 68, in call
await self.app(scope, receive, sender)
File “/home/pai/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py”, line 21, in call
raise e
File “/home/pai/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py”, line 18, in call
await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 718, in call
await route.handle(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 276, in handle
await self.app(scope, receive, send)
File “/home/pai/lib/python3.9/site-packages/starlette/routing.py”, line 66, in app
response = await func(request)
File “/home/pai/lib/python3.9/site-packages/fastapi/routing.py”, line 237, in app
raw_response = await run_endpoint_function(
File “/home/pai/lib/python3.9/site-packages/fastapi/routing.py”, line 163, in run_endpoint_function
return await dependant.call(**values)
File “/mnt/workspace/langchain-ChatGLM/api.py”, line 293, in local_doc_chat
for resp, history in local_doc_qa.get_knowledge_based_answer(
File “/mnt/workspace/langchain-ChatGLM/chains/local_doc_qa.py”, line 231, in get_knowledge_based_answer
related_docs_with_score = vector_store.similarity_search_with_score(query, k=self.top_k)
File “/home/pai/lib/python3.9/site-packages/langchain/vectorstores/faiss.py”, line 221, in similarity_search_with_score
docs = self.similarity_search_with_score_by_vector(embedding, k)
File “/mnt/workspace/langchain-ChatGLM/vectorstores/MyFAISS.py”, line 86, in similarity_search_with_score_by_vector
_id0 = self.index_to_docstore_id[l]
KeyError: 23

这段报错信息似乎说明各个文件的文本向量库是以字典形式储存的。在删除一个文件后某一个key对应的value缺失导致报错。由这一点基本上可以说明langchain-ChatGLM官方给出的api.py关于删除知识库文件的部分是有bug的。我尝试了对api.py进行修改,但目前还没有成功。

3 LLM只采用知识库中一个文件的内容。在我使用种植番茄的例子之前我使用的是一个做披萨的例子,传入了3个披萨配方然后问模型“怎么做披萨”.模型自始至终只会基于第一个配方回答,但是在引用文段中包括了其他两个配方的内容。最后发现可能的问题是第一个配方中每一条内容之间都有空格换行,而其他两个配方内容是连在一起的。有可能embedding模型在对文本分段过程中受到了格式的影响。因此在传入知识库文件时要尽量保持文件格式一致。

以上是我开发langchain-ChatGLM客户端的记录。如果大家谁发现了我提到这两个遗留问题的解决方法也请帮忙告诉我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决代理IP负载均衡与性能优化的双重挑战

在当今数字化时代,代理IP的应用范围日益广泛,它不仅在数据爬取、网络抓取等领域发挥着重要作用,也成为网络安全和隐私保护的有力工具。然而,面对庞大的数据流量和复杂的网络环境,如何实现代理IP的负载均衡和性能优化成…

反复 Failed to connect to github.com port 443 after xxx ms

前提:使用了代理,浏览器能稳定访问github,但git clone一直超时 解决方案: 1. git config --global http.proxy http://127.0.0.1:1080 2. 代理设置端口1080 3. 1080可自定义 感谢来自这篇博客和评论区的提醒:解决…

Flutter 状态组件 InheritedWidget

Flutter 状态组件 InheritedWidget 视频 前言 今天会讲下 inheritedWidget 组件,InheritedWidget 是 Flutter 中非常重要和强大的一种 Widget,它可以使 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget,从而简化了状态管理和数据传递…

SpringBoot的三层架构以及IOCDI

目录 一、IOC&DI入门 二、三层架构 数据库访问层 业务逻辑层 控制层 一、IOC&DI入门 在软件开发中,IOC(Inversion of Control)和DI(Dependency Injection)是密切相关的概念。 IOC(控制反转&a…

常见的远程代码执行漏洞的注入点和注入方式

10个常见的远程代码执行漏洞的注入点和注入方式的举例: 用户输入:当用户输入未经验证和过滤的数据被用于构建动态命令或查询时,攻击者可以通过输入恶意代码来执行远程命令。 文件上传功能:如果文件上传功能没有正确地验证和限制上…

webpack如何实现热更新?

webpack如何实现热更新? 要使用 Webpack 实现热更新,可以按照以下步骤进行配置: 1.在项目中安装 Webpack 和相关的开发依赖: npm install webpack webpack-cli webpack-dev-server --save-dev2.创建一个名为 webpack.dev.js 的…

flask中的蓝图

flask中的蓝图 在 Flask 中,蓝图(Blueprint)是一种组织路由和服务的方法,它允许你在应用中更灵活地组织代码。蓝图可以大致理解为应用或者应用中的一部分,可以在蓝图中定义路由、错误处理程序以及静态文件等。然后可以…

Qt 调用 Microsoft Excel 组件生成 Excel 文档

在.pro文件中添加模块: QT += core gui axcontainer参考界面:界面中只有一个 pushButton 按钮。 参考代码: mainwindow.h: #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include

【leetcode】541. 反转字符串 II

给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转这 2k 字符中的前 k 个字符。 如果剩余字符少于 k 个&#xff0c;则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个&#xff0c;则反转前 k 个字符…

细讲TCP三次握手四次挥手(一)

计算机网络体系结构 在计算机网络的基本概念中&#xff0c;分层次的体系结构是最基本的。计算机网络体系结构的抽象概念较多&#xff0c;在学习时要多思考。这些概念对后面的学习很有帮助。 网络协议是什么&#xff1f; 在计算机网络要做到有条不紊地交换数据&#xff0c;就必…

Unity 性能优化二:内存问题

目录 策略导致的内存问题 GFX内存 纹理资源 压缩格式 Mipmap 网格资源 Read/Write 顶点数据 骨骼 静态合批 Shader资源 Reserved Memory RenderTexture 动画资源 音频资源 字体资源 粒子系统资源 Mono堆内存 策略导致的内存问题 1. Assetbundle 打包的时候…

项目——负载均衡在线OJ

目录 项目介绍开发环境所用技术项目宏观结构编写思路1. 编写compile_server1.1 编译模块编写1.2 运行功能1.3compile_runner 编译与运行1.4 编写compile_server.cpp调用compile_run模块&#xff0c;形成网络服务 2. 编写基于MVC的oj_server2.1 oj_server.cpp的编写2.2 oj_model…

后端性能测试的类型

目录 性能测试的类型 负载测试(load testing) 压力测试(Stress Testing) 可扩展性测试( 尖峰测试(Spike Testing) 耐久性测试(Endurance Testing) 并发测试(Concurrency Testing) 容量测试(Capacity Testing) 资料获取方法 性能测试的类型 性能测试&#xff1a;确定软…

pytorch模型的保存与加载

1 pytorch保存和加载模型的三种方法 PyTorch提供了三种方式来保存和加载模型&#xff0c;在这三种方式中&#xff0c;加载模型的代码和保存模型的代码必须相匹配&#xff0c;才能保证模型的加载成功。通常情况下&#xff0c;使用第一种方式&#xff08;保存和加载模型状态字典…

【Linux下6818开发板(ARM)】硬件空间挂载

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

linux----源码安装如何加入到系统服务中(systemclt)

将自己源码安装的软件加入到系统服务中。例如nginx,mysql 就以nginx为例&#xff0c;源码安装&#xff0c;加入到系统服务中 使用yum安装nginx&#xff0c;自动会加入到系统服务 16-Linux系统服务 - 刘清政 - 博客园 (cnblogs.com) 第一步: 源码安装好nginx之后&#xff0…

【Maven】Maven配置国内镜像

文章目录 1. 配置maven的settings.xml文件1.1. 先把镜像mirror配置好1.2. 再把仓库配置好 2. 在idea中引用3. 参考资料 网上配置maven国内镜像的文章很多&#xff0c;为什么选择我&#xff0c;原因是&#xff1a;一次配置得永生、仓库覆盖广、仓库覆盖全面、作者自用的配置。 1…

JavaSE - Sting类

目录 一. 字符串的定义 二. String类中的常用方法 1. 比较两个字符串是否相等&#xff08;返回值是boolean类型&#xff09; 2. 比较两个字符串的大小&#xff08;返回值是int类型&#xff09; 3. 字符串查找 &#xff08;1&#xff09;s1.charAt(index) index:下标&…

Vue3中使用pinia

在Vue 3中使用Pinia&#xff0c;您需要按照以下步骤进行设置&#xff1a; 安装Pinia&#xff1a; npm install pinia创建和配置Pinia存储&#xff1a; // main.jsimport { createApp } from vue import { createPinia } from pinia import App from ./App.vueconst app create…

基于RK3588+AI的边缘计算算法方案:智慧园区、智慧社区、智慧物流

RK3588 AI 边缘计算主板规格书简介 关于本文档 本文档详细介绍了基于Rockchip RK3588芯片的AI边缘计算主板外形、尺寸、技术规格&#xff0c;以及详细的硬件接口设计参考说明&#xff0c;使客户可以快速将RK3588边缘计算主板应用于工业互联网、智慧城市、智慧安防、智慧交通&am…