智能体开发快速入门

第1节:智能体概述

1. 什么是智能体

关于智能体的定义,网上的说法总是云里雾里,我这里总结一个,对新手比较友好的解释:

智能体是可以自主规划任务和调用工具的应用程序

这里有两部分需要了解

自主规划任务

自主规划任务指的是:用户提出了一个要求,具体需要有那几步来完成任务,每一步具体做什么,都由智能体来规划。

调用工具

在执行任务的过程中,如果涉及到调用工具,智能体可以基于大模型的分析,自主调用工具,以便完成任务。

什么是自主规划任务和调用工具呢,举个例子:

例如我和智能体说:

下载昨天的新闻

智能体的执行步骤是:

  1. 调用新闻接口工具,获取昨天的新闻。
  2. 调用文件系统工具,将获取到的新闻保存到本地。

这就是智能体的功能,理解了些,就知道了智能体的基本概念。我们的这个文档目的,也是为了让大家可以独立开发这样的智能体。

2. 为什么需要智能体

有朋友看到这可能会想问,AI大模型不能调用工具和规划任务吗,为什么需要智能体来做这些呢。讨论这个问题,我们先从下面三个说说大模型的能力限制。

  1. 大模型没有记忆,只是一个问答机器人。
  2. 大模型反馈的内容,都是以文本形式呈现。
  3. 大模型不能感知外部环境,无法获取实时信息。

基于大模型能力的局限,我们就需要在大模型外部包裹一层应用程序,来扩充大模型的能力,这一层应用程序置于大模型与用户之间,作为一个代理(agent),成为了用户与大模型之间的桥梁,这就是智能体的作用。

3. 智能体开发工具

智能体开发工具分为低代码工具和代码框架,主流的低代码工具包括coze、dify、n8n等。代码框架包括LangChain,LangGraph,LlamaIndex等,我们本节教程主要使用LangChain框架进行智能体开发。

4. LangChain概述

LangChain可以让基于大模型的应用开发变得更简单。

文档地址open in new window

langchain提供了哪些功能

  1. 接入大模型的快捷方法。
  2. 封装好的提示词面板
  3. 记忆功能
  4. 方便的工具调用方法
  5. 可以实现RAG的文档检索功能

了解了上面这五点,就可以开发自己的智能体了。

第2节:创建项目

1. 创建虚拟环境

开发智能体项目之前,为了防止项目依赖于电脑全局环境冲突,我们先创建一个虚拟环境。

创建虚拟环境

python -m venv .venv

激活虚拟环境

 .venv\Scripts\activate

2. 安装依赖

激活虚拟环境后,下载langChain框架,以及必要的开发工具。

pip install langchain langchain-openai python-dotenv
  • langchain:智能体开发框架
  • langchain-openai:用来接入大模型
  • python-dotenv:用.env文件存放大模型秘钥。

如果下载速度较慢,可以讲pip的下载地址更改为国内的镜像地址,修改方法我已经放在《常用工具使用手册》中,有需要的小伙伴可以到【晓舟的AI交流66群】获取文档资料。

第3节:接入大模型

要想在langchain中接入AI大模型,我们需要三个参数:

  • API key
  • 大模型名称
  • 提供服务的地址

这里我们以DeepSeek为例, 先 访问DeepSeek 大模型官网open in new window 注册账号,然后在控制台创建一个API key,接着在DeepSeek文档中找到DeepSeek大模型的名称,以及提供服务的地址。

有了这三个参数,为了防止账号API key被盗用,我们还要创建一个.env文件来存放API key。

然后使用dotenv模块引入API key

示例代码如下

from dotenv import load_dotenv
import os
load_dotenv()
print(os.getenv("DEEPSEEK_MODEL"))

配置好API key,就可以接入大模型了,示例代码如下:

from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os
load_dotenv()
llm = ChatOpenAI(
    model=os.getenv("DEEPSEEK_MODEL"),
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url=os.getenv("DEEPSEEK_API_BASE")
)
prompt = "你是谁"
response = llm.invoke(prompt);
print(response.content)

如果自己编写的项目需要推送到Git仓库,要将.env 文件添加到你的 .gitignore 文件中,防止意外将 API Key 推送到线上。.gitignore文件如下

.venv/
.env

关于Git的使用方法,我已经写在《常用工具使用手册》中,有需要的小伙伴可以到【晓舟的AI交流66群】获取。

到这里,运行程序,如果一切顺利,我们就可以看到DeepSeek大模型的回复了。

第4章:链与提示词

1. 链

上一节我们接入大模型之后,是通过下面的方式给AI输入提示词的

prompt = "你是谁"
response = llm.invoke(prompt);
print(response.content)

在实际开发中,为了让提示词更加灵活,我们会把提示词写在一个提示词模板组件中,示例代码如下

# 引入提示词模板
from langchain.prompts import ChatPromptTemplate
# 创建一个提示词模板组件
prompt = ChatPromptTemplate("你是谁?")

