0


Langchain-Chatchat开源库使用的随笔记(一)

CSDN越来越不好用了,
降低CSDN发文,本篇后续持续更新会在:
Langchain-Chatchat开源库使用的随笔记
https://zhuanlan.zhihu.com/p/676061269

笔者最近在研究Langchain-Chatchat,所以本篇作为随笔记进行记录。
最近核心探索的是知识库的使用,其中关于文档如何进行分块的详细,可以参考笔者的另几篇文章:

  • 大模型RAG 场景、数据、应用难点与解决(四)
  • RAG 分块Chunk技术优劣、技巧、方法汇总(五)

原项目地址:

  • Langchain-Chatchat
  • WIKI教程(有点简单)

在这里插入图片描述


1 Chatchat项目结构

整个结构是

  1. server

启动API,然后项目内自行调用API。
API详情可见:

  1. http://xxx:7861/docs

,整个代码架构还是蛮适合深入学习

在这里插入图片描述


2 Chatchat一些代码学习

2.1 12个分块函数统一使用

截止 20231231 笔者看到chatchat一共有12个分chunk的函数
这12个函数如何使用、大致点评可以参考笔者的另外文章(RAG 分块Chunk技术优劣、技巧、方法汇总(五)):

  1. CharacterTextSplitter
  2. LatexTextSplitter
  3. MarkdownHeaderTextSplitter
  4. MarkdownTextSplitter
  5. NLTKTextSplitter
  6. PythonCodeTextSplitter
  7. RecursiveCharacterTextSplitter
  8. SentenceTransformersTokenTextSplitter
  9. SpacyTextSplitter
  10. AliTextSplitter
  11. ChineseRecursiveTextSplitter
  12. ChineseTextSplitter

借用chatchat项目中的

  1. test/custom_splitter/test_different_splitter.py

来看看一起调用

  1. make_text_splitter

函数:

  1. from langchain import document_loaders
  2. from server.knowledge_base.utils import make_text_splitter
  3. # 使用DocumentLoader读取文件
  4. filepath = "knowledge_base/samples/content/test_files/test.txt"
  5. loader = document_loaders.UnstructuredFileLoader(filepath, autodetect_encoding=True)
  6. docs = loader.load()
  7. CHUNK_SIZE = 250
  8. OVERLAP_SIZE = 50
  9. splitter_name = 'AliTextSplitter'
  10. text_splitter = make_text_splitter(splitter_name, CHUNK_SIZE, OVERLAP_SIZE)
  11. if splitter_name == "MarkdownHeaderTextSplitter":
  12. docs = text_splitter.split_text(docs[0].page_content)
  13. for doc in docs:
  14. if doc.metadata:
  15. doc.metadata["source"] = os.path.basename(filepath)
  16. else:
  17. docs = text_splitter.split_documents(docs)
  18. for doc in docs:
  19. print(doc)

2.2 知识库问答Chat的使用

本节参考chatchat开源项目的

  1. tests\api\test_stream_chat_api_thread.py

以及

  1. tests\api\test_stream_chat_api.py

来探索一下知识库问答调用,包括:

  • 流式调用
  • 单次调用
  • 多线程并发调用

