
在实际 AI 应用开发中构建一个能理解用户意图、调用工具并完成复杂任务的智能体Agent是当前技术探索的热点。谷歌推出的 Gemini Spark 智能体正是这一方向的典型代表它展示了将大语言模型与工具调用能力结合以完成订机票、整理邮件等实际任务的潜力。对于开发者而言理解智能体的核心工作机制并能在自己的项目中实现类似功能远比单纯关注某个产品的地区开放状态更有价值。本文将从工程实践角度拆解一个类似“Spark 智能体”的智能体系统是如何工作的。我们将不依赖任何特定闭源平台而是使用开源工具和框架构建一个具备工具调用能力的本地化智能体原型。通过这个过程你将掌握智能体的核心概念、工作流设计、工具集成方法以及本地部署的关键步骤。无论你是想为现有应用添加 AI 能力还是探索多智能体协作本文提供的思路和代码都将是一个扎实的起点。1. 理解智能体从概念到工作流在讨论具体实现之前必须厘清“智能体”在此语境下的准确含义。它并非指一个独立的、拥有固定功能的 AI 程序而是一个由大语言模型LLM、规划器Planner、工具集Tools和记忆模块Memory组成的动态系统。1.1 智能体的核心组件一个典型的任务型智能体包含以下四个核心部分它们协同工作形成一个决策与执行的闭环大语言模型LLM作为智能体的“大脑”负责理解用户输入的自然语言进行推理和规划并决定下一步该调用哪个工具或如何回复用户。它不直接执行操作而是生成决策指令。工具集Tools这是智能体的“手”和“脚”。每个工具都是一个具体的函数或 API 接口能执行一个明确的操作例如查询天气、搜索网页、发送邮件、操作数据库等。LLM 通过调用这些工具来影响外部世界。规划器与执行器Planner/Executor这部分是智能体的“调度中心”。它接收 LLM 的决策解析出需要调用的工具和参数然后安全地执行对应的工具函数并将执行结果返回给 LLM 进行下一轮决策。记忆模块Memory智能体的“短期与长期记忆”。它保存对话历史、工具执行结果和上下文信息确保智能体在多轮对话中保持连贯性并能基于历史信息进行更合理的规划。1.2 智能体的工作流程智能体处理一个用户请求如“帮我订一张下周五从北京飞往上海的机票”的标准工作流程如下意图理解与任务分解LLM 首先分析用户请求将其分解为一系列可执行的子任务。例如[确认出行日期 查询航班信息 选择合适航班 填写乘机人信息 完成支付]。工具选择与参数提取对于当前需要执行的子任务如“查询航班信息”LLM 从已注册的工具集中选择最合适的工具如search_flights并根据对话上下文生成调用该工具所需的参数如departure_city: “北京”, arrival_city: “上海”, date: “下周五”。工具安全执行执行器接收到{“tool”: “search_flights”, “args”: {…}}这样的结构化调用指令后在安全沙箱或受控环境中运行对应的工具函数并获取结果如一个航班列表的 JSON 数据。结果分析与下一步决策执行器将工具返回的结果反馈给 LLM。LLM 分析结果判断当前子任务是否完成并决定下一步动作是继续调用下一个工具如“选择航班”还是将整合后的信息回复给用户。循环与交付上述步骤循环进行直到所有子任务完成或遇到无法解决的问题。最终LLM 生成一个面向用户的、自然语言的总结或确认信息。这个流程的关键在于LLM 始终处于“指挥官”的位置它不直接操作订票系统而是通过调度“工具兵”来完成具体工作。2. 环境准备与核心工具选型为了构建一个可运行的智能体原型我们需要选择一套开源技术栈。我们的目标是搭建一个轻量级、易于理解和扩展的本地开发环境。2.1 基础环境与 Python 设置本项目基于 Python 进行开发。请确保你的环境满足以下要求操作系统macOS, Linux (如 Ubuntu)或 Windows (建议使用 WSL2)。Python 版本 3.9。推荐使用 3.10 或 3.11 以获得最佳兼容性。包管理工具使用pip进行依赖管理。强烈建议使用虚拟环境venv或conda来隔离项目依赖。创建并激活虚拟环境的命令如下# 创建虚拟环境 python -m venv agent_env # 激活虚拟环境 (Linux/macOS) source agent_env/bin/activate # 激活虚拟环境 (Windows) agent_env\Scripts\activate2.2 核心框架与库的选择我们将使用LangChain作为智能体框架的核心。LangChain 是一个用于开发由 LLM 驱动的应用程序的流行框架它抽象了智能体、链、工具等概念提供了丰富的内置工具和易于扩展的接口。同时我们需要一个 LLM 作为智能体的“大脑”。为了完全本地化和可控制我们选择使用Ollama来在本地运行开源大模型。Ollama 可以方便地拉取和运行如Llama 3、Mistral、Gemma等模型。此外我们还需要一些示例工具。我们将创建两个简单的工具一个模拟搜索一个模拟计算。最终的依赖列表如下库名版本 (示例)作用langchain0.1.0智能体框架提供核心的 Agent、Chain、Tool 等抽象。langchain-community0.0.10LangChain 社区维护的工具和集成。ollama0.1.0用于与本地 Ollama 服务交互的 Python 客户端。requests2.31.0用于创建调用外部 API 的工具。python-dotenv1.0.0管理环境变量如需接入在线 API如 SerpAPI。在激活的虚拟环境中使用以下命令安装依赖pip install langchain langchain-community ollama requests python-dotenv2.3 本地大模型服务部署 (Ollama)Ollama 的安装非常简单。访问 Ollama 官网 下载对应操作系统的安装包并安装。安装完成后打开终端拉取一个中等尺寸的模型例如llama3.1:8bollama pull llama3.1:8b拉取完成后Ollama 服务会自动在本地启动默认端口 11434。你可以通过以下命令测试模型是否运行正常ollama run llama3.1:8b在出现的提示符后输入Hello看到模型回复即表示服务正常。按CtrlD退出交互模式。注意首次拉取模型可能需要较长时间取决于你的网络速度和模型大小。llama3.1:8b是一个在性能和资源消耗上比较平衡的选择适合在个人电脑上运行。如果你的机器配置较低可以考虑更小的模型如phi3:mini。3. 构建一个具备工具调用能力的本地智能体现在我们将开始编写代码构建我们的第一个智能体。这个智能体将能够理解用户关于“天气”和“数学计算”的请求并通过调用相应的工具来回答。3.1 项目结构与初始化创建一个新的项目目录结构如下local_agent_demo/ ├── .env # 环境变量文件可选 ├── tools/ # 自定义工具目录 │ └── custom_tools.py ├── agent_core.py # 智能体核心逻辑 └── main.py # 主程序入口首先我们在tools/custom_tools.py中定义两个简单的工具。在 LangChain 中工具可以通过tool装饰器或继承BaseTool类来创建。# tools/custom_tools.py from langchain.tools import tool import requests import json import math tool def get_weather(city: str) - str: 获取指定城市的当前天气信息。这是一个模拟工具。 # 注意这是一个模拟函数。真实场景应调用如 OpenWeatherMap 的 API。 # 这里我们返回一个模拟的固定响应。 weather_data { “北京”: {“weather”: “晴”, “temperature”: “25°C”, “humidity”: “40%”}, “上海”: {“weather”: “多云”, “temperature”: “28°C”, “humidity”: “65%”}, “广州”: {“weather”: “阵雨”, “temperature”: “30°C”, “humidity”: “80%”}, } if city in weather_data: return json.dumps(weather_data[city], ensure_asciiFalse) else: return json.dumps({“error”: f“未找到城市 {city} 的天气信息”}, ensure_asciiFalse) tool def calculate(expression: str) - str: 计算一个简单的数学表达式。支持 , -, *, /, **, sqrt()。 # 警告在生产环境中直接 eval 是极度危险的这里仅用于演示。 # 应使用安全的表达式解析库如 asteval。 try: # 做一些简单的安全过滤非常基础 expression expression.replace(“sqrt”, “math.sqrt”) allowed_chars set(“0123456789-*/. ()**mathsqrt”) if not all(c in allowed_chars for c in expression): return “错误表达式中包含不允许的字符。” result eval(expression, {“__builtins__”: {}}, {“math”: math}) return str(result) except Exception as e: return f“计算错误{e}” # 将工具放入一个列表方便后续注册 CUSTOM_TOOLS [get_weather, calculate]关键安全提示上述calculate工具使用了eval这在演示中为了方便但在任何面向用户的生产环境中都是绝对禁止的因为它会执行任意代码导致严重的安全漏洞。生产环境必须使用安全的数学表达式解析库。3.2 创建智能体核心接下来在agent_core.py中我们连接 Ollama 服务加载工具并创建智能体。# agent_core.py from langchain.agents import AgentExecutor, create_react_agent from langchain_community.llms import Ollama from langchain.prompts import PromptTemplate from tools.custom_tools import CUSTOM_TOOLS def create_agent(): 创建并返回一个配置好的智能体执行器。 # 1. 初始化本地 LLM (通过 Ollama) # 确保 ollama serve 正在运行且模型已下载如 llama3.1:8b llm Ollama(model“llama3.1:8b”, temperature0.1) # temperature 控制创造性对于工具调用任务建议设置较低如0.1-0.3以保证稳定性。 # 2. 定义智能体的提示词模板 # ReAct 框架的提示词会指导模型进行“思考-行动-观察”的循环。 prompt PromptTemplate.from_template(“”” 你是一个乐于助人的AI助手可以调用工具来帮助用户解决问题。 你可以使用的工具有 {tools} 请严格按照以下格式回应 思考你需要先思考当前情况决定是否需要使用工具以及使用哪个工具。 行动你需要调用的工具名称必须是以下之一[{tool_names}] 行动输入调用该工具所需的输入必须是一个简单的字符串。 观察工具返回的结果。 ... (这个“思考/行动/观察”循环可以重复多次) 当你最终得出答案时必须以以下格式开始 最终答案你的回答 开始 之前的对话记录 {chat_history} 用户输入{input} {agent_scratchpad}“””) # 3. 创建智能体 # 使用 ReAct 框架这是一种让 LLM 进行推理和行动的有效范式。 agent create_react_agent(llm, CUSTOM_TOOLS, prompt) # 4. 创建智能体执行器 # 它负责运行智能体处理工具调用循环。 agent_executor AgentExecutor( agentagent, toolsCUSTOM_TOOLS, verboseTrue, # 设为 True 可以看到详细的思考过程调试时非常有用。 handle_parsing_errorsTrue, # 当模型输出格式错误时尝试修复。 max_iterations5, # 限制最大循环次数防止无限循环。 early_stopping_method“generate”, # 当模型决定不再调用工具时停止。 ) return agent_executor if __name__ “__main__”: # 快速测试 agent create_agent() result agent.invoke({“input”: “上海今天天气怎么样”}) print(result[“output”])3.3 编写主程序并运行测试最后在main.py中我们创建一个简单的交互循环。# main.py from agent_core import create_agent def main(): print(“初始化本地智能体... (使用 Ollama Llama 3.1)”) agent create_agent() print(“\n智能体已就绪。你可以询问天气或进行简单计算。输入 ‘quit’ 或 ‘exit’ 退出。”) print(“示例”) print(“ - 北京天气如何”) print(“ - 计算一下 15 的平方加上 20 除以 4 等于多少”) while True: try: user_input input(“\n “).strip() if user_input.lower() in [“quit”, “exit”, “q”]: print(“再见”) break if not user_input: continue # 调用智能体 response agent.invoke({“input”: user_input}) print(f“\n助手{response[‘output’]}”) except KeyboardInterrupt: print(“\n程序被中断。”) break except Exception as e: print(f“\n处理请求时出错{e}”) if __name__ “__main__”: main()现在运行你的智能体确保 Ollama 服务正在运行运行ollama serve或已通过ollama run启动。在项目根目录下执行python main.py你应该会看到类似以下的交互过程verboseTrue会打印详细日志初始化本地智能体... (使用 Ollama Llama 3.1) 智能体已就绪。你可以询问天气或进行简单计算。输入 ‘quit’ 或 ‘exit’ 退出。 示例 - 北京天气如何 - 计算一下 15 的平方加上 20 除以 4 等于多少 北京天气如何 进入新的 AgentExecutor 链... 思考用户想知道北京的天气。我有一个工具叫 get_weather 可以获取城市天气。 行动get_weather 行动输入北京 观察{“weather”: “晴”, “temperature”: “25°C”, “humidity”: “40%”} 思考我已经通过工具获取了北京的天气信息现在可以回答用户了。 最终答案北京今天的天气是晴天气温 25°C湿度 40%。 链结束。 助手北京今天的天气是晴天气温 25°C湿度 40%。4. 核心机制详解与参数调优成功运行基础智能体后我们需要深入理解其内部机制并知道如何调整参数以适应更复杂的任务。4.1 ReAct 框架与智能体循环我们的智能体使用了ReAct (Reason Act)框架。这是当前让 LLM 可靠调用工具的主流范式之一。其核心循环是Reason (思考)LLM 分析当前状态用户问题、历史、可用工具并决定下一步。Act (行动)LLM 生成一个结构化的行动指令通常是工具名和输入参数。Observe (观察)执行器运行工具并将结果文本或 JSON作为观察返回给 LLM。循环LLM 基于新的观察再次进行思考决定是继续行动还是给出最终答案。在agent_core.py的提示词模板中我们强制 LLM 按照思考... 行动... 行动输入... 观察...的格式输出就是为了引导它遵循这个循环。AgentExecutor负责解析这个输出执行行动并将观察插回下一轮的提示词中通过{agent_scratchpad}占位符。4.2 关键配置参数解析在创建AgentExecutor时有几个参数对智能体的行为和稳定性至关重要参数类型默认值/示例作用与影响verboseboolFalse设为True时会在控制台打印完整的思考、行动、观察链是调试智能体逻辑的必备工具。handle_parsing_errorsbool/strFalse当 LLM 的输出不符合预期的工具调用格式时是否尝试修复。设为True或“generate”可以增加鲁棒性但可能掩盖根本问题。max_iterationsint15限制 ReAct 循环的最大次数。必须设置以防止智能体陷入无限循环或处理过于复杂的任务耗尽资源。对于简单任务5-10 次足够。early_stopping_methodstr“force”停止条件。“force”在达到max_iterations时强制停止“generate”允许 LLM 自己决定何时输出最终答案通常更友好。memoryBaseMemoryNone为智能体添加记忆。可以是一个ConversationBufferMemory的实例使智能体拥有多轮对话能力。llm的temperaturefloat0.1控制 LLM 输出的随机性。对于工具调用任务强烈建议设置为较低值0.1-0.3以保证工具名称和参数生成的准确性。高temperature会导致不可预测的工具调用。4.3 为智能体添加对话记忆目前的智能体是“单轮”的它不会记住之前的对话。要让它像 ChatGPT 一样进行连贯的多轮对话需要引入记忆模块。修改agent_core.py中的create_agent函数# agent_core.py (修改部分) from langchain.memory import ConversationBufferMemory # ... 其他导入不变 ... def create_agent(): llm Ollama(model“llama3.1:8b”, temperature0.1) # 新增创建对话记忆 memory ConversationBufferMemory(memory_key“chat_history”, return_messagesTrue) prompt PromptTemplate.from_template(“”” ... (之前的提示词模板注意我们已经有了 {chat_history} 占位符) ... “””) # 提示词模板保持不变因为它已经包含了 {chat_history} agent create_react_agent(llm, CUSTOM_TOOLS, prompt) agent_executor AgentExecutor( agentagent, toolsCUSTOM_TOOLS, verboseTrue, handle_parsing_errorsTrue, max_iterations5, early_stopping_method“generate”, memorymemory, # 关键将 memory 对象传入执行器 ) return agent_executor同时需要修改main.py中的调用方式因为现在invoke需要传入一个包含input和chat_history的字典而AgentExecutor会自动管理chat_history。我们之前的调用方式agent.invoke({“input”: user_input})仍然有效因为AgentExecutor会与绑定的memory自动交互。现在你的智能体可以处理这样的对话了用户北京天气如何 助手北京今天是晴天25°C。 用户那湿度呢 助手能根据上下文知道“湿度”指的是北京的湿度5. 常见问题排查与性能优化在开发和运行智能体时你可能会遇到以下典型问题。这里提供一套排查路径。5.1 智能体不调用工具直接回答现象对于“北京天气”智能体直接编造一个答案而不是调用get_weather工具。可能原因与排查提示词问题检查提示词模板是否清晰说明了工具的使用格式思考/行动/观察。确保{tools}和{tool_names}被正确替换。可以在verboseTrue模式下查看实际发送给模型的提示词。LLM 能力不足较小的或未经专门训练的模型可能无法可靠遵循复杂指令。尝试换一个更强的模型如llama3.1:8b-llama3.1:70b或qwen2.5:7b。Temperature 过高将temperature调低如 0.1减少随机性。工具描述不清确保tool装饰器下的文档字符串获取指定城市的当前天气信息。清晰、准确。LLM 主要靠这个描述来选择工具。5.2 工具调用格式解析错误现象控制台报错OutputParserException提示无法解析模型的输出。可能原因与排查模型输出格式错误在verboseTrue模式下查看模型输出的原始文本。它可能没有严格按照“行动工具名”的格式输出。这通常也是模型指令遵循能力或提示词的问题。启用错误处理确保AgentExecutor的handle_parsing_errorsTrue。这会让执行器尝试让模型重试或进行修复。简化提示词对于能力较弱的模型使用更简单、更强制性的提示词。LangChain 内置了一些针对不同模型的优化提示词可以尝试create_react_agent的其他变体或使用ZeroShotAgent。5.3 智能体陷入无限循环现象智能体反复调用同一个工具或在不同工具间来回切换无法给出最终答案。可能原因与排查设置迭代上限这是最重要的防护措施。务必设置max_iterations如 5 或 10。工具返回结果不明确工具返回的结果可能无法让 LLM 判断任务是否完成。例如搜索工具返回“未找到结果”LLM 可能认为需要换关键词再搜。确保工具在失败时返回清晰、结构化的错误信息。任务过于复杂或模糊LLM 无法将模糊的用户请求分解为清晰的步骤。引导用户提出更具体的问题或在智能体前端增加一个“任务澄清”的步骤。5.4 本地模型响应缓慢现象每次交互都要等待很长时间。优化建议模型量化使用 Ollama 提供的量化版本模型如llama3.1:8b:q4_0。量化能在几乎不损失精度的情况下大幅减少内存占用和提升推理速度。使用ollama pull llama3.1:8b:q4_0拉取。硬件加速确保 Ollama 正确利用了你的 GPU如果可用。运行ollama run llama3.1:8b时观察终端输出或使用nvidia-smiNVIDIA查看 GPU 是否被占用。调整参数减少max_iterations和max_new_tokens在 Ollama 模型参数中设置来限制单次生成的文本长度。6. 从原型到生产扩展方向与最佳实践一个玩具级的智能体与一个可用于生产的智能体之间存在巨大差距。以下是将你的智能体原型推向更实用阶段需要考虑的方向和建议。6.1 扩展工具集真正的智能体威力在于其工具集。你可以集成无数外部服务网络搜索使用SerpAPI或DuckDuckGo Search让智能体获取实时信息。代码执行集成一个安全的代码沙箱如Jupyter Kernel让智能体可以运行数据分析脚本。文件操作创建工具来读取、总结、修改本地文件需严格控制权限。数据库查询连接数据库让智能体通过自然语言进行数据查询。API 集成连接企业内部系统如 CRM、ERP、工单系统等。在 LangChain 中社区已经提供了 数百种内置工具 可以直接使用。6.2 提升智能体规划能力基础的 ReAct 智能体对于复杂、多步骤的任务可能规划能力不足。可以考虑更高级的架构Plan-and-Execute 架构使用一个“规划者”LLM 先将整个任务分解成详细的步骤列表再由一个“执行者”LLM 或智能体按步骤调用工具。LangChain 的PlanAndExecute执行器实现了这一模式。智能体路由Multi-Agent创建多个各司其职的智能体如“搜索专家”、“数据分析师”、“文案写手”并由一个“主管”智能体根据任务类型来路由请求。这适用于非常复杂的任务流水线。6.3 生产环境部署考量如果你打算将智能体部署给真实用户使用必须考虑以下方面安全性工具权限隔离每个工具应在最小必要权限下运行。文件操作工具不能访问系统关键目录。输入验证与过滤对所有用户输入和工具参数进行严格的验证、清洗和转义防止注入攻击。禁用危险工具像我们演示中calculate使用的eval必须被替换。访问控制对智能体本身和其背后的工具 API 实施身份认证和速率限制。可靠性错误处理与降级工具调用失败时智能体应有优雅的降级策略如返回缓存数据、提示用户重试。超时控制为 LLM 调用和每个工具调用设置超时避免长时间阻塞。日志与监控详细记录智能体的思考过程、工具调用和结果。这不仅是调试的需要也是理解用户意图、发现系统缺陷的关键。性能与成本缓存对频繁且结果不变的查询如某些天气信息、静态数据实施缓存。异步处理对于耗时长的任务采用异步模式先返回任务 ID让用户后续查询结果。模型选型在效果和成本间权衡。简单的任务可以使用小模型复杂任务再调用大模型。用户体验流式响应不要让用户等待整个思考链完成再看到结果。可以先将 LLM 的“思考”部分以“正在为您查询…”的形式流式输出再逐步展示工具调用结果和最终答案。确认与澄清对于涉及重要操作的任务如发送邮件、修改数据智能体应在执行前向用户确认。构建一个成熟可用的智能体系统是一个复杂的工程本文提供的原型是通往这个目标的第一个坚实台阶。从理解工作流开始逐步集成更强大的工具并持续在安全性、可靠性和用户体验上迭代你就能打造出真正解决实际问题的 AI 智能体。