Elasticsearch:与多个 PDF 聊天 | LangChain Python 应用教程(免费 LLMs 和嵌入)

在本博客中,你将学习创建一个 LangChain 应用程序,以使用 ChatGPT API 和 Huggingface 语言模型与多个 PDF 文件聊天。

如上所示,我们在最最左边摄入 PDF 文件,并它们连成一起,并分为不同的 chunks。我们可以通过使用 huggingface 来对 chunks 进行处理并形成 embeddings。我们把 embeddings 写入到 Elasticsearch 向量数据库中,并保存。在搜索的时候,我们通过 LangChain 来进行向量化,并使用 Elasticsearch 进行向量搜索。在最后,我们通过大模型的使用,针对提出的问题来进行提问。我们最终的界面如下:

如上所示,它可以针对我们的问题进行回答。进一步阅读 

  • 使用 LangChain 和 Elasticsearch 对私人数据进行人工智能搜索
  • 使用 LangChain 和 Elasticsearch 的隐私优先 AI 搜索

所有的源码可以在地址 GitHub - liu-xiao-guo/ask-multiple-pdfs: A Langchain app that allows you to chat with multiple PDFs 进行下载。

安装

如果你还没有安装好自己的 Elasticsearch 及 Kibana 的话,那么请参考如下的链接:

  • 如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch

  • Kibana:如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana

在安装的时候,我们选择 Elastic Stack 9.x 的安装指南来进行安装。在默认的情况下,Elasticsearch 集群的访问具有 HTTPS 的安全访问。

在安装时,我们可以在 Elasticsearch 的如下地址找到相应的证书文件 http_ca.crt:

$ pwd
/Users/liuxg/elastic/elasticsearch-8.10.0/config/certs
$ ls
http.p12      http_ca.crt   transport.p12

我们需要把该证书拷贝到项目文件的根目录下:

$ tree -L 3
.
├── app.py
├── docs
│   └── PDF-LangChain.jpg
├── htmlTemplates.py
├── http_ca.crt
├── lib_embeddings.py
├── lib_indexer.py
├── lib_llm.py
├── lib_vectordb.py
├── myapp.py
├── pdf_files
│   ├── sample1.pdf
│   └── sample2.pdf
├── readme.md
├── requirements.txt
└── simple.cfg

如上所示,我们把 http_ca.crt 拷贝到应用的根目录下。我们在 pdf_files 里放了两个用于测试的 PDF 文件。你可以使用自己的 PDF 文件来进行测试。我们在 simple.cfg 做如下的配置:

ES_SERVER: "localhost" 
ES_PASSWORD: "vXDWYtL*my3vnKY9zCfL"
ES_FINGERPRINT: "e2c1512f617f432ddf242075d3af5177b28f6497fecaaa0eea11429369bb7b00"

在上面,我们需要配置 ES_SERVER。这个是 Elasticsearch 集群的地址。这里的 ES_PASSWORD 是 Elasticsearch 的超级用户 elastic 的密码。我们可以在 Elasticsearch 第一次启动的画面中找到这个 ES_FINGERPRINT:

你还可以在 Kibana 的配置文件 confgi/kibana.yml 文件中获得 fingerprint 的配置:

在项目的目录中,我们还可以看到一个叫做 .env-example 的文件。我们可以使用如下的命令把它重新命名为 .env:

mv .env.example .env

在 .env 中,我们输入 huggingface.co 网站得到的 token:

$ cat .env
OPENAI_API_KEY=your_openai_key
HUGGINGFACEHUB_API_TOKEN=your_huggingface_key

在本例中,我们将使用 huggingface 来进行测试。如果你需要使用到 OpenAI,那么你需要配置它的 key。有关 huggingface 的开发者 key,你可以在地址获得。

运行项目

在运行项目之前,你需要做一下安装的动作:

python3 -m venv env
source env/bin/activate
python3 -m pip install --upgrade pip
pip install -r requirements.txt

创建界面

本应用的界面,我们采用是 streamlit 来创建的。它的创建也是非常地简单。我们可以在 myapp.py 中看到如下的代码:

