1. LLM 的使用方式
从之前的代码可以看出,LLM 的典型使用方法是:
python
llm.invoke(question) # 一次性调用,一问一答
llm.stream(question) # 流式输出,一问一答
你问 LLM 一句,然后它回答一句。这揭示了一个本质:LLM 是一个被动响应的文字推理引擎。它的能力范围仅限于理解输入文本(推理、总结、翻译)和生成输出文本。
2. LLM:一个只动口不动手的推理引擎
2.1 LLM 就是一个推理引擎

python
from langchain_community.chat_models import ChatTongyi
# 1.创建模型客户端
llm = ChatTongyi(model="qwen3-max")
# 2.调用模型
user_input = "明天北京的天气怎么样?"
messages = [
("system", "你是一个友好的天气查询助手"),
("human", f"{user_input}")
]
result = llm.invoke(messages)
print(type(result)) # <class 'langchain_core.messages.ai.AIMessage'>
print(result.content) # 你好!要获取明天北京的天气情况,我建议你查看权威天气预报平台
现实中的许多问题,仅靠"输出文字"是无法解决的。例如:
-
"帮我查一下明天的天气并写进备忘录"
-
"查询订单ID为6742835的商品是什么?"
对于这些实时查询数据都需要去查询对应的接口,比如天气查询接口,后台订单ID查询接口。但是 LLM 无法帮助我们去查询接口,无法帮我们做事情,只能说不能做,这就有点无语了。

2.2 给 LLM 提供一些外部的工具
大模型目前只能生成文本回复,无法直接执行具体操作。为此,我们可以为其提供一系列可调用的工具(工具可理解为函数),交由大模型根据任务需求自主调用。这一方案是可行的。
那如何给 LLM 提供工具呢?LangChain:模型的工具调用:查看官方文档
2.2.1 给 LLM 提供工具
python
# 1.创建模型客户端
llm = ChatTongyi(model="qwen3-max")
# 2.给 LLM 添加工具
@tool
def get_weather(location: str) -> str:
"""
获取某个位置的天气信息
:param location: 地理位置
:return: 天气情况
"""
return f"{location}的天气是100摄氏度,请你准备防晒。"
# 2.2.将工具绑定到 LLM
tools = [get_weather]
model_with_tools = llm.bind_tools(tools=tools)
如何给工具提供描述信息:
规则:工具必须提供描述信息,否则会报错:ValueError: Function must have a docstring if description not provided.
原因:LLM(Agent 的大脑)需要依赖工具描述来判断:
- 该工具是做什么的
- 什么情况下应该调用它
如果没有描述,LLM 无法根据用户意图选择正确的工具。
结论:为每个工具编写清晰的描述(docstring 或显式 description),是 Agent 能够正确调用工具的前提。
但是要记住:我们之前说过,大模型本身是没有任何记忆的。所以模型绑定工具后,每次调用都必须携带工具信息。即使某次不需要调用工具,模型也需要知道工具有哪些(它可以选择不用)。

2.2.2 携带工具的LLM去调用大模型
python
# 3.调用模型
user_input = "明天北京的天气怎么样?"
messages = [
("system", "你是一个友好的天气查询助手"),
("human", f"{user_input}")
]
result = model_with_tools.invoke(messages)
print(type(result)) # <class 'langchain_core.messages.ai.AIMessage'>
print(result)
"""
{
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"function": {
"arguments": "{\"location\": \"北京\"}",
"name": "get_weather"
},
"id": "call_4cca4f03f0f149878644f727",
"index": 0,
"type": "function"
}
]
},
"response_metadata": {
"model_name": "qwen3-max",
"finish_reason": "tool_calls",
"request_id": "82419405-acf9-9a5d-b281-35f55537cc25",
"token_usage": {
"input_tokens": 282,
"output_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
},
"total_tokens": 303
}
},
"id": "lc_run--019f0c50-62e0-7351-9913-40c15f074e8c-0",
"tool_calls": [
{
"name": "get_weather",
"args": {
"location": "北京"
},
"id": "call_4cca4f03f0f149878644f727",
"type": "tool_call"
}
],
"invalid_tool_calls": []
}
"""
模型的内容没有直接返回(content属性为空),是因为模型部署在远端,而工具代码在本地执行,两者处于不同环境。因此,模型无法直接运行本地函数,而是返回一个指示,告知用户需要手动执行该函数,并将执行结果反馈给模型。

