Skip to main content

代码理解

在Collab中打开

使用案例

源代码分析是最受欢迎的LLM应用之一(例如,GitHub Co-PilotCode InterpreterCodiumCodeium),用于以下用例:

  • 通过代码库进行问答以了解其工作原理
  • 使用LLM提供重构或改进建议
  • 使用LLM对代码进行文档化

图像描述

概述

对于代码的问答流程遵循我们在文档问答中所做的步骤,但有一些区别:

特别是,我们可以采用分割策略,它执行以下几个操作:

  • 将代码中的每个顶级函数和类加载到单独的文档中。
  • 将剩余部分放入单独的文档中。
  • 保留有关每个分割来自何处的元数据

快速入门

pip install openai tiktoken chromadb langchain  

# 设置环境变量OPENAI_API_KEY或从.env文件中加载
# import dotenv

# dotenv.load_env()

我们将按照这个笔记本的结构进行操作,并使用上下文感知的代码分割

加载数据

我们将使用langchain.document_loaders.TextLoader上传所有的Python项目文件。

以下脚本遍历LangChain存储库中的文件,并加载每个.py文件(也称为文档):

from git import Repo  
from langchain.text_splitter import Language
from langchain.document_loaders.generic import GenericLoader
from langchain.document_loaders.parsers import LanguageParser

API参考:

# 克隆
repo_path = "/Users/rlm/Desktop/test_repo"
repo = Repo.clone_from("https://github.com/hwchase17/langchain", to_path=repo_path)

我们使用LanguageParser加载py代码,它将:

  • 将顶级函数和类放在一起(成为一个单独的文档)
  • 将剩余的代码放入一个单独的文档
  • 保留有关每个拆分来自何处的元数据
# 加载
loader = GenericLoader.from_filesystem(
repo_path+"/libs/langchain/langchain",
glob="**/*",
suffixes=[".py"],
parser=LanguageParser(language=Language.PYTHON, parser_threshold=500)
)
documents = loader.load()
len(documents)
1293

拆分

Document分割成用于嵌入和向量存储的块。

我们可以使用指定了languageRecursiveCharacterTextSplitter

from langchain.text_splitter import RecursiveCharacterTextSplitter
python_splitter = RecursiveCharacterTextSplitter.from_language(language=Language.PYTHON,
chunk_size=2000,
chunk_overlap=200)
texts = python_splitter.split_documents(documents)
len(texts)

API参考:

3748

RetrievalQA

我们需要以一种可以语义搜索其内容的方式存储文档。

最常见的方法是嵌入每个文档的内容,然后将嵌入和文档存储在向量存储中。

在设置向量存储检索器时:

深入了解

  • 这里浏览超过40个向量存储集成。
  • 这里查看有关向量存储的进一步文档。
  • 这里浏览超过30个文本嵌入集成。
  • 这里查看有关嵌入模型的进一步文档。
    from langchain.vectorstores import Chroma  
from langchain.embeddings.openai import OpenAIEmbeddings
db = Chroma.from_documents(texts, OpenAIEmbeddings(disallowed_special=()))
retriever = db.as_retriever(
search_type="mmr", # 也可以测试 "similarity"
search_kwargs={"k": 8},
)

API 参考:

聊天

测试一下 chatbots.

深入了解

  • 浏览超过55个LLM和聊天模型的集成这里
  • 在LLMs和聊天模型上查看更多文档这里
  • 使用本地LLMs:PrivateGPTGPT4All的流行程度凸显了本地运行LLMs的重要性。
from langchain.chat_models import ChatOpenAI  
from langchain.memory import ConversationSummaryMemory
from langchain.chains import ConversationalRetrievalChain
llm = ChatOpenAI(model_name="gpt-4")
memory = ConversationSummaryMemory(llm=llm,memory_key="chat_history",return_messages=True)
qa = ConversationalRetrievalChain.from_llm(llm, retriever=retriever, memory=memory)

API参考:

    question = "如何将源代码加载为文档,用于对代码进行QA,将代码分割为类和函数?"  
