26. AI-Agent-LangChain

发布于:2025-09-12 ⋅ 阅读:(29) ⋅ 点赞:(0)


前言

AI Agent

AI Agent(人工智能代理)是一种能够感知环境、自主决策并执行动作的智能实体。与传统AI系统不同,Agent不仅能回答问题,还能主动完成一系列复杂任务。

简单来说,如果把大语言模型(LLM)比作一个"超级大脑",那么AI Agent就是给这个大脑装上了"手脚"和"工具",让它能够像人类一样主动行动,而不仅仅是被动回答问题。


一、LangChain快速入门与底层原理

python版本:3.12.4

简介

LangChain 是一个开源的 Python AI 应用开发框架, 它提供了构建基于大模型的 AI 应用所需的模块和工具。通过 LangChain, 开发者可以轻松地与大型语言模型 (LLM) 集成, 完成文本生成、问答、翻译、对话等任务。LangChain 降低了 AI 应用开发的门槛, 让任何人都可以基于 LLM 构建属于自己的创意应用。

LangChain 特性:

  • LLM 和提示(Prompt):LangChain 对所有 LLM 大模型进行了 API 抽象,统一了大模型访问 API,同时提供了 Prompt 提示模板管理机制。
  • 链 (Chain):Langchain 对一些常见的场景封装了一些现成的模块,例如:基于上下文信息的问答系统,自然语言生成 SQL 查询等等,因为实现这些任务的过程就像工作流一样,一步一步的执行,所以叫链 (chain)。
  • LCEL:LangChain Expression Language (LCEL), langchain 新版本的核心特性,用于解决工作流编排问题,通过 LCEL 表达式,我们可以灵活的自定义 AI 任务处理流程,也就是灵活自定义链 (Chain)
  • 数据增强生成 (RAG):因为大模型 (LLM) 不了解新的信息,无法回答新的问题,所以我们可以将新的信息导入到 LLM,用于增强 LLM 生成内容的质量,这种模式叫做 RAG 模式(Retrieval Augmented Generation)。
  • Agents:是一种基于大模型(LLM)的应用设计模式,利用 LLM 的自然语言理解和推理能力(LLM 作为大脑)),根据用户的需求自动调用外部系统、设备共同去完成任务,例如:用户输入 “明天请假一天”, 大模型(LLM)自动调用请假系统,发起一个请假申请。
  • 模型记忆(memory):让大模型 (llm) 记住之前的对话内容,这种能力成为模型记忆(memory)。

LangChain 框架组成

LangChain 框架组成

LangChain 框架由几个部分组成,包括:

  • LangChain 库:Python 和 JavaScript 库。包含接口和集成多种组件的运行时基础,以及现成的链和代理的实现。
  • LangChain 模板:Langchain 官方提供的一些 AI 任务模板。
  • LangServe:基于 FastAPI 可以将 Langchain 定义的链 (Chain),发布成为 REST API。
  • LangSmith:开发平台,是个云服务,支持 Langchain debug、任务监控。

LangChain 库 (Libraries)

LangChain 库本身由几个不同的包组成。

  • **langchain-core**:基础抽象和 LangChain 表达语言。
  • **langchain-community**:第三方集成,主要包括 langchain 集成的第三方组件。
  • **langchain**:主要包括链 (chain)、代理(agent) 和检索策略。

langchain 任务处理流程

如上图,langChain 提供一套提示词模板 (prompt template) 管理工具,负责处理提示词,然后传递给大模型处理,最后处理大模型返回的结果,

LangChain 对大模型的封装主要包括 LLM 和 Chat Model 两种类型。

  • LLM - 问答模型,模型接收一个文本输入,然后返回一个文本结果。
  • Chat Model - 对话模型,接收一组对话消息,然后返回对话消息,类似聊天消息一样。

核心概念

1. LLMs

LangChain 封装的基础模型,模型接收一个文本输入,然后返回一个文本结果。

2. Chat Models

聊天模型(或者成为对话模型),与 LLMs 不同,这些模型专为对话场景而设计。模型可以接收一组对话消息,然后返回对话消息,类似聊天消息一样。

3. 消息(Message)

指的是聊天模型(Chat Models)的消息内容,消息类型包括包括 HumanMessage、AIMessage、SystemMessage、FunctionMessage 和 ToolMessage 等多种类型的消息。