myapp.py

import streamlit as st
from dotenv import load_dotenv
from PyPDF2 import PdfReader
from htmlTemplates import css, bot_template, user_templatedef get_pdf_texts(pdf_docs):text = ""for pdf in pdf_docs:pdf_reader = PdfReader(pdf)for page in pdf_reader.pages:text += page.extract_text()return textdef main():load_dotenv()st.set_page_config(page_title="Chat with multiple PDFs", page_icon=":books:")st.write(css, unsafe_allow_html=True)st.header("Chat with multiple PDFs :books:")user_question = st.text_input("Ask a question about your documents")if user_question:passst.write(user_template.replace("{{MSG}}", "Hello, human").replace("{{MSG1}}", " "), unsafe_allow_html=True)st.write(bot_template.replace("{{MSG}}", "Hello, robot").replace("{{MSG1}}", " "), unsafe_allow_html=True)# Add a side barwith st.sidebar:st.subheader("Your documents")pdf_docs = st.file_uploader("Upload your PDFs here and press on click on Process", accept_multiple_files=True)print(pdf_docs)if st.button("Process"):with st.spinner("Processing"):# Get pdf text fromraw_text = get_pdf_texts(pdf_docs)st.write(raw_text)if __name__ == "__main__":main()

在上面的代码中,我创建了一个 sidebar 用来选择需要的 PDF 文件。我们可以点击 Process 按钮来显示已经提取的 PDF 文本。我们可以使用如下的命令来运行应用:

(venv) $ streamlit run myapp.py
venv) $ streamlit run myapp.pyYou can now view your Streamlit app in your browser.Local URL: http://localhost:8502Network URL: http://198.18.1.13:8502

运行完上面的命令后,我们可以在浏览器中打开应用:

我们点击 Browse files,并选中 PDF 文件:

点击上面的 Process,我们可以看到:

在上面,我们为了显示的方便,我使用 st.write 直接把结果写到浏览器的页面里。我们接下来需要针对这个长的文字进行切分为一个一个的 chunks。我们需要按照模型的需要,不能超过模型允许的最大值。

上面我简单地叙述了 UI 的构造。最终完整的 myapp.py 的设计如下:

myapp.py

import streamlit as st
from dotenv import load_dotenv
from PyPDF2 import PdfReader
from langchain.text_splitter import CharacterTextSplitter
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from htmlTemplates import css, bot_template, user_templateimport lib_indexer
import lib_llm
import lib_embeddings
import lib_vectordbindex_name = "pdf_docs"def get_pdf_text(pdf):text = ""pdf_reader = PdfReader(pdf)for page in pdf_reader.pages:text += page.extract_text()return textdef get_pdf_texts(pdf_docs):text = ""for pdf in pdf_docs:pdf_reader = PdfReader(pdf)for page in pdf_reader.pages:text += page.extract_text()return textdef get_text_chunks(text):text_splitter = CharacterTextSplitter(separator="\n", chunk_size=1000,chunk_overlap=200,length_function=len)chunks = text_splitter.split_text(text)# chunks = text_splitter.split_documents(text)return chunksdef get_text_chunks1(text):text_splitter = RecursiveCharacterTextSplitter(chunk_size=384, chunk_overlap=0)chunks = text_splitter.split_text(text)return chunksdef handle_userinput(db, llm_chain_informed, user_question):similar_docs = db.similarity_search(user_question)print(f'The most relevant passage: \n\t{similar_docs[0].page_content}')## 4. Ask Local LLM context informed prompt# print(">> 4. Asking The Book ... and its response is: ")informed_context= similar_docs[0].page_contentresponse = llm_chain_informed.run(context=informed_context,question=user_question)st.write(user_template.replace("{{MSG}}", user_question).replace("{{MSG1}}", " "), unsafe_allow_html=True)st.write(bot_template.replace("{{MSG}}", response).replace("{{MSG1}}", similar_docs[0].page_content),unsafe_allow_html=True)def main():# # Huggingface embedding setuphf = lib_embeddings.setup_embeddings()# # # ## Elasticsearch as a vector dbdb, url = lib_vectordb.setup_vectordb(hf, index_name)# # # ## set up the conversational LLMllm_chain_informed= lib_llm.make_the_llm()load_dotenv()st.set_page_config(page_title="Chat with multiple PDFs", page_icon=":books:")st.write(css, unsafe_allow_html=True)st.header("Chat with multiple PDFs :books:")user_question = st.text_input("Ask a question about your documents")if user_question:handle_userinput(db, llm_chain_informed, user_question)st.write(user_template.replace("{{MSG}}", "Hello, human").replace("{{MSG1}}", " "), unsafe_allow_html=True)st.write(bot_template.replace("{{MSG}}", "Hello, robot").replace("{{MSG1}}", " "), unsafe_allow_html=True)# Add a side barwith st.sidebar:st.subheader("Your documents")pdf_docs = st.file_uploader("Upload your PDFs here and press on click on Process", accept_multiple_files=True)print(pdf_docs)if st.button("Process"):with st.spinner("Processing"):# Get pdf text from# raw_text = get_pdf_text(pdf_docs[0])raw_text = get_pdf_texts(pdf_docs)# st.write(raw_text)print(raw_text)# Get the text chunkstext_chunks = get_text_chunks(raw_text)# st.write(text_chunks)# Create vector storelib_indexer.loadPdfChunks(text_chunks, url, hf, db, index_name)if __name__ == "__main__":main()