result = qa(question)
result['answer']
        '要将源代码加载为文档以进行代码QA,您可以使用`CodeLoader`类。该类允许您加载源代码文件并将其分割为类和函数。\n\n以下是如何使用`CodeLoader`类的示例:\n\n```python\nfrom langchain.document_loaders.code import CodeLoader\n\n# 指定源代码文件的路径\ncode_file_path = "path/to/code/file.py"\n\n# 创建CodeLoader类的实例\ncode_loader = CodeLoader(code_file_path)\n\n# 加载代码作为文档\ndocuments = code_loader.load()\n\n# 遍历文档\nfor document in documents:\n    # 访问类或函数名称\n    name = document.metadata["name"]\n    \n    # 访问代码内容\n    code = document.page_content\n    \n    # 根据需要处理代码\n    # ...\n```\n\n在上面的示例中,`code_file_path`应替换为实际源代码文件的路径。`CodeLoader`类的`load()`方法将返回一个`Document`对象列表,其中每个文档表示源代码中的一个类或函数。您可以使用`metadata["name"]`属性访问类或函数名称,并使用每个`Document`对象的`page_content`属性访问代码内容。\n\n然后,您可以根据需要处理代码以进行QA任务。'  
    questions = [  
"类层次结构是什么?",
"哪些类是从Chain类派生的?",
"关于Chain类的类层次结构,您提出了哪一个改进建议?",
]

for question in questions:
result = qa(question)
print(f"-> **问题**:{question} \n")
print(f"**答案**:{result['answer']} \n")
-> **问题**:类层次结构是什么?   

**答案**:面向对象编程中的类层次结构是当类从其他类派生时形成的结构。派生类是基类的子类,也称为超类。这个层次结构是基于面向对象编程中继承的概念形成的,其中子类继承了超类的属性和功能。

在给定的上下文中,我们有以下类层次结构的示例:

1. `BaseCallbackHandler --> <name>CallbackHandler` 表示`BaseCallbackHandler`是一个基类,`<name>CallbackHandler`(如`AimCallbackHandler``ArgillaCallbackHandler`等)是从`BaseCallbackHandler`继承的派生类。

2. `BaseLoader --> <name>Loader` 表示`BaseLoader`是一个基类,`<name>Loader`(如`TextLoader``UnstructuredFileLoader`等)是从`BaseLoader`继承的派生类。

3. `ToolMetaclass --> BaseTool --> <name>Tool` 表示`ToolMetaclass`是一个基类,`BaseTool`是从`ToolMetaclass`继承的派生类,`<name>Tool`(如`AIPluginTool``BaseGraphQLTool`等)是进一步派生的类,它们从`BaseTool`继承。

-> **问题**:哪些类是从Chain类派生的?

**答案**:从Chain类派生的类有:

1. LLMSummarizationCheckerChain
2. MapReduceChain
3. OpenAIModerationChain
4. NatBotChain
5. QAGenerationChain
6. QAWithSourcesChain
7. RetrievalQAWithSourcesChain
8. VectorDBQAWithSourcesChain
9. RetrievalQA
10. VectorDBQA
11. LLMRouterChain
12. MultiPromptChain
13. MultiRetrievalQAChain
14. MultiRouteChain
15. RouterChain
16. SequentialChain
17. SimpleSequentialChain
18. TransformChain
19. BaseConversationalRetrievalChain
20. ConstitutionalChain

-> **问题**:关于Chain类的类层次结构,您提出了哪一个改进建议?

**答案**:作为一个AI模型,我没有个人意见。然而,一个建议是改进Chain类层次结构的文档。当前的注释和文档字符串提供了一些细节,但包括更明确的关于层次结构的解释,每个子类的角色以及它们彼此之间的关系的解释可能会有所帮助。此外,加入UML图表或其他可视化工具可以帮助开发人员更好地理解类的结构和交互。

您可以查看LangSmith跟踪以了解底层发生了什么:

  • 特别是,代码在检索输出中被很好地结构化和保持在一起
  • 检索到的代码和聊天历史记录被传递给LLM进行答案提炼

图像描述