惊落一身雪 发表于 2024-8-25 21:59:41

LangChain 工程架构剖析

LangChain是什么

LangChain 是一个基于开源大语言模子的 AI 工程开辟框架,旨在使研究人员和开辟人员能够更轻松地构建、实验和部署以自然语言处置惩罚(NLP)为中心的应用程序。它提供了多种组件和工具,可帮助用户利用最近的语言模子希望,如大型 Transformer 模子等,并且可以与 Hugging Face 等平台集成。LangChain 的焦点理念是将语言模子用作协作工具,通过它,开辟者可以构建出处置惩罚复杂使命的系统,并且可以高效地对接不同的数据源和应用程序接口(APIs)。
技能架构

https://i-blog.csdnimg.cn/blog_migrate/b089a0a8d1577f17cc57a69af1054a4a.png
如图,从下至上分别是:


[*]LangChain 库:Python 和 JavaScript 库,包含接口和集成,用于各种组件的组合,以及现成的链和代理的实现。
[*]LangChain 模板:易于部署的各种使命的参考架构集合。
[*]LangServe:将 LangChain 链部署为 REST API 的库。
[*]LangSmith:开辟平台,可用于调试、测试、评估和监控基于任何 LLM 框架构建的链,并与 LangChain 无缝集成。
LangChain 库

LangChain 的焦点组件和本领(六大焦点抽象,用于构建复杂的AI应用,同时保持了良好的扩展本领。)


[*]Models(模子):
Chat Models(聊天模子): LangChain 提供了一个尺度接口,支持聊天模子。这些模子是语言模子的变体,其接口基于消息而不是原始文本。您可以使用不同类型的消息(如 AIMessage、HumanMessage、SystemMessage 和 ChatMessage)与聊天模子举行交互。
Embeddings(嵌入): 帮助将文本转换为向量表示,以便在应用程序中举行处置惩罚。
LLMs(大型语言模子): LangChain 支持大型语言模子,比方 ChatGPT,用于各种自然语言处置惩罚使命。
[*]Prompts(提示):
Prompt Templates(提示模板): 提供易于部署的参考架构,适用于不同使命。
[*]Indexes(索引):
Vector Databases(向量数据库): 用于存储和检索向量表示,比方文本嵌入。
Interaction Layer Prompts(交互层提示): 用于构建用户与模子之间的交互。
[*]Memory(记忆):
External Knowledge(外部知识): 整合外部知识源,丰富模子的知识库。
External Tools(外部工具): 与外部环境举行交互,比方通过 API 哀求执行利用。
[*]Chains(链):
LangChain提供了LCEL(LangChain Expression Language)声明式编程语言,将不同组件组合起来办理特定使命,比方在大量文本中查找信息。
[*]Agents(代理):
使得 LLMs 能够与外部环境举行交互,比方通过 API 哀求执行利用。
总之,LangChain 是一个强盛的工具箱,不仅涵盖了基础工具,还为个性化需求提供了自定义组件办理方案。它使开辟者能够更专注于创新和优化产物功能,从原型到生产环境的转化变得更加高效。
使用例子

最原始的RESTful API -> LangChain-OpenAI SDK

python
复制代码
import os
import requests

# API Key
api_key = os.getenv('OPENAI_API_KEY')

# 头部信息
headers = {
    'Content-Type': 'application/json',
    'Authorization': f'Bearer {api_key}'
}

# 准备数据
data = {
    'model': 'gpt-4',
    'messages': [{'role': 'user', 'content': '什么是图计算?'}],
    'temperature': 0.7
}

# 调用API
url = 'https://api.openai.com/v1/chat/completions'
response = requests.post(url, json=data, headers=headers)
answer = response.json()['choices']['message']['content']
print(answer)
ini
复制代码
from langchain_openai import ChatOpenAI

# 调用Chat Completion API
llm = ChatOpenAI(model_name='gpt-4')
response = llm.invoke('什么是图计算?')
print(response)
IO抽象

https://i-blog.csdnimg.cn/blog_migrate/bac01ec75f16ce6a1358e68db656f2eb.png
对于文本天生模子服务来说,现实的输入和输出本质上都是字符串,因此直接裸调用LLM服务带来的问题是要在输入格式化和输出效果剖析上做大量的重复的文本处置惩罚工作。LangChain当然考虑到这一点,提供了Prompt和OutputParser抽象,用户可以根据自己的必要选择具体的实现类型使用。
ini
复制代码
from langchain_openai import ChatOpenAI

# 调用Chat Completion API
llm = ChatOpenAI(model_name='gpt-4')
response = llm.invoke('什么是图计算?')
print(response)
ini
复制代码
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_template("{question}")