创建嵌入模型

lib_embedding.py

## for embeddings
from langchain.embeddings import HuggingFaceEmbeddingsdef setup_embeddings():# Huggingface embedding setupprint(">> Prep. Huggingface embedding setup")model_name = "sentence-transformers/all-mpnet-base-v2"return HuggingFaceEmbeddings(model_name=model_name)

 创建向量存储

lib_vectordb.py

import os
from config import Config## for vector store
from langchain.vectorstores import ElasticVectorSearchdef setup_vectordb(hf,index_name):# Elasticsearch URL setupprint(">> Prep. Elasticsearch config setup")with open('simple.cfg') as f:cfg = Config(f)endpoint = cfg['ES_SERVER']username = "elastic"password = cfg['ES_PASSWORD']ssl_verify = {"verify_certs": True,"basic_auth": (username, password),"ca_certs": "./http_ca.crt",}url = f"https://{username}:{password}@{endpoint}:9200"return ElasticVectorSearch( embedding = hf, elasticsearch_url = url, index_name = index_name, ssl_verify = ssl_verify), url

 创建使用带有上下文和问题变量的提示模板的离线 LLM

lib_llm.py

## for conversation LLM
from langchain import PromptTemplate, HuggingFaceHub, LLMChain
from langchain.llms import HuggingFacePipeline
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, AutoModelForSeq2SeqLMdef make_the_llm():# Get Offline flan-t5-large ready to go, in CPU modeprint(">> Prep. Get Offline flan-t5-large ready to go, in CPU mode")model_id = 'google/flan-t5-large'# go for a smaller model if you dont have the VRAMtokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForSeq2SeqLM.from_pretrained(model_id) #load_in_8bit=True, device_map='auto'pipe = pipeline("text2text-generation",model=model, tokenizer=tokenizer, max_length=100)local_llm = HuggingFacePipeline(pipeline=pipe)# template_informed = """# I know the following: {context}# Question: {question}# Answer: """template_informed = """I know: {context}when asked: {question}my response is: """prompt_informed = PromptTemplate(template=template_informed, input_variables=["context", "question"])return LLMChain(prompt=prompt_informed, llm=local_llm)

写入以向量表示的 PDF 文件

以下是我的分块和向量存储代码。 它需要在 Elasticsearch 中准备好组成的 Elasticsearch url、huggingface 嵌入模型、向量数据库和目标索引名称

lib_indexer.py


