06_Agent和工具 - 让AI自主决策和调用工具

本课程介绍LangChain的Agent机制,让AI从”被动执行”升级为”自主决策”。你将学会创建工具、构建Agent、理解ReAct循环的工作原理。课程详细讲解工具的三种创建方式、Agent的类型选择、调试技巧,并通过实战案例展示如何让AI自主选择和组合工具完成复杂任务。

🎯 学习目标

  • 理解Agent的概念和价值
  • 掌握工具(Tool)的创建和使用
  • 学会构建Agent应用
  • 理解Agent的决策过程

📖 核心概念

Agent是什么?

Chain vs Agent

Chain(链)= 固定流程

1
2
3
4
5
6
步骤1 → 步骤2 → 步骤3 → 完成

特点:
- 流程固定
- 按顺序执行
- 你决定步骤

Agent(代理)= 自主决策

1
2
3
4
5
6
7
8
9
         ┌→ 用工具A ┐
输入 → AI思考 ┼→ 用工具B ┼→ AI判断 → 完成
└→ 用工具C ┘ ↓
继续思考

特点:
- AI自己决定用什么工具
- AI自己决定步骤顺序
- AI自己判断是否完成

生活类比:员工 vs 助手

Chain = 员工

  • 给他详细的操作手册
  • 严格按手册执行
  • 不会变通

Agent = 助手

  • 给他工具箱和目标
  • 自己决定怎么做
  • 会灵活处理

工具(Tool)是什么?

工具就是给AI的”技能”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 没有工具:AI只能"说话"
AI: "今天北京天气怎么样?"
回复: "我不知道,我无法查询实时信息"

# 有工具:AI可以"行动"
AI: "今天北京天气怎么样?"

AI思考: "我需要使用天气查询工具"

调用工具: get_weather("北京")

获得结果: "晴天,25度"

回复: "北京今天晴天,温度25度"

Agent的工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 接收任务

2. 思考:需要做什么?

3. 决策:用什么工具?

4. 执行:调用工具

5. 观察:工具返回什么?

6. 判断:任务完成了吗?
├─ 是 → 返回结果
└─ 否 → 回到步骤2

这个过程叫做:ReAct循环

  • Reasoning(推理):思考
  • Action(行动):执行
  • 循环进行

💻 Agent类型

1. OpenAI Functions Agent

1
2
3
4
5
6
7
8
# 最推荐:适合OpenAI兼容模型
from langchain.agents import create_openai_functions_agent

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

特点:

  • 使用函数调用功能
  • 最可靠
  • 国内模型大多支持

2. React Agent

1
2
3
4
5
6
7
8
# 经典:适合所有模型
from langchain.agents import create_react_agent

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

特点:

  • 使用提示词引导
  • 兼容性好
  • 思考过程清晰

3. Structured Chat Agent

1
2
3
4
5
6
7
8
# 适合需要结构化输入的工具
from langchain.agents import create_structured_chat_agent

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

💻 工具创建方式

方式1:使用@tool装饰器(最简单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain.tools import tool

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

Args:
city: 城市名称,如"北京"

Returns:
天气描述
"""
# 实际应该调用天气API
return f"{city}今天晴天,25度"

# LangChain自动:
# - 读取函数名作为工具名
# - 读取docstring作为工具描述
# - 读取参数注解作为参数说明

方式2:继承BaseTool(灵活)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain.tools import BaseTool
from pydantic import Field

class CalculatorTool(BaseTool):
name: str = "calculator"
description: str = "计算数学表达式,如:3+5*2"

def _run(self, expression: str) -> str:
"""执行工具"""
try:
result = eval(expression)
return str(result)
except Exception as e:
return f"计算错误:{e}"

方式3:使用StructuredTool(复杂参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langchain.tools import StructuredTool
from pydantic import BaseModel

class SearchInput(BaseModel):
query: str = Field(description="搜索关键词")
max_results: int = Field(default=5, description="最多返回几条结果")

def search_func(query: str, max_results: int) -> str:
"""搜索函数"""
return f"搜索'{query}'的前{max_results}条结果"

search_tool = StructuredTool.from_function(
func=search_func,
name="search",
description="搜索信息",
args_schema=SearchInput
)

🎨 实用工具示例

工具1:数据库查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@tool
def query_database(sql: str) -> str:
"""
执行SQL查询

Args:
sql: SQL查询语句

Returns:
查询结果
"""
# 实际实现
import sqlite3
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
cursor.execute(sql)
results = cursor.fetchall()
return str(results)

工具2:文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@tool
def read_file(filepath: str) -> str:
"""
读取文件内容

Args:
filepath: 文件路径

Returns:
文件内容
"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return f"读取失败:{e}"

工具3:API调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@tool
def call_api(endpoint: str) -> str:
"""
调用REST API

Args:
endpoint: API端点

Returns:
API响应
"""
import requests
response = requests.get(endpoint)
return response.text

📊 Agent最佳实践

1. 工具描述要清晰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ❌ 不好的描述
@tool
def tool1(x: str) -> str:
"""做一些事情"""
pass

# ✅ 好的描述
@tool
def search_order(order_id: str) -> str:
"""
根据订单号查询订单信息

参数:
order_id: 订单号,格式如 "ORD123456"

返回:
订单的详细信息,包括状态、金额、物流等

使用场景:
- 用户询问订单状态
- 用户想查看订单详情
"""
pass

2. 限制Agent步数

1
2
3
4
5
6
7
8
9
from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10, # 最多10步
max_execution_time=60, # 最多60秒
verbose=True
)