# 创建输出解析器
output_parser = StrOutputParser()

# 调用LLM
message = prompt.invoke({'question': '什么是图计算?'})
response = llm.invoke(message)
answer = output_parser.invoke(response)
print(answer)
组装成链

1.LCEL
LangChain的表达式语言(LCEL)通过重载__or__运算符的思路,构建了雷同Unix管道运算符的设计,实现更简洁的LLM调用形式。
ini
复制代码
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_template("{question}")

# 创建输出解析器
output_parser = StrOutputParser()

# 调用LLM
message = prompt.invoke({'question': '什么是图计算?'})
response = llm.invoke(message)
answer = output_parser.invoke(response)
print(answer)
ini
复制代码
# 创建Chain
chain = prompt | llm | output_parser

# 调用Chain
answer = chain.invoke({'question': '什么是图计算?'})
print(answer)
2.RunnablePassthrough
为了简化Chain的参数调用格式,也可以借助RunnablePassthrough透传上游参数输入。
ini
复制代码
from langchain_core.runnables import RunnablePassthrough

# 创建Chain
chain = {"question": RunnablePassthrough()} | prompt | llm | output_parser

# 调用Chain
answer = chain.invoke('什么是图计算?')
print(answer)
3.DAG
代码示例输出:苹果是一种营养丰富的水果,具有帮助消化、保护心脏、低沉糖尿病风险、强化免疫系统、帮助减肥、保护视力、防备哮喘、抗癌和提升记忆力等多种好处。然而,过度食用大概不适当的食用方式也可能带来一些倒霉影响,如引发过敏、导致腹泻、对牙齿造成伤害、可能携带农药残留、影响正常饮食和钙质吸收、增加蛀牙风险和引发胃痛等。因此,我们在享受苹果带来的好处的同时,也必要注意适量和正确的食用方式。
ini
复制代码
from operator import itemgetter

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建输出解析器
output_parser = StrOutputParser()

# 创建Prompt
topic_prompt = ChatPromptTemplate.from_template("生成一种'{input}'的名称")
good_prompt = ChatPromptTemplate.from_template("列举{topic}的好处:")
bad_prompt = ChatPromptTemplate.from_template("列举{topic}的坏处:")
summary_prompt = ChatPromptTemplate.from_messages(
    [
      ("ai", "{topic}"),
      ("human", "好处:\n{good}\n\n坏处:\n{bad}"),
      ("system", "生成最终结论"),
    ]
)

# 创建组合Chain
topic_chain = topic_prompt | llm | output_parser | {"topic": RunnablePassthrough()}
goods_chain = good_prompt | llm | output_parser
bads_chain = bad_prompt | llm | output_parser
summary_chain = summary_prompt | llm | output_parser
chain = (
    topic_chain
    | {
      "good": goods_chain,
      "bad": bads_chain,
      "topic": itemgetter("topic"),
    }
    | summary_chain
)

# 调用chain
answer = chain.invoke({"input": '常见水果'})
print(answer)
4.LangSmith
https://i-blog.csdnimg.cn/blog_migrate/1004aaf504eb4802d9d6f38dcf866564.png
5.LangGraph
基于LCEL确实能形貌比力复杂的LangChain计算图布局,但依然有DAG自然的设计限制,即不能支持“循环”。于是LangChain社区推出了一个新的项目——LangGraph,期望基于LangChain构建支持循环和跨多链的计算图布局,以形貌更复杂的,乃至具备自动化属性的AI工程应用逻辑,比如智能体应用。其具体使用方式可以参考LangGraph文档。
python
复制代码
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langgraph.graph import END, MessageGraph

# 初始化聊天模型
model = ChatOpenAI(temperature=0)

# 创建一个 MessageGraph
graph = MessageGraph()

# 添加一个名为 "oracle" 的节点,它执行聊天模型并返回结果
graph.add_node("oracle", model)
graph.add_edge("oracle", END)
graph.set_entry_point("oracle")

# 编译图
runnable = graph.compile()

# 运行图
result = runnable.invoke(HumanMessage("What is 1 + 1?"))
print(result)# 输出聊天模型的回答
Memory(记忆)

通过Chain,LangChain相当于以“工作流”的形式,将LLM与IO组件举行了有秩序的连接,从而具备构建复杂AI工程流程的本领。而我们都知道LLM提供的文本天生服务自己不提供记忆功能,必要用户自己管理对话历史。因此引入Memory组件,可以很好地扩展AI工程的本领边界。
https://i-blog.csdnimg.cn/blog_migrate/fcb4df9e23d16a35541e3b3eac510653.png
RAG(检索增强)