2.2.3 手动执行函数
python
模型返回结果 result
│
▼
┌───────────────────┐
│ result.tool_calls │
│ 是否存在? │
└────────┬──────────┘
│
┌────┴────┐
▼ ▼
有 无
│ │
▼ ▼
追加 AI 直接输出
消息到 result.content
历史
│
▼
遍历每个 tool_call
│
▼
根据名称查找工具
│
┌───┴───┐
▼ ▼
找到 未找到
│ │
▼ ▼
执行工具 记录错误
│ │
▼ ▼
将结果封装为 ToolMessage
│
▼
追加到消息历史 messages
│
▼
(返回模型继续生成最终回复)
代码实现逻辑如下:
python
# 4. 根据 tool_calls 调用工具
tool_map = {tool.name: tool for tool in tools}
if result.tool_calls:
# 关键步骤:将 AI 的响应添加到消息历史中
messages.append(result)
for tool_call in result.tool_calls:
tool_name = tool_call.get("name")
tool_args = tool_call.get("args", {})
tool_id = tool_call.get("id")
tool_func = tool_map.get(tool_name)
if tool_func:
try:
tool_result = tool_func.invoke(tool_args)
print(f"调用工具 '{tool_name}' 成功,结果: {tool_result}")
# 创建 ToolMessage 并将结果添加到消息历史
tool_message = ToolMessage(
content=tool_result,
tool_call_id=tool_id
)
messages.append(tool_message)
except Exception as e:
print(f"调用工具 '{tool_name}' 失败: {e}")
tool_message = ToolMessage(
content=f"工具调用失败: {str(e)}",
tool_call_id=tool_id
)
messages.append(tool_message)
else:
print(f"未找到工具: {tool_name}")
tool_message = ToolMessage(
content=f"未找到工具: {tool_name}",
tool_call_id=tool_id
)
messages.append(tool_message)
else:
print("没有需要调用的工具")
print(result.content)
2.2.4 打印消息列表
python
# 5. 再次调用模型,获取最终回答
print("\n" + "=" * 50)
print("消息历史:")
for msg in messages:
print(f"{type(msg).__name__}: {msg}")
print("=" * 50 + "\n")
输出结果如下:
python
==================================================
消息历史:
tuple: ('system', '你是一个友好的天气查询助手')
tuple: ('human', '明天北京的天气怎么样?')
AIMessage: content='' additional_kwargs={'tool_calls': [{'function': {'arguments': '{"location": "北京"}', 'name': 'get_weather'}, 'id': 'call_05debaf69eb542bd8db0d9a8', 'index': 0, 'type': 'function'}]} response_metadata={'model_name': 'qwen3-max', 'finish_reason': 'tool_calls', 'request_id': '95c6dab9-0834-981f-a244-cdec4b4fe02c', 'token_usage': {'input_tokens': 282, 'output_tokens': 21, 'prompt_tokens_details': {'cached_tokens': 0}, 'total_tokens': 303}} id='lc_run--019f0c66-b7f1-7522-a43a-9391befb7bb2-0' tool_calls=[{'name': 'get_weather', 'args': {'location': '北京'}, 'id': 'call_05debaf69eb542bd8db0d9a8', 'type': 'tool_call'}] invalid_tool_calls=[]
ToolMessage: content='北京的天气是100摄氏度,请你准备防晒。' tool_call_id='call_05debaf69eb542bd8db0d9a8'
==================================================
2.2.5 再次调用模型
python
result = model_with_tools.invoke(messages)
print(type(result))
print(result)
print(f"\n最终回答: {result.content if result.content else '无内容'}")
2.3 并行工具调用
python
model_with_tools = model.bind_tools([get_weather])
response = model_with_tools.invoke(
"波士顿和东京的天气怎么样?"
)
# 模型可能会生成多个工具调用
print(response.tool_calls)
"""
{
"tool_calls": [
{
"name": "get_weather",
"args": {
"location": "波士顿"
},
"id": "call_6063aff5b3ab4b2486e27ad4",
"type": "tool_call"
},
{
"name": "get_weather",
"args": {
"location": "东京"
},
"id": "call_13ffe4ac5a3f41fe807f2b69",
"type": "tool_call"
}
]
}
"""
2.4 给 LLM 提供多个工具
python
# 1. 创建模型客户端
llm = ChatTongyi(model="qwen3-max")
# 2. 给 LLM 添加工具
@tool
def get_weather(location: str) -> str:
"""
获取某个位置的天气信息
:param location: 地理位置
:return: 天气情况
"""
return f"{location}的天气是100摄氏度,请你准备防晒。"
@tool
def get_recommendations(city: str) -> str:
"""
获取某个城市的旅游景点推荐
:param city: 城市名称
:return: 推荐景点列表
"""
recommendations = {
"北京": "故宫、天安门、长城、颐和园、天坛、鸟巢",
"上海": "外滩、东方明珠、迪士尼乐园、豫园",
"杭州": "西湖、灵隐寺、宋城、雷峰塔",
"成都": "锦里、宽窄巷子、大熊猫基地、都江堰",
"西安": "兵马俑、大雁塔、古城墙、华清池"
}
return recommendations.get(city, f"抱歉,暂无{city}的景点推荐信息。")
# 3. 绑定工具到 LLM
tools = [get_weather, get_recommendations]
model_with_tools = llm.bind_tools(tools=tools)
# 4. 调用模型
user_input_1 = "北京明天天气怎么样?并且北京有什么好玩的地方?"
# user_input = "北京明天天气怎么样?"
messages = [
("system", "你是一个友好的旅游和天气查询助手,可以同时查询天气和景点推荐。"),
("human", f"{user_input}")
]
result = model_with_tools.invoke(messages)
print(type(result)) # <class 'langchain_core.messages.ai.AIMessage'>
print(result)
3. Function Calling
Function Calling(函数调用) 是指大模型在生成回复时,不直接输出文本,而是输出一个结构化的调用指令,指示系统去执行某个特定的函数,并将执行结果作为后续回复的依据。
简单来说,Function Calling 让大模型从"只能说话"变成了"可以提出执行需求"。
之前大家可能听过很多个概念,比如:Function Calling、Tool Calling。其实说的都是一个东西,都是说的工具调用。不过工具有很多种形式。比如:@tool 装饰器去装饰过的函数 & MCP & skills。
3.1 DeepSeek & kimi 提供的MCP工具