关于提示词模板,我们一会儿细说,这里先讲“链”的概念。

langchian中的“链”,示例代码如下:

chain = prompt | llm
response = chain.invoke({})
print(response.content)

代码中的chain = prompt | model是就是“链”。

在 LangChain 中,链可以将多个组件组合起来,按特定顺序执行,以完成更复杂任务的逻辑单元。这是LangChain的表达式语言 ,简称LCEL

LCEL 的核心是 | (管道) 操作符,类似于 Unix/Linux 中的管道命令。它允许我们将一个组件的返回值向下传递给下一个组件作为输入。

基本语法:

chain = component_A | component_B | component_C
chain.invoke({"parm":"value"})

这表示:

  1. {"parm":"value"}传递给 component_A
  2. component_A 的输出作为输入传递给 component_B
  3. component_B 的输出作为输入传递给 component_C
  4. chain在调用invoke方法后,返回值就是 component_C 的输出。 这就是langchain中“链”的使用方法。

因此示例代码chain = prompt | model的作用是当执行chain.invoke()的时候,把一个空字典传递给提示词模板,然后再传递给大模型,最后获取大模型的返回值。这样,我们就成功拿到了达摩习惯你的返回结果。

2. 提示词模板

了解了链的概念之后,我们接着看提示词模板,可以在ChatPromptTemplate方法中进一步划分系统提示词和用户提示词。系统提示词用来设置提问的背景,用户提示词用来描述具体问题。示例代码如下:

prompt = ChatPromptTemplate([
    ("system", "你是一个擅长心理学的学习助手,帮我用心理学的理论知识解决我学习过程中的困惑"),
    ("user", "我为什么学习不好?")
])

chain = prompt | llm
response = chain.invoke({})
print(response.content)

可以看到,大模型按照要求给我们回复了内容。

我们还可以通过langchain提示词模板提供的语法来传递参数,示例代码如下:

prompt = ChatPromptTemplate([
    ("system", "你是一个擅长心理学的学习助手,帮我用心理学的理论知识解决我学习过程中的困惑"),
    ("user", "{input}")
])

chain = prompt | llm
response = chain.invoke({"input": "我为什么学习不好"})
print(response.content)

这样用户就可以向大模型输入自定义的问题了。

这里ChatPromptTemplate的参数是一个又元组组成的列表元组只能存放两个参数,第一个是角色,第二个是提示词,如果多余两个参数langchain会报错。

第5章:解析器

在开发智能体的过程中,我们可能希望通过大模型,得到指定格式的数据,例如字符串,布尔, JSON等。解析器 (Output Parsers) 的作用就是将输出结果转化为我们期望的格式。

1. 解析为字符串

在之前的代码,我们使用response.content来获取大模型的返回。

prompt = "你是谁"
response = llm.invoke(prompt);
print(response.content)

为了更方便地获取返回字符串,我们可以用一个解析器将返回的response直接解析成字符串,核心代码如下:

from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate([
    ("system", "你是一个擅长心理学的学习助手,帮我用心理学的理论知识解决我学习过程中的困惑"),
    ("user", "{input}")
])

chain = prompt | llm | StrOutputParser();
response = chain.invoke({"input": "我为什么学习不好"})
print(response)

流式输出内容

我们拿到的字符串也可以通过流式输出的形式展现给用户,就像我们使用AI聊天机器人一样。输出内容的示例代码如下:

for chunk in chain.stream({}):
	print(chunk,end="",flush=True)

end=""参数的作用是:默认情况下,print()会在结尾添加换行符\n,设置end=""后,不会添加换行符,使下一个 chunk 继续在同一行输出。 flush=True参数的作用是:默认情况下,输出会有缓冲区,内容不会立即显示,设置flush=True强制立即将内容输出到终端。

2. 解析为布尔值

BooleanOutputParser解析器可以讲结果解析为布尔值,但是需要注意的是,我们需要在提示词里要求大模型返回的结果是“yes”或“no”,否则程序运行会出现异常。

from langchain.output_parsers.boolean import BooleanOutputParser
prompt = ChatPromptTemplate([	
    ("system", "你来判断这个人是否适合结婚,请用yes或no回答"),
    ("user", "{input}")
])

parser = BooleanOutputParser();
chain = prompt | llm | parser
response = chain.invoke(
    #{"input": "脾气差,人品差,不顾家,收入低"}
    {"input": "脾气好,人品好,热爱生活,收入高"}
)
print(response)

3. 解析为json格式

下面的代码可以将返回的结果解析为json格式。

from langchain_core.output_parsers.json import JsonOutputParser

prompt = ChatPromptTemplate([
    ("system", "你是一个格式转换助手,将我输入的内容转换为JSON格式。"),
    ("human", "请将以下内容转换为JSON格式:{input}")
])

parser = JsonOutputParser();
chain = prompt | llm | parser
response = chain.invoke(
    {"input": "我是晓舟,是一名程序员"}
)
print(response)