拥有记忆后,确实扩展了AI工程的应用场景。但是在专有领域,LLM无法学习到所有的专业知识细节,因此在面向专业领域知识的提问时,无法给出可靠准确的回答,乃至会“胡言乱语”,这种征象称之为LLM的“幻觉”。检索增强天生(RAG)把信息检索技能和大模子结合起来,将检索出来的文档和提示词一起提供给大模子服务,从而天生更可靠的答案,有效的缓解大模子推理的“幻觉”问题。
https://i-blog.csdnimg.cn/blog_migrate/3bb37bca654448fac48b6df0a547d28f.png
相比提示词工程,RAG有更丰富的上下文和数据样本,可以不必要用户提供过多的背景形貌,即能天生比力符适用户预期的答案。相比于模子微调,RAG可以提升问答内容的时效性和可靠性,同时在一定水平上保护了业务数据的隐私性。
但由于每次问答都涉及外部系统数据检索,因此RAG的响应时延相对较高。别的,引用的外部知识数据会斲丧大量的模子Token资源。因此,用户必要结合自身的现实应用场景做符合的技能选型。
python
复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores.faiss import FAISS
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings, ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_template('基于上下文:{context}\n回答:{input}')

# 创建输出解析器
output_parser = StrOutputParser()

# 模拟文档
docs =

# 文档嵌入
splits = RecursiveCharacterTextSplitter().split_documents(docs)
vector_store = FAISS.from_documents(splits, OpenAIEmbeddings())
retriever = vector_store.as_retriever()

# 创建Chain
chain_no_context = RunnablePassthrough() | llm | output_parser
chain = (
    {"context": retriever, "input": RunnablePassthrough()}
    | prompt | llm | output_parser
)

# 调用Chain
print(chain_no_context.invoke('蚂蚁图数据库开源了吗?'))
print(chain.invoke('蚂蚁图数据库开源了吗?'))
https://i-blog.csdnimg.cn/blog_migrate/9804e711c91c07bcab2def15d8b8281f.png
结合示例和向量数据库的存取过程,我们简单理解一下RAG中关键组件:


[*]DocumentLoader:从外部系统检索文档数据。简单起见,示例中直接构造了测试文档对象。现实上LangChain提供了文档加载器BaseLoader的接口抽象和大量实现,具体可根据自身必要选择使用。
[*]TextSplitter:将文档分割成块,以顺应大模子上下文窗口。示例中采用了常用的RecursiveCharacterTextSplitter,其他参考LangChain的TextSplitter接口和实现。
[*]EmbeddingsModel:文本嵌入模子,提供将文本编码为向量的本领。文档写入和查询匹配前都会先执行文本嵌入编码。示例采用了OpenAI的文本嵌入模子服务,其他参考LangChain的Embeddings接口和实现。
[*]VectorStore:向量存储,提供向量存储和相似性检索(ANN算法)本领。LangChain支持的向量存储参考VectorStore接口和实现。示例采用了Meta的Faiss向量数据库。
[*]Retriever:向量存储的查询器。一般和VectorStore配套实现,通过as_retriever方法获取,LangChain提供的Retriever抽象接口是BaseRetriever。
Tool(插件)

“会使用工具”是人类和动物的根本区别。
要构建更强盛的AI工程应用,只有天生文本这样的“纸上谈兵”本领自然是不够的。工具不仅仅是“肢体”的延伸,更是为“大脑”插上了想象力的“翅膀”。借助工具,才气让AI应用的本领真正具备无穷的可能,才气从“认识天下”走向“改变天下”。
这里不得不提到OpenAI的Chat Completion API提供的函数调用本领(注意这里不是Assistant的函数调用),通过在对话哀求内附加tools参数形貌工具的定义格式(原先的functions参数已逾期),LLM会根据提示词推断出必要调用哪些工具,并提供具体的调用参数信息。用户必要根据返回的工具调用信息,自行触发相干工具的回调。下一章内容我们可以看到工具的调用动作可以通过Agent自主接受。
https://i-blog.csdnimg.cn/blog_migrate/e3d7de94c253e4b3e0c3199fcfe51a68.png
python
复制代码
from openai import OpenAI
import json

client = OpenAI()

# Example dummy function hard coded to return the same weather
# In production, this could be your backend API or an external API
def get_current_weather(location, unit="fahrenheit"):
    """Get the current weather in a given location"""
    if "tokyo" in location.lower():
      return json.dumps({"location": "Tokyo", "temperature": "10", "unit": unit})
    elif "san francisco" in location.lower():
      return json.dumps({"location": "San Francisco", "temperature": "72", "unit": unit})
    elif "paris" in location.lower():
      return json.dumps({"location": "Paris", "temperature": "22", "unit": unit})
    else:
      return json.dumps({"location": location, "temperature": "unknown"})