from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import TextLoader## for vector store
from langchain.vectorstores import ElasticVectorSearch
from elasticsearch import Elasticsearch
from config import Configwith open('simple.cfg') as f:cfg = Config(f)fingerprint = cfg['ES_FINGERPRINT']
endpoint = cfg['ES_SERVER']
username = "elastic"
password = cfg['ES_PASSWORD']
ssl_verify = {"verify_certs": True,"basic_auth": (username, password),"ca_certs": "./http_ca.crt"
}url = f"https://{username}:{password}@{endpoint}:9200"def parse_book(filepath):loader = TextLoader(filepath)documents = loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=384, chunk_overlap=0)docs = text_splitter.split_documents(documents)return docsdef parse_triplets(filepath):docs = parse_book(filepath)result = []for i in range(len(docs) - 2):concat_str = docs[i].page_content + " " + docs[i+1].page_content + " " + docs[i+2].page_contentresult.append(concat_str)return result#db.from_texts(docs, embedding=hf, elasticsearch_url=url, index_name=index_name)## load book utility
## params
##  filepath: where to get the book txt ... should be utf-8
##  url: the full Elasticsearch url with username password and port embedded
##  hf: hugging face transformer for sentences
##  db: the VectorStore Langcahin object ready to go with embedding thing already set up
##  index_name: name of index to use in ES
##
##  will check if the index_name exists already in ES url before attempting split and load
def loadBookTriplets(filepath, url, hf, db, index_name):with open('simple.cfg') as f:cfg = Config(f)fingerprint = cfg['ES_FINGERPRINT']es = Elasticsearch( [ url ], basic_auth = ("elastic", cfg['ES_PASSWORD']), ssl_assert_fingerprint = fingerprint, http_compress = True  )## Parse the book if necessaryif not es.indices.exists(index=index_name):print(f'\tThe index: {index_name} does not exist')print(">> 1. Chunk up the Source document")results = parse_triplets(filepath)print(">> 2. Index the chunks into Elasticsearch")elastic_vector_search= ElasticVectorSearch.from_documents( docs,embedding = hf, elasticsearch_url = url, index_name = index_name, ssl_verify = ssl_verify)else:print("\tLooks like the pdfs are already loaded, let's move on")def loadBookBig(filepath, url, hf, db, index_name):es = Elasticsearch( [ url ], basic_auth = ("elastic", cfg['ES_PASSWORD']), ssl_assert_fingerprint = fingerprint, http_compress = True  )## Parse the book if necessaryif not es.indices.exists(index=index_name):print(f'\tThe index: {index_name} does not exist')print(">> 1. Chunk up the Source document")docs = parse_book(filepath)# print(docs)print(">> 2. Index the chunks into Elasticsearch")elastic_vector_search= ElasticVectorSearch.from_documents( docs,embedding = hf, elasticsearch_url = url, index_name = index_name, ssl_verify = ssl_verify)   else:print("\tLooks like the pdfs are already loaded, let's move on")def loadPdfChunks(chunks, url, hf, db, index_name):    es = Elasticsearch( [ url ], basic_auth = ("elastic", cfg['ES_PASSWORD']), ssl_assert_fingerprint = fingerprint, http_compress = True  )## Parse the book if necessaryif not es.indices.exists(index=index_name):print(f'\tThe index: {index_name} does not exist')        print(">> 2. Index the chunks into Elasticsearch")print("url: ", url)print("index_name", index_name)elastic_vector_search = db.from_texts( chunks,embedding = hf, elasticsearch_url = url, index_name = index_name, ssl_verify = ssl_verify)   else:print("\tLooks like the pdfs are already loaded, let's move on")

提问

我们使用 streamlit 的 input 来进行提问:

    user_question = st.text_input("Ask a question about your documents")if user_question:handle_userinput(db, llm_chain_informed, user_question)

当我们打入 ENTER 键后,上面的代码调用 handle_userinput(db, llm_chain_informed, user_question):