4. 提示 (prompts)

LangChain 封装了一组专门用于提示词 (prompts) 管理的工具类,方便我们格式化提示词 (prompts) 内容。

5. 输出解析器 (Output Parsers)

如上图介绍,Langchain 接受大模型 (llm) 返回的文本内容之后,可以使用专门的输出解析器对文本内容进行格式化,例如解析 json、或者将 llm 输出的内容转成 python 对象。

6. Retrievers

为方便我们将私有数据导入到大模型(LLM), 提高模型回答问题的质量,LangChain 封装了检索框架 (Retrievers),方便我们加载文档数据、切割文档数据、存储和检索文档数据。

7. 向量存储 (Vector stores)

为支持私有数据的语义相似搜索,langchain 支持多种向量数据库。

8. Agents

智能体 (Agents),通常指的是以大模型(LLM)作为决策引擎,根据用户输入的任务,自动调用外部系统、硬件设备共同完成用户的任务,是一种以大模型(LLM)为核心的应用设计模式。

应用场景

  • 对话机器人: 构建智能的对话助手、客服机器人、聊天机器人等。
  • 知识库问答: 结合知识图谱, 进行开放域问题的问答服务。
  • 智能写作: 如文章写作、创意写作、文本摘要等

快速入门

安装LangChain

要安装LangChain,可以使用Pip和Conda进行安装。以下是安装LangChain的步骤:

使用Pip:

pip install langchain

初始化模型

在使用LangChain之前,需要导入LangChain x OpenAI集成包,并设置API密钥作为环境变量或直接传递给OpenAI LLM类。

首先,获取OpenAI的API密钥,可以通过创建账户并访问此链接来获取。然后,可以将API密钥设置为环境变量,方法如下:

export OPENAI_API_KEY="YOUR_API_KEY"

接下来,初始化模型:

from langchain_openai import ChatOpenAI
llm = ChatOpenAI()

使用LLM

使用LLM来回答问题非常简单。可以直接调用LLM的invoke方法,并传入问题作为参数。此外,还可以通过提示模板(prompt template)生成提示词,用于向模型(LLM)发送指令。

下面演示了如何构建一个简单的LLM链(chains):

from langchain_core.prompts import ChatPromptTemplate

# 创建一个提示模板(prompt template)
# 这里以对话模型的消息格式为例子,不熟悉openai对话模型消息格式,建议先学习OpenAI的API教程
# 下面消息模板,定义两条消息,system消息告诉模型扮演什么角色,user消息代表用户输入的问题,这里用了一个占位符{input} 代表接受一个模版参数input。
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是世界级的技术专家"),
    ("user", "{input}")
])

# 基于LCEL 表达式构建LLM链,lcel语法类似linux的pipeline语法,从左到右按顺序执行
# 下面编排了一个简单的工作流,首先执行prompt完成提示词模板(prompt template)格式化处理, 然后将格式化后的prompt传递给llm模型执行,最终返回llm执行结果。
chain = prompt | llm

# 调用LLM链并设置模板参数input,  invoke会把调用参数传递给prompt提示模板,开始chain定义的步骤开始逐步执行。
chain.invoke({"input": "帮我写一篇关于AI的技术文章,100个字"})

输出转换

LLM的输出通常是一条消息,为了更方便处理结果,可以将消息转换为字符串。下面展示如何将LLM的输出消息转换为字符串:

from langchain_core.output_parsers import StrOutputParser

# 创建一个字符串输出解析器
output_parser = StrOutputParser()

# 将输出解析器添加到LLM链中,跟前面的例子,区别就是工作流编排,最后一步将llm模型输出的结果传递给output_parser进行格式转换
chain = prompt | llm | output_parser

# 调用LLM链并提出问题
chain.invoke({"input": "帮我写一篇langchain的技术文章,100个字"})

以上是关于LLM链的介绍,希望能帮助您更好地理解如何安装LangChain并构建不同类型的链。

二、LangChain提示词工程应用实践

Prompt templates(提示词模板)

语言模型以文本作为输入 - 这个文本通常被称为提示词(prompt)。在开发过程中,对于提示词通常不能直接硬编码,不利于提示词管理,而是通过提示词模板进行维护,类似开发过程中遇到的短信模板、邮件模板等等。