3. 处理工具错误

1
2
3
4
5
6
7
8
9
10
11
@tool
def safe_tool(param: str) -> str:
"""安全的工具"""
try:
# 执行操作
result = do_something(param)
return f"成功:{result}"
except ValueError as e:
return f"参数错误:{e},请检查输入"
except Exception as e:
return f"执行失败:{e},请稍后重试"

4. 添加权限控制

1
2
3
4
5
6
7
8
9
10
ALLOWED_OPERATIONS = ["read", "search"]

@tool
def controlled_tool(operation: str) -> str:
"""受控的工具"""
if operation not in ALLOWED_OPERATIONS:
return f"权限不足,只允许:{ALLOWED_OPERATIONS}"

# 执行操作
return do_operation(operation)

🔍 常见问题

Q1: Agent为什么不调用我的工具?

可能原因:

  1. 工具描述不清楚

    1
    2
    3
    4
    5
    # AI不知道什么时候该用这个工具
    @tool
    def tool1(x: str) -> str:
    """工具""" # ❌ 太简略
    pass
  2. 工具名字不直观

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @tool
    def t1(x: str) -> str: # ❌ 工具名太短
    """..."""
    pass

    # 改为:
    @tool
    def search_weather(city: str) -> str: # ✅ 一看就懂
    """..."""
    pass
  3. 提示词没有引导

    1
    2
    3
    4
    5
    6
    7
    8
    # 在prompt中明确说明有哪些工具
    prompt = """
    你有以下工具可用:
    - search_weather: 查询天气
    - search_news: 搜索新闻

    当用户问天气时,使用search_weather工具。
    """

Q2: Agent循环不停怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 设置最大步数
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=5 # 最多5步就停止
)

# 2. 设置超时
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_execution_time=30 # 30秒超时
)

# 3. 使用early_stopping_method
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
early_stopping_method="generate" # 达到最大步数时生成回答
)

Q3: 如何调试Agent?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 开启verbose模式
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True # 打印详细过程
)

# 2. 使用callbacks
from langchain.callbacks import StdOutCallbackHandler

agent_executor.invoke(
input,
config={"callbacks": [StdOutCallbackHandler()]}
)

# 3. 查看中间步骤
result = agent_executor.invoke(input, return_intermediate_steps=True)
print(result["intermediate_steps"])

📝 练习任务

练习1:计算器Agent

创建一个数学计算Agent:

  • 工具:加法、减法、乘法、除法
  • 能够分步骤计算复杂表达式
  • 示例:”计算(3+5)*2-10”

练习2:文件管理Agent

创建文件操作Agent:

  • 工具:读文件、写文件、列目录、搜索文件
  • 能够根据用户指令操作文件
  • 示例:”找出所有Python文件并统计行数”

练习3:智能客服Agent

创建客服Agent:

  • 工具:查订单、查物流、退货、换货
  • 能够处理用户的各种问题
  • 多步骤解决复杂问题

🎯 检查清单

完成本课后,你应该能够:

  • 理解Agent和Chain的区别
  • 创建自定义工具
  • 构建Agent应用
  • 理解Agent的决策过程
  • 调试Agent问题
  • 控制Agent行为

💡 核心要点

  1. Agent让AI更智能
    • 自主决策
    • 灵活使用工具
    • 处理复杂任务
  2. 工具是Agent的能力
    • 描述要清晰
    • 错误处理要完善
    • 权限要控制
  3. 调试很重要
    • 开启verbose查看过程
    • 设置限制避免失控
    • 优化提示词引导

本节代码示例

01_simple_tools.py

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"""
简单工具创建和使用
演示:如何给AI添加"技能"
"""

from langchain.tools import tool
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from dotenv import load_dotenv
import os
from datetime import datetime

load_dotenv()