def handle_userinput(db, llm_chain_informed, user_question):similar_docs = db.similarity_search(user_question)print(f'The most relevant passage: \n\t{similar_docs[0].page_content}')## 4. Ask Local LLM context informed prompt# print(">> 4. Asking The Book ... and its response is: ")informed_context= similar_docs[0].page_contentresponse = llm_chain_informed.run(context=informed_context,question=user_question)st.write(user_template.replace("{{MSG}}", user_question).replace("{{MSG1}}", " "), unsafe_allow_html=True)st.write(bot_template.replace("{{MSG}}", response).replace("{{MSG1}}", similar_docs[0].page_content),unsafe_allow_html=True)

首先它使用 db 进行相似性搜索,然后我们再使用大模型来得到我们想要的答案。

运行结果

我们使用命令来运行代码:

streamlit run myapp.py

我们在浏览器中选择在 pdf_files 中的两个 PDF 文件:

在上面,我们输入想要的问题:

上面的问题是:

what do I make all the same and put a cup next to him on the desk?

再进行提问:

上面的问题是:

when should you come? I will send a car to meet you from the half past four arrival at Harrogate Station.

上面的问题是:

what will I send to meet you from the half past four arrival at Harrogate Station?

你进行多次尝试其它的问题。Happy journery :)

有关 ChatGPT 的使用也是基本相同的。你需要使用 ChatGPT 的模型及其相应的 key 即可。在这里就不赘述了。

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

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

相关文章

QA 云计算实验问题汇总

Q Win11中VMware虚拟网卡有感叹号 2023-9-27一位同学的win11的两个VMware17的虚拟网卡都有感叹号 A 清除注册表 步骤1 关闭VMWare虚拟化软件 步骤2 使用CCleaner pro 清理注册表 步骤3 重启系统 步骤4 VMware虚拟网卡上的感叹号消失。 Q Win11上的VisualBox的网卡消失了…

DBA数据库运维-MySQL安装篇(glibc,源码)

1. MySQL数据库版本 版本说明社区版: MySQL Community Edition (GPL)1.可以看做是企业版的“广泛体验版(小白鼠版)",未经各个专有系统平台的压力和性能测试 2.基于GPL协议发布,可以随意下载使用 3.没有任何官方技术支持服务企业版:MySQL Enterpris…

JUC中的设计模式

文章目录 1. 终止模式之两阶段终止模式 1. 终止模式之两阶段终止模式 需求:用一个线程每两秒检测***状态,当不想检测时,用另一个线程将其停止 在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事…

解决AndroidStudio 2022.3.1版本 引入maven报错的问题