2.2.1 流式调用

  1. import requests
  2. import json
  3. import sys
  4. api_base_url = 'http://0.0.0.0:7861'
  5. api="/chat/knowledge_base_chat"
  6. url = f"{api_base_url}{api}"
  7. headers = {
  8. 'accept': 'application/json',
  9. 'Content-Type': 'application/json',
  10. }
  11. data = {
  12. "query": "如何提问以获得高质量答案",
  13. "knowledge_base_name": "ZWY_V2_m3e-large",
  14. "history": [
  15. {
  16. "role": "user",
  17. "content": "你好"
  18. },
  19. {
  20. "role": "assistant",
  21. "content": "你好,我是 ChatGLM"
  22. }
  23. ],
  24. "stream": True
  25. }
  26. # dump_input(data, api)
  27. response = requests.post(url, headers=headers, json=data, stream=True)
  28. print("\n")
  29. print("=" * 30 + api + " output" + "="*30)
  30. for line in response.iter_content(None, decode_unicode=True):
  31. data = json.loads(line)
  32. if "answer" in data:
  33. print(data["answer"], end="", flush=True)
  34. pprint(data)
  35. assert "docs" in data and len(data["docs"]) > 0
  36. assert response.status_code == 200
  37. >>>==============================/chat/knowledge_base_chat output==============================
  38. 你好!提问以获得高质量答案,以下是一些建议:
  39. 1. 尽可能清晰明确地表达问题:确保你的问题表述清晰、简洁、明确,以便我能够准确理解你的问题并给出恰当的回答。
  40. 2. 提供足够的上下文信息:提供相关的背景信息和上下文,以便我能够更好地理解你的问题,并给出更准确的回答。
  41. 3. 使用简洁的语言:尽量使用简单、明了的语言,以便我能够快速理解你的问题。
  42. 4. 避免使用缩写和俚语:避免使用缩写和俚语,以便我能够准确理解你的问题。
  43. 5. 分步提问:如果问题比较复杂,可以分步提问,这样我可以逐步帮助你解决问题。
  44. 6. 检查你的问题:在提问之前,请检查你的问题是否完整、清晰且准确。
  45. 7. 提供反馈:如果你对我的回答不满意,请提供反馈,以便我改进我的回答。
  46. 希望这些建议能帮助你更好地提问,获得高质量的答案。

结构也比较简单,call 知识库问答的URL,然后返回,通过

  1. response.iter_content

来进行流式反馈。

2.2.2 正常调用以及处理并发

  1. import requests
  2. import json
  3. import sys
  4. api_base_url = 'http://0.0.0.0:7861'
  5. api="/chat/knowledge_base_chat"
  6. url = f"{api_base_url}{api}"
  7. headers = {
  8. 'accept': 'application/json',
  9. 'Content-Type': 'application/json',
  10. }
  11. data = {
  12. "query": "如何提问以获得高质量答案",
  13. "knowledge_base_name": "ZWY_V2_m3e-large",
  14. "history": [
  15. {
  16. "role": "user",
  17. "content": "你好"
  18. },
  19. {
  20. "role": "assistant",
  21. "content": "你好,我是 ChatGLM"
  22. }
  23. ],
  24. "stream": True
  25. }
  26. # 正常调用并存储结果
  27. result = []
  28. response = requests.post(url, headers=headers, json=data, stream=True)
  29. for line in response.iter_content(None, decode_unicode=True):
  30. data = json.loads(line)
  31. result.append(data)
  32. answer = ''.join([r['answer'] for r in result[:-1]]) # 正常的结果
  33. >>> ' 你好,很高兴为您提供帮助。以下是一些提问技巧,可以帮助您获得高质量的答案:\n\n1. 尽可能清晰明确地表达问题:确保您的问题准确、简洁、明确,以便我可以更好地理解您的问题并为您提供最佳答案。\n2. 提供足够的上下文信息:提供相关的背景信息和上下文,以便我更好地了解您的问题,并能够更准确地回答您的问题。\n3. 使用简洁的语言:尽量使用简单、明了的语言,以便我能够更好地理解您的问题。\n4. 避免使用缩写和俚语:尽量使用标准语言,以确保我能够正确理解您的问题。\n5. 分步提问:如果您有一个复杂的问题,可以将其拆分成几个简单的子问题,这样我可以更好地回答每个子问题。\n6. 检查您的拼写和语法:拼写错误和语法错误可能会使我难以理解您的问题,因此请检查您的提问,以确保它们是正确的。\n7. 指定问题类型:如果您需要特定类型的答案,请告诉我,例如数字、列表或步骤等。\n\n希望这些技巧能帮助您获得高质量的答案。如果您有其他问题,请随时问我。'
  34. refer_doc = result[-1] # 参考文献
  35. >>> {'docs': ["<span style='color:red'>未找到相关文档,该回答为大模型自身能力解答!</span>"]}