64b8936c0bccdc569ca15c504a302d55u=1578766093,829081525&fm=253&fmt=auto&app=120&f=JPEGw=1302&h=500webp

什么是提示词模板?

提示词模板本质上跟平时大家使用的邮件模板、短信模板没什么区别,就是一个字符串模板,模板可以包含一组模板参数,通过模板参数值可以替换模板对应的参数。

一个提示词模板可以包含下面内容:

  • 发给大语言模型(LLM)的指令。

  • 一组问答示例,以提醒AI以什么格式返回请求。

  • 发给语言模型的问题。

创建一个提示词模板(prompt template)

可以使用 PromptTemplate 类创建简单的提示词。

提示词模板可以内嵌任意数量的模板参数,然后通过参数值格式化模板内容。

from langchain.prompts import PromptTemplate

# 定义一个提示模板,包含adjective和content两个模板变量,模板变量使用{}包括起来
prompt_template = PromptTemplate.from_template(
    "给我讲一个关于{content}的{adjective}笑话。"

)

# 通过模板参数格式化提示模板
result = prompt_template.format(adjective="冷", content="猴子")
print(result)

模板输出结果:

给我讲一个关于猴子的冷笑话。

聊天消息提示词模板(chat prompt template)

聊天模型(Chat Model)以聊天消息列表作为输入,这个聊天消息列表的消息内容也可以通过提示词模板进行管理。这些聊天消息与原始字符串不同,因为每个消息都与“角色(role)”相关联。

例如,在OpenAI的Chat Completion API中,Openai的聊天模型,给不同的聊天消息定义了三种角色类型分别是助手(assistant)、人类(human)或系统(system)角色:

  • 助手(Assistant) 消息指的是当前消息是AI回答的内容

  • 人类(user)消息指的是你发给AI的内容

  • 系统(system)消息通常是用来给AI身份进行描述。

创建聊天消息模板例子

from langchain_core.prompts import ChatPromptTemplate

# 通过一个消息数组创建聊天消息模板
# 数组每一个元素代表一条消息,每个消息元组,第一个元素代表消息角色(也成为消息类型),第二个元素代表消息内容。
# 消息角色:system代表系统消息、human代表人类消息,ai代表LLM返回的消息内容
# 下面消息定义了2个模板参数name和user_input
chat_template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一位人工智能助手,你的名字是{name}。"),
        ("human", "你好"),
        ("ai", "我很好,谢谢!"),
        ("human", "{user_input}"),
    ]
)

# 通过模板参数格式化模板内容
messages = chat_template.format_messages(name="Bob", user_input="你的名字叫什么?")
print(messages)

另外一种消息格式例子:

from langchain.prompts import HumanMessagePromptTemplate
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate

# 使用langchain定义的SystemMessage、HumanMessagePromptTemplate等工具类定义消息,跟前面的例子类似,下面定义了两条消息
chat_template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=(
                "你是一个乐于助人的助手,可以润色内容,使其看起来起来更简单易读。"
            )
        ),
        HumanMessagePromptTemplate.from_template("{text}"),
    ]
)

# 使用模板参数格式化模板
messages = chat_template.format_messages(text="我不喜欢吃好吃的东西")
print(messages)

通常我们不会直接使用format_messages函数格式化提示模板(prompt templae)内容, 而是交给Langchain框架自动处理。

MessagesPlaceholder

这个提示模板负责在特定位置添加消息列表。 在上面的 ChatPromptTemplate 中,我们看到了如何格式化两条消息,每条消息都是一个字符串。 但是,如果我们希望用户传入一个消息列表,我们将其插入到特定位置,该怎么办? 这就是您使用 MessagesPlaceholder 的方式。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage
prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    MessagesPlaceholder("msgs")
])
prompt_template.invoke({"msgs": [HumanMessage(content="hi!")]})

这将生成两条消息,第一条是系统消息,第二条是我们传入的 HumanMessage。 如果我们传入了 5 条消息,那么总共会生成 6 条消息(系统消息加上传入的 5 条消息)。 这对于将一系列消息插入到特定位置非常有用。 另一种实现相同效果的替代方法是,不直接使用 MessagesPlaceholder 类,而是:

prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    ("placeholder", "{msgs}") # <-- 这是更改的部分
])

提示词追加示例(Few-shot prompt templates)

