LangChain与LangGraph实战:构建可调用工具的AI智能体

📅 2026/8/21 14:36:13
LangChain与LangGraph实战:构建可调用工具的AI智能体
如果你正在寻找一套能真正跑通、能解决实际问题的 AI Agent 开发实战指南那么这篇文章就是为你准备的。我们不谈空泛的概念直接聚焦于如何将 LangChain、MCPModel Context Protocol和 LangGraph 这三个核心组件组合起来构建一个功能完整、可扩展的智能体Agent。本文的目标是让你在最短时间内从环境搭建、核心概念理解到亲手构建一个具备工具调用和状态管理能力的 Agent并最终能将其部署为可用的服务。无论你是想快速验证一个想法还是为现有系统添加 AI 能力这里提供的路径都能帮你避开初期 99% 的配置和概念陷阱。整个实战流程围绕一个核心目标打造一个能理解用户需求、自主选择并调用合适工具如搜索、计算、文件读写、并管理复杂多轮对话状态的 AI Agent。我们将使用 LangChain 作为框架基石利用 MCP 协议来标准化和集成外部工具并通过 LangGraph 来设计和控制 Agent 的决策流程。这种组合是目前构建复杂、可靠 Agent 系统的最佳实践之一。接下来你会看到一套完整的操作指南。我们将从最核心的能力和所需环境讲起然后一步步完成安装部署、构建一个示例 Agent、进行功能测试并最终探讨如何将其封装为 API 服务以及处理更复杂的任务。文章的重点是“怎么做”和“为什么这么做”确保每个步骤你都能跟上并验证结果。1. 核心能力速览在深入代码之前我们先快速了解这个技术栈能为你带来什么以及你需要准备什么。能力项说明核心功能构建具备规划、工具调用、状态记忆和循环执行能力的 AI 智能体Agent。技术栈LangChain(应用框架)、MCP(工具协议)、LangGraph(工作流/状态机)。主要价值将大语言模型LLM的能力与外部工具、数据源无缝连接实现自动化任务处理。硬件门槛极低。开发阶段主要依赖 LLM 的 API 调用如 OpenAI, DeepSeek本地只需普通 CPU 和网络。本地部署大模型需 GPU但非必须。启动方式通过 Python 脚本启动可封装为 Flask/FastAPI Web 服务或后台常驻进程。接口能力支持标准的 HTTP API可接收自然语言请求返回结构化结果或执行流。批量任务通过 LangGraph 的检查点Checkpoint和并发设计支持异步和批量任务处理。适合场景AI 客服、自动化数据分析、智能工作流助手、代码辅助工具、个性化内容生成等。简单来说你不需要昂贵的显卡也能开始学习和大规模开发。真正的资源消耗在于调用大模型 API而本地开发环境只需能运行 Python 即可。2. 适用场景与使用边界这个技术组合并非万能理解其边界能让你更好地应用它。它非常适合以下场景流程自动化将重复性的、基于规则和判断的任务交给 Agent如信息汇总、报告生成、数据提取。复杂决策支持需要结合实时信息如网络搜索、数据库查询进行多步骤推理的任务。交互式应用构建聊天机器人、智能助手不仅能对话还能“动手”操作其他软件或数据。原型快速验证利用 LangChain 丰富的组件和 MCP 的快速工具集成快速搭建 AI 应用原型。需要注意的边界与限制并非全自动魔法Agent 的可靠性受限于 LLM 的推理能力、工具的定义质量以及工作流的设计。需要精心设计提示词Prompt和错误处理。工具依赖Agent 的能力边界由其可调用的工具决定。为它连接数据库、搜索引擎、业务 API 是关键。成本与延迟每一步 LLM 调用都会产生 API 成本和时间延迟。复杂的多步工作流需要权衡效果与开销。安全与合规Agent 能执行工具操作这意味着必须严格控制其权限。切勿让 Agent 拥有删除文件、发送邮件、操作资金等高风险权限而不加审计。所有涉及用户数据、隐私信息的处理必须符合相关法律法规。3. 环境准备与前置条件让我们开始准备实战环境。以下是确保流程顺畅所需的基础条件。操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu 20.04)。本文命令以 Linux/macOS 的 bash 和 Windows 的 PowerShell 为例。Python 版本推荐使用 Python 3.10 或 3.11。这是 LangChain 等库兼容性最好的版本。包管理工具确保已安装pip。强烈建议使用虚拟环境venv或conda来隔离项目依赖。大模型 API 密钥你需要一个能够访问大模型服务的 API Key。我们将以DeepSeek和OpenAI兼容的 API 为例因为它们易于获取且成本较低。你需要前往相应平台注册并获取 Key。代码编辑器VS Code, PyCharm 或任何你熟悉的 IDE。网络连接需要能稳定访问外网以下载 Python 包和调用模型 API。核心依赖概览我们将主要安装langchain,langchain-community,langgraph, 以及 MCP 相关的客户端和服务器库。langchain是主框架langgraph用于构建有状态的图工作流而 MCP 相关的库则用于工具集成。4. 安装部署与启动方式我们从一个干净的虚拟环境开始一步步安装所有必要的组件。步骤 1创建并激活虚拟环境# 在项目目录下操作 # 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Linux/macOS: source venv/bin/activate # Windows: # .\venv\Scripts\activate步骤 2安装核心库我们将安装 LangChain、LangGraph 以及用于连接 DeepSeek API 的库。这里使用langchain-openai是因为 DeepSeek 的 API 与 OpenAI 兼容。pip install langchain langgraph langchain-openai步骤 3安装 MCP 相关组件MCP 包含两部分客户端在你的 Agent 代码中调用和服务器实际运行工具。我们还需要安装一些标准工具服务器。# 安装 MCP 客户端和基础工具 pip install mcp langchain-mcp # 安装一些常用的 MCP 服务器工具例如文件系统工具 # 注意你可能需要根据特定工具服务器的要求进行安装这里以标准示例为例。 # 通常MCP 服务器是独立的可执行文件或 Python 包。步骤 4获取并设置 API 密钥在代码中我们需要设置大模型的 API 密钥。建议使用环境变量管理避免密钥硬编码在代码中。# Linux/macOS export DEEPSEEK_API_KEY你的-deepseek-api-key # Windows (PowerShell) # $env:DEEPSEEK_API_KEY你的-deepseek-api-key如果你的 DeepSeek 密钥需要指定 base_url也可以在代码中设置。至此基础环境就搭建完成了。接下来我们将进入最核心的部分构建第一个 Agent。5. 功能测试与效果验证构建你的第一个 Agent我们将构建一个简单的 Agent它可以使用计算器和网络搜索模拟两个工具来回答用户问题。5.1 项目结构与初始化创建一个新的项目目录例如my_first_agent并在其中创建以下文件my_first_agent/ ├── tools/ # 存放自定义工具或 MCP 服务器配置 ├── agent.py # 主 Agent 逻辑文件 └── requirements.txt # 依赖列表可由 pip freeze requirements.txt 生成5.2 编写基础 Agent 代码 (agent.py)我们将首先使用 LangChain 原生的工具调用方式构建一个简单 Agent然后再引入 LangGraph 增加状态管理。# agent.py - 基础版LangChain 自定义工具 import os from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate from langchain.tools import tool # 1. 定义工具 tool def calculator(expression: str) - str: 计算一个数学表达式。支持加减乘除和括号。例如(3 5) * 2 try: # 警告在生产环境中使用 eval 是危险的这里仅用于演示。 # 应使用更安全的表达式求值库如 ast.literal_eval 或 numexpr。 result eval(expression) return f计算结果: {result} except Exception as e: return f计算错误: {e} tool def search_web(query: str) - str: 模拟网络搜索。返回模拟的搜索结果。 # 在实际应用中这里会调用 SerperAPI、Google Search API 或 DuckDuckGo 等。 # 此处返回模拟数据。 return f关于 {query} 的模拟搜索结果\n- 结果1: 相关链接1\n- 结果2: 相关链接2\n- 摘要: 这是关于 {query} 的模拟摘要。 # 2. 设置 LLM (以 DeepSeek 为例) llm ChatOpenAI( modeldeepseek-chat, # 或你使用的具体模型名称 api_keyos.getenv(DEEPSEEK_API_KEY), base_urlhttps://api.deepseek.com/v1, # DeepSeek API 端点 temperature0.1 # 降低随机性使工具调用更稳定 ) # 3. 创建提示词模板 prompt ChatPromptTemplate.from_messages([ (system, 你是一个乐于助人的助手可以调用工具来回答问题。请根据用户问题决定是否需要调用工具以及调用哪个工具。), (placeholder, {chat_history}), # LangGraph 中会更重要 (human, {input}), (placeholder, {agent_scratchpad}), ]) # 4. 组装 Agent tools [calculator, search_web] agent create_tool_calling_agent(llmllm, toolstools, promptprompt) # 5. 创建执行器 agent_executor AgentExecutor(agentagent, toolstools, verboseTrue) # 6. 测试运行 if __name__ __main__: # 测试用例 1数学计算 print( 测试 1: 数学计算 ) result1 agent_executor.invoke({input: 请计算一下 (12 34) * 2 等于多少}) print(f用户: 请计算一下 (12 34) * 2 等于多少) print(f助手: {result1[output]}\n) # 测试用例 2需要搜索的问题 print( 测试 2: 模拟搜索 ) result2 agent_executor.invoke({input: LangChain 是什么}) print(f用户: LangChain 是什么) print(f助手: {result2[output]}\n) # 测试用例 3混合任务需要 Agent 自主规划 print( 测试 3: 混合任务 ) result3 agent_executor.invoke({input: 先搜索一下‘最近的热点新闻’然后告诉我其中一条新闻标题里包含的数字乘以 10 是多少。}) print(f用户: 先搜索一下‘最近的热点新闻’然后告诉我其中一条新闻标题里包含的数字乘以 10 是多少。) print(f助手: {result3[output]})运行与验证在终端激活虚拟环境并运行脚本python agent.py预期输出你应该能看到类似以下的输出verboseTrue会显示 Agent 的思考过程调用 LLM、选择工具、执行工具 测试 1: 数学计算 进入新的 Agent 执行链... 我调用 calculator 工具来计算表达式 (12 34) * 2。 ... 用户: 请计算一下 (12 34) * 2 等于多少 助手: 计算结果: 92 ...如果看到工具被成功调用并返回了正确结果说明你的基础 Agent 已经跑通了5.3 引入 LangGraph 构建有状态 Agent基础AgentExecutor对于简单任务足够但对于需要复杂状态管理、循环、条件分支的多轮对话LangGraph是更强大的选择。下面我们升级代码使用 LangGraph 来重构 Agent。创建一个新文件agent_with_graph.py# agent_with_graph.py - 使用 LangGraph 增强状态管理 import os from typing import TypedDict, Annotated, Sequence import operator from langgraph.graph import StateGraph, END from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode from langchain_openai import ChatOpenAI from langchain.tools import tool from langchain_core.messages import HumanMessage, AIMessage, ToolMessage # 1. 定义状态结构 class AgentState(TypedDict): messages: Annotated[Sequence, add_messages] # 自动累加消息历史 # 2. 定义同样的工具复用 tool def calculator(expression: str) - str: 计算一个数学表达式。支持加减乘除和括号。例如(3 5) * 2 try: result eval(expression) return f计算结果: {result} except Exception as e: return f计算错误: {e} tool def search_web(query: str) - str: 模拟网络搜索。返回模拟的搜索结果。 return f关于 {query} 的模拟搜索结果\n- 结果1: 相关链接1\n- 结果2: 相关链接2\n- 摘要: 这是关于 {query} 的模拟摘要。 # 3. 初始化 LLM 和工具 llm ChatOpenAI( modeldeepseek-chat, api_keyos.getenv(DEEPSEEK_API_KEY), base_urlhttps://api.deepseek.com/v1, temperature0.1 ) tools [calculator, search_web] llm_with_tools llm.bind_tools(tools) # 将工具绑定到 LLM # 4. 定义图节点 def agent_node(state: AgentState): 负责决策的节点根据对话历史决定是回复用户还是调用工具。 messages state[messages] # 调用 LLM response llm_with_tools.invoke(messages) # 返回包含 LLM 响应消息的新状态 return {messages: [response]} def tool_node(state: AgentState): 负责执行工具的节点。 last_message state[messages][-1] tool_calls last_message.tool_calls results [] for tool_call in tool_calls: tool_name tool_call[name] tool_input tool_call[args] # 找到对应的工具函数并执行 tool_to_use {t.name: t for t in tools}[tool_name] result tool_to_use.invoke(tool_input) # 构造 ToolMessage 返回给 Agent results.append(ToolMessage(contentresult, tool_call_idtool_call[id])) return {messages: results} # 5. 构建图工作流 workflow StateGraph(AgentState) # 添加节点 workflow.add_node(agent, agent_node) workflow.add_node(tools, tool_node) # 设置入口点 workflow.set_entry_point(agent) # 定义边根据 LLM 响应决定下一步 def route_after_agent(state: AgentState): 路由逻辑如果 LLM 响应中有工具调用就去工具节点否则结束。 last_message state[messages][-1] if last_message.tool_calls: return tools else: return END workflow.add_conditional_edges( agent, route_after_agent, { tools: tools, END: END } ) # 从工具节点执行后总是回到 Agent 节点进行下一步决策 workflow.add_edge(tools, agent) # 编译图 app workflow.compile() # 6. 测试运行 if __name__ __main__: # 初始化状态 initial_state: AgentState {messages: [HumanMessage(content请计算 (100 - 25) / 5 等于多少)]} print( 测试 LangGraph Agent: 数学计算 ) for event in app.stream(initial_state, stream_modevalues): event[messages][-1].pretty_print() print(\n 测试 LangGraph Agent: 多轮对话与工具调用 ) # 模拟一个多轮对话先搜索再计算 state: AgentState {messages: [HumanMessage(content搜索一下‘圆周率 π 的值’)]} print(用户: 搜索一下‘圆周率 π 的值’) for event in app.stream(state, stream_modevalues): msg event[messages][-1] if isinstance(msg, HumanMessage): continue msg.pretty_print() # 接着问状态中保留了历史 state[messages].append(HumanMessage(content很好现在请用 π 计算一个半径为 3 的圆的面积。)) print(\n用户: 很好现在请用 π 计算一个半径为 3 的圆的面积。) for event in app.stream(state, stream_modevalues): msg event[messages][-1] if isinstance(msg, HumanMessage): continue msg.pretty_print()运行与验证python agent_with_graph.py预期效果你将看到 Agent 首先调用搜索工具获取 π 的值模拟然后在下一轮对话中它记得之前的上下文并调用计算器工具完成面积计算。这演示了 LangGraph 如何优雅地管理多轮对话状态和工具调用循环。6. 接口 API 与批量任务将 Agent 封装成 API 服务是投入实际使用的关键一步。我们使用 FastAPI 来快速创建一个 Web 服务。6.1 创建 FastAPI 服务安装 FastAPI 和 Uvicornpip install fastapi uvicorn创建api_server.py# api_server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Optional import asyncio from agent_with_graph import app as agent_app # 导入我们之前编译好的 LangGraph app # 定义请求和响应模型 class ChatRequest(BaseModel): message: str session_id: Optional[str] None # 用于区分不同会话 class ChatResponse(BaseModel): session_id: str response: str tool_calls: List[dict] [] # 初始化 FastAPI 应用 api_app FastAPI(titleLangChainLangGraph Agent API) # 简单的内存会话存储生产环境应使用 Redis、数据库等 sessions {} def get_session_messages(session_id: str): 获取或初始化指定会话的消息历史。 if session_id not in sessions: sessions[session_id] [] return sessions[session_id] api_app.post(/chat, response_modelChatResponse) async def chat_endpoint(request: ChatRequest): 主要的聊天端点。 try: session_id request.session_id or default_session messages get_session_messages(session_id) # 将用户新消息加入历史 from langchain_core.messages import HumanMessage messages.append(HumanMessage(contentrequest.message)) # 准备初始状态 initial_state {messages: messages} # 流式或一次性执行 Agent 图 final_state None async for event in agent_app.astream(initial_state, stream_modevalues): final_state event if not final_state: raise HTTPException(status_code500, detailAgent execution failed.) # 获取最新的 AI 回复 ai_response_msg final_state[messages][-1] response_text ai_response_msg.content if hasattr(ai_response_msg, content) else str(ai_response_msg) # 更新会话历史 sessions[session_id] final_state[messages] # 提取工具调用信息如果有 tool_calls_info [] if hasattr(ai_response_msg, tool_calls) and ai_response_msg.tool_calls: tool_calls_info [{name: tc[name], args: tc[args]} for tc in ai_response_msg.tool_calls] return ChatResponse( session_idsession_id, responseresponse_text, tool_callstool_calls_info ) except Exception as e: raise HTTPException(status_code500, detailfInternal server error: {str(e)}) api_app.get(/health) async def health_check(): 健康检查端点。 return {status: healthy} if __name__ __main__: import uvicorn uvicorn.run(api_app, host0.0.0.0, port8000)6.2 启动与测试 API启动服务python api_server.py服务将在http://127.0.0.1:8000启动。测试 API 使用curl或 Postman 进行测试。# 发送一个聊天请求 curl -X POST http://127.0.0.1:8000/chat \ -H Content-Type: application/json \ -d {message: 计算 2 的 10 次方, session_id: test_user_1} # 健康检查 curl http://127.0.0.1:8000/health你应该会收到一个包含 Agent 回复的 JSON 响应。6.3 批量任务处理对于批量任务核心思路是利用会话隔离和异步处理。会话隔离为每个批量任务或每个用户分配唯一的session_id确保状态不互相干扰。异步队列对于大量任务可以使用asyncio、Celery或RQ等任务队列。将每个用户的请求包装成一个异步任务放入队列中处理。一个简单的异步批量处理示例概念import asyncio from concurrent.futures import ThreadPoolExecutor async def process_batch_requests(requests: List[ChatRequest]): 并发处理一批聊天请求。 tasks [] for req in requests: # 为每个请求创建任务注意要深拷贝或确保状态隔离 task asyncio.create_task(handle_single_request(req)) tasks.append(task) results await asyncio.gather(*tasks, return_exceptionsTrue) return results # 在实际的 handle_single_request 函数中你需要为每个请求初始化一个独立的 Agent 图实例或确保状态完全隔离。重要提示LangGraph 的Checkpoint机制非常适合保存和恢复任务状态对于需要暂停/恢复的长时间运行批量任务尤其有用。你可以将检查点存储到数据库实现任务的持久化。7. 资源占用与性能观察由于我们的 Agent 核心依赖于远程 LLM API因此本地资源占用主要集中在内存RAM和网络 I/O。CPU/GPU本地运行时Python 进程和 LangChain/LangGraph 框架本身消耗的 CPU 可忽略不计。除非你在本地运行大模型如通过 Ollama否则 GPU 不是必须的。内存RAM主要占用来自加载的 Python 库和缓存。一个简单的 Agent 服务通常在几百 MB 到 1 GB 左右。如果处理大量并发或缓存大量会话历史内存使用会增加。网络延迟这是性能瓶颈。每次 Agent 决策和工具调用都可能涉及一次或多次 LLM API 调用。优化策略包括缓存对常见或重复的查询结果进行缓存。超时设置为 API 调用设置合理的超时时间。异步调用使用async/await避免阻塞提高并发处理能力。精简上下文合理设计提示词和对话历史管理避免发送过长的无用上下文给 LLM这既能降低成本也能提升速度。监控建议使用psutil库监控进程内存。在 FastAPI 中使用中间件记录每个请求的响应时间。监控 LLM API 的调用次数和费用。8. 常见问题与排查方法在开发和运行过程中你可能会遇到以下典型问题。问题现象可能原因排查方式解决方案导入 LangChain 或 LangGraph 失败Python 版本不兼容虚拟环境未激活依赖未正确安装。检查 Python 版本 (python --version)。确认虚拟环境已激活 (which python)。使用 Python 3.10/3.11。在项目目录下重新激活 venv并运行pip install -r requirements.txt。调用 LLM API 超时或报错API 密钥错误或过期网络问题模型名称或 base_url 不正确服务端限流。检查环境变量DEEPSEEK_API_KEY是否设置。用curl或 Postman 直接测试 API 端点。查看错误信息详情。确认密钥有效。检查base_url和model参数。添加重试逻辑和更长的超时时间。Agent 不调用工具直接回答工具定义描述不清LLM 温度 (temperature) 过高提示词 (system prompt) 未明确要求使用工具。检查工具函数的docstring是否清晰。将temperature调低如 0.1。强化系统提示词。完善工具描述。调整 LLM 参数。在提示词中明确指示“你必须使用可用工具来回答问题”。LangGraph 图编译或运行出错状态 (State) 定义与节点返回值不匹配图节点或边定义有误。仔细检查StateGraph的State类型定义和每个节点返回的字典结构是否一致。使用app.get_graph().draw_mermaid()可视化图结构需安装pygraphviz。确保所有节点都返回与State类型兼容的字典。简化图结构逐步调试。多轮对话中状态混乱会话 (session_id) 管理不当不同用户的消息历史混在一起。检查 API 服务中是否正确地根据session_id隔离了messages列表。确保为每个会话/用户维护独立的消息历史存储。使用数据库或 Redis 持久化会话状态。工具执行出错如计算器 eval 不安全工具函数内部逻辑有 bug 或存在安全风险。在工具函数内部添加详细的日志和异常捕获。避免在生产环境使用eval。使用安全的计算库如numexpr。对所有输入进行验证和清理。部署后 API 性能差未使用异步LLM API 调用是瓶颈没有缓存。使用性能分析工具如cProfile,py-spy定位热点。监控 API 响应时间。将同步的invoke改为异步的ainvoke。引入缓存机制如langchain.cache。考虑对 LLM 调用进行批处理。9. 最佳实践与使用建议基于实战经验以下建议能帮助你构建更稳健、高效的 Agent 系统从简单开始逐步复杂化先让一个工具调用跑通再增加第二个最后引入 LangGraph 处理复杂流程。不要一开始就设计庞大的图。精心设计工具工具的描述 (docstring) 要清晰、具体。这是 LLM 能否正确理解和使用工具的关键。工具函数内部要有完善的错误处理和日志。管理好上下文长度对话历史会不断增长。设定一个合理的上下文窗口大小或者使用LangChain的摘要、向量检索等方法来压缩和提取关键历史信息避免因 token 超限导致 API 调用失败或成本激增。实现健壮的错误处理在 Agent 工作流中对 LLM 调用、工具执行、网络请求等都可能出错。使用try...except包裹关键步骤并设计降级策略例如工具调用失败时让 Agent 告知用户并尝试其他方法。为生产环境做准备配置管理使用.env文件或配置中心管理 API 密钥、模型参数等。日志记录集成logging模块记录详细的运行日志便于调试和审计。监控告警监控 API 的可用性、响应时间、错误率以及 LLM 的 token 消耗。安全隔离严格控制工具的执行权限特别是文件操作、网络请求等。考虑在沙箱环境中运行不可信的工具。深入理解 MCP本文为简化使用了原生工具定义。MCP 的真正优势在于标准化和动态集成。研究如何将你的工具打包为独立的 MCP 服务器这样任何支持 MCP 的客户端包括未来的 LangChain 版本都可以无缝使用它们极大提升了工具的可复用性和生态互操作性。10. 总结与下一步通过本文的实战演练你应该已经掌握了使用 LangChain LangGraph 构建一个具备工具调用和状态管理能力的 AI Agent 的核心流程。我们从环境搭建开始经历了定义工具、构建基础 Agent、升级为有状态的 LangGraph 工作流最后将其封装为可对外提供服务的 API。这条路径清晰地展示了从原型到可运行服务的关键步骤。最值得尝试的下一步集成真实工具将示例中的模拟搜索工具替换为真实的 Google Search API、数据库查询或企业内部系统接口。探索 MCP 服务器尝试将一个工具如文件阅读器改造成符合 MCP 协议的独立服务器让你的 Agent 通过 MCP 客户端来调用它体验标准化的好处。优化工作流在你的 LangGraph 中尝试添加更多类型的节点比如用于验证工具调用结果的“审核节点”或者根据结果进行条件分支的“路由节点”。前端交互为你的 FastAPI 后端构建一个简单的 Web 前端例如使用 Gradio 或 Streamlit实现一个交互式的聊天界面。构建 AI Agent 是一个迭代和持续优化的过程。这个技术栈提供了强大的灵活性和扩展能力剩下的就是结合你的具体业务场景去设计和打磨那个最合适的“智能体”了。建议将本文中的代码作为起点收藏备用并在实际项目中不断调整和深化。