慕雪的小助手正在绞尽脑汁···
慕雪小助手的总结
DeepSeek & LongCat

欢迎阅读慕雪撰写的AI Agent专栏,本专栏目录如下

  1. 【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?
  2. 【AI】AI对26届及今后计算机校招的影响
  3. 【Agent.01】AI Agent智能体开发专题引言
  4. 【Agent.02】市面上常见的大模型有哪些?
  5. 【Agent.03】带你学会写一个基础的Prompt
  6. 【Agent.04】AI时代的hello world:调用OpenAI接口,与大模型交互
  7. 【Agent.05】OpenAI接口Function Calling工具调用详解
  8. 【Agent.06】使用openai sdk实现多轮对话
  9. 【Agent.07】什么是Agent?从Chat到ReAct的AI进化之路
  10. 【Agent.08】LangChain的第一个Demo:从零开始构建Agent

本专栏的所有代码都可以在https://gitee.com/musnows/agent-blog找到。

在上一篇文章中,我们了解了Agent的概念和ReAct工作原理。现在,让我们正式开始学习如何使用Agent框架来构建自己的AI Agent。本文将以业界最流行的LangChain框架为例,带大家编写第一个Agent程序。

1. 什么是LangChain?

1.1. LangChain简介

LangChain是一个专门用于构建AI应用的框架,特别是Agent应用。它就像是AI应用开发的"瑞士军刀",提供了构建Agent所需的各种工具和组件。

Agent SDK和基础的OpenAI接口调用的区别,在本专栏前几篇文章中已经阐述过了,这里不再赘述。AI Agent编程中,如果没有特殊需要,都推荐使用LangChain来编写,方便后续维护和拓展能力。

1.2. 为什么选择LangChain?

在众多Agent框架中,我们选择LangChain作为入门框架,主要有以下几个原因:

  1. 生态成熟:LangChain是目前最流行的Agent框架,文档完善,社区活跃
  2. 上手简单:相比其他框架,LangChain的学习曲线相对平缓
  3. 功能丰富:提供了从基础的LLM调用到复杂的Agent编排的完整工具链
  4. 兼容性好:支持多种大模型提供商,包括OpenAI、Anthropic、Goggle等

1.3. LangChain的核心组件

在开始写代码之前,让我们先了解一下LangChain的分层。

LangChain 分为三层,从下往上依次增强功能:

  • 第一层:模型层。统一接口连接不同的 LLM。无论用 OpenAI、Claude 还是其他模型,调用方式都一样。
  • 第二层:工具和代理。给模型添加工具能力。模型可以选择调用不同的工具去完成任务(比如搜索、数据库查询等)。
  • 第三层:工作流编排。在LangChain的基础上,可用 LangGraph 把多个步骤连接成一个完整流程,串联整个工作流。比如先分类用户问题,再去搜索,最后生成回答。

这三层设计的好处是:你可以从简单场景开始,随着需求变复杂再往上加功能。直到达到预订目标。

2. 环境准备

2.1. 安装LangChain SDK

首先,我们需要安装LangChain。在开始之前,请确保你已经安装了Python 3.10+版本。

1
pip install langchain langchain-openai python-dotenv

这里我们安装了几个关键包:

  • langchain:LangChain的核心框架
  • langchain-openai:OpenAI模型的支持包(不安装的话没办法使用OpenAI API)
  • python-dotenv:环境变量管理工具(加载.env文件)

如果你使用了uv,可以直接在本专栏Agent仓库下使用uv run运行示例代码,无需关注第三方包安装。

2.2. 配置环境变量

为了安全地管理API密钥,我们使用.env文件来存储敏感信息。在项目根目录创建.env文件:

1
2
3
4
# OpenAI API配置
OPENAI_API_KEY=你的_api_key
OPENAI_BASE_URL=https://api.siliconflow.cn/v1
OPENAI_MODEL=Qwen/Qwen2.5-7B-Instruct