输出结果:

{'name': '晓舟', 'profession': '程序员'}

第6章:工具调用

文档最开始说过,智能体是可以自主规划任务和调用工具的应用程序,那么智能体到底要如何调用工具呢,我们分别讲解自定义工具和内置工具的使用方法。

1. 自定义工具

使用@tool装饰器,可以将一个函数包装成一个langchain的工具,工具需要编写文档字符串,来说明工具所需的参数以及返回值,这部分文档字符串是用来让大模型识别工具用的,所以一定要编写清楚,否则大模型不能成功调用工具。

下面的代码我们定义了两个工具,一个用来计算加法,一个用来计算减法。

from langchain_core.tools import tool

@tool("AddNumbers")  # 工具名称
def add_numbers(a: float, b: float) -> float:
    """计算两个数字的和。输入应为两个数字(如:{'a': 3, 'b': 5})"""
    return a + b

@tool("SubtractNumbers")
def subtract_numbers(a: float, b: float) -> float:
    """计算两个数字的差(a减b)。输入应为两个数字(如:{'a': 10, 'b': 3})"""
    return a - b

result = add_numbers.invoke({"a": 5, "b": 4})
result = subtract_numbers.invoke({"a": 5, "b": 4})
print(result)

虽然工具被成功调用,但是大家发现,这个工具的调用和普通函数的调用没区别,那么,如何让智能体按照用户的要求,自行调用工具呢。

这就需要使用langchain的agent组件,创建和调用使用agent组件的方式入下面代码所示:

from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts.chat import MessagesPlaceholder
@tool("AddNumbers")  # 工具名称
def add_numbers(a: float, b: float) -> float:
    """计算两个数字的和。输入应为两个数字(如:{'a': 3, 'b': 5})"""
    return a + b

@tool("SubtractNumbers")
def subtract_numbers(a: float, b: float) -> float:
    """计算两个数字的差(a减b)。输入应为两个数字(如:{'a': 10, 'b': 3})"""
    return a - b

tools = [add_numbers, subtract_numbers]

prompt = ChatPromptTemplate([··
    ("system", "你是一个能使用工具的助手。根据问题判断是否需要调用工具,如需调用,严格按照工具要求的格式传递参数。"),
    ("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])

agent = create_tool_calling_agent(
    llm=llm,
    tools=tools,
    prompt=prompt
)

agent_executor = AgentExecutor.from_agent_and_tools(
    agent=agent,
    tools=tools,
    verbose=True  
)

result = agent_executor.invoke({"input": "计算10加上20的结果"})
print(result)

上面的代码需要注意一下几点:

  1. 工具需要存放在一个tools列表中,方便后续传递给agent
  2. 提示词需要传入一个agent_scratchpad,它的作用是在 agent 运行过程中,记录自己的思考步骤、中间结果和工具调用过程。
  3. create_tool_calling_agent的作用是初始化一个agent
  4. AgentExecutoragent执行器,它负责指挥agent调用工具并解决问题,verbose=True参数的作用是“在终端显示调用过程”,方便调试。

可以看到这个例子中,如果我们说的是计算加法,agent就会自动调用加法工具,如果我们说的是计算减法,agent就会自动调用减法工具。这实现了智能体的基本功能。

2. langchain内置工具

在开发智能体的过程中,大部分工具是不用我们手动编写的,LangChain 提供了许多可以直接使用的内置工具,通常在 langchain_community.tools中。使用工具需要先安装依赖:

pip install langchain-community

这里是langchain的工具文档open in new window,如果有工具使用需求,可以参考文档内容自行调用。

下面以一个“文件系统”工具为例写一个案例,让文件系统工具来操作本地文件

from langchain_community.agent_toolkits import FileManagementToolkit

tookKit = FileManagementToolkit(
    root_dir = "D:/test"
)

tools = tookKit.get_tools();

prompt = ChatPromptTemplate([
    ("system", "你是一个智能体,帮我调用工具。"),
    ("user", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad")
])
  
response = agent_executor.invoke({
    "input":"请创建一个名为'notes.txt'的文件,并在其中写入'这是我的笔记:今天天气晴朗,适合散步。'"
})
print(response)

FileManagementToolkit我们定义了D:/test为智能体可以操作的文件目录,这里斜杠的方向别写错了tookKit.get_tools()方法返回一个工具列表。

定义文件和获取工具列表之后,其他的操作就和上面的案例相同了,只要我们对智能体说出文件操作的要求,智能体就会通过FileManagementToolkit工具操作本地文件。

总结

本节内容我们通过langchain的核心组件和功能,讲解了智能体开发的快速入门流程。后续晓舟还会持续更新智能体开发文档和案例。

大家可以在抖音【晓舟报告】首页加入【晓舟的AI交流66群】,一起交流技术问题,并获取更多学习资料。

最后祝大家早日富可敌国,【晓舟的直播时间是】工作日中午12:00,抖音直播间见啦,拜拜~