提示词中包含交互样本的作用是为了帮助模型更好地理解用户的意图,从而更好地回答问题或执行任务。小样本提示模板是指使用一组少量的示例来指导模型处理新的输入。这些示例可以用来训练模型,以便模型可以更好地理解和回答类似的问题。

例子:

Q: 什么是蝙蝠侠?
A: 蝙蝠侠是一个虚构的漫画人物。

Q: 什么是torsalplexity?
A: 未知。

Q: 什么是语言模型?
A:

告诉模型根据,Q是问题,A是答案,按这种格式进行问答交互。

下面讲解的就是Lanchain针对在提示词中插入少量交互样本提供的工具类。

使用示例集

创建示例集

下面定义一个examples示例数组,里面包含一组问答样例。

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate


examples = [
  {
    "question": "谁的寿命更长,穆罕默德·阿里还是艾伦·图灵?",
    "answer":
"""
这里需要跟进问题吗:是的。
跟进:穆罕默德·阿里去世时多大?
中间答案:穆罕默德·阿里去世时74岁。
跟进:艾伦·图灵去世时多大?
中间答案:艾伦·图灵去世时41岁。
所以最终答案是:穆罕默德·阿里
"""
  },
  {
    "question": "craigslist的创始人是什么时候出生的?",
    "answer":
"""
这里需要跟进问题吗:是的。
跟进:craigslist的创始人是谁?
中间答案:craigslist由Craig Newmark创立。
跟进:Craig Newmark是什么时候出生的?
中间答案:Craig Newmark于1952年12月6日出生。
所以最终答案是:1952年12月6日
"""
  },
  {
    "question": "乔治·华盛顿的祖父母中的母亲是谁?",
    "answer":
"""
这里需要跟进问题吗:是的。
跟进:乔治·华盛顿的母亲是谁?
中间答案:乔治·华盛顿的母亲是Mary Ball Washington。
跟进:Mary Ball Washington的父亲是谁?
中间答案:Mary Ball Washington的父亲是Joseph Ball。
所以最终答案是:Joseph Ball
"""
  },
  {
    "question": "《大白鲨》和《皇家赌场》的导演都来自同一个国家吗?",
    "answer":
"""
这里需要跟进问题吗:是的。
跟进:《大白鲨》的导演是谁?
中间答案:《大白鲨》的导演是Steven Spielberg。
跟进:Steven Spielberg来自哪里?
中间答案:美国。
跟进:《皇家赌场》的导演是谁?
中间答案:《皇家赌场》的导演是Martin Campbell。
跟进:Martin Campbell来自哪里?
中间答案:新西兰。
所以最终答案是:不是
"""
  }
]
创建小样本示例的格式化程序

通过PromptTemplate对象,简单的在提示词模板中插入样例。

example_prompt = PromptTemplate(input_variables=["question", "answer"], template="问题:{question}\\n{answer}")

# 提取examples示例集合的一个示例的内容,用于格式化模板内容
print(example_prompt.format(**examples[0]))

返回:

问题:谁的寿命更长,穆罕默德·阿里还是艾伦·图灵?

这里需要跟进问题吗:是的。
跟进:穆罕默德·阿里去世时多大?
中间答案:穆罕默德·阿里去世时74岁。
跟进:艾伦·图灵去世时多大?
中间答案:艾伦·图灵去世时41岁。
所以最终答案是:穆罕默德·阿里
将示例和格式化程序提供给FewShotPromptTemplate

通过FewShotPromptTemplate对象,批量插入示例内容。

# 接收examples示例数组参数,通过example_prompt提示词模板批量渲染示例内容
# suffix和input_variables参数用于在提示词模板最后追加内容, input_variables用于定义suffix中包含的模板参数
prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="问题:{input}",
    input_variables=["input"]
)

print(prompt.format(input="乔治·华盛顿的父亲是谁?"))

返回:

问题:谁的寿命更长,穆罕默德·阿里还是艾伦·图灵?

这里需要跟进问题吗:是的。
跟进:穆罕默德·阿里去世时多大?
中间答案:穆罕默德·阿里去世时74岁。
跟进:艾伦·图灵去世时多大?
中间答案:艾伦·图灵去世时41岁。
所以最终答案是:穆罕默德·阿里

问题:craigslist的创始人是什么时候出生的?