慕雪小贴士:如果你是在自己的仓库里面使用.env,这个.env文件一定要记得添加到.gitignore中,避免把API密钥上传到代码仓库!

3. 第一个LangChain Agent

让我们来写一个简单的天气查询Agent。这个Agent能够回答用户关于天气的问题,虽然它只是返回一个模拟的结果。

3.1. 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv(override=True)

def get_weather(city: str) -> str:
"""获取指定城市的天气信息

Args:
city: 城市名称

Returns:
天气信息字符串
"""
return f"{city}的天气总是晴朗的!今天的温度是25°C,非常适合出门。"

# 配置模型:设置 base URL 和 API key
model = init_chat_model(
model=os.getenv("OPENAI_MODEL"), # 从环境变量获取模型
model_provider="openai", # 必须设置成openai才能正常识别格式
api_key=os.getenv("OPENAI_API_KEY"), # 从环境变量获取 API Key
base_url=os.getenv("OPENAI_BASE_URL") # 从环境变量获取 Base URL
)

# 创建Agent
agent = create_agent(
model=model, # 传入配置好的模型实例
tools=[get_weather], # 传入工具列表
system_prompt="你是一个有帮助的助手,可以回答天气相关的问题。",
)

# 运行Agent
ret = agent.invoke(
{"messages": [{"role": "user", "content": "北京的天气怎么样?"}]}
)

# 打印回答
print(ret.get('messages', [])[-1].content)

3.2. 代码解析

让我们一步步来解析这段代码:

第一步:导入依赖

1
2
3
4
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
import os
from dotenv import load_dotenv

这里我们导入了LangChain的核心组件:

  • create_agent:用于创建Agent的函数
  • init_chat_model:用于初始化聊天模型
  • os:用于读取环境变量
  • load_dotenv:用于加载.env文件

第二步:配置模型

1
2
3
4
5
6
model = init_chat_model(
model=os.getenv("OPENAI_MODEL"),
model_provider="openai",
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL")
)

这里我们使用了init_chat_model来创建一个聊天模型实例。虽然我们使用的是通义千问模型,但设置model_provider="openai"是因为LangChain的OpenAI接口最通用,兼容所有OpenAI格式的API。

第三步:定义工具函数

1
2
3
4
5
6
7
8
9
10
def get_weather(city: str) -> str:
"""获取指定城市的天气信息

Args:
city: 城市名称

Returns:
天气信息字符串
"""
return f"{city}的天气总是晴朗的!今天的温度是25°C,非常适合出门。"

这是一个简单的工具函数,LangChain会自动识别函数的文档字符串和参数类型,并将其转换为可调用的工具。这里的返回值和之前Function Calling章节一样,都是写死的一个假的返回值。