升级新版Android Studio 2022.3.1后发现引入maven报错: 1、报错的日志 ^ Unexpected tokens (use ‘;’ to separate expressions on the same line)。 2、解决问题 2.1 新版本的引入方式: repositories {maven { url uri("https://maven.aliy…

前端开发网站推荐

每个人都会遇见那么一个人,永远无法忘却,也永远不能拥有。 以下是一些可以用来查找和比较前端框架的推荐网站: JavaScript框架比较: 这些网站提供了对不同JavaScript框架和库的详细比较和评估。 JavaScripting: 提供了大量的JavaS…

【CSS 中 link 和@import 的区别】

<link> 和 import 都可以用于引入 CSS 文件&#xff0c;但是两者有以下区别&#xff1a; 加载时间&#xff1a;<link> 标签在页面加载时同时加载&#xff0c;而 import 是在页面加载后才开始加载。 兼容性&#xff1a;<link> 标签可以被所有的浏览器正确解释…

防火墙基础之H3C防火墙分支与分支之间双向地址转换

分支与分支之间双向地址转换 原理概述&#xff1a; 防火墙&#xff08;英语&#xff1a;Firewall&#xff09;技术是通过有机结合各类用于安全管理​与筛选的软件和硬件​设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资…

凉鞋的 Godot 笔记 102. 场景与节点的增删改查

在上一篇&#xff0c;我们完成了 Godot 引擎的 Hello World 输出&#xff0c;并且完成了第一个基本循环: 通过这次基本循环的完成&#xff0c;我们获得了一点点的 Godot 使用经验&#xff0c;这非常重要。 有实践经验后再去补充理论 和 先学习理论后去实践相比&#xff0c;前者…

5、【第一步】自定义模型集成

【第一步】自定义模型集成 简介自定义模型类配置文件模型测试简介 Qlib 的模型库包括 LightGBM、MLP、LSTM 等模型。这些模型都是预测模型的例子。除了 Qlib 提供的默认模型外,用户还可以将他们自己的自定义模型集成到 Qlib 中。 用户可以按照以下步骤集成他们自己的自定义模…

线性筛和埃氏筛

线性筛&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<ut…

C++ Primer 一 变量和基本类型

本章讲解C内置的数据类型&#xff08;如&#xff1a;字符、整型、浮点数等&#xff09;和自定义数据类型的机制。下一章讲解C标准库里面定义的更加复杂的数据类型&#xff0c;比如可变长字符串和向量等。 1.基本内置类型 C内置的基本类型包括&#xff1a;算术类型和空类型。算…

【iptables 实战】03 自定义链

一、新建一个自定义链 当前的机器IP为&#xff1a;10.1.0.10 自定义链IN_WEB&#xff0c;拒绝指定源ip的报文 [roottest-c ~]# iptables -t filter -I IN_WEB -s 10.1.0.11 -j REJECT [roottest-c ~]# iptables -t filter --line -nvL IN_WEB Chain IN_WEB (0 references) nu…

python-切换镜像源和使用PyCharm进行第三方开源包安装

文章目录 前言python-切换镜像源和使用PyCharm进行第三方开源包安装1. 切换镜像源2. 使用PyCharm进行第三方开源包安装 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每…

【Linux】线程概念

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ Linux       &#x1f6f0;️社区 :✈️ 进步学堂       &#x1f6f0…

常见加密和解密方法介绍。

介绍常见的加密和解密方法。 加密是利用数学方法将明文转化为密文&#xff0c;从而达到保护数据的目的。 通过加密可保证数据的机密性、完整性、鉴别性。 机密性&#xff1a;通过数据加密实现。只允许特定用户访问和阅读信息。 完整性&#xff1a;通过数字加密、散列、数字签名…

《Vue.js+Spring Boot全栈开发实战》简介

大家好&#xff0c;我是老卫。 恰逢中秋国庆双节&#xff0c;不想出门看人山&#xff0c;惟愿宅家阅书海&#xff01; 今天开箱的这本书是《Vue.jsSpring Boot全栈开发实战》。 外观 从书名故名思议&#xff0c;就是基于Vue.jsSpring Boot来实现企业级应用全栈开发。 该书由…

开源后台管理系统 (go-vue-admin)

go-vue-admin 是一套基于go语言开源的后台管理系统。功能参考诺依网站 &#xff0c;前后端分离。 简介 前端采用vue3、Element Plus 、RuoYi-Vue3后端采用gofrome 框架、mysql、redis、Jwt实现了一键生成前后端代码&#xff0c;高效开发。 内置功能 用户管理&#xff1a;用…

AGX-Orin问题汇总

版本5.0.2 1、eth1网口不插网线link switch的问题&#xff1f; 2、eth1的速度不对&#xff0c;不能自动link到100m swith的问题&#xff1f; 3、eth0和eth1同时使用&#xff0c;会eth0不通&#xff0c;需要动态获取一次 网关问题吗&#xff1f; 4、eth0的arp不完整&…

LabVIEW工业虚拟仪器的标准化实施

LabVIEW工业虚拟仪器的标准化实施 创建计算机化的测试和测量系统&#xff0c;从计算机桌面控制外部测量硬件设备&#xff0c;以及在计算机屏幕上显示的类似仪器的面板上查看来自外部设备的测试或测量数据&#xff0c;所有这些都需要虚拟仪器系统软件。该软件允许用户执行所有这…

Axios post请求出现500错误

笔者在编写前端form表单传后端数据的时候&#xff0c;出现了以下问题 一、问题场景 当我用axios发送post请求的时候&#xff0c;出现了500错误 笔者找了很长时间错误&#xff0c;代码没问题&#xff0c;后端接口也没问题&#xff0c;后来发现问题出在实体类上了 当前端post请…