Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
J
jinchat-server
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
aigc-pioneer
jinchat-server
Commits
a887df17
提交
a887df17
authored
6月 13, 2023
作者:
imClumsyPanda
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add knowledge_base folder and move vector_store and content inside
上级
585b4427
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
129 行增加
和
96 行删除
+129
-96
api.py
api.py
+62
-51
local_doc_qa.py
chains/local_doc_qa.py
+3
-2
model_config.py
configs/model_config.py
+4
-6
README.md
knowledge_base/samples/content/README.md
+35
-13
test.jpg
knowledge_base/samples/content/test.jpg
+0
-0
test.pdf
knowledge_base/samples/content/test.pdf
+0
-0
test.txt
knowledge_base/samples/content/test.txt
+0
-0
image_loader.py
loader/image_loader.py
+1
-1
pdf_loader.py
loader/pdf_loader.py
+1
-1
webui.py
webui.py
+15
-14
webui_st.py
webui_st.py
+8
-8
没有找到文件。
api.py
浏览文件 @
a887df17
...
@@ -15,7 +15,7 @@ from typing_extensions import Annotated
...
@@ -15,7 +15,7 @@ from typing_extensions import Annotated
from
starlette.responses
import
RedirectResponse
from
starlette.responses
import
RedirectResponse
from
chains.local_doc_qa
import
LocalDocQA
from
chains.local_doc_qa
import
LocalDocQA
from
configs.model_config
import
(
VS_ROOT_PATH
,
UPLOAD
_ROOT_PATH
,
EMBEDDING_DEVICE
,
from
configs.model_config
import
(
KB
_ROOT_PATH
,
EMBEDDING_DEVICE
,
EMBEDDING_MODEL
,
NLTK_DATA_PATH
,
EMBEDDING_MODEL
,
NLTK_DATA_PATH
,
VECTOR_SEARCH_TOP_K
,
LLM_HISTORY_LEN
,
OPEN_CROSS_DOMAIN
)
VECTOR_SEARCH_TOP_K
,
LLM_HISTORY_LEN
,
OPEN_CROSS_DOMAIN
)
import
models.shared
as
shared
import
models.shared
as
shared
...
@@ -80,15 +80,15 @@ class ChatMessage(BaseModel):
...
@@ -80,15 +80,15 @@ class ChatMessage(BaseModel):
def
get_folder_path
(
local_doc_id
:
str
):
def
get_folder_path
(
local_doc_id
:
str
):
return
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
local_doc_id
)
return
os
.
path
.
join
(
KB_ROOT_PATH
,
local_doc_id
,
"content"
)
def
get_vs_path
(
local_doc_id
:
str
):
def
get_vs_path
(
local_doc_id
:
str
):
return
os
.
path
.
join
(
VS_ROOT_PATH
,
local_doc_id
)
return
os
.
path
.
join
(
KB_ROOT_PATH
,
local_doc_id
,
"vector_store"
)
def
get_file_path
(
local_doc_id
:
str
,
doc_name
:
str
):
def
get_file_path
(
local_doc_id
:
str
,
doc_name
:
str
):
return
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
local_doc_id
,
doc_name
)
return
os
.
path
.
join
(
KB_ROOT_PATH
,
local_doc_id
,
"content"
,
doc_name
)
async
def
upload_file
(
async
def
upload_file
(
...
@@ -147,64 +147,76 @@ async def upload_files(
...
@@ -147,64 +147,76 @@ async def upload_files(
return
BaseResponse
(
code
=
500
,
msg
=
file_status
)
return
BaseResponse
(
code
=
500
,
msg
=
file_status
)
async
def
list_kbs
():
# Get List of Knowledge Base
if
not
os
.
path
.
exists
(
KB_ROOT_PATH
):
all_doc_ids
=
[]
else
:
all_doc_ids
=
[
folder
for
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
(
async
def
list_docs
(
knowledge_base_id
:
Optional
[
str
]
=
Query
(
default
=
None
,
description
=
"Knowledge Base Name"
,
example
=
"kb1"
)
knowledge_base_id
:
Optional
[
str
]
=
Query
(
default
=
None
,
description
=
"Knowledge Base Name"
,
example
=
"kb1"
)
):
):
if
knowledge_base_id
:
local_doc_folder
=
get_folder_path
(
knowledge_base_id
)
local_doc_folder
=
get_folder_path
(
knowledge_base_id
)
if
not
os
.
path
.
exists
(
local_doc_folder
):
if
not
os
.
path
.
exists
(
local_doc_folder
):
return
{
"code"
:
1
,
"msg"
:
f
"Knowledge base {knowledge_base_id} not found"
}
return
{
"code"
:
1
,
"msg"
:
f
"Knowledge base {knowledge_base_id} not found"
}
all_doc_names
=
[
all_doc_names
=
[
doc
doc
for
doc
in
os
.
listdir
(
local_doc_folder
)
for
doc
in
os
.
listdir
(
local_doc_folder
)
if
os
.
path
.
isfile
(
os
.
path
.
join
(
local_doc_folder
,
doc
))
if
os
.
path
.
isfile
(
os
.
path
.
join
(
local_doc_folder
,
doc
))
]
]
return
ListDocsResponse
(
data
=
all_doc_names
)
return
ListDocsResponse
(
data
=
all_doc_names
)
else
:
if
not
os
.
path
.
exists
(
UPLOAD_ROOT_PATH
):
all_doc_ids
=
[]
else
:
all_doc_ids
=
[
folder
for
folder
in
os
.
listdir
(
UPLOAD_ROOT_PATH
)
if
os
.
path
.
isdir
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
folder
))
]
return
ListDocsResponse
(
data
=
all_doc_ids
)
async
def
delete_kbs
(
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
))
return
BaseResponse
(
code
=
200
,
msg
=
f
"Knowledge Base {knowledge_base_id} delete success"
)
async
def
delete_docs
(
async
def
delete_docs
(
knowledge_base_id
:
str
=
Query
(
...
,
knowledge_base_id
:
str
=
Query
(
...
,
description
=
"Knowledge Base Name"
,
description
=
"Knowledge Base Name"
,
example
=
"kb1"
),
example
=
"kb1"
),
doc_name
:
Optional
[
str
]
=
Query
(
doc_name
:
str
=
Query
(
None
,
description
=
"doc name"
,
example
=
"doc_name_1.pdf"
None
,
description
=
"doc name"
,
example
=
"doc_name_1.pdf"
),
),
):
):
# TODO: 确认是否支持批量删除文件
knowledge_base_id
=
urllib
.
parse
.
unquote
(
knowledge_base_id
)
knowledge_base_id
=
urllib
.
parse
.
unquote
(
knowledge_base_id
)
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
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"
}
return
{
"code"
:
1
,
"msg"
:
f
"Knowledge base {knowledge_base_id} not found"
}
if
doc_name
:
doc_path
=
get_file_path
(
knowledge_base_id
,
doc_name
)
doc_path
=
get_file_path
(
knowledge_base_id
,
doc_name
)
if
os
.
path
.
exists
(
doc_path
):
if
os
.
path
.
exists
(
doc_path
):
os
.
remove
(
doc_path
)
os
.
remove
(
doc_path
)
# 删除上传的文件后重新生成知识库(FAISS)内的数据
# 删除上传的文件后重新生成知识库(FAISS)内的数据
# TODO: 删除向量库中对应文件
remain_docs
=
await
list_docs
(
knowledge_base_id
)
remain_docs
=
await
list_docs
(
knowledge_base_id
)
if
len
(
remain_docs
.
data
)
==
0
:
if
len
(
remain_docs
.
data
)
==
0
:
shutil
.
rmtree
(
get_folder_path
(
knowledge_base_id
),
ignore_errors
=
True
)
shutil
.
rmtree
(
get_folder_path
(
knowledge_base_id
),
ignore_errors
=
True
)
else
:
local_doc_qa
.
init_knowledge_vector_store
(
get_folder_path
(
knowledge_base_id
),
get_vs_path
(
knowledge_base_id
)
)
return
BaseResponse
(
code
=
200
,
msg
=
f
"document {doc_name} delete success"
)
else
:
else
:
return
BaseResponse
(
code
=
1
,
msg
=
f
"document {doc_name} not found"
)
local_doc_qa
.
init_knowledge_vector_store
(
get_folder_path
(
knowledge_base_id
),
get_vs_path
(
knowledge_base_id
)
)
return
BaseResponse
(
code
=
200
,
msg
=
f
"document {doc_name} delete success"
)
else
:
else
:
shutil
.
rmtree
(
get_folder_path
(
knowledge_base_id
))
return
BaseResponse
(
code
=
1
,
msg
=
f
"document {doc_name} not found"
)
return
BaseResponse
(
code
=
200
,
msg
=
f
"Knowledge Base {knowledge_base_id} delete success"
)
async
def
local_doc_chat
(
async
def
local_doc_chat
(
...
@@ -221,7 +233,7 @@ async def local_doc_chat(
...
@@ -221,7 +233,7 @@ async def local_doc_chat(
],
],
),
),
):
):
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
knowledge_base_id
)
vs_path
=
get_vs_path
(
knowledge_base_id
)
if
not
os
.
path
.
exists
(
vs_path
):
if
not
os
.
path
.
exists
(
vs_path
):
# return BaseResponse(code=1, msg=f"Knowledge base {knowledge_base_id} not found")
# return BaseResponse(code=1, msg=f"Knowledge base {knowledge_base_id} not found")
return
ChatMessage
(
return
ChatMessage
(
...
@@ -278,6 +290,7 @@ async def bing_search_chat(
...
@@ -278,6 +290,7 @@ async def bing_search_chat(
source_documents
=
source_documents
,
source_documents
=
source_documents
,
)
)
async
def
chat
(
async
def
chat
(
question
:
str
=
Body
(
...
,
description
=
"Question"
,
example
=
"工伤保险是什么?"
),
question
:
str
=
Body
(
...
,
description
=
"Question"
,
example
=
"工伤保险是什么?"
),
history
:
List
[
List
[
str
]]
=
Body
(
history
:
List
[
List
[
str
]]
=
Body
(
...
@@ -310,8 +323,9 @@ async def stream_chat(websocket: WebSocket, knowledge_base_id: str):
...
@@ -310,8 +323,9 @@ async def stream_chat(websocket: WebSocket, knowledge_base_id: str):
turn
=
1
turn
=
1
while
True
:
while
True
:
input_json
=
await
websocket
.
receive_json
()
input_json
=
await
websocket
.
receive_json
()
question
,
history
,
knowledge_base_id
=
input_json
[
"question"
],
input_json
[
"history"
],
input_json
[
"knowledge_base_id"
]
question
,
history
,
knowledge_base_id
=
input_json
[
"question"
],
input_json
[
"history"
],
input_json
[
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
knowledge_base_id
)
"knowledge_base_id"
]
vs_path
=
get_vs_path
(
knowledge_base_id
)
if
not
os
.
path
.
exists
(
vs_path
):
if
not
os
.
path
.
exists
(
vs_path
):
await
websocket
.
send_json
({
"error"
:
f
"Knowledge base {knowledge_base_id} not found"
})
await
websocket
.
send_json
({
"error"
:
f
"Knowledge base {knowledge_base_id} not found"
})
...
@@ -351,9 +365,6 @@ async def document():
...
@@ -351,9 +365,6 @@ async def document():
return
RedirectResponse
(
url
=
"/docs"
)
return
RedirectResponse
(
url
=
"/docs"
)
def
api_start
(
host
,
port
):
def
api_start
(
host
,
port
):
global
app
global
app
global
local_doc_qa
global
local_doc_qa
...
...
chains/local_doc_qa.py
浏览文件 @
a887df17
...
@@ -187,8 +187,9 @@ class LocalDocQA:
...
@@ -187,8 +187,9 @@ class LocalDocQA:
torch_gc
()
torch_gc
()
else
:
else
:
if
not
vs_path
:
if
not
vs_path
:
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_path
=
os
.
path
.
join
(
KB_ROOT_PATH
,
f
"""{"".join(lazy_pinyin(os.path.splitext(file)[0]))}_FAISS_{datetime.datetime.now().strftime("
%
Y
%
m
%
d_
%
H
%
M
%
S")}"""
)
f
"""{"".join(lazy_pinyin(os.path.splitext(file)[0]))}_FAISS_{datetime.datetime.now().strftime("
%
Y
%
m
%
d_
%
H
%
M
%
S")}"""
,
"vector_store"
)
vector_store
=
MyFAISS
.
from_documents
(
docs
,
self
.
embeddings
)
# docs 为Document列表
vector_store
=
MyFAISS
.
from_documents
(
docs
,
self
.
embeddings
)
# docs 为Document列表
torch_gc
()
torch_gc
()
...
...
configs/model_config.py
浏览文件 @
a887df17
...
@@ -119,10 +119,8 @@ USE_PTUNING_V2 = False
...
@@ -119,10 +119,8 @@ USE_PTUNING_V2 = False
# LLM running device
# LLM running device
LLM_DEVICE
=
"cuda"
if
torch
.
cuda
.
is_available
()
else
"mps"
if
torch
.
backends
.
mps
.
is_available
()
else
"cpu"
LLM_DEVICE
=
"cuda"
if
torch
.
cuda
.
is_available
()
else
"mps"
if
torch
.
backends
.
mps
.
is_available
()
else
"cpu"
# 知识库默认存储路径
VS_ROOT_PATH
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"vector_store"
)
KB_ROOT_PATH
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"knowledge_base"
)
UPLOAD_ROOT_PATH
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"content"
)
# 基于上下文的prompt模版,请务必保留"{question}"和"{context}"
# 基于上下文的prompt模版,请务必保留"{question}"和"{context}"
PROMPT_TEMPLATE
=
"""已知信息:
PROMPT_TEMPLATE
=
"""已知信息:
...
@@ -139,10 +137,10 @@ SENTENCE_SIZE = 100
...
@@ -139,10 +137,10 @@ SENTENCE_SIZE = 100
# 匹配后单段上下文长度
# 匹配后单段上下文长度
CHUNK_SIZE
=
250
CHUNK_SIZE
=
250
#
LLM input history length
#
传入LLM的历史记录长度
LLM_HISTORY_LEN
=
3
LLM_HISTORY_LEN
=
3
#
return top-k text chunk from vector store
#
知识库检索时返回的匹配内容条数
VECTOR_SEARCH_TOP_K
=
5
VECTOR_SEARCH_TOP_K
=
5
# 知识检索内容相关度 Score, 数值范围约为0-1100,如果为0,则不生效,经测试设置为小于500时,匹配结果更精准
# 知识检索内容相关度 Score, 数值范围约为0-1100,如果为0,则不生效,经测试设置为小于500时,匹配结果更精准
...
...
content/samples
/README.md
→
knowledge_base/samples/content
/README.md
浏览文件 @
a887df17
# 基于本地知识
的 ChatGLM
应用实现
# 基于本地知识
库的 ChatGLM 等大语言模型
应用实现
## 介绍
## 介绍
🌍
[
_READ THIS IN ENGLISH_
](
README_en.md
)
🌍
[
_READ THIS IN ENGLISH_
](
README_en.md
)
🤖️ 一种利用
[
ChatGLM-6B
](
https://github.com/THUDM/ChatGLM-6B
)
+
[
langchain
](
https://github.com/hwchase17/langchain
)
实现的基于本地知识的 ChatGLM 应用。增加
[
clue-ai/ChatYuan
](
https://github.com/clue-ai/ChatYuan
)
项目的模型
[
ClueAI/ChatYuan-large-v2
](
https://huggingface.co/ClueAI/ChatYuan-large-v2
)
的支持
。
🤖️ 一种利用
[
langchain
](
https://github.com/hwchase17/langchain
)
思想实现的基于本地知识库的问答应用,目标期望建立一套对中文场景与开源模型支持友好、可离线运行的知识库问答解决方案
。
💡 受
[
GanymedeNil
](
https://github.com/GanymedeNil
)
的项目
[
document.ai
](
https://github.com/GanymedeNil/document.ai
)
和
[
AlexZhangji
](
https://github.com/AlexZhangji
)
创建的
[
ChatGLM-6B Pull Request
](
https://github.com/THUDM/ChatGLM-6B/pull/216
)
启发,建立了全
部基于开源模型实现的本地知识问答应用
。
💡 受
[
GanymedeNil
](
https://github.com/GanymedeNil
)
的项目
[
document.ai
](
https://github.com/GanymedeNil/document.ai
)
和
[
AlexZhangji
](
https://github.com/AlexZhangji
)
创建的
[
ChatGLM-6B Pull Request
](
https://github.com/THUDM/ChatGLM-6B/pull/216
)
启发,建立了全
流程可使用开源模型实现的本地知识库问答应用。现已支持使用
[
ChatGLM-6B
](
https://github.com/THUDM/ChatGLM-6B
)
等大语言模型直接接入,或通过
[
fastchat
](
https://github.com/lm-sys/FastChat
)
api 形式接入 Vicuna, Alpaca, LLaMA, Koala, RWKV 等模型
。
✅ 本项目中 Embedding 默认选用的是
[
GanymedeNil/text2vec-large-chinese
](
https://huggingface.co/GanymedeNil/text2vec-large-chinese/tree/main
)
,LLM 默认选用的是
[
ChatGLM-6B
](
https://github.com/THUDM/ChatGLM-6B
)
。依托上述模型,本项目可实现全部使用
**开源**
模型
**离线私有部署**
。
✅ 本项目中 Embedding 默认选用的是
[
GanymedeNil/text2vec-large-chinese
](
https://huggingface.co/GanymedeNil/text2vec-large-chinese/tree/main
)
,LLM 默认选用的是
[
ChatGLM-6B
](
https://github.com/THUDM/ChatGLM-6B
)
。依托上述模型,本项目可实现全部使用
**开源**
模型
**离线私有部署**
。
⛓️ 本项目实现原理如下图所示,过程包括加载文件 -> 读取文本 -> 文本分割 -> 文本向量化 -> 问句向量化 -> 在文本向量中匹配出与问句向量最相似的
`top k`
个 -> 匹配出的文本作为上下文和问题一起添加到
`prompt`
中 -> 提交给
`LLM`
生成回答。
⛓️ 本项目实现原理如下图所示,过程包括加载文件 -> 读取文本 -> 文本分割 -> 文本向量化 -> 问句向量化 -> 在文本向量中匹配出与问句向量最相似的
`top k`
个 -> 匹配出的文本作为上下文和问题一起添加到
`prompt`
中 -> 提交给
`LLM`
生成回答。
📺
[
原理介绍视频
](
https://www.bilibili.com/video/BV13M4y1e7cN/?share_source=copy_web&vd_source=e6c5aafe684f30fbe41925d61ca6d514
)
![
实现原理图
](
img/langchain+chatglm.png
)
![
实现原理图
](
img/langchain+chatglm.png
)
从文档处理角度来看,实现流程如下:
从文档处理角度来看,实现流程如下:
![
实现原理图2
](
img/langchain+chatglm2.png
)
![
实现原理图2
](
img/langchain+chatglm2.png
)
🚩 本项目未涉及微调、训练过程,但可利用微调或训练对本项目效果进行优化。
🚩 本项目未涉及微调、训练过程,但可利用微调或训练对本项目效果进行优化。
🌐
[
AutoDL 镜像
](
https://www.codewithgpu.com/i/imClumsyPanda/langchain-ChatGLM/langchain-ChatGLM
)
🌐
[
AutoDL 镜像
](
https://www.codewithgpu.com/i/imClumsyPanda/langchain-ChatGLM/langchain-ChatGLM
)
...
@@ -33,7 +36,7 @@
...
@@ -33,7 +36,7 @@
-
ChatGLM-6B 模型硬件需求
-
ChatGLM-6B 模型硬件需求
注:如未将模型下载至本地,请执行前检查`$HOME/.cache/huggingface/`文件夹剩余空间,模型文件下载至本地需要 15 GB 存储空间。
注:如未将模型下载至本地,请执行前检查`$HOME/.cache/huggingface/`文件夹剩余空间,模型文件下载至本地需要 15 GB 存储空间。
注:一些其它的可选启动项见[项目启动选项](docs/StartOption.md)
模型下载方法可参考 [常见问题](docs/FAQ.md) 中 Q8。
模型下载方法可参考 [常见问题](docs/FAQ.md) 中 Q8。
|
**量化等级**
|
**最低 GPU 显存**
(推理) |
**最低 GPU 显存**
(高效参数微调) |
|
**量化等级**
|
**最低 GPU 显存**
(推理) |
**最低 GPU 显存**
(高效参数微调) |
...
@@ -79,9 +82,10 @@ docker run --gpus all -d --name chatglm -p 7860:7860 -v ~/github/langchain-ChatG
...
@@ -79,9 +82,10 @@ docker run --gpus all -d --name chatglm -p 7860:7860 -v ~/github/langchain-ChatG
### 软件需求
### 软件需求
本项目已在 Python 3.8 - 3.10,CUDA 11.7 环境下完成测试。已在 Windows、ARM 架构的 macOS、Linux 系统中完成测试。
本项目已在 Python 3.8
.1
- 3.10,CUDA 11.7 环境下完成测试。已在 Windows、ARM 架构的 macOS、Linux 系统中完成测试。
vue前端需要node18环境
vue前端需要node18环境
### 从本地加载模型
### 从本地加载模型
请参考
[
THUDM/ChatGLM-6B#从本地加载模型
](
https://github.com/THUDM/ChatGLM-6B#从本地加载模型
)
请参考
[
THUDM/ChatGLM-6B#从本地加载模型
](
https://github.com/THUDM/ChatGLM-6B#从本地加载模型
)
...
@@ -94,6 +98,8 @@ vue前端需要node18环境
...
@@ -94,6 +98,8 @@ vue前端需要node18环境
在开始执行 Web UI 或命令行交互前,请先检查
[
configs/model_config.py
](
configs/model_config.py
)
中的各项模型参数设计是否符合需求。
在开始执行 Web UI 或命令行交互前,请先检查
[
configs/model_config.py
](
configs/model_config.py
)
中的各项模型参数设计是否符合需求。
如需通过 fastchat 以 api 形式调用 llm,请参考
[
fastchat 调用实现
](
docs/fastchat.md
)
### 3. 执行脚本体验 Web UI 或命令行交互
### 3. 执行脚本体验 Web UI 或命令行交互
> 注:鉴于环境部署过程中可能遇到问题,建议首先测试命令行脚本。建议命令行脚本测试可正常运行后再运行 Web UI。
> 注:鉴于环境部署过程中可能遇到问题,建议首先测试命令行脚本。建议命令行脚本测试可正常运行后再运行 Web UI。
...
@@ -122,9 +128,17 @@ $ pnpm i
...
@@ -122,9 +128,17 @@ $ pnpm i
$
npm run dev
$
npm run dev
```
```
执行后效果如下图所示:
VUE 前端界面如下图所示:
1.
`对话`
界面
!
[
](img/vue_0521_0.png)
2.
`知识库问答`
界面
!
[
](img/vue_0521_1.png)
3.
`Bing搜索`
界面
!
[
](img/vue_0521_2.png)
WebUI 界面如下图所示:
1.
`对话`
Tab 界面
1.
`对话`
Tab 界面
!
[
](img/webui_05
10
_0.png)
!
[
](img/webui_05
21
_0.png)
2.
`知识库测试 Beta`
Tab 界面
2.
`知识库测试 Beta`
Tab 界面
!
[
](img/webui_0510_1.png)
!
[
](img/webui_0510_1.png)
3.
`模型配置`
Tab 界面
3.
`模型配置`
Tab 界面
...
@@ -177,36 +191,44 @@ Web UI 可以实现如下功能:
...
@@ -177,36 +191,44 @@ Web UI 可以实现如下功能:
-
[
]
Langchain 应用
-
[
]
Langchain 应用
-
[
x
]
接入非结构化文档(已支持 md、pdf、docx、txt 文件格式)
-
[
x
]
接入非结构化文档(已支持 md、pdf、docx、txt 文件格式)
-
[
]
搜索引擎与本地网页接入
-
[
x
]
jpg 与 png 格式图片的 OCR 文字识别
-
[
x
]
搜索引擎接入
-
[
]
本地网页接入
-
[
]
结构化数据接入(如 csv、Excel、SQL 等)
-
[
]
结构化数据接入(如 csv、Excel、SQL 等)
-
[
]
知识图谱/图数据库接入
-
[
]
知识图谱/图数据库接入
-
[
]
Agent 实现
-
[
]
Agent 实现
-
[
]
增加更多 LLM 模型支持
-
[
x
]
增加更多 LLM 模型支持
-
[
x
]
[
THUDM/chatglm-6b
]
(https://huggingface.co/THUDM/chatglm-6b)
-
[
x
]
[
THUDM/chatglm-6b
]
(https://huggingface.co/THUDM/chatglm-6b)
-
[
x
]
[
THUDM/chatglm-6b-int8
]
(https://huggingface.co/THUDM/chatglm-6b-int8)
-
[
x
]
[
THUDM/chatglm-6b-int8
]
(https://huggingface.co/THUDM/chatglm-6b-int8)
-
[
x
]
[
THUDM/chatglm-6b-int4
]
(https://huggingface.co/THUDM/chatglm-6b-int4)
-
[
x
]
[
THUDM/chatglm-6b-int4
]
(https://huggingface.co/THUDM/chatglm-6b-int4)
-
[
x
]
[
THUDM/chatglm-6b-int4-qe
]
(https://huggingface.co/THUDM/chatglm-6b-int4-qe)
-
[
x
]
[
THUDM/chatglm-6b-int4-qe
]
(https://huggingface.co/THUDM/chatglm-6b-int4-qe)
-
[
x
]
[
ClueAI/ChatYuan-large-v2
]
(https://huggingface.co/ClueAI/ChatYuan-large-v2)
-
[
x
]
[
ClueAI/ChatYuan-large-v2
]
(https://huggingface.co/ClueAI/ChatYuan-large-v2)
-
[
x
]
[
fnlp/moss-moon-003-sft
]
(https://huggingface.co/fnlp/moss-moon-003-sft)
-
[
x
]
[
fnlp/moss-moon-003-sft
]
(https://huggingface.co/fnlp/moss-moon-003-sft)
-
[
]
增加更多 Embedding 模型支持
-
[
x
]
支持通过调用
[
fastchat
](
https://github.com/lm-sys/FastChat
)
api 调用 llm
-
[
x
]
增加更多 Embedding 模型支持
-
[
x
]
[
nghuyong/ernie-3.0-nano-zh
]
(https://huggingface.co/nghuyong/ernie-3.0-nano-zh)
-
[
x
]
[
nghuyong/ernie-3.0-nano-zh
]
(https://huggingface.co/nghuyong/ernie-3.0-nano-zh)
-
[
x
]
[
nghuyong/ernie-3.0-base-zh
]
(https://huggingface.co/nghuyong/ernie-3.0-base-zh)
-
[
x
]
[
nghuyong/ernie-3.0-base-zh
]
(https://huggingface.co/nghuyong/ernie-3.0-base-zh)
-
[
x
]
[
shibing624/text2vec-base-chinese
]
(https://huggingface.co/shibing624/text2vec-base-chinese)
-
[
x
]
[
shibing624/text2vec-base-chinese
]
(https://huggingface.co/shibing624/text2vec-base-chinese)
-
[
x
]
[
GanymedeNil/text2vec-large-chinese
]
(https://huggingface.co/GanymedeNil/text2vec-large-chinese)
-
[
x
]
[
GanymedeNil/text2vec-large-chinese
]
(https://huggingface.co/GanymedeNil/text2vec-large-chinese)
-
[
x
]
[
moka-ai/m3e-small
]
(https://huggingface.co/moka-ai/m3e-small)
-
[
x
]
[
moka-ai/m3e-base
]
(https://huggingface.co/moka-ai/m3e-base)
-
[
]
Web UI
-
[
]
Web UI
-
[
x
]
利用 gradio 实现 Web UI DEMO
-
[
x
]
基于 gradio 实现 Web UI DEMO
-
[
x
]
基于 streamlit 实现 Web UI DEMO
-
[
x
]
添加输出内容及错误提示
-
[
x
]
添加输出内容及错误提示
-
[
x
]
引用标注
-
[
x
]
引用标注
-
[
]
增加知识库管理
-
[
]
增加知识库管理
-
[
x
]
选择知识库开始问答
-
[
x
]
选择知识库开始问答
-
[
x
]
上传文件/文件夹至知识库
-
[
x
]
上传文件/文件夹至知识库
-
[
x
]
知识库测试
-
[
]
删除知识库中文件
-
[
]
删除知识库中文件
-
[
]
利用 streamlit 实现 Web UI Demo
-
[
x
]
支持搜索引擎问答
-
[
]
增加 API 支持
-
[
]
增加 API 支持
-
[
x
]
利用 fastapi 实现 API 部署方式
-
[
x
]
利用 fastapi 实现 API 部署方式
-
[
]
实现调用 API 的 Web UI Demo
-
[
]
实现调用 API 的 Web UI Demo
-
[
x
]
VUE 前端
## 项目交流群
## 项目交流群
![
二维码
](
img/qr_code_
17
.jpg
)
![
二维码
](
img/qr_code_
30
.jpg
)
🎉 langchain-ChatGLM 项目交流群,如果你也对本项目感兴趣,欢迎加入群聊参与讨论交流。
🎉 langchain-ChatGLM 项目交流群,如果你也对本项目感兴趣,欢迎加入群聊参与讨论交流。
content/samples
/test.jpg
→
knowledge_base/samples/content
/test.jpg
浏览文件 @
a887df17
File moved
content/samples
/test.pdf
→
knowledge_base/samples/content
/test.pdf
浏览文件 @
a887df17
File moved
content/samples
/test.txt
→
knowledge_base/samples/content
/test.txt
浏览文件 @
a887df17
File moved
loader/image_loader.py
浏览文件 @
a887df17
...
@@ -33,7 +33,7 @@ class UnstructuredPaddleImageLoader(UnstructuredFileLoader):
...
@@ -33,7 +33,7 @@ class UnstructuredPaddleImageLoader(UnstructuredFileLoader):
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
filepath
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"
content"
,
"samples
"
,
"test.jpg"
)
filepath
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"
knowledge_base"
,
"samples"
,
"content
"
,
"test.jpg"
)
loader
=
UnstructuredPaddleImageLoader
(
filepath
,
mode
=
"elements"
)
loader
=
UnstructuredPaddleImageLoader
(
filepath
,
mode
=
"elements"
)
docs
=
loader
.
load
()
docs
=
loader
.
load
()
for
doc
in
docs
:
for
doc
in
docs
:
...
...
loader/pdf_loader.py
浏览文件 @
a887df17
...
@@ -49,7 +49,7 @@ class UnstructuredPaddlePDFLoader(UnstructuredFileLoader):
...
@@ -49,7 +49,7 @@ class UnstructuredPaddlePDFLoader(UnstructuredFileLoader):
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
filepath
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"
content"
,
"samples
"
,
"test.pdf"
)
filepath
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
"
knowledge_base"
,
"samples"
,
"content
"
,
"test.pdf"
)
loader
=
UnstructuredPaddlePDFLoader
(
filepath
,
mode
=
"elements"
)
loader
=
UnstructuredPaddlePDFLoader
(
filepath
,
mode
=
"elements"
)
docs
=
loader
.
load
()
docs
=
loader
.
load
()
for
doc
in
docs
:
for
doc
in
docs
:
...
...
webui.py
浏览文件 @
a887df17
...
@@ -16,9 +16,9 @@ nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path
...
@@ -16,9 +16,9 @@ nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path
def
get_vs_list
():
def
get_vs_list
():
lst_default
=
[
"新建知识库"
]
lst_default
=
[
"新建知识库"
]
if
not
os
.
path
.
exists
(
VS
_ROOT_PATH
):
if
not
os
.
path
.
exists
(
KB
_ROOT_PATH
):
return
lst_default
return
lst_default
lst
=
os
.
listdir
(
VS
_ROOT_PATH
)
lst
=
os
.
listdir
(
KB
_ROOT_PATH
)
if
not
lst
:
if
not
lst
:
return
lst_default
return
lst_default
lst
.
sort
()
lst
.
sort
()
...
@@ -141,14 +141,14 @@ def reinit_model(llm_model, embedding_model, llm_history_len, no_remote_model, u
...
@@ -141,14 +141,14 @@ def reinit_model(llm_model, embedding_model, llm_history_len, no_remote_model, u
def
get_vector_store
(
vs_id
,
files
,
sentence_size
,
history
,
one_conent
,
one_content_segmentation
):
def
get_vector_store
(
vs_id
,
files
,
sentence_size
,
history
,
one_conent
,
one_content_segmentation
):
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_id
)
vs_path
=
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"vector_store"
)
filelist
=
[]
filelist
=
[]
if
local_doc_qa
.
llm
and
local_doc_qa
.
embeddings
:
if
local_doc_qa
.
llm
and
local_doc_qa
.
embeddings
:
if
isinstance
(
files
,
list
):
if
isinstance
(
files
,
list
):
for
file
in
files
:
for
file
in
files
:
filename
=
os
.
path
.
split
(
file
.
name
)[
-
1
]
filename
=
os
.
path
.
split
(
file
.
name
)[
-
1
]
shutil
.
move
(
file
.
name
,
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
,
filename
))
shutil
.
move
(
file
.
name
,
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"content"
,
filename
))
filelist
.
append
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
,
filename
))
filelist
.
append
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"content"
,
filename
))
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
filelist
,
vs_path
,
sentence_size
)
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
filelist
,
vs_path
,
sentence_size
)
else
:
else
:
vs_path
,
loaded_files
=
local_doc_qa
.
one_knowledge_add
(
vs_path
,
files
,
one_conent
,
one_content_segmentation
,
vs_path
,
loaded_files
=
local_doc_qa
.
one_knowledge_add
(
vs_path
,
files
,
one_conent
,
one_content_segmentation
,
...
@@ -168,7 +168,7 @@ def change_vs_name_input(vs_id, history):
...
@@ -168,7 +168,7 @@ def change_vs_name_input(vs_id, history):
if
vs_id
==
"新建知识库"
:
if
vs_id
==
"新建知识库"
:
return
gr
.
update
(
visible
=
True
),
gr
.
update
(
visible
=
True
),
gr
.
update
(
visible
=
False
),
None
,
history
return
gr
.
update
(
visible
=
True
),
gr
.
update
(
visible
=
True
),
gr
.
update
(
visible
=
False
),
None
,
history
else
:
else
:
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_id
)
vs_path
=
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"vector_store"
)
if
"index.faiss"
in
os
.
listdir
(
vs_path
):
if
"index.faiss"
in
os
.
listdir
(
vs_path
):
file_status
=
f
"已加载知识库{vs_id},请开始提问"
file_status
=
f
"已加载知识库{vs_id},请开始提问"
else
:
else
:
...
@@ -220,11 +220,11 @@ def add_vs_name(vs_name, chatbot):
...
@@ -220,11 +220,11 @@ def add_vs_name(vs_name, chatbot):
visible
=
False
),
chatbot
visible
=
False
),
chatbot
else
:
else
:
# 新建上传文件存储路径
# 新建上传文件存储路径
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_name
)):
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_name
,
"content"
)):
os
.
makedirs
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_name
))
os
.
makedirs
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_name
,
"content"
))
# 新建向量库存储路径
# 新建向量库存储路径
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_name
)):
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_name
,
"vector_store"
)):
os
.
makedirs
(
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_name
))
os
.
makedirs
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_name
,
"vector_store"
))
vs_status
=
f
"""已新增知识库"{vs_name}",将在上传文件并载入成功后进行存储。请在开始对话前,先完成文件上传。 """
vs_status
=
f
"""已新增知识库"{vs_name}",将在上传文件并载入成功后进行存储。请在开始对话前,先完成文件上传。 """
chatbot
=
chatbot
+
[[
None
,
vs_status
]]
chatbot
=
chatbot
+
[[
None
,
vs_status
]]
return
gr
.
update
(
visible
=
True
,
choices
=
get_vs_list
(),
value
=
vs_name
),
gr
.
update
(
return
gr
.
update
(
visible
=
True
,
choices
=
get_vs_list
(),
value
=
vs_name
),
gr
.
update
(
...
@@ -234,12 +234,13 @@ def add_vs_name(vs_name, chatbot):
...
@@ -234,12 +234,13 @@ def add_vs_name(vs_name, chatbot):
# 自动化加载固定文件间中文件
# 自动化加载固定文件间中文件
def
reinit_vector_store
(
vs_id
,
history
):
def
reinit_vector_store
(
vs_id
,
history
):
try
:
try
:
shutil
.
rmtree
(
VS_ROOT_PATH
)
shutil
.
rmtree
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"vector_store"
)
)
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_id
)
vs_path
=
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"vector_store"
)
sentence_size
=
gr
.
Number
(
value
=
SENTENCE_SIZE
,
precision
=
0
,
sentence_size
=
gr
.
Number
(
value
=
SENTENCE_SIZE
,
precision
=
0
,
label
=
"文本入库分句长度限制"
,
label
=
"文本入库分句长度限制"
,
interactive
=
True
,
visible
=
True
)
interactive
=
True
,
visible
=
True
)
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
UPLOAD_ROOT_PATH
,
vs_path
,
sentence_size
)
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"content"
),
vs_path
,
sentence_size
)
model_status
=
"""知识库构建成功"""
model_status
=
"""知识库构建成功"""
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
e
)
logger
.
error
(
e
)
...
@@ -285,7 +286,7 @@ default_theme_args = dict(
...
@@ -285,7 +286,7 @@ default_theme_args = dict(
with
gr
.
Blocks
(
css
=
block_css
,
theme
=
gr
.
themes
.
Default
(
**
default_theme_args
))
as
demo
:
with
gr
.
Blocks
(
css
=
block_css
,
theme
=
gr
.
themes
.
Default
(
**
default_theme_args
))
as
demo
:
vs_path
,
file_status
,
model_status
=
gr
.
State
(
vs_path
,
file_status
,
model_status
=
gr
.
State
(
os
.
path
.
join
(
VS_ROOT_PATH
,
get_vs_list
()[
0
]
)
if
len
(
get_vs_list
())
>
1
else
""
),
gr
.
State
(
""
),
gr
.
State
(
os
.
path
.
join
(
KB_ROOT_PATH
,
get_vs_list
()[
0
],
"vector_store"
)
if
len
(
get_vs_list
())
>
1
else
""
),
gr
.
State
(
""
),
gr
.
State
(
model_status
)
model_status
)
gr
.
Markdown
(
webui_title
)
gr
.
Markdown
(
webui_title
)
with
gr
.
Tab
(
"对话"
):
with
gr
.
Tab
(
"对话"
):
...
...
webui_st.py
浏览文件 @
a887df17
...
@@ -20,9 +20,9 @@ nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path
...
@@ -20,9 +20,9 @@ nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path
def
get_vs_list
():
def
get_vs_list
():
lst_default
=
[
"新建知识库"
]
lst_default
=
[
"新建知识库"
]
if
not
os
.
path
.
exists
(
VS
_ROOT_PATH
):
if
not
os
.
path
.
exists
(
KB
_ROOT_PATH
):
return
lst_default
return
lst_default
lst
=
os
.
listdir
(
VS
_ROOT_PATH
)
lst
=
os
.
listdir
(
KB
_ROOT_PATH
)
if
not
lst
:
if
not
lst
:
return
lst_default
return
lst_default
lst
.
sort
()
lst
.
sort
()
...
@@ -144,18 +144,18 @@ def init_model(llm_model: str = 'chat-glm-6b', embedding_model: str = 'text2vec'
...
@@ -144,18 +144,18 @@ def init_model(llm_model: str = 'chat-glm-6b', embedding_model: str = 'text2vec'
def
get_vector_store
(
vs_id
,
files
,
sentence_size
,
history
,
one_conent
,
one_content_segmentation
):
def
get_vector_store
(
vs_id
,
files
,
sentence_size
,
history
,
one_conent
,
one_content_segmentation
):
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_id
)
vs_path
=
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"vector_store"
)
filelist
=
[]
filelist
=
[]
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
)):
if
not
os
.
path
.
exists
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"content"
)):
os
.
makedirs
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
))
os
.
makedirs
(
os
.
path
.
join
(
KB_ROOT_PATH
,
vs_id
,
"content"
))
if
local_doc_qa
.
llm
and
local_doc_qa
.
embeddings
:
if
local_doc_qa
.
llm
and
local_doc_qa
.
embeddings
:
if
isinstance
(
files
,
list
):
if
isinstance
(
files
,
list
):
for
file
in
files
:
for
file
in
files
:
filename
=
os
.
path
.
split
(
file
.
name
)[
-
1
]
filename
=
os
.
path
.
split
(
file
.
name
)[
-
1
]
shutil
.
move
(
file
.
name
,
os
.
path
.
join
(
shutil
.
move
(
file
.
name
,
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
,
filename
))
KB_ROOT_PATH
,
vs_id
,
"content"
,
filename
))
filelist
.
append
(
os
.
path
.
join
(
filelist
.
append
(
os
.
path
.
join
(
UPLOAD_ROOT_PATH
,
vs_id
,
filename
))
KB_ROOT_PATH
,
vs_id
,
"content"
,
filename
))
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
vs_path
,
loaded_files
=
local_doc_qa
.
init_knowledge_vector_store
(
filelist
,
vs_path
,
sentence_size
)
filelist
,
vs_path
,
sentence_size
)
else
:
else
:
...
@@ -516,7 +516,7 @@ with st.form('my_form', clear_on_submit=True):
...
@@ -516,7 +516,7 @@ with st.form('my_form', clear_on_submit=True):
last_response
=
output_messages
()
last_response
=
output_messages
()
for
history
,
_
in
answer
(
q
,
for
history
,
_
in
answer
(
q
,
vs_path
=
os
.
path
.
join
(
vs_path
=
os
.
path
.
join
(
VS_ROOT_PATH
,
vs_path
),
KB_ROOT_PATH
,
vs_path
,
"vector_store"
),
history
=
[],
history
=
[],
mode
=
mode
,
mode
=
mode
,
score_threshold
=
score_threshold
,
score_threshold
=
score_threshold
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论