注意:函数的文档字符串(就是"""包裹的函数注释,python中称作docstring)非常重要!LangChain会使用它作为Agent Function Calling的Desc工具描述,LLM会根据它来理解工具的功能和使用方法。

第四步:创建Agent

1
2
3
4
5
agent = create_agent(
model=model,
tools=[get_weather],
system_prompt="你是一个有帮助的助手,可以回答天气相关的问题。",
)

这是最关键的一步,我们将模型、工具和系统提示组合在一起,创建了一个完整的Agent。

第五步:运行Agent

1
2
3
4
5
ret = agent.invoke(
{"messages": [{"role": "user", "content": "北京的天气怎么样?"}]}
)

print(ret.get('messages', [])[-1].content)

我们使用invoke方法来运行Agent,传入用户的问题,然后从返回结果中提取AI的回答。

3.3. 运行结果

运行这个程序,你会看到类似这样的输出:

1
北京的天气总是晴朗的!今天的温度是25°C,非常适合出门。

可以看到,Agent成功地识别了用户的天气查询需求,自动调用了我们的get_weather工具,并给出了一个友好的回答。

4. LangChain的返回值结构

如下是上面代码中ret打印出来的结构,是一个dict。

这里的结构和我们使用OpenAI API时维护的messages数组基本一致,只不过LangChain帮我们在原始数据的基础上加了一层封装。

每个Dict结构的详细含义,在注释里面写清楚了,参考理解即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{
'messages': [
# 因为我们没有设置system prompt,所以第一条就是user message
# langchain会给每一个消息都上一个id,方便标识和管理
HumanMessage(content='北京的天气怎么样?',
additional_kwargs={},
response_metadata={},
id='88e2d0c7-fe27-486a-9acd-8b2b02e07b11'),
# 这是ai对我们问题的回答,其中response_metadata包裹了一些当前会话的元数据
# response_metadata是当前请求的数据,usage_metadata是当前会话累积数据
# finish_reason标明当前AI消息的类型,tool_calls代表是一个工具调用消息
AIMessage(content='',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 22,
'prompt_tokens': 216,
'total_tokens': 238,
'completion_tokens_details': None,
'prompt_tokens_details': None,
'cache_write_tokens': 0,
'cache_read_tokens': 0,
'input_tokens': 0,
'output_tokens': 0
},
'model_provider': 'openai',
'model_name': 'longcat-flash-chatai-api',
'system_fingerprint': None,
'id': 'ad72c555243646738b62d2f48a552d23',
'finish_reason': 'tool_calls',
'logprobs': None
},
id='lc_run--cd5fb602-3103-4d67-8daa-640e90f255d2-0',
# 注意这里的tool_calls的id,会使用这个标识工具调用返回值
# 这里返回的tool_calls是一个list,因为AI可以一次性返回多个工具调用参数
tool_calls=[{
'name': 'get_weather',
'args': {
'city': '北京'
},
'id': '608a87b7-042d-4b82-b9a6-e285db91b54d',
'type': 'tool_call'
}],
usage_metadata={
'input_tokens': 216,
'output_tokens': 22,
'total_tokens': 238,
'input_token_details': {},
'output_token_details': {}
}),
# 可以看到,这里的tool_call_id和上面的id是对应上的
ToolMessage(content='北京的天气总是晴朗的!今天的温度是25°C,非常适合出门。',
name='get_weather',
id='b5dfa3b9-1c35-451f-a74c-611153a17f4f',
tool_call_id='608a87b7-042d-4b82-b9a6-e285db91b54d'),
# AI获取到了工具调用结果,生成了最终的回答
# finish_reason: stop代表这条消息就是终止消息了
AIMessage(content='北京的天气总是晴朗的!今天的温度是25°C,非常适合出门。',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 17,
'prompt_tokens': 269,
'total_tokens': 286,
'completion_tokens_details': None,
'prompt_tokens_details': None,
'cache_write_tokens': 0,
'cache_read_tokens': 0,
'input_tokens': 0,
'output_tokens': 0
},
'model_provider': 'openai',
'model_name': 'longcat-flash-chatai-api',
'system_fingerprint': None,
'id': '3b6d83b7b37c4cbbb057b95bc4329b23',
'finish_reason': 'stop',
'logprobs': None
},
id='lc_run--c9fa02c7-4b3c-461f-a1a0-04e80cb9a62f-0',
usage_metadata={
'input_tokens': 269,
'output_tokens': 17,
'total_tokens': 286,
'input_token_details': {},
'output_token_details': {}
})
]
}

对于用户而言,我们在这个期间没有关注过每一个阶段的任何返回值,全部数据都由LangChain请求并处理相应了,我们只需要关注Agent返回的最终的结果是否符合预期,以及这个过程中是否会抛出异常等等……

5. The end

本文只对LangChain的Demo做了个基本的测试和说明,下一篇文章就会开始以实际场景,构建我们的测试Agent了。

对于LangChain这些Agent框架而言,刚开始学习时,我们没有必要全知全会,多借助AI的能力去快速学习上手这些Agent的SDK,尽快从初学者变成能够使用SDK基本进行Agent开发的程序员,然后再去慢慢精进相关的知识!