这里需要跟进问题吗:是的。
跟进:craigslist的创始人是谁?
中间答案:craigslist由Craig Newmark创立。
跟进:Craig Newmark是什么时候出生的?
中间答案:Craig Newmark于1952年12月6日出生。
所以最终答案是:1952年12月6日

问题:乔治·华盛顿的祖父母中的母亲是谁?

这里需要跟进问题吗:是的。
跟进:乔治·华盛顿的母亲是谁?
中间答案:乔治·华盛顿的母亲是Mary Ball Washington。
跟进:Mary Ball Washington的父亲是谁?
中间答案:Mary Ball Washington的父亲是Joseph Ball。
所以最终答案是:Joseph Ball

问题:《大白鲨》和《皇家赌场》的导演都来自同一个国家吗?

这里需要跟进问题吗:是的。
跟进:《大白鲨》的导演是谁?
中间答案:《大白鲨》的导演是Steven Spielberg。
跟进:Steven Spielberg来自哪里?
中间答案:美国。
跟进:《皇家赌场》的导演是谁?
中间答案:《皇家赌场》的导演是Martin Campbell。
跟进:Martin Campbell来自哪里?
中间答案:新西兰。
所以最终答案是:不是

问题:乔治·华盛顿的父亲是谁?

使用示例选择器

将示例提供给ExampleSelector

这里重用前一部分中的示例集和提示词模板(prompt template)。但是,不会将示例直接提供给FewShotPromptTemplate对象,把全部示例插入到提示词中,而是将它们提供给一个ExampleSelector对象,插入部分示例。

这里我们使用SemanticSimilarityExampleSelector类。该类根据与输入的相似性选择小样本示例。它使用嵌入模型计算输入和小样本示例之间的相似性,然后使用向量数据库执行相似搜索,获取跟输入相似的示例。

  • 提示:这里涉及向量计算、向量数据库,在AI领域这两个主要用于数据相似度搜索,例如:查询相似文章内容、相似的图片、视频等等,这里先简单了解下就行。
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 这是可供选择的示例列表。
    examples,
    # 这是用于生成嵌入的嵌入类,该嵌入用于衡量语义相似性。
    OpenAIEmbeddings(),
    # 这是用于存储嵌入和执行相似性搜索的VectorStore类。
    Chroma,
    # 这是要生成的示例数。
    k=1
)

# 选择与输入最相似的示例。
question = "乔治·华盛顿的父亲是谁?"
selected_examples = example_selector.select_examples({"question": question})
print(f"最相似的示例:{question}")
for example in selected_examples:
    print("\\n")
    for k, v in example.items():
        print(f"{k}:{v}")

这里匹配了跟问题相似的例子,下面是返回:

使用本地API直接运行Chroma。
    使用DuckDB内存中的数据库。数据将是短暂的。
    最相似的示例:乔治·华盛顿的父亲是谁?

    question:乔治·华盛顿的祖父母中的母亲是谁?
    answer:
    这里需要跟进问题吗:是的。
    跟进:乔治·华盛顿的母亲是谁?
    中间答案:乔治·华盛顿的母亲是Mary Ball Washington。
    跟进:Mary Ball Washington的父亲是谁?
    中间答案:Mary Ball Washington的父亲是Joseph Ball。
    所以最终答案是:Joseph Ball
将示例选择器提供给FewShotPromptTemplate

最后,创建一个FewShotPromptTemplate对象。根据前面的example_selector示例选择器,选择一个跟问题相似的例子。

prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    suffix="问题:{input}",
    input_variables=["input"]
)

print(prompt.format(input="乔治·华盛顿的父亲是谁?"))

返回:

问题:乔治·华盛顿的祖父母中的母亲是谁?

这里需要跟进问题吗:是的。
跟进:乔治·华盛顿的母亲是谁?
中间答案:乔治·华盛顿的母亲是Mary Ball Washington。
跟进:Mary Ball Washington的父亲是谁?
中间答案:Mary Ball Washington的父亲是Joseph Ball。
所以最终答案是:Joseph Ball

问题:乔治·华盛顿的父亲是谁?

本文的引用仅限自我学习如有侵权,请联系作者删除。
参考知识

LangChain快速入门与底层原理



网站公告

今日签到

点亮在社区的每一天
去签到