-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RQA tools for Tianji #29
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
9d2cde0
add chrama database for RQA
liuxianyi 445237a
add zhupu model with langchain
liuxianyi 0826f57
add test for rqa with zhipu
liuxianyi 3d3a4cc
update requirement with langchain package
liuxianyi f477123
update requirement with gradio package
liuxianyi 7506208
add RQA demo
liuxianyi 3ddd796
update model name
liuxianyi 62b129e
update: Path regularization
liuxianyi ef7492b
doc Remove redundant comments
liuxianyi 265d4db
remove reduntant comments
liuxianyi 594655d
update process_data
liuxianyi 03c5cf7
update exec pre-commit
liuxianyi 0a80886
Update config.py
liuxianyi dda7353
code reconstrcut
liuxianyi 70fa80c
perf
liuxianyi a3a4710
update RQA config
liuxianyi 6e5f60b
perf
liuxianyi 0aeb89c
update prompt
liuxianyi 6939235
update RQA config
liuxianyi ec31c39
delete embeddings storages
liuxianyi dd5fb25
docs add
liuxianyi 8f412e4
docs add
liuxianyi d6bebf2
perf docs
liuxianyi ad29da5
perf RQA docs
liuxianyi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ __pycache__/ | |
build/ | ||
tianji.egg-info/ | ||
temp/ | ||
*.bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# 知识外挂 RQA 使用说明 | ||
|
||
一共包含3个步骤: | ||
|
||
1. 准备数据 | ||
1. 构建embdding数据库 | ||
1. 根据自定义embdding数据库,实现知识外挂 RQA | ||
|
||
# 数据准备 | ||
|
||
参考[text](Tianji/tianji/knowledges/RQA/script/process_data.py) | ||
|
||
# 构建embdding数据库 | ||
|
||
填写配置文件 | ||
|
||
```python | ||
# 原始数据位置 online 设置为空 | ||
ORIGIN_DATA = "" | ||
# 持久化数据库位置,例如 chroma/liyi/ | ||
PERSIST_DIRECTORY = "" | ||
# Sentence-Transformer词向量模型权重位置 | ||
HF_SENTENCE_TRANSFORMER_WEIGHT = ( | ||
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" | ||
) | ||
``` | ||
|
||
运行脚本,基于Chroma构建向量数据库 | ||
|
||
```python | ||
python Tianji/tianji/knowledges/RQA/emb.py | ||
``` | ||
|
||
# 运行 Demo | ||
|
||
```python | ||
python Tianji/run/tianji_rqa_demo.py | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,7 @@ transformers_stream_generator==0.0.4 | |
modelscope | ||
tiktoken | ||
einops | ||
|
||
# for RQA | ||
langchain==0.0.292 | ||
gradio==3.50.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
from langchain.vectorstores import Chroma | ||
from langchain.embeddings.huggingface import HuggingFaceEmbeddings | ||
from tianji.knowledges.RQA.model import Zhipu_LLM | ||
from langchain.prompts import PromptTemplate | ||
from langchain.chains import RetrievalQA | ||
from tianji.knowledges.RQA.config import RQA_ST_Liyi_Chroma_Config | ||
|
||
|
||
def load_chain(): | ||
embeddings = HuggingFaceEmbeddings( | ||
model_name=RQA_ST_Liyi_Chroma_Config.HF_SENTENCE_TRANSFORMER_WEIGHT | ||
) | ||
|
||
persist_directory = RQA_ST_Liyi_Chroma_Config.PERSIST_DIRECTORY | ||
vectordb = Chroma( | ||
persist_directory=persist_directory, | ||
embedding_function=embeddings, | ||
) | ||
|
||
llm = Zhipu_LLM() | ||
|
||
template = """使用以下上下文中文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答 | ||
案。尽量使答案调理清楚,内容详实。总是在回答的最后说”谢谢你的提问!“。 | ||
{context} | ||
问题: {question} | ||
详细真实的答案:""" | ||
|
||
QA_CHAIN_PROMPT = PromptTemplate( | ||
input_variables=["context", "question"], template=template | ||
) | ||
|
||
qa_chain = RetrievalQA.from_chain_type( | ||
llm, | ||
retriever=vectordb.as_retriever(), | ||
return_source_documents=True, | ||
chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}, | ||
) | ||
|
||
return qa_chain | ||
|
||
|
||
class Model_center: | ||
""" | ||
模型广场,目前支持智浦AI | ||
""" | ||
def __init__(self): | ||
self.chain = load_chain() | ||
|
||
def qa_chain_self_answer(self, question: str, chat_history: list = []): | ||
if question is None or len(question) < 1: | ||
return "", chat_history | ||
try: | ||
chat_history.append((question, self.chain({"query": question})["result"])) | ||
return "", chat_history | ||
except Exception as e: | ||
return e, chat_history | ||
|
||
|
||
import gradio as gr | ||
|
||
model_center = Model_center() | ||
block = gr.Blocks() | ||
with block as demo: | ||
with gr.Row(equal_height=True): | ||
with gr.Column(scale=15): | ||
gr.Markdown( | ||
"""<h1><center>InternLM</center></h1> | ||
<center>人情世故</center> | ||
""" | ||
) | ||
|
||
with gr.Row(): | ||
with gr.Column(scale=4): | ||
chatbot = gr.Chatbot(height=450, show_copy_button=True) | ||
msg = gr.Textbox(label="Prompt/问题") | ||
|
||
with gr.Row(): | ||
db_wo_his_btn = gr.Button("Chat") | ||
with gr.Row(): | ||
clear = gr.ClearButton(components=[chatbot], value="Clear console") | ||
|
||
db_wo_his_btn.click( | ||
model_center.qa_chain_self_answer, | ||
inputs=[msg, chatbot], | ||
outputs=[msg, chatbot], | ||
) | ||
|
||
gr.Markdown( | ||
"""提醒:<br> | ||
1. 初始化数据库时间可能较长,请耐心等待。 | ||
2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。 <br> | ||
""" | ||
) | ||
gr.close_all() | ||
demo.launch() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from tianji.knowledges.RAG.demo import model_center | ||
|
||
if __name__ == "__main__": | ||
model = model_center() | ||
question = "如何给长辈敬酒?" | ||
chat_history = [] | ||
_, response = model.qa_chain_self_answer(question, chat_history) | ||
print(response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# from metagpt.const import METAGPT_ROOT as TIANJI_PATH | ||
class RQA_ST_Liyi_Chroma_Config: | ||
""" | ||
检索问答增强(RQA)配置文件: | ||
基于Chroma检索数据库; | ||
基于Sentence-Transformer词向量模型构建的外挂礼仪(Liyi)知识库。 | ||
""" | ||
|
||
# 原始数据位置 online 设置为空 | ||
ORIGIN_DATA = "" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有关这种需要配置的文档,可以在 docs目录下 新增一个 文档解释(有哪些参数可以修改 |
||
# 持久化数据库位置,例如 chroma/liyi/ | ||
PERSIST_DIRECTORY = "" | ||
# Sentence-Transformer词向量模型权重位置 | ||
HF_SENTENCE_TRANSFORMER_WEIGHT = ( | ||
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from langchain.document_loaders import DirectoryLoader, TextLoader | ||
from langchain.text_splitter import RecursiveCharacterTextSplitter | ||
from langchain.embeddings.huggingface import HuggingFaceEmbeddings | ||
from langchain.vectorstores import Chroma | ||
|
||
from . import RQA_ST_Liyi_Chroma_Config | ||
|
||
if __name__ == "__main__": | ||
persist_directory = RQA_ST_Liyi_Chroma_Config.PERSIST_DIRECTORY | ||
data_directory = RQA_ST_Liyi_Chroma_Config.ORIGIN_DATA | ||
loader = DirectoryLoader(data_directory, glob="*.txt", loader_cls=TextLoader) | ||
|
||
text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=150) | ||
split_docs = text_splitter.split_documents(loader.load()) | ||
|
||
embeddings = HuggingFaceEmbeddings( | ||
model_name="/root/weights/model/sentence-transformer" | ||
) | ||
vectordb = Chroma.from_documents( | ||
documents=split_docs, embedding=embeddings, persist_directory=persist_directory | ||
) | ||
vectordb.persist() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .langchain.model import InternLM_LLM, Zhipu_LLM, OpenAI_LLM |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
from langchain.llms.base import LLM | ||
from typing import Any, List, Optional | ||
from langchain.callbacks.manager import CallbackManagerForLLMRun | ||
from transformers import AutoTokenizer, AutoModelForCausalLM | ||
import torch | ||
import os | ||
|
||
|
||
class InternLM_LLM(LLM): | ||
tokenizer: AutoTokenizer = None | ||
model: AutoModelForCausalLM = None | ||
|
||
def __init__(self, model_path: str): | ||
super().__init__() | ||
print("正在从本地加载模型...") | ||
self.tokenizer = AutoTokenizer.from_pretrained( | ||
model_path, trust_remote_code=True | ||
) | ||
self.model = ( | ||
AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True) | ||
.to(torch.bfloat16) | ||
.cuda() | ||
) | ||
self.model = self.model.eval() | ||
print("完成本地模型的加载") | ||
|
||
def _call( | ||
self, | ||
prompt: str, | ||
stop: Optional[List[str]] = None, | ||
run_manager: Optional[CallbackManagerForLLMRun] = None, | ||
**kwargs: Any | ||
): | ||
system_prompt = """你是一名AI助手名为天机(SocialAI),也可称为来事儿AI。它能够处理中国传统人情世故的任务,例如如何敬酒、如何说好话、如何会来事儿等。 | ||
""" | ||
messages = [(system_prompt, "")] | ||
response, history = self.model.chat(self.tokenizer, prompt, history=messages) | ||
return response | ||
|
||
@property | ||
def _llm_type(self) -> str: | ||
return "InternLM" | ||
|
||
|
||
class Zhipu_LLM(LLM): | ||
tokenizer: AutoTokenizer = None | ||
model: AutoModelForCausalLM = None | ||
client: Any = None | ||
|
||
def __init__(self): | ||
super().__init__() | ||
from zhipuai import ZhipuAI | ||
|
||
print("初始化模型...") | ||
self.client = ZhipuAI(api_key=os.environ.get("zhupuai_key")) | ||
print("完成模型初始化") | ||
|
||
def _call( | ||
self, | ||
prompt: str, | ||
stop: Optional[List[str]] = None, | ||
run_manager: Optional[CallbackManagerForLLMRun] = None, | ||
**kwargs: Any | ||
): | ||
system_prompt = """你是一名AI助手名为天机(SocialAI),也可称为来事儿AI。它能够处理中国传统人情世故的任务,例如如何敬酒、如何说好话、如何会来事儿等。 | ||
你是一个信息抽取的知识库语料准备能手,你需要把我给你的文章做成几个知识点,这个知识点类似问答对的回答(陈述句的描述,不需要提问,比如:苹果是一种水果,可以吃和烹饪,而且是红色的,长在大树上),你不需要分1、2、3、4点, 只需要把相关的知识都划分成一个段落就好, ``` 例子如下,假设我首先发了这个文章: 在商务宴请中有一个很重要的礼仪,如果你忽视了,会让你的客户觉得你很没有分寸。大家都知道在饭桌上谈生意,往往会比在办公室正儿八经坐着谈成的几率会更大。在这其中当然离不开酒的路牢,所以在商务宴请中敬酒的礼仪是非常重要的。 敬酒时先给对方斟酒,然后再给自己斟酒。右手拿酒杯,左手托杯底。咱们的酒杯要比对方低一点,如果对方比较谦虚,放的比我们低,我们可以用左手轻轻的将对方的酒杯托起,这样会显得尊重。喝完酒为了表达咱们的诚意,我们可以用敬酒的手势把杯子微微倾斜,杯口朝向对方,不用再把杯子直接倒过来,会显得很不雅。大家在敬酒的时候呢,还有哪些注意事项呢?咱们可以留言一起讨论一下。 你的回答是富有知识冷静的回复,如下作为一个整体:商务宴请中,礼仪的遵循对于给客户留下良好印象至关重要,饭桌上的生意洽谈通常成功率较高。在敬酒环节,应优先为对方斟酒,随后再为自己斟,且需用右手持杯,左手托底。敬酒时,酒杯应保持低于对方酒杯,以示尊敬;若对方酒杯位置更低,可轻轻用左手托起对方酒杯。喝完酒后,应以敬酒手势将杯子微微倾斜,杯口朝向对方,避免直接倒转酒杯,以维持礼貌和风度。 ``` 接下来你帮我解析新的知识,你只需要回复这个新的知识文章相关的内容就好,不要回复例子的内容!文章如下: ``` 你知道一场正式饭局的喝酒流程和敬酒节奏吗?如果不知道这个视频,一定要点赞收藏,因为你早晚用的上一场商务酒局。一般有这六个阶段,而大部分人在第二和第五阶段最容易犯错。接下来咱分别说说,先说这酒局第一阶段开场的共同酒喝多少你得看主场。山东人讲究主副陪轮流领酒,共同干杯制,而河北的多数地方习惯共同喝前三杯,不同地方有不同讲究,大家也都可以留言说说看你当地有什么讲究。如果大家点赞关注够热情,我后期可以专门出一集全国各地喝酒习俗的总结。 这第二阶段就是东道主开始敬酒了。这时候一般都是东道主或主陪率先从主宾开始依次向每一位客人敬酒,这个阶段依次和轮流意识很重要。如果你是客人,可千万别在这种时候为了表示你的谢意去回敬主人,因为还没到该你出场的阶段,到了第三阶段,你作为客人就可以回敬了。可以由你方领头的带着大家先共同回敬,然后再分别回敬。 接着进入第四阶段,喝主题酒及重点酒,根据被情者与主题的关系把主题点出来,喝进去是桌上人明白为啥喝这场酒。嘿嘿这第五阶段就是自由酒阶段了。跟谁投脾气就可以过去跟他喝杯相见恨晚酒。跟谁还有未了的话题可以用酒来讨教,看谁不顺眼也可以用酒来挑战。尤其是带着任务来了,一定要抓紧时间落实任务,因为过了这阶段就不自由了。 在第六阶段,也就是最后喝满堂红了,差不多该散席了。主陪一般都会发话,大家各扫门前酒,共喝满堂红。这杯酒喝下去意味着酒事正式结束,下面的节目能吃吃该吐吐。商务宴请中,礼仪的遵循对于给客户留下良好印象至关重要,饭桌上的生意洽谈通常成功率较高。在敬酒环节,应优先为对方斟酒,随后再为自己斟,且需用右手持杯,左手托底。敬酒时,酒杯应保持低于对方酒杯,以示尊敬;若对方酒杯位置更低,可轻轻用左手托起对方酒杯。喝完酒后,应以敬酒手势将杯子微微倾斜,杯口朝向对方,避免直接倒转酒杯,以维持礼貌和风度。 ``` | ||
""" | ||
response = self.client.chat.completions.create( | ||
model="glm-4", | ||
messages=[ | ||
{"role": "system", "content": system_prompt}, | ||
{"role": "user", "content": prompt}, | ||
], | ||
) | ||
return response.choices[0].message.content | ||
|
||
@property | ||
def _llm_type(self) -> str: | ||
return "ZhipuLM" | ||
|
||
|
||
class OpenAI_LLM(LLM): | ||
tokenizer: AutoTokenizer = None | ||
model: AutoModelForCausalLM = None | ||
client: Any = None | ||
|
||
def __init__(self, base_url="https://api.deepseek.com/v1"): | ||
super().__init__() | ||
from openai import OpenAI | ||
|
||
print("初始化模型...") | ||
self.client = OpenAI( | ||
api_key=os.environ.get("openai_key", None), base_url=base_url | ||
) | ||
print("完成模型初始化") | ||
|
||
def _call( | ||
self, | ||
prompt: str, | ||
stop: Optional[List[str]] = None, | ||
run_manager: Optional[CallbackManagerForLLMRun] = None, | ||
**kwargs: Any | ||
): | ||
system_prompt = """你是一名AI助手名为天机(SocialAI),也可称为来事儿AI。它能够处理中国传统人情世故的任务,例如如何敬酒、如何说好话、如何会来事儿等。 | ||
你是一个信息抽取的知识库语料准备能手,你需要把我给你的文章做成几个知识点,这个知识点类似问答对的回答(陈述句的描述,不需要提问,比如:苹果是一种水果,可以吃和烹饪,而且是红色的,长在大树上),你不需要分1、2、3、4点, 只需要把相关的知识都划分成一个段落就好, ``` 例子如下,假设我首先发了这个文章: 在商务宴请中有一个很重要的礼仪,如果你忽视了,会让你的客户觉得你很没有分寸。大家都知道在饭桌上谈生意,往往会比在办公室正儿八经坐着谈成的几率会更大。在这其中当然离不开酒的路牢,所以在商务宴请中敬酒的礼仪是非常重要的。 敬酒时先给对方斟酒,然后再给自己斟酒。右手拿酒杯,左手托杯底。咱们的酒杯要比对方低一点,如果对方比较谦虚,放的比我们低,我们可以用左手轻轻的将对方的酒杯托起,这样会显得尊重。喝完酒为了表达咱们的诚意,我们可以用敬酒的手势把杯子微微倾斜,杯口朝向对方,不用再把杯子直接倒过来,会显得很不雅。大家在敬酒的时候呢,还有哪些注意事项呢?咱们可以留言一起讨论一下。 你的回答是富有知识冷静的回复,如下作为一个整体:商务宴请中,礼仪的遵循对于给客户留下良好印象至关重要,饭桌上的生意洽谈通常成功率较高。在敬酒环节,应优先为对方斟酒,随后再为自己斟,且需用右手持杯,左手托底。敬酒时,酒杯应保持低于对方酒杯,以示尊敬;若对方酒杯位置更低,可轻轻用左手托起对方酒杯。喝完酒后,应以敬酒手势将杯子微微倾斜,杯口朝向对方,避免直接倒转酒杯,以维持礼貌和风度。 ``` 接下来你帮我解析新的知识,你只需要回复这个新的知识文章相关的内容就好,不要回复例子的内容!文章如下: ``` 你知道一场正式饭局的喝酒流程和敬酒节奏吗?如果不知道这个视频,一定要点赞收藏,因为你早晚用的上一场商务酒局。一般有这六个阶段,而大部分人在第二和第五阶段最容易犯错。接下来咱分别说说,先说这酒局第一阶段开场的共同酒喝多少你得看主场。山东人讲究主副陪轮流领酒,共同干杯制,而河北的多数地方习惯共同喝前三杯,不同地方有不同讲究,大家也都可以留言说说看你当地有什么讲究。如果大家点赞关注够热情,我后期可以专门出一集全国各地喝酒习俗的总结。 这第二阶段就是东道主开始敬酒了。这时候一般都是东道主或主陪率先从主宾开始依次向每一位客人敬酒,这个阶段依次和轮流意识很重要。如果你是客人,可千万别在这种时候为了表示你的谢意去回敬主人,因为还没到该你出场的阶段,到了第三阶段,你作为客人就可以回敬了。可以由你方领头的带着大家先共同回敬,然后再分别回敬。 接着进入第四阶段,喝主题酒及重点酒,根据被情者与主题的关系把主题点出来,喝进去是桌上人明白为啥喝这场酒。嘿嘿这第五阶段就是自由酒阶段了。跟谁投脾气就可以过去跟他喝杯相见恨晚酒。跟谁还有未了的话题可以用酒来讨教,看谁不顺眼也可以用酒来挑战。尤其是带着任务来了,一定要抓紧时间落实任务,因为过了这阶段就不自由了。 在第六阶段,也就是最后喝满堂红了,差不多该散席了。主陪一般都会发话,大家各扫门前酒,共喝满堂红。这杯酒喝下去意味着酒事正式结束,下面的节目能吃吃该吐吐。商务宴请中,礼仪的遵循对于给客户留下良好印象至关重要,饭桌上的生意洽谈通常成功率较高。在敬酒环节,应优先为对方斟酒,随后再为自己斟,且需用右手持杯,左手托底。敬酒时,酒杯应保持低于对方酒杯,以示尊敬;若对方酒杯位置更低,可轻轻用左手托起对方酒杯。喝完酒后,应以敬酒手势将杯子微微倾斜,杯口朝向对方,避免直接倒转酒杯,以维持礼貌和风度。 ``` | ||
""" | ||
response = self.client.chat.completions.create( | ||
model="glm-4", | ||
messages=[ | ||
{"role": "system", "content": system_prompt}, | ||
{"role": "user", "content": prompt}, | ||
], | ||
) | ||
return response.choices[0].message.content | ||
|
||
@property | ||
def _llm_type(self) -> str: | ||
return "OpenAILM" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要注释(这个东西的作用