# 定义工具
@tool
def get_current_time() -> str:
"""
获取当前时间

Returns:
当前时间的字符串表示
"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


@tool
def calculator(expression: str) -> str:
"""
计算数学表达式

Args:
expression: 数学表达式,如 "3+5*2"

Returns:
计算结果
"""
try:
result = eval(expression)
return f"{expression} = {result}"
except Exception as e:
return f"计算错误:{e}"


@tool
def search_info(query: str) -> str:
"""
搜索信息(模拟)

Args:
query: 搜索关键词

Returns:
搜索结果
"""
# 模拟搜索结果
mock_data = {
"python": "Python是一种高级编程语言,由Guido van Rossum创建于1991年",
"langchain": "LangChain是一个用于开发由语言模型驱动的应用程序的框架",
"ai": "人工智能(AI)是计算机科学的一个分支,致力于创建智能机器"
}

for key, value in mock_data.items():
if key in query.lower():
return value

return f"未找到关于'{query}'的信息"


def example1_test_tools() -> None:
"""示例1:测试工具"""
print("=== 示例1:测试工具 ===\n")

# 直接调用工具
print("⏰ 调用时间工具:")
time_result = get_current_time.invoke({})
print(f" 结果:{time_result}\n")

print("🧮 调用计算器工具:")
calc_result = calculator.invoke({"expression": "10 + 5 * 2"})
print(f" 结果:{calc_result}\n")

print("🔍 调用搜索工具:")
search_result = search_info.invoke({"query": "Python"})
print(f" 结果:{search_result}\n")


def example2_agent_with_tools() -> None:
"""示例2:让Agent使用工具"""
print("=== 示例2:Agent使用工具 ===\n")

# 创建LLM
llm = ChatOpenAI(
base_url=os.getenv("ALIBABA_BASE_URL"),
api_key=os.getenv("ALIBABA_API_KEY"),
model="qwen-plus",
temperature=0,
)

# 准备工具列表
tools = [get_current_time, calculator, search_info]

# 创建Agent提示词
prompt = PromptTemplate.from_template("""
回答以下问题。你有以下工具可用:

{tools}

工具描述:
{tool_names}

使用这个格式:

Question: 需要回答的问题
Thought: 思考需要做什么
Action: 要使用的工具,必须是 [{tool_names}] 中的一个
Action Input: 工具的输入
Observation: 工具的返回结果
... (可以重复 Thought/Action/Action Input/Observation 多次)
Thought: 我现在知道最终答案了
Final Answer: 最终答案

开始!

Question: {input}
Thought: {agent_scratchpad}
""")

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

# 创建Agent执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 显示思考过程
max_iterations=5,
handle_parsing_errors=True
)

# 测试不同问题
test_cases = [
"现在几点了?",
"计算 15 * 3 + 7",
"告诉我关于LangChain的信息"
]

for question in test_cases:
print(f"\n{'='*60}")
print(f"❓ 问题:{question}")
print('='*60)

try:
result = agent_executor.invoke({"input": question})
print(f"\n✅ 最终答案:{result['output']}\n")
except Exception as e:
print(f"\n❌ 错误:{e}\n")


def example3_complex_task() -> None:
"""示例3:复杂任务"""
print("\n=== 示例3:需要多个工具的复杂任务 ===\n")

llm = ChatOpenAI(
base_url=os.getenv("ALIBABA_BASE_URL"),
api_key=os.getenv("ALIBABA_API_KEY"),
model="qwen-plus",
temperature=0,
)

tools = [get_current_time, calculator, search_info]

prompt = PromptTemplate.from_template("""
回答以下问题。你有以下工具:

{tools}

工具名称:{tool_names}

格式:
Question: 问题
Thought: 思考
Action: 工具名
Action Input: 输入
Observation: 结果
... (重复)
Thought: 知道答案了
Final Answer: 答案

Question: {input}
{agent_scratchpad}
""")

agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=10
)

# 需要组合使用多个工具的问题
question = "先告诉我现在的时间,然后搜索Python的信息,最后计算2的10次方"

print(f"❓ 复杂问题:{question}\n")
print("="*60 + "\n")

try:
result = agent_executor.invoke({"input": question})
print(f"\n✅ 最终答案:\n{result['output']}\n")
except Exception as e:
print(f"\n❌ 错误:{e}\n")


def main() -> None:
"""主函数"""
example1_test_tools()
print("\n" + "="*70 + "\n")

example2_agent_with_tools()
print("\n" + "="*70 + "\n")

example3_complex_task()


if __name__ == "__main__":
main()

📚 下一步

现在你已经学会了Agent和工具,接下来学习如何让AI访问知识库:


继续学习: 第7课:RAG应用 →