然后来看一下并发:

  1. # 并发调用
  2. def knowledge_chat(api="/chat/knowledge_base_chat"):
  3. url = f"{api_base_url}{api}"
  4. data = {
  5. "query": "如何提问以获得高质量答案",
  6. "knowledge_base_name": "samples",
  7. "history": [
  8. {
  9. "role": "user",
  10. "content": "你好"
  11. },
  12. {
  13. "role": "assistant",
  14. "content": "你好,我是 ChatGLM"
  15. }
  16. ],
  17. "stream": True
  18. }
  19. result = []
  20. response = requests.post(url, headers=headers, json=data, stream=True)
  21. for line in response.iter_content(None, decode_unicode=True):
  22. data = json.loads(line)
  23. result.append(data)
  24. return result
  25. from concurrent.futures import ThreadPoolExecutor, as_completed
  26. import time
  27. threads = []
  28. times = []
  29. pool = ThreadPoolExecutor()
  30. start = time.time()
  31. for i in range(10):
  32. t = pool.submit(knowledge_chat)
  33. threads.append(t)
  34. for r in as_completed(threads):
  35. end = time.time()
  36. times.append(end - start)
  37. print("\nResult:\n")
  38. pprint(r.result())
  39. print("\nTime used:\n")
  40. for x in times:
  41. print(f"{x}")

通过concurrent的

  1. ThreadPoolExecutor

,

  1. as_completed

进行反馈


3 知识库相关实践问题

3.1 .md格式的文件 支持非常差

我们在

  1. configs/kb_config.py

可以看到:

  1. # TextSplitter配置项,如果你不明白其中的含义,就不要修改。
  2. text_splitter_dict = {
  3. "ChineseRecursiveTextSplitter": {
  4. "source": "huggingface", # 选择tiktoken则使用openai的方法
  5. "tokenizer_name_or_path": "",
  6. },
  7. "SpacyTextSplitter": {
  8. "source": "huggingface",
  9. "tokenizer_name_or_path": "gpt2",
  10. },
  11. "RecursiveCharacterTextSplitter": {
  12. "source": "tiktoken",
  13. "tokenizer_name_or_path": "cl100k_base",
  14. },
  15. "MarkdownHeaderTextSplitter": {
  16. "headers_to_split_on":
  17. [
  18. ("#", "head1"),
  19. ("##", "head2"),
  20. ("###", "head3"),
  21. ("####", "head4"),
  22. ]
  23. },
  24. }
  25. # TEXT_SPLITTER 名称
  26. TEXT_SPLITTER_NAME = "ChineseRecursiveTextSplitter"

chatchat看上去创建新知识库的时候,仅支持一个知识库一个

  1. TEXT_SPLITTER_NAME

的方法,并不能做到不同的文件,使用不同的切块模型。
所以如果要一个知识库内,不同文件使用不同的切分方式,需要自己改整个结构代码;然后重启项目

同时,chatchat项目对markdown的源文件,支持非常差,我们来看看:

  1. from langchain import document_loaders
  2. from server.knowledge_base.utils import make_text_splitter
  3. # 载入
  4. filepath = "matt/智能XXX.md"
  5. loader = document_loaders.UnstructuredFileLoader(filepath,autodetect_encoding=True)
  6. docs = loader.load()
  7. # 切分
  8. splitter_name = 'ChineseRecursiveTextSplitter'
  9. text_splitter = make_text_splitter(splitter_name, CHUNK_SIZE, OVERLAP_SIZE)
  10. if splitter_name == "MarkdownHeaderTextSplitter":
  11. docs = text_splitter.split_text(docs[0].page_content)
  12. for doc in docs:
  13. if doc.metadata:
  14. doc.metadata["source"] = os.path.basename(filepath)
  15. else:
  16. docs = text_splitter.split_documents(docs)
  17. for doc in docs:
  18. print(doc)

首先chatchat对.md文件读入使用的是

  1. UnstructuredFileLoader

,但是没有加

  1. mode="elements"

(参考:LangChain:万能的非结构化文档载入详解(一))
所以,你可以认为,读入后,

  1. #

会出现丢失,于是你即使选择了

  1. MarkdownHeaderTextSplitter

,也还是无法使用。
目前来看,不建议上传

  1. .md

格式的文档,比较好的方法是:

  • 文件改成 doc,可以带# / ## / ###
  • 更改configs/kb_config.py当中的TEXT_SPLITTER_NAME = "MarkdownHeaderTextSplitter"

本文转载自: https://blog.csdn.net/sinat_26917383/article/details/135317522
版权归原作者 悟乙己 所有, 如有侵权,请联系我们删除。

“Langchain-Chatchat开源库使用的随笔记(一)”的评论:

还没有评论