def run_conversation():
    # Step 1: send the conversation and available functions to the model
    messages = [{"role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?"}]
    tools = [
      {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "Get the current weather in a given location",
                "parameters": {
                  "type": "object",
                  "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city and state, e.g. San Francisco, CA",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                  },
                  "required": ["location"],
                },
            },
      }
    ]
    response = client.chat.completions.create(
      model="gpt-3.5-turbo-0125",
      messages=messages,
      tools=tools,
      tool_choice="auto",# auto is default, but we'll be explicit
    )
    response_message = response.choices.message
    tool_calls = response_message.tool_calls
    # Step 2: check if the model wanted to call a function
    if tool_calls:
      # Step 3: call the function
      # Note: the JSON response may not always be valid; be sure to handle errors
      available_functions = {
            "get_current_weather": get_current_weather,
      }# only one function in this example, but you can have multiple
      messages.append(response_message)# extend conversation with assistant's reply
      # Step 4: send the info for each function call and function response to the model
      for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions
            function_args = json.loads(tool_call.function.arguments)
            function_response = function_to_call(
                location=function_args.get("location"),
                unit=function_args.get("unit"),
            )
            messages.append(
                {
                  "tool_call_id": tool_call.id,
                  "role": "tool",
                  "name": function_name,
                  "content": function_response,
                }
            )# extend conversation with function response
      second_response = client.chat.completions.create(
            model="gpt-3.5-turbo-0125",
            messages=messages,
      )# get a new response from the model where it can see the function response
      return second_response
print(run_conversation())
python
复制代码
import random

from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI


# 定义Tool
@tool
def get_temperature(city: str) -> int:
    """获取指定城市的当前气温"""
    return random.randint(-20, 50)


# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建JSON输出解析器
output_parser = JsonOutputToolsParser()

# 创建Chain
chain = (
    RunnablePassthrough()
    | llm.bind_tools(tools=)
    | output_parser
)

# 调用Chain
print(chain.invoke('杭州今天多少度?'))
代码示例输出:
css
复制代码
[{'type': 'get_temperature', 'args': {'city': '杭州'}}]
Agent(智能体)

Agent的焦点思想是使用大型语言模子(LLM)来选择要采取的行动序列。在Chain中行动序列是硬编码的,而Agent则采用语言模子作为推理引擎来确定以什么样的顺序采取什么样的行动。Agent相比Chain最典范的特点是“自治”,它可以通过借助LLM专长的推理本领,自动化地决策获取什么样的知识,采取什么样的行动,直到完成用户设定的终极目标。
https://i-blog.csdnimg.cn/blog_migrate/32864811355ab0ed862d1b0915b60310.png 因此,作为一个智能体,必要具备以下焦点本领:


[*]规划:借助于LLM强盛的推理本领,实现使命目标的规划拆解和自我反思。
[*]记忆:具备短期记忆(上下文)和恒久记忆(向量存储),以及快速的知识检索本领。
[*]行动:根据拆解的使命需求正确地调用工具以达到使命的目标。
[*]协作:通过与其他智能体交互互助,完成更复杂的使命目标。
python
复制代码
import random

from langchain.agents import create_openai_tools_agent, \
    AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
    HumanMessagePromptTemplate, SystemMessagePromptTemplate
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI()

# 定义Tool
@tool
def get_temperature(city: str) -> int:
    """获取指定城市的当前气温"""
    return random.randint(-20, 50)


# 创建Agent提示词模板
prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template('You are a helpful assistant'),
    MessagesPlaceholder(variable_name='chat_history', optional=True),
    HumanMessagePromptTemplate.from_template('{input}'),
    MessagesPlaceholder(variable_name='agent_scratchpad')
])

# 创建Agent
tools =
agent = create_openai_tools_agent(llm, tools, prompt=prompt)

# 执行Agent
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
print(agent_executor.invoke({'input': '今天杭州多少度?'})['output'])
如何学习大模子 AI ?

由于新岗位的生产服从,要优于被取代岗位的生产服从,所以现实上整个社会的生产服从是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比力晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的原理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和发展。
我意识到有很多履历和知识值得分享给各人,也可以通过我们的本领和履历解答各人在人工智能学习中的很多狐疑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋侪无法获得正确的资料得到学习提升,故此将并将紧张的AI大模子资料包罗AI大模子入门学习头脑导图、佳构AI大模子学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
页: [1]
查看完整版本: LangChain 工程架构剖析