14-day 4-多工具协作 + 错误处理-实验1
Day 4:多工具协作 + 错误处理 实验
- ✅ 创建虚拟环境
- ✅ 安装pip依赖包
- ✅
robust_agent.py程序(含divide、is_leap_year、get_current_time、模拟search_web)
✅ 第一步:创建虚拟环境
# 创建工作目录
mkdir day4 && cd day4
# 创建虚拟环境
python3 -m venv day4-robust
# 激活虚拟环境
source day4-robust/bin/activate
# 升级 pip
pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/
✅ 第二步:安装依赖
pip install \
"langchain==0.1.20" \
"langchain-core==0.1.52" \
"langchain-openai==0.0.6" \
"langchainhub==0.1.15" \
python-dotenv \
requests \
beautifulsoup4 \
-i https://mirrors.aliyun.com/pypi/simple/
✅ 第三步:创建 .env(复用你的 Key)
echo 'DASHSCOPE_API_KEY=sk-6adfbafe2c854892c720982666cex' > .env
✅ 第四步:创建 robust_agent_1.py
tee robust_agent_1.py <<'EOF'
# robust_agent.py
import os
from datetime import datetime
from dotenv import load_dotenv
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain_openai import ChatOpenAI
# 加载 API Key
load_dotenv()
@tool
def get_current_time(dummy: str = ""):
"""获取当前日期和时间"""
return datetime.now().strftime("%Y年%m月%d日 %H:%M")
@tool
def divide(a_str: str):
"""
安全除法计算:输入格式为 'a,b',返回 a / b 的结果。
示例输入: "10,3" → 输出: "3.333..."
"""
try:
parts = a_str.split(",")
if len(parts) != 2:
return "错误:请输入两个数字,用逗号分隔,如 '10,2'"
a = float(parts[0].strip())
b = float(parts[1].strip())
if b == 0:
return "错误:除数不能为零!"
result = a / b
return str(result)
except ValueError:
return "错误:请输入有效的数字"
except Exception as e:
return f"计算失败: {str(e)}"
@tool
def is_leap_year(year_str: str):
"""
判断指定年份是否为闰年。
输入:年份(字符串),如 "2024"
输出:"2024年是闰年" 或 "2023年不是闰年"
"""
try:
year = int(year_str.strip())
is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
if is_leap:
return f"{year}年是闰年"
else:
return f"{year}年不是闰年"
except ValueError:
return "错误:请输入有效的年份(如 2024)"
except Exception as e:
return f"判断失败: {str(e)}"
@tool
def search_web(query: str):
"""
模拟网页搜索(仅返回固定摘要,避免真实爬虫问题)
实际项目应替换为合法搜索引擎 API(如 SerpAPI)
"""
return f"(模拟)关于 '{query}' 的搜索结果摘要:这是一个演示响应。"
# 初始化 Qwen 模型
llm = ChatOpenAI(
model="qwen-max",
temperature=0,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.getenv("DASHSCOPE_API_KEY")
)
# 注册所有工具
tools = [get_current_time, divide, is_leap_year, search_web]
# 加载 ReAct 提示模板
prompt = hub.pull("hwchase17/react")
# 创建 Agent
agent = create_react_agent(llm, tools, prompt)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=6
)
# 交互式主循环
print("🛡️ Day 4:鲁棒智能体(多工具 + 错误处理)")
print("尝试提问:")
print("- 计算 100 除以 0")
print("- 2024年是闰年吗?")
print("- 现在几点?再算一下 2024 除以 4")
print("- 输入 quit 退出\n")
while True:
try:
user_input = input("💬 问题: ").strip()
if not user_input:
continue
if user_input.lower() in {"quit", "exit", "q"}:
print("👋 再见!")
break
result = agent_executor.invoke({"input": user_input})
print("\n" + "=" * 60)
print("🤖 回答:")
print(result["output"])
print()
except KeyboardInterrupt:
print("\n👋 被中断,再见!")
break
except Exception as e:
print(f"❌ 系统错误: {e}\n")
EOF
✅ 第五步:运行程序
python robust_agent_1.py
🖨️ 预期输出示例
测试 1:除零错误处理
💬 问题: 计算 100 除以 0
Thought: 我需要使用 divide 工具
Action: divide
Action Input: "100,0"
Observation: 错误:除数不能为零!
Final Answer: 无法计算 100 除以 0,因为除数不能为零。
测试 2:闰年判断(结合时间工具)
💬 问题: 2024年是闰年吗?
Thought: 使用 is_leap_year 工具
Action: is_leap_year
Action Input: "2024"
Observation: 2024年是闰年
Final Answer: 是的,2024年是闰年。
测试 3:多工具组合
💬 问题: 现在几点?再算一下 2024 除以 4
Thought: 先查时间,再做除法
Action: get_current_time
Action Input: ""
Observation: 2026年01月25日 16:30
Thought: 现在计算 2024 / 4
Action: divide
Action Input: "2024,4"
Observation: 506.0
Final Answer: 当前时间是 2026年01月25日 16:30,2024 除以 4 等于 506.0。
程序解析
当你问 “2024是不是闰年?” 时,这个 robust_agent.py 程序是如何运作的?
整个过程体现了 ReAct 智能体的核心能力:自主决策 + 工具调用 + 错误隔离。
🧠 第一步:用户输入
💬 问题: 2024是不是闰年?
程序将这个问题包装成:
{"input": "2024是不是闰年?"}
交给 agent_executor.invoke() 处理。
🤖 第二步:LLM(Qwen)进行 ReAct 推理
Agent 使用从 hub.pull("hwchase17/react") 加载的提示模板,
结合工具列表(get_current_time, divide, is_leap_year, search_web),开始思考。
它会生成类似以下的内部推理链:
Thought: 用户想知道2024年是否是闰年。我有一个专门的工具 is_leap_year 可以判断闰年。
Action: is_leap_year
Action Input: "2024"
✅ 注意:LLM 没有自己计算,而是主动选择调用工具 ——这是这章程序思想中不一样的地方!
🔧 第三步:执行工具 is_leap_year("2024")
程序调用你定义的函数:
@tool
def is_leap_year(year_str: str):
year = int("2024") # → 2024
is_leap = (2024 % 4 == 0 and 2024 % 100 != 0) or (2024 % 400 == 0)
# 计算:2024 ÷ 4 = 506(整除),且 2024 ÷ 100 = 20.24(不整除)
# 所以 is_leap = True
return "2024年是闰年"
✅ 返回结果:
Observation: 2024年是闰年
💡 即使 LLM 对闰年规则记错了(比如以为“能被4整除就是闰年”),工具的代码逻辑是确定且正确的,保证了答案准确。
🧾 第四步:LLM 生成最终回答
看到 Observation 后,LLM 继续推理:
Thought: 我现在知道答案了。
Final Answer: 是的,2024年是闰年。
程序提取 result["output"] 并打印:
============================================================
🤖 回答:
是的,2024年是闰年。
✅ 全流程总结(ReAct 五步闭环)
| 步骤 | 内容 |
|---|---|
| 1. Thought | “用户问2024是否闰年,我有 is_leap_year 工具可用” |
| 2. Action | 调用 is_leap_year |
| 3. Action Input | "2024" |
| 4. Observation | "2024年是闰年"(来自你写的 Python 函数) |
| 5. Final Answer | “是的,2024年是闰年。” |
agent强制规则之一
因为你在使用 create_react_agent + hub.pull("hwchase17/react"),这个 prompt 强制要求:
“只有当有合适的工具时才行动;否则不能瞎猜。”
而且你的 divide 工具太局限 —— 它只做除法,还要求逗号分隔。加法、乘法、括号等完全不在支持范围内。
所以 Agent 会说:“我无能为力”。
🔍 为什么这样做比“让 LLM 直接回答”更好?
| 方式 | 风险 | 优势 |
|---|---|---|
| LLM 直接回答 | - 可能记错规则(如忽略“整百年必须被400整除”)- 无法验证- 输出不可靠 | 快,但不可信 |
调用工具 is_leap_year |
- 规则由代码实现,100% 正确- 错误可捕获(如输入“abc”会返回错误)- 可测试、可维护 | 可靠、可审计、可扩展 |
🎯 核心思想, 各司其职:
- 把“确定性计算”交给工具
- 把“语言理解与规划”交给 LLM。
💡
“大模型擅长‘说话’,但不擅长‘算数’;
智能体的智慧,在于知道什么时候该请‘专家工具’帮忙。”
浙公网安备 33010602011771号