INFO 2023-06-17 11:36:30,140-1d:
ERROR 2023-06-17 11:36:30,141-1d: Error: source file could not be loaded
ERROR 2023-06-17 11:36:30,142-1d: Package not found at '/tmp/tmp33_8507e/08服务器软硬件环境配置说明书.docx'
INFO 2023-06-17 11:36:30,143-1d: /home/aiadmin/langchain-ChatGLM-master/content/DK12/08服务器软硬件环境配置说明书.docx 未能成功加载
INFO 2023-06-17 11:36:30,143-1d: 文件均未成功加载,请检查依赖包或替换为其他文件再次上传。
最终处理办法很简单,执行以下卸载和安装命令:
yum remove openoffice* libreoffice*
yum install libreoffice*
如果yum安装下载过慢,还可以手动下载安装,参考这篇博客:
下载地址,下载下面三个文件:
解压文件,执行如下命令
mkdir /usr/libreoffice
tar -zxvf LibreOffice_6.0.3_Linux_x86-64_rpm.tar.gz -C /usr/libreoffice/
tar -zxvf LibreOffice_6.0.3_Linux_x86-64_rpm_sdk.tar.gz -C /usr/libreoffice/
tar -zxvf LibreOffice_6.0.3_Linux_x86-64_rpm_langpack_zh-CN.tar.gz -C /usr/libreoffice/
cd /usr/libreoffice/LibreOffice_6.0.3_Linux_x86-64_rpm/RPMS
yum localinstall *.rpm
安装完成之后,建立软连接:
ln -s /usr/bin/libreoffice7.5 /usr/bin/soffice
=============================================================================
下面记录一下心路历程,启发一下问题解决思路。
本地部署langchain-chatglm过程确实不复杂,参考安装指南进行环境安装。到huggingface下载chatglm-6b和text2vec-large-chinese,并修改配置文件configs/model_config.py中对应的配置,就可以直接运行了。
真的是顺利呀,赶紧上传了一个pdf文档总结一下,真的挺好用。再上传一个word文档吧,就出错了。在安装指南发现了下面这段话:
大致意思就是说想要使用langchain.document_loaders.UnstructuredFileLoader来处理非结构化文档,需要安装额外的依赖。但是文档中提到的地址已经失效了,我找到了下面这个有效地址。按照文档中的说明,依次执行了以下包的安装:
# 1.libmagic
# 安装 file-devel 软件包,其中包含 libmagic 库和相关的开发文件
# libmagic是一个独立的C库,用于文件类型识别
# python-magic和python-magic-bin都是基于libmagic的Python封装库,在python中通过`import magic`导入使用
yum install file-devel
# 2.Poppler是一个用于处理 PDF 文件的开源工具集,poppler-utils 包含了一些常用的 Poppler 工具,如 pdftotext、pdfinfo、pdfimages 等
yum install poppler-utils
# 3.tesseract是一个开源的OCR(光学字符识别)引擎,它能够将图像中的文本识别为可编辑的文本
yum install tesseract
# 4.libxml2是一个用于解析和操作 XML 文件的开源库
yum install libxml2
# 5.libxslt 是一个用于处理 XSLT(可扩展样式表语言转换)的开源库
yum install libxslt
由于网络隔离,nltk无法直接执行下载,因此手动下载。
import nltk
nltk.download('punkt')
数据说明,手动下载地址,搜索关键字进行下载,并根据报错提示进行数据文件放置。这一块的报错是在我手工调试word文档异常问题的过程中出现并处理的,路径位置也是根据报错信息的查找路径选择了其中一个。
我放在了我的虚拟环境的目录下:
# nltk
~/langchain-ChatGLM/nltk_data/taggers/
# punkt
~/langchain-ChatGLM/nltk_data/tokenizers/
当然了,经历这一波安装,并没有解决问题,依然还是无法处理word文档。
那就看看加载word文档的代码吧local_doc_qa.py
,大致调用逻辑是下面这样,报错出现在load_and_split
方法
from langchain.document_loaders import UnstructuredFileLoader, UnstructuredWordDocumentLoader
from textsplitter import ChineseTextSplitter
loader = UnstructuredWordDocumentLoader("/home/aiadmin/h2412730/testReport2.doc", mode="elements")
textsplitter = ChineseTextSplitter(pdf=False, sentence_size=100)
docs = loader.load_and_split(text_splitter=textsplitter)
那就往下继续调试,看看langchain.UnstructuredWordDocumentLoader代码的实现吧。
class UnstructuredWordDocumentLoader(UnstructuredFileLoader):
"""Loader that uses unstructured to load word documents."""
def _get_elements(self) -> List:
from unstructured.__version__ import __version__ as __unstructured_version__
from unstructured.file_utils.filetype import FileType, detect_filetype
unstructured_version = tuple(
[int(x) for x in __unstructured_version__.split(".")]
)
# NOTE(MthwRobinson) - magic will raise an import error if the libmagic
# system dependency isn't installed. If it's not installed, we'll just
# check the file extension
try:
import magic # noqa: F401
is_doc = detect_filetype(self.file_path) == FileType.DOC
except ImportError:
_, extension = os.path.splitext(str(self.file_path))
is_doc = extension == ".doc"
if is_doc and unstructured_version < (0, 4, 11):
raise ValueError(
f"You are on unstructured version {__unstructured_version__}. "
"Partitioning .doc files is only supported in unstructured>=0.4.11. "
"Please upgrade the unstructured package and try again."
)
if is_doc:
from unstructured.partition.doc import partition_doc
return partition_doc(filename=self.file_path, **self.unstructured_kwargs)
else:
from unstructured.partition.docx import partition_docx
return partition_docx(filename=self.file_path, **self.unstructured_kwargs)
经分析_get_elements
是关键的调用方法,逐行验证发现以下问题
loader = UnstructuredWordDocumentLoader("/home/aiadmin/h2412730/testReport2.doc", mode="elements")
于是查看detect_filetype
的逻辑,发现安装magic和不安装magic是两套检测逻辑。于是卸载python-magic,文件类型检测正常。
docx文件可以正常处理。但是doc还是无法加载,提示以下信息:
docx.opc.exceptions.PackageNotFoundError: Package not found at '/tmp/tmpmzlvwg1m/testReport.docx'
这里有两点比较奇怪:
partition_doc
方法吧,一看恍然大明白@process_metadata()
@add_metadata_with_filetype(FileType.DOC)
def partition_doc(
filename: Optional[str] = None,
file: Optional[IO] = None,
include_page_breaks: bool = True,
**kwargs,
) -> List[Element]:
"""Partitions Microsoft Word Documents in .doc format into its document elements.
Parameters
----------
filename
A string defining the target filename path.
file
A file-like object using "rb" mode --> open(filename, "rb").
"""
# Verify that only one of the arguments was provided
if filename is None:
filename = ""
exactly_one(filename=filename, file=file)
if len(filename) > 0:
_, filename_no_path = os.path.split(os.path.abspath(filename))
base_filename, _ = os.path.splitext(filename_no_path)
if not os.path.exists(filename):
raise ValueError(f"The file {filename} does not exist.")
elif file is not None:
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.write(file.read())
tmp.close()
filename = tmp.name
_, filename_no_path = os.path.split(os.path.abspath(tmp.name))
base_filename, _ = os.path.splitext(filename_no_path)
with tempfile.TemporaryDirectory() as tmpdir:
convert_office_doc(filename, tmpdir, target_format="docx")
docx_filename = os.path.join(tmpdir, f"{base_filename}.docx")
elements = partition_docx(
filename=docx_filename,
metadata_filename=filename,
include_page_breaks=include_page_breaks,
)
return elements
这是unstruncture包的partition_doc的实现,基本逻辑很明显,把doc文件转换为docx文件,然后调用partition_docx方法。那就解释了为什么要找docx文件了。既然找不到这个临时转换生成的docx文件,肯定是convert_office_doc这个转换方法出错了。那就继续看看这个方法的实现:
def convert_office_doc(input_filename: str, output_directory: str, target_format: str):
"""Converts a .doc file to a .docx file using the libreoffice CLI."""
# NOTE(robinson) - In the future can also include win32com client as a fallback for windows
# users who do not have LibreOffice installed
# ref: https://stackoverflow.com/questions/38468442/
# multiple-doc-to-docx-file-conversion-using-python
command = [
"soffice",
"--headless",
"--convert-to",
target_format,
"--outdir",
output_directory,
input_filename,
]
try:
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, error = process.communicate()
except FileNotFoundError:
raise FileNotFoundError(
"""soffice command was not found. Please install libreoffice
on your system and try again.
- Install instructions: https://www.libreoffice.org/get-help/install-howto/
- Mac: https://formulae.brew.sh/cask/libreoffice
- Debian: https://wiki.debian.org/LibreOffice""",
)
logger.info(output.decode().strip())
if error:
logger.error(error.decode().strip())
这里的方法实现和调用其实隐藏了底层的错误。就是子进程的错误信息只是做了日志输出,调用者partition_doc并没有判断,而是直接执行后续方法调用,因此隐藏了错误的发生点。
当然问题到这里很明显了,就是执行soffice进行文档转换,执行这个报错。找到了问题源头,搜到了以下解决方法:
# Error: no export filter for teste.docx found, aborting
yum remove openoffice* libreoffice*
yum install libreoffice*
手动安装方法可以参考文章开头。
其实一路下来,总的来说,还是要找到问题的根源,才能解决问题。根本原因分析,理论很明确,执行过程却不总是那么顺利。回过头来,发现路上其实有几个问题: