基于需求文档、设计文档、测试用例的测试答疑助手
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">本文将基于 LangChain 实现一个 mini 的实战案例。这次实战<span style="color: black;">重点</span>完成的任务,<span style="color: black;">便是</span>设计一个测试答疑助手,这个测试答疑助手的<span style="color: black;">重点</span>功能为基于本地的文档和数据,回答给出的自然语言问题,<span style="color: black;">例如</span><span style="color: black;">有些</span>数据的统计,<span style="color: black;">查询</span>、组合。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">示例<span style="color: black;">运用</span>数据</p>测试用例文档设计文档需求文档<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">实践演练</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">实战设计思路</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="//q3.itc.cn/images01/20240509/0efd91e467ea43668a3d640195808bee.jpeg" style="width: 50%; margin-bottom: 20px;"></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">安装依赖</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">安装依赖 chromadb,chromadb是一个简单快捷的向量数据库,为了减少对embedding模型的请求次数,设置数据<span style="color: black;">保留</span>的<span style="color: black;">理学</span>位置,<span style="color: black;">这般</span>多次运行代码<span style="color: black;">亦</span>不会重复请求模型转换向量:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">pip install chromadb</p>embedding:相当于一个“桥梁” —— 翻译:把<span style="color: black;">照片</span>,文字,视频以及音频<span style="color: black;">所有</span>转换为数字,并且<span style="color: black;">包括</span>了数据的信息,使得大模型都能”懂“,能利用这些数字去做训练和推理。向量:向量本身是一组数字,<span style="color: black;">然则</span>在几何上,向量的各个数字组<span style="color: black;">成为了</span>多维的数组空间,向量的<span style="color: black;">每一个</span>维度<span style="color: black;">表率</span>该空间的·一个<span style="color: black;">区别</span>的特征<span style="color: black;">或</span>属性。向量数据库:专门用于存储和管理向量数据的数据库,能对向量数据进行<span style="color: black;">有效</span>的操作。<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">代码实现</p><span style="color: black;">经过</span>环境变量设置API Token,以及<span style="color: black;">关联</span>依赖的导入:<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># <span style="color: black;">有些</span>LangChain的依赖导入 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">from langchain.chat_models import ChatOpenAI </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">from langchain.embeddings import OpenAIEmbeddings </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">from langchain.text_splitter import MarkdownHeaderTextSplitter</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">from langchain.vectorstores import Chroma </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 加载openai 的 API Tokenfrom utils.data_load import get_auth </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># <span style="color: black;">经过</span>环境变量设置API Token,<span style="color: black;">由于</span>代码中的部分模型调用实例<span style="color: black;">没</span>法直接传递参数,<span style="color: black;">必须</span><span style="color: black;">经过</span>环境变量设置的方式将token传递给自动构建的OpenAI请求</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">import os </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">os.environ = open_ai_token</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">os.environ = open_ai_url</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">2.处理源文档,将其切片处理:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 定义一个函数,用于加载本地文件中的文本 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">def load_text_from_file(path: str) -> str: </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">return open(path, encoding=utf-8).read() </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># <span style="color: black;">按照</span>测试用例文档中的结构,定义一个MarkdownHeaderTextSplitter实例,用于将markdown文档切分为文本片段,方便后续embedding处理和向量数据库的构建</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">testcase_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("##", "模块名<span style="color: black;">叫作</span>"), ("###", "用例名<span style="color: black;">叫作</span>")])</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">docs = testcase_splitter.split_text(load_text_from_file(path="docs/测试用例.md")) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># <span style="color: black;">一样</span>处理设计文档,<span style="color: black;">由于</span>设计文档中的结构与测试用例文档不一致,<span style="color: black;">因此</span>要新定义一个MarkdownHeaderTextSplitter实例</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">design_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("##", "功能名<span style="color: black;">叫作</span>"), ("###", "模块名<span style="color: black;">叫作</span>")]) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">docs += design_splitter.split_text(load_text_from_file(path="docs/设计文档.md"))</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 同理,处理需求文档 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">prd_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("###", "功能名<span style="color: black;">叫作</span>")]) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">docs += prd_splitter.split_text(load_text_from_file(path="docs/需求文档.md"))</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 将切分后的文本片段输出,<span style="color: black;">能够</span>查看切分结果,和里面附带的数据信息 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">for doc in docs: </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">print(doc) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">3.<span style="color: black;">倘若</span>数据存在,则加载<span style="color: black;">运用</span>,<span style="color: black;">倘若</span>不存在,则写入:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">persist_directory = chroma</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 读取数据</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">if os.path.isdir(persist_directory): </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">vectordb = Chroma(persist_directory=persist_directory, embedding_function=OpenAIEmbeddings())</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">else: </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 将切分后的数据,<span style="color: black;">经过</span>OpenAIEmbeddings实例,转换为向量数据,</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 并<span style="color: black;">保留</span>到向量数据库中,持久化到本地指定目录下 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">vectordb = Chroma.from_documents( </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">documents=docs, </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">embedding=OpenAIEmbeddings(),</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">persist_directory=persist_directory </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">4.将向量数据库中检索到的文本片段以及提示词组<span style="color: black;">作为</span> prompt,向大模型获取返回信息:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 设定<span style="color: black;">最后</span>提出的问题 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">question = "问题:请统计出用户注册用例中,用到的所有用户名、<span style="color: black;">暗码</span>、电子邮箱数据,并将它们列成一个表格" </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 从向量数据库中找到<span style="color: black;">类似</span>度最高的k条文本片段数据</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">answer_docs = vectordb.similarity_search(query=question, k=4) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># ChatGPT3.5大模型调用实例 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">llm = ChatOpenAI(temperature=0.0) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 将向量数据库中检索到的文本片段组装成字符串,<span style="color: black;">做为</span>输入的数据源</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">resource_doc = "".join() </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># 将数据源字符串和问题组装成<span style="color: black;">最后</span>请求大模型的字符串 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">final_llm_text = f"{resource_doc} {question}"</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"># <span style="color: black;">经过</span>大模型获取字符串的回答信息 </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">response = llm.call_as_llm(message=final_llm_text) </p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">print(f"回答结果:\n{response}")</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">运行结果示例</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="//q1.itc.cn/images01/20240509/02720dee5f6947b0b0d4f965b974fbc0.jpeg" style="width: 50%; margin-bottom: 20px;"></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">举荐</span>学习</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="//q2.itc.cn/images01/20240509/ee0361860c1943fc817a5253f1ba6ace.png" style="width: 50%; margin-bottom: 20px;"><a style="color: black;"><span style="color: black;">返回<span style="color: black;">外链论坛:http://www.fok120.com/</span>,查看<span style="color: black;">更加多</span></span></a></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">责任编辑:网友投稿</span></p>
哈哈、笑死我了、太搞笑了吧等。 你的话深深触动了我,仿佛说出了我心里的声音。 期待更新、坐等、迫不及待等。 祝福你、祝你幸福、早日实现等。 你说得对,我们一起加油,未来可期。 你的话语如春风拂面,让我心生暖意。
页:
[1]