Files
ai-agent-dev/25_项目实战二.ipynb
2026-07-08 10:09:42 +08:00

773 lines
27 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 25_项目实战二多工具协作型智能体\n",
"\n",
"## 学习目标\n",
"1. 掌握多工具协作型智能体的项目开发方法\n",
"2. 实现结合搜索引擎、计算工具和文件读取的复合型智能体\n",
"3. 学会调试和优化智能体的执行流程\n",
"\n",
"本节课会完成一个小型项目:构建一个可以根据用户问题自动选择工具的智能体。它可以模拟搜索资料、执行计算、读取文件,并把结果整理成最终回答。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. 什么是多工具协作型智能体\n",
"\n",
"普通问答智能体主要依靠大模型自身回答问题,但大模型并不适合完成所有事情。\n",
"\n",
"例如:\n",
"\n",
"- 想知道最新信息,需要搜索工具\n",
"- 想做精确数学计算,需要计算工具\n",
"- 想分析本地资料,需要文件读取工具\n",
"- 想保存结果,需要写文件工具\n",
"\n",
"多工具协作型智能体就像一个会安排工作的助理:它先理解用户问题,再判断该用哪个工具,最后把工具结果整理成答案。\n",
"\n",
"通俗地说,它的核心流程是:\n",
"\n",
"```text\n",
"用户问题 -> 判断需要什么工具 -> 调用工具 -> 整理工具结果 -> 返回答案\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. 项目需求分析\n",
"\n",
"本项目要实现一个教学版多工具智能体。为了让代码容易运行,我们先不用真实搜索引擎和真实文件系统,而是用 Python 函数模拟工具。\n",
"\n",
"项目需求如下:\n",
"\n",
"| 需求 | 说明 |\n",
"| --- | --- |\n",
"| 搜索工具 | 根据关键词返回模拟搜索结果 |\n",
"| 计算工具 | 执行简单数学表达式 |\n",
"| 文件读取工具 | 从模拟文件中读取内容 |\n",
"| 工具选择 | 根据用户问题判断应该调用哪个工具 |\n",
"| 结果汇总 | 把工具返回结果整理为自然语言答案 |\n",
"\n",
"第一版项目重点不是做得复杂,而是把“工具定义、工具选择、工具调用、结果汇总”的完整流程跑通。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. 项目模块划分\n",
"\n",
"我们把项目拆成 5 个模块:\n",
"\n",
"1. 定义工具:准备搜索、计算、文件读取函数\n",
"2. 注册工具:把工具放入统一的工具表\n",
"3. 选择工具:根据用户问题决定用哪个工具\n",
"4. 执行工具:调用对应函数并拿到结果\n",
"5. 汇总答案:把工具执行结果整理给用户\n",
"\n",
"这种拆法的好处是每个模块只负责一件事,后续调试和扩展都更方便。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. 第一步:定义搜索工具\n",
"\n",
"真实项目中,搜索工具可能会调用搜索引擎 API。这里为了教学简单用一个字典模拟搜索数据库。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"search_database = {\n",
" \"LangChain\": \"LangChain 是用于开发大模型应用的框架,常用于提示词、链、工具和 RAG 应用开发。\",\n",
" \"LangGraph\": \"LangGraph 是用于构建有状态、多步骤、可分支智能体流程的框架。\",\n",
" \"RAG\": \"RAG 是检索增强生成技术,先检索资料,再基于资料生成答案。\",\n",
"}\n",
"\n",
"\n",
"def search_tool(query):\n",
" for keyword, result in search_database.items():\n",
" if keyword.lower() in query.lower():\n",
" return result\n",
" return \"没有找到相关搜索结果。\"\n",
"\n",
"\n",
"print(search_tool(\"请介绍 LangGraph\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码定义了一个模拟搜索工具:\n",
"\n",
"1. `search_database` 是一个字典,用来模拟搜索引擎中的资料。\n",
"2. 字典的 key 是关键词,例如 `LangChain`、`LangGraph`、`RAG`。\n",
"3. 字典的 value 是搜索结果,也就是工具返回给智能体的信息。\n",
"4. `search_tool(query)` 接收用户查询内容。\n",
"5. `keyword.lower() in query.lower()` 用来做不区分大小写的关键词匹配。\n",
"6. 如果命中关键词,就返回对应搜索结果;如果没有命中,就返回“没有找到相关搜索结果”。\n",
"\n",
"这个工具虽然简单,但已经体现了搜索工具的基本思想:输入查询,返回相关资料。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. 第二步:定义计算工具\n",
"\n",
"大模型有时会算错数,所以精确计算应该交给计算工具。\n",
"\n",
"下面实现一个简单计算器,只允许安全的数学字符,避免执行危险代码。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def calculator_tool(expression):\n",
" allowed_chars = set(\"0123456789+-*/(). \" )\n",
" if not set(expression) <= allowed_chars:\n",
" return \"表达式中包含不支持的字符。\"\n",
"\n",
" try:\n",
" result = eval(expression)\n",
" return f\"计算结果是:{result}\"\n",
" except Exception as error:\n",
" return f\"计算失败:{error}\"\n",
"\n",
"\n",
"print(calculator_tool(\"128 * 36 + 50\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码定义了一个计算工具:\n",
"\n",
"1. `calculator_tool(expression)` 接收一个数学表达式字符串。\n",
"2. `allowed_chars` 定义允许出现的字符,包括数字、加减乘除、小括号、小数点和空格。\n",
"3. `set(expression) <= allowed_chars` 用来检查表达式中的字符是否都在允许范围内。\n",
"4. `eval(expression)` 会执行数学表达式并得到结果。\n",
"5. `try...except` 用来捕获计算错误,例如表达式格式不正确。\n",
"\n",
"注意:真实项目中要谨慎使用 `eval`。这里已经做了简单字符限制,但生产环境通常会使用更安全的数学表达式解析库。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. 第三步:定义文件读取工具\n",
"\n",
"文件读取工具适合回答和本地资料相关的问题。这里用字典模拟文件内容。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fake_files = {\n",
" \"project_plan.txt\": \"项目计划:第一周完成需求分析,第二周完成原型开发,第三周完成测试和优化。\",\n",
" \"meeting_notes.txt\": \"会议纪要:团队决定优先开发知识库问答功能,然后再增加多工具调用能力。\",\n",
"}\n",
"\n",
"\n",
"def file_reader_tool(file_name):\n",
" if file_name in fake_files:\n",
" return fake_files[file_name]\n",
" return \"没有找到这个文件。\"\n",
"\n",
"\n",
"print(file_reader_tool(\"project_plan.txt\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码定义了一个模拟文件读取工具:\n",
"\n",
"1. `fake_files` 是模拟文件系统key 是文件名value 是文件内容。\n",
"2. `file_reader_tool(file_name)` 接收文件名。\n",
"3. 如果文件名存在于 `fake_files` 中,就返回对应文件内容。\n",
"4. 如果文件名不存在,就返回提示信息。\n",
"\n",
"真实项目中,这个工具可以改成读取本地文件、数据库记录、对象存储文件或企业文档系统。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. 第四步:注册工具\n",
"\n",
"如果工具越来越多,不能每次都手动写大量 if 语句。更好的方式是把工具统一注册到一个工具表中。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tools = {\n",
" \"search\": {\n",
" \"name\": \"搜索工具\",\n",
" \"description\": \"用于查询 LangChain、LangGraph、RAG 等知识。\",\n",
" \"function\": search_tool,\n",
" },\n",
" \"calculator\": {\n",
" \"name\": \"计算工具\",\n",
" \"description\": \"用于执行数学表达式计算。\",\n",
" \"function\": calculator_tool,\n",
" },\n",
" \"file_reader\": {\n",
" \"name\": \"文件读取工具\",\n",
" \"description\": \"用于读取模拟文件内容。\",\n",
" \"function\": file_reader_tool,\n",
" },\n",
"}\n",
"\n",
"print(tools.keys())\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码把所有工具放入统一的 `tools` 字典:\n",
"\n",
"1. `search`、`calculator`、`file_reader` 是工具编号,后续智能体会用编号选择工具。\n",
"2. `name` 是工具的中文名称,方便展示给用户或调试时阅读。\n",
"3. `description` 是工具说明,真实智能体可以根据说明判断工具用途。\n",
"4. `function` 保存真正要调用的 Python 函数。\n",
"5. `tools.keys()` 可以查看当前注册了哪些工具。\n",
"\n",
"工具注册表的好处是:新增工具时,只需要往表里加一项,不需要大幅修改主流程。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. 第五步:实现工具选择器\n",
"\n",
"工具选择器负责判断用户问题应该交给哪个工具。\n",
"\n",
"真实项目中,工具选择可以由大模型完成。这里为了便于理解,先用关键词规则实现。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def select_tool(user_input):\n",
" if any(word in user_input for word in [\"计算\", \"加\", \"减\", \"乘\", \"除\", \"+\", \"-\", \"*\", \"/\"]):\n",
" return \"calculator\"\n",
"\n",
" if any(word in user_input for word in [\"文件\", \"计划\", \"会议\", \"project_plan\", \"meeting_notes\"]):\n",
" return \"file_reader\"\n",
"\n",
" return \"search\"\n",
"\n",
"\n",
"print(select_tool(\"帮我计算 12 * 8\"))\n",
"print(select_tool(\"读取项目计划文件\"))\n",
"print(select_tool(\"LangChain 是什么\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码实现了一个规则版工具选择器:\n",
"\n",
"1. `select_tool(user_input)` 接收用户输入。\n",
"2. 如果问题中包含计算相关词语或数学符号,就选择 `calculator`。\n",
"3. 如果问题中包含文件、计划、会议等词语,就选择 `file_reader`。\n",
"4. 如果前面规则都没有命中,默认选择 `search`。\n",
"\n",
"这种规则方法简单直观,适合教学和小项目。但当问题复杂时,可以让大模型根据工具描述自动选择工具。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. 第六步:为不同工具提取参数\n",
"\n",
"选择工具之后,还需要给工具传入合适的参数。\n",
"\n",
"例如计算工具需要数学表达式,文件读取工具需要文件名,搜索工具需要搜索关键词。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import re\n",
"\n",
"\n",
"def extract_tool_input(user_input, tool_name):\n",
" if tool_name == \"calculator\":\n",
" match = re.search(r\"[0-9+\\-*/(). ]+\", user_input)\n",
" return match.group().strip() if match else user_input\n",
"\n",
" if tool_name == \"file_reader\":\n",
" if \"meeting\" in user_input or \"会议\" in user_input:\n",
" return \"meeting_notes.txt\"\n",
" return \"project_plan.txt\"\n",
"\n",
" return user_input\n",
"\n",
"\n",
"print(extract_tool_input(\"帮我计算 128 * 36 + 50\", \"calculator\"))\n",
"print(extract_tool_input(\"读取会议纪要\", \"file_reader\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码负责从用户输入中提取工具参数:\n",
"\n",
"1. `import re` 引入正则表达式模块。\n",
"2. `extract_tool_input(user_input, tool_name)` 根据工具类型提取不同参数。\n",
"3. 对计算工具,`re.search(r\"[0-9+\\-*/(). ]+\", user_input)` 会从文本中找出数学表达式。\n",
"4. `match.group().strip()` 取出匹配结果并去掉前后空格。\n",
"5. 对文件读取工具,如果用户提到会议,就读取 `meeting_notes.txt`;否则默认读取 `project_plan.txt`。\n",
"6. 对搜索工具,直接把用户输入作为查询内容。\n",
"\n",
"参数提取是工具调用中很重要的一步。工具选对了,但参数给错了,结果也会不准确。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 10. 第七步:实现智能体主流程\n",
"\n",
"现在把工具选择、参数提取、工具调用和答案汇总连接起来,形成完整智能体。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def multi_tool_agent(user_input):\n",
" tool_name = select_tool(user_input)\n",
" tool_info = tools[tool_name]\n",
" tool_input = extract_tool_input(user_input, tool_name)\n",
" tool_result = tool_info[\"function\"](tool_input)\n",
"\n",
" final_answer = f\"用户问题:{user_input}\\n\"\n",
" final_answer += f\"选择工具:{tool_info['name']}\\n\"\n",
" final_answer += f\"工具输入:{tool_input}\\n\"\n",
" final_answer += f\"工具结果:{tool_result}\"\n",
" return final_answer\n",
"\n",
"\n",
"print(multi_tool_agent(\"帮我计算 128 * 36 + 50\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码实现了智能体主流程:\n",
"\n",
"1. `multi_tool_agent(user_input)` 是智能体入口函数。\n",
"2. `select_tool(user_input)` 判断应该使用哪个工具。\n",
"3. `tools[tool_name]` 从工具注册表中取出工具信息。\n",
"4. `extract_tool_input(user_input, tool_name)` 为工具准备输入参数。\n",
"5. `tool_info[\"function\"](tool_input)` 调用真正的工具函数。\n",
"6. 最后把用户问题、选择的工具、工具输入和工具结果拼接成最终答案。\n",
"\n",
"这就是多工具协作型智能体的最小可用版本。它的重点不是某个工具有多复杂,而是能自动完成“选工具、调工具、汇总结果”。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 11. 测试多个问题\n",
"\n",
"下面用不同类型的问题测试智能体,观察它是否能选择正确工具。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"test_questions = [\n",
" \"LangChain 是什么?\",\n",
" \"帮我计算 25 * 4 + 18\",\n",
" \"读取项目计划文件\",\n",
" \"会议纪要里说了什么?\",\n",
"]\n",
"\n",
"for question in test_questions:\n",
" print(\"=\" * 40)\n",
" print(multi_tool_agent(question))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码用于批量测试:\n",
"\n",
"1. `test_questions` 保存多个测试问题。\n",
"2. 第一个问题适合搜索工具。\n",
"3. 第二个问题适合计算工具。\n",
"4. 第三个和第四个问题适合文件读取工具。\n",
"5. `for question in test_questions` 逐个运行智能体。\n",
"6. `print(\"=\" * 40)` 用分隔线区分不同测试结果。\n",
"\n",
"测试时要重点观察两点:工具是否选对,工具输入是否提取正确。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 12. 加入调试日志\n",
"\n",
"智能体项目经常需要调试,因为错误可能发生在工具选择、参数提取或工具执行阶段。\n",
"\n",
"下面给主流程增加调试日志,方便定位问题。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def debug_multi_tool_agent(user_input, verbose=True):\n",
" if verbose:\n",
" print(f\"[调试] 用户输入:{user_input}\")\n",
"\n",
" tool_name = select_tool(user_input)\n",
" if verbose:\n",
" print(f\"[调试] 选择工具:{tool_name}\")\n",
"\n",
" tool_input = extract_tool_input(user_input, tool_name)\n",
" if verbose:\n",
" print(f\"[调试] 工具输入:{tool_input}\")\n",
"\n",
" tool_result = tools[tool_name][\"function\"](tool_input)\n",
" if verbose:\n",
" print(f\"[调试] 工具结果:{tool_result}\")\n",
"\n",
" return f\"最终回答:{tool_result}\"\n",
"\n",
"\n",
"print(debug_multi_tool_agent(\"帮我计算 10 + 20 * 3\"))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码增加了调试能力:\n",
"\n",
"1. `verbose=True` 表示默认打印调试信息。\n",
"2. 第一条日志显示用户原始输入。\n",
"3. 第二条日志显示智能体选择了哪个工具。\n",
"4. 第三条日志显示传给工具的输入参数。\n",
"5. 第四条日志显示工具返回结果。\n",
"6. 如果不想看日志,可以调用 `debug_multi_tool_agent(question, verbose=False)`。\n",
"\n",
"调试日志能帮助我们判断问题到底出在哪里:是工具没选对,还是参数没提对,还是工具本身执行失败。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 13. 用 LangGraph 表达多工具流程\n",
"\n",
"当前流程是普通 Python 函数。实际项目中,如果流程变复杂,可以用 LangGraph 把步骤拆成节点。\n",
"\n",
"工作流可以设计为:\n",
"\n",
"```text\n",
"START -> 选择工具 -> 执行工具 -> 汇总答案 -> END\n",
"```\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from typing import TypedDict\n",
"\n",
"\n",
"class AgentState(TypedDict):\n",
" user_input: str\n",
" tool_name: str\n",
" tool_input: str\n",
" tool_result: str\n",
" final_answer: str\n",
"\n",
"\n",
"def choose_tool_node(state: AgentState):\n",
" tool_name = select_tool(state[\"user_input\"])\n",
" tool_input = extract_tool_input(state[\"user_input\"], tool_name)\n",
" return {\"tool_name\": tool_name, \"tool_input\": tool_input}\n",
"\n",
"\n",
"def execute_tool_node(state: AgentState):\n",
" tool_result = tools[state[\"tool_name\"]][\"function\"](state[\"tool_input\"])\n",
" return {\"tool_result\": tool_result}\n",
"\n",
"\n",
"def summarize_node(state: AgentState):\n",
" tool_label = tools[state[\"tool_name\"]][\"name\"]\n",
" final_answer = f\"我使用了{tool_label},得到结果:{state['tool_result']}\"\n",
" return {\"final_answer\": final_answer}\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码定义了 LangGraph 版本需要的 State 和节点:\n",
"\n",
"1. `AgentState` 表示工作流中流动的数据。\n",
"2. `user_input` 保存用户问题。\n",
"3. `tool_name` 保存选择出的工具编号。\n",
"4. `tool_input` 保存传给工具的参数。\n",
"5. `tool_result` 保存工具执行结果。\n",
"6. `final_answer` 保存最终回答。\n",
"7. `choose_tool_node` 负责工具选择和参数提取。\n",
"8. `execute_tool_node` 负责调用工具。\n",
"9. `summarize_node` 负责把工具结果整理成用户能看懂的回答。\n",
"\n",
"用 LangGraph 后,每个步骤变成独立节点,流程更清晰,也更容易扩展。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 14. 构建 LangGraph 工作流\n",
"\n",
"如果当前环境没有安装 LangGraph可以先运行`%pip install langgraph`。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 如果没有安装 LangGraph取消下一行注释后运行\n",
"# %pip install langgraph\n",
"\n",
"from langgraph.graph import END, START, StateGraph\n",
"\n",
"\n",
"workflow = StateGraph(AgentState)\n",
"\n",
"workflow.add_node(\"choose_tool\", choose_tool_node)\n",
"workflow.add_node(\"execute_tool\", execute_tool_node)\n",
"workflow.add_node(\"summarize\", summarize_node)\n",
"\n",
"workflow.add_edge(START, \"choose_tool\")\n",
"workflow.add_edge(\"choose_tool\", \"execute_tool\")\n",
"workflow.add_edge(\"execute_tool\", \"summarize\")\n",
"workflow.add_edge(\"summarize\", END)\n",
"\n",
"agent_app = workflow.compile()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码把节点连接成完整工作流:\n",
"\n",
"1. `StateGraph(AgentState)` 创建一张流程图,并指定状态结构。\n",
"2. `add_node` 注册节点函数。\n",
"3. `START -> choose_tool` 表示从工具选择节点开始。\n",
"4. `choose_tool -> execute_tool` 表示选好工具后执行工具。\n",
"5. `execute_tool -> summarize` 表示拿到工具结果后进行汇总。\n",
"6. `summarize -> END` 表示汇总完成后结束。\n",
"7. `compile()` 把流程图编译成可运行对象。\n",
"\n",
"这和前面的普通 Python 主流程做的是同一件事,只是 LangGraph 更适合管理复杂流程。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 15. 运行 LangGraph 版智能体\n",
"\n",
"现在用一个问题测试 LangGraph 版多工具智能体。\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"initial_state = {\n",
" \"user_input\": \"会议纪要里说了什么?\",\n",
" \"tool_name\": \"\",\n",
" \"tool_input\": \"\",\n",
" \"tool_result\": \"\",\n",
" \"final_answer\": \"\",\n",
"}\n",
"\n",
"final_state = agent_app.invoke(initial_state)\n",
"print(final_state[\"final_answer\"])\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 代码解释\n",
"\n",
"这段代码运行 LangGraph 工作流:\n",
"\n",
"1. `initial_state` 是初始状态,只有 `user_input` 有真实内容,其他字段先留空。\n",
"2. `agent_app.invoke(initial_state)` 启动工作流。\n",
"3. 工作流会依次执行工具选择、工具调用和结果汇总。\n",
"4. `final_state` 是运行结束后的完整状态。\n",
"5. `final_state[\"final_answer\"]` 取出最终答案。\n",
"\n",
"这个例子说明当一个智能体需要多个步骤协作时LangGraph 可以让流程更有结构。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 16. 调试和优化建议\n",
"\n",
"多工具智能体常见问题包括:\n",
"\n",
"| 问题 | 可能原因 | 优化方法 |\n",
"| --- | --- | --- |\n",
"| 工具选错 | 工具描述不清或规则太简单 | 优化工具描述,增加分类规则或让模型判断 |\n",
"| 参数提错 | 用户表达复杂 | 增加参数抽取逻辑或使用结构化输出 |\n",
"| 工具报错 | 输入格式不符合要求 | 在调用前做参数校验 |\n",
"| 答案不清晰 | 只返回原始工具结果 | 增加结果解释和格式化输出 |\n",
"| 流程难排查 | 缺少中间状态 | 增加调试日志或保存执行轨迹 |\n",
"\n",
"开发智能体时,不要只看最终答案,也要观察每一步的中间状态。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 17. 小结\n",
"\n",
"本节课完成了一个多工具协作型智能体项目。\n",
"\n",
"你需要重点掌握:\n",
"\n",
"1. 多工具智能体的核心是根据任务选择合适工具。\n",
"2. 工具通常包含名称、描述和函数。\n",
"3. 工具调用前要先选择工具,再提取参数。\n",
"4. 调试时要观察工具选择、工具输入和工具结果。\n",
"5. LangGraph 可以把复杂工具调用流程拆成清晰节点。\n",
"\n",
"掌握这个项目后,就可以继续接入真实搜索 API、真实文件读取、大模型工具调用和更复杂的工作流。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 18. 练习题\n",
"\n",
"1. 新增一个 `weather_tool`,模拟查询天气。\n",
"2. 修改 `select_tool`,让它可以根据“天气”关键词选择天气工具。\n",
"3. 修改 `calculator_tool`,支持平方和取余计算。\n",
"4. 给 `multi_tool_agent` 增加错误处理,当工具不存在时返回友好提示。\n",
"5. 在 LangGraph 工作流中新增一个 `debug_node`,专门输出当前 State。\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}