接着之前的内容梳理我的个人理解,也就是通过langchain使用大语言模型的第三步,对输入进行包装。不过目前对于Agent和Chain之间的关系还是有些理解不够,看起来Agent能够使用工具且自主性更强一些。
一、提示词模板:
首先是前面一直在使用的PromptTemplate,通过它接收添加了变量的原始模板,再通过.format方法为其传入变量,前面我也试着用这种简单方式实现了角色扮演类的功能。
然后是ChatPromptTemplate模板,这个模板看起来有些抽象,同时提供了系统提示词与用户提示词,看起来也可以拿来实现多轮对话,让模型以一个基于系统提示词的角色的身份做出回应。但聊天的形式也使得回复可能没有那么丰富,比如角色扮演过程中角色的状态描述。在这个模板的基础上,通过“step by step”实现了类似思维链的过程,这可能也是让模型以角色的身份做出回应的好处,因为它可以不断地思索。在课外扩展部分提到的tree-of-thoughts库中,则似乎是如下逻辑:
通过ThreadPoolExecutor并行生成多个思想——使用ToTDFSAgent这个类对思想进行排序,并根据评估分数进行剪枝——如果思想的评估分数高于剪枝阈值,则递归调用这一过程——如果结果的评估分数高于阈值threshold,则返回该结果。当然,这个过程似乎也是用自然语言教模型实现的。
此外是FewShotPromptTemplate模板,这个模板比较容易理解,能够比较系统地传入一些供模型学习的样本,不过看起来通过PromptTemplate也能够为模型提供举例。
二、链:
链相较于直接使用模型,相当于把整个Model I/O的流程封装到链里面,可以使用.apply基于由字典构成的列表批量调用,.generate方法则可在apply的基础上同时返回completion_tokens、total_tokens等信息。
最开始接触到的链应该是第三节构造RAG时使用的RetrievalQA 链,为它传入一个存储了切分过的向量数据库vectorstore的MultiQueryRetriever。向量数据库获取的向量是通过embedding模型处理过的原始文字。对于它的理解大概是一个不映射为新embedding而是直接将原始embedding矩阵压缩为向量(类似于BERT用来计算相似度时表征语义所使用的通过提取cls或平均池化这类方式?)并存储,不过当时的召回率不太理想,模型总是回答不知道。在之后的课程里,则将它进一步封装在了VectorstoreIndexCreator这个类中。
顺序链SequentialChain看起来主要实现的功能是为每一个模型设计身份,再基于前一个模型的输出作为下一个模型的输入,实现对结果的不断迭代,不过看起来也会导致很大的token开销。
路由链RouterChain则像是MoE那样,可以在前者的基础上,通过MultiPromptChain进行整合,再选择性地执行定义好的下游链或默认链ConversationChain。ConversationChain链还可以设置记忆,通过比较丰富的方式以比较简洁的代码获得记忆,比之前尝试角色扮演llm时粗暴地以变量形式传入似乎方便不少。其中的混合记忆尽量保留最近互动中的原始内容,而通过max_token_limit这个参数实现当最新的对话文字长度在300字之内的时候,LangChain会记忆原始对话内容;当对话文字超出了这个参数的长度,那么模型就会把所有超过预设长度的内容进行总结,以节省Token数量。
此外,在BabyAGI的实现过程中,似乎也多次看到chain的身影,通过各种自定义的链封装提示词模板,能够让代码变得更加简洁。