4. Agent 教学
为了解决 LLM 无法执行实际任务的问题,引入 Agent(智能体)。
智能体的本质:Agent = LLM(推理大脑) + 三大关键能力。
三大关键能力:
-
自主规划 -- 将复杂目标拆解为可执行步骤。
-
工具调用 -- 主动调用外部 API / 搜索 / 代码解释器。【这是 Agent 和 LLM的核心区别】
-
记忆机制 -- 保留历史信息与中间结果,支持动态决策。【就不需要像我们刚才那样手动去维护 messages 消息列表】
一句话总结:Agent 让 LLM 从"只会说"进化为"能够做"
4.1 如何快速创建 Agent
LangChain:快速创建一个 Agent:查看官方文档
python
from langchain_community.chat_models import ChatTongyi
from langchain_core.tools import tool
from langchain.agents import create_agent
# 1.创建 tool
@tool
def get_goods_info_by_id(goods_id: int) -> str | None:
"""
根据商品goods_id去查询商品信息
:param goods_id: 商品id
:return: 查询到的商品信息,没查询到就返回空
"""
goods_info = {
1: "爱疯手机",
2: "华为电脑"
}
if goods_id not in goods_info:
print(f"id为{goods_id}的商品不存在")
return None
goods = goods_info[goods_id]
print(f"id为{goods_id}的商品为:{goods}")
return goods
# 2. 创建 Agent
agent = create_agent(
model=ChatTongyi(model="qwen3-max"), # 给 Agent 提供大脑
tools=[get_goods_info_by_id], # 给 Agent 提供工具
system_prompt="你是一个商品信息查询助手,如果用户传入商品ID,请你帮他查询是什么商品"
)
# 3.调用 agent 去帮我们查询商品信息
result = agent.invoke({
"messages": [{"role": "user", "content": "商品id为1,这个是一个什么商品"}]
})
# 4.打印结果
print(type(result)) # <class 'dict'>
print(result)
print(result['messages'])
print(len(result['messages']))
print('\n' + '=' * 20)
for msg in result['messages']:
print(f"消息类型:{type(msg).__name__},消息内容:{msg}")
4.2 异常处理 & 重试
智能体发生异常如何处理?
python
@wrap_tool_call
def handle_tool_errors(request, handler):
"""使用自定义消息处理工具执行错误。"""
try:
return handler(request)
except Exception as e:
# 向模型返回自定义错误消息
return ToolMessage(
content=f"工具错误:请检查您的输入并重试。({str(e)})",
tool_call_id=request.tool_call["id"]
)
python
# ========== 2. 定义错误处理中间件(支持重试) ==========
@wrap_tool_call
def handle_tool_errors(request, handler):
"""使用自定义消息处理工具执行错误,支持最多重试3次。"""
max_retries = 3
retry_delay = 1 # 每次重试间隔(秒)
last_exception = None
for attempt in range(max_retries):
try:
# 尝试执行工具
return handler(request)
except Exception as e:
last_exception = e
print(f"工具执行失败(第 {attempt + 1}/{max_retries} 次尝试): {e}")
# 如果不是最后一次尝试,等待后继续重试
if attempt < max_retries - 1:
time.sleep(retry_delay)
continue
else:
# 最后一次尝试仍失败,返回错误消息
print(f"工具执行失败,已重试 {max_retries} 次,放弃。")
return ToolMessage(
content=f"工具错误:请检查您的输入并重试。({str(last_exception)})",
tool_call_id=request.tool_call["id"]
)
4.3 Agent 链式工具调用 & React
4.3.1 Agent 链式工具调用
在 4.1 如何快速创建 Agent中,展示了Agent调用一个工具的全过程,那么如果有多个工具,Agent 又该会如何调用呢?
代码展示:根据商品名称去查询商品ID,然后根据商品ID查询商品价格。
python
# 准备数据
product_db = {
"iPhone": {"id": 101, "price": 5999},
"MacBook": {"id": 102, "price": 12999},
"小米手机": {"id": 103, "price": 3999},
}
# 2.2. 工具A:根据名称获取ID
@tool(description="根据商品名称获取对应的商品ID")
def get_product_id_by_name(name: str) -> int | None:
"""输入商品名称(如'iPhone'),返回商品ID"""
name_lower = name.lower()
for prod_name, info in product_db.items():
if prod_name.lower() == name_lower:
print(f"[工具A] 商品 '{name}' 的 ID 是 {info['id']}")
return info['id']
print(f"[工具A] 未找到商品 '{name}'")
return None
# 2.3. 工具B:根据ID获取价格
@tool(description="根据商品ID查询商品价格")
def get_product_price_by_id(product_id: int) -> str | None:
"""输入商品ID,返回价格字符串"""
for prod_name, info in product_db.items():
if info['id'] == product_id:
price = info['price']
print(f"[工具B] ID {product_id} 的商品 '{prod_name}' 价格为 {price} 元")
return f"{prod_name} 的价格是 {price} 元"
print(f"[工具B] 未找到 ID {product_id} 对应的商品")
return None
方法查询路径:商品名称 -> 调用get_product_id_by_name 获取商品ID->调用get_product_price_by_id获取商品价格。
结果如下:
python
{
"messages": [
{
"type": "human",
"content": "我想知道小米手机的价格是多少?",
"additional_kwargs": {},
"response_metadata": {},
"id": "4b1bc177-b202-4158-a3ed-efd4ec7879e3"
},
{
"type": "ai",
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"function": {
"arguments": "{\"name\": \"小米手机\"}",
"name": "get_product_id_by_name"
},
"id": "call_a0e2c3054bb94ef08062334b",
"index": 0,
"type": "function"
}
]
},
"response_metadata": {
"model_name": "qwen3-max",
"finish_reason": "tool_calls",
"request_id": "beb2e38a-11b9-9db5-b36b-6170b0d37e77",
"token_usage": {
"input_tokens": 368,
"output_tokens": 24,
"prompt_tokens_details": {
"cached_tokens": 256
},
"total_tokens": 392
}
},
"id": "lc_run--019f0ca2-0436-7fc0-a403-2ec0ff5f44f8-0",
"tool_calls": [
{
"name": "get_product_id_by_name",
"args": {
"name": "小米手机"
},
"id": "call_a0e2c3054bb94ef08062334b",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
{
"type": "tool",
"content": "103",
"name": "get_product_id_by_name",
"id": "584b6a53-ea75-494a-8e02-67c4d5905a1f",
"tool_call_id": "call_a0e2c3054bb94ef08062334b"
},
{
"type": "ai",
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"function": {
"arguments": "{\"product_id\": 103}",
"name": "get_product_price_by_id"
},
"id": "call_8e2b1f2e0e564deb9acf7a69",
"index": 0,
"type": "function"
}
]
},
"response_metadata": {
"model_name": "qwen3-max",
"finish_reason": "tool_calls",
"request_id": "252a1912-fa52-91ae-91be-e3567de6fe3d",
"token_usage": {
"input_tokens": 410,
"output_tokens": 27,
"prompt_tokens_details": {
"cached_tokens": 0
},
"total_tokens": 437
}
},
"id": "lc_run--019f0ca2-0981-77d1-824d-5c1078d0fddf-0",
"tool_calls": [
{
"name": "get_product_price_by_id",
"args": {
"product_id": 103
},
"id": "call_8e2b1f2e0e564deb9acf7a69",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
{
"type": "tool",
"content": "小米手机 的价格是 3999 元",
"name": "get_product_price_by_id",
"id": "256d8747-f588-4d06-9f5f-8370f82b2f95",
"tool_call_id": "call_8e2b1f2e0e564deb9acf7a69"
},
{
"type": "ai",
"content": "小米手机的价格是 3999 元。",
"additional_kwargs": {},
"response_metadata": {
"model_name": "qwen3-max",
"finish_reason": "stop",
"request_id": "b8562514-d35d-9a3a-8fb7-6353757b0faf",
"token_usage": {
"input_tokens": 464,
"output_tokens": 12,
"prompt_tokens_details": {
"cached_tokens": 384
},
"total_tokens": 476
}
},
"id": "lc_run--019f0ca2-0dee-78a0-8569-eb856e93f238-0",
"tool_calls": [],
"invalid_tool_calls": []
}
]
}
流程如下:
python
过程展示:查询"小米手机的价格"
初始用户输入
HumanMessage
content: "我想知道小米手机的价格是多少?"
第一步:Agent 决定调用工具 get_product_id_by_name
AIMessage(第1次)
content: ""
tool_calls: [ { name: "get_product_id_by_name", args: { "name": "小米手机" } } ]
含义:模型根据系统提示(先获取 ID 再查价格)主动调用工具 A,参数为商品名称。
第二步:执行工具 get_product_id_by_name
ToolMessage(对应第1个工具调用)
name: "get_product_id_by_name"
content: "103"
含义:工具模拟数据库查询,将商品名"小米手机"映射为商品 ID 103,返回结果。
第三步:Agent 收到 ID 后,决定调用工具 get_product_price_by_id
AIMessage(第2次)
content: ""
tool_calls: [ { name: "get_product_price_by_id", args: { "product_id": 103 } } ]
含义:模型看到工具 A 返回的 ID 103,自动构造工具 B 的参数,再次发起调用。
第四步:执行工具 get_product_price_by_id
ToolMessage(对应第2个工具调用)
name: "get_product_price_by_id"
content: "小米手机 的价格是 3999 元"
含义:工具 B 根据 ID 103 查询价格并返回完整的价格描述。
第五步:Agent 生成最终回答
AIMessage(最终)
content: "小米手机的价格是 3999 元。"
tool_calls: []
含义:模型将工具 B 返回的结果整理成自然语言答案,结束流程。
4.3.2 ReAct 思想
ReAct 思想(Reasoning + Acting)是一种结合推理和行动的 Agent 设计模式,由论文《ReAct: Synergizing Reasoning and Acting in Language Models》提出。它的核心是让大语言模型在解决复杂任务时,交替进行"思考"和"执行操作",而不是一次性生成最终答案。
传统的大语言模型通常只做"一步推理"或"直接输出结果"。
ReAct 模式将任务分解为 Thought → Action → Observation 的循环:
-
Thought(思考):模型用自然语言描述当前已知信息、下一步计划。
-
Action(行动):调用一个外部工具(如搜索、计算器、数据库查询、API 调用)。
-
Observation(观察):获取工具返回的结果,作为新的输入。
如此循环,直到模型认为可以给出最终答案(Final Answer)。
Function Calling 实战:Text To SQL
1. 什么是 Text To SQL
Text To SQL 是指根据用户的自然语言,由 Agent 自动生成对应的 SQL 查询语句,执行该 SQL 从数据库中获取数据,最后将查询结果以自然语言的形式返回给用户。

2. Text To SQL - 最简单的完整流程

2.1 数据库准备

sql
-- 1. 创建商品表
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID',
name VARCHAR(100) NOT NULL COMMENT '商品名称',
category VARCHAR(50) NOT NULL COMMENT '商品类别'
) COMMENT '商品信息表';
-- 2. 创建订单表
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
product_id INT NOT NULL COMMENT '商品ID',
quantity INT NOT NULL COMMENT '购买数量',
order_date DATE NOT NULL COMMENT '订单日期',
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
) COMMENT '订单记录表';
-- 插入商品数据
INSERT INTO products (name, category) VALUES
('小米手机', '电子产品'),
('华为手机', '电子产品'),
('苹果手机', '电子产品'),
('联想笔记本', '电子产品'),
('耐克运动鞋', '服装'),
('阿迪达斯运动鞋', '服装'),
('格力空调', '家电'),
('美的冰箱', '家电');
-- 插入订单数据
INSERT INTO orders (product_id, quantity, order_date) VALUES
(1, 2, '2024-01-15'),
(2, 1, '2024-01-16'),
(3, 3, '2024-01-20'),
(1, 1, '2024-02-01'),
(4, 2, '2024-02-05'),
(5, 5, '2024-02-10'),
(3, 2, '2024-02-15'),
(2, 1, '2024-03-01'),
(6, 3, '2024-03-05'),
(7, 1, '2024-03-10'),
(8, 2, '2024-03-12'),
(3, 1, '2024-03-15');
2.2 创建 Agent

3. 代码实现
https://github.com/LZD88/AI-Konwledge-Learning/tree/master/agent/text_to_sql