AI代理工具调用实战:为ChatGPT添加电脑操控与网页浏览能力

AI代理工具调用实战:为ChatGPT添加电脑操控与网页浏览能力 在实际的 AI 应用开发中让大语言模型如 ChatGPT不仅能理解和生成文本还能直接操控计算机如打开应用、执行命令或通过内置浏览器访问实时网络信息是提升其作为智能助手实用性的关键一步。这类功能通常被称为“工具调用”Tool Calling或“代理”Agent能力它允许模型在对话中识别用户意图并安全地执行外部操作。本文将围绕如何为类似 ChatGPT 的模型赋予电脑操控与内置浏览器能力从核心概念、环境搭建、代码实现到安全实践提供一个完整的、可落地的技术方案。本文适合有一定 Python 基础对 AI 应用开发、OpenAI API 或相关开源模型如 GPT-4o, Claude, DeepSeek有基本了解的开发者。你将学会如何构建一个能够理解“请帮我打开计算器”或“查一下今天的天气”这类指令并实际执行操作的程序。我们将使用 Python 作为主要开发语言并重点介绍如何通过代码安全地实现这些功能。1. 理解 AI 代理与工具调用的工作机制在深入代码之前必须先理解这类应用是如何工作的。其核心并非模型本身直接操作你的电脑而是通过一个“代理”框架来协调。1.1 从对话到行动的基本流程一个典型的 AI 代理执行流程包含以下几个步骤用户输入用户提出一个需要执行操作的请求例如“打开记事本并搜索最近的新闻。”意图识别大语言模型分析用户的自然语言判断其是否需要调用外部工具如“打开记事本”需要调用系统命令“搜索新闻”需要调用浏览器。工具匹配与参数解析模型从预定义的工具列表中选择合适的工具并解析出执行该工具所需的参数例如对于“打开记事本”工具是execute_command参数是程序路径notepad.exe。工具执行代理框架即我们编写的程序安全地调用对应的本地函数或 API。结果收集与总结工具执行后产生结果如命令执行成功或网页内容该结果被返回给大语言模型。最终回复生成模型根据工具执行结果生成一段自然语言回复给用户完成整个交互。这个流程的核心在于“工具调用”协议例如 OpenAI 的function calling或 Anthropic 的tool use。1.2 关键技术组件实现上述流程需要三个关键技术组件大语言模型LLM负责理解用户意图和生成回复。可以使用云端 API如 OpenAI GPT-4, Anthropic Claude或本地部署的开源模型。代理框架Agent Framework负责管理对话流程、工具调用决策和结果处理。常见的框架有 LangChain, LlamaIndex, Semantic Kernel 等它们提供了高层抽象简化开发。本文为了清晰理解原理会从相对底层的实现开始。工具集Tools一系列可被模型调用的函数每个函数封装一个具体能力如执行系统命令、控制浏览器、读写文件等。2. 环境准备与依赖配置我们将构建一个基于 Python 和 OpenAI API兼容其他提供类似功能的 API的简单代理。以下是所需的环境和依赖。2.1 Python 环境与核心库首先确保你的系统已安装 Python 3.8 或更高版本。然后使用pip安装核心依赖库。# 创建并激活一个虚拟环境推荐 python -m venv ai_agent_env source ai_agent_env/bin/activate # Linux/macOS # ai_agent_env\Scripts\activate # Windows # 安装核心库 pip install openai requests selenium beautifulsoup4各库的作用说明openai: 官方 Python SDK用于调用 OpenAI ChatGPT 模型如 gpt-3.5-turbo, gpt-4。requests: 用于发送 HTTP 请求访问网络 API如天气查询。selenium: 用于自动化控制网页浏览器如 Chrome, Firefox实现“内置浏览器”功能。beautifulsoup4: 用于解析 HTML 网页内容从selenium获取的页面中提取所需信息。2.2 浏览器驱动配置针对 Seleniumselenium需要对应的浏览器驱动才能工作。以 Chrome 为例确保已安装 Google Chrome 浏览器。查看 Chrome 版本在浏览器地址栏输入chrome://version/。从 ChromeDriver 下载页面 下载与你的 Chrome 版本匹配的chromedriver。将下载的chromedriver.exe(Windows) 或chromedriver(Linux/macOS) 文件放在系统 PATH 包含的目录下如 Python 安装目录的Scripts文件夹或者直接在代码中指定其路径。2.3 API 密钥配置如果使用 OpenAI API你需要一个有效的 API 密钥。访问 OpenAI Platform 并登录。在 API Keys 页面生成一个新的密钥。重要不要将密钥硬编码在代码中。最佳实践是使用环境变量。# 在命令行中设置环境变量临时 export OPENAI_API_KEYyour-api-key-here # Linux/macOS set OPENAI_API_KEYyour-api-key-here # Windows Command Prompt $env:OPENAI_API_KEYyour-api-key-here # Windows PowerShell在代码中通过os.environ读取import os api_key os.environ.get(OPENAI_API_KEY) if not api_key: raise ValueError(请设置 OPENAI_API_KEY 环境变量)3. 构建核心工具函数代理的能力完全由我们提供的工具函数决定。下面实现“电脑操控”和“内置浏览器”相关的核心工具。3.1 系统命令执行工具这个工具允许 AI 执行基本的系统命令如打开应用程序、列出文件等。出于安全考虑必须严格限制可执行的命令范围。import subprocess import platform def execute_command(command: str, args: list None) - str: 安全地执行系统命令。 参数: command (str): 要执行的基础命令如 notepad, calc, ls, dir. args (list, optional): 命令的参数列表. 返回: str: 命令执行的输出结果或错误信息. # 定义允许的安全命令白名单 SAFE_COMMANDS { windows: [notepad, calc, mspaint, write, magnify], linux: [ls, pwd, date, echo, cat], darwin: [ls, pwd, date, echo, cat, open] # macOS } system platform.system().lower() # 处理 macOS 的 system 名称 if system darwin: safe_list SAFE_COMMANDS[darwin] else: safe_list SAFE_COMMANDS.get(system, []) # 安全检查命令必须在白名单内 if command not in safe_list: return f错误出于安全考虑不允许执行命令 {command}。请联系管理员。 try: # 构建命令参数列表 cmd_list [command] if args: cmd_list.extend(args) # 执行命令并捕获输出 result subprocess.run(cmd_list, capture_outputTrue, textTrue, shellFalse, timeout30) if result.returncode 0: output result.stdout if result.stdout else 命令执行成功。 else: output f命令执行出错 (返回码 {result.returncode}): {result.stderr} return output except subprocess.TimeoutExpired: return 错误命令执行超时。 except Exception as e: return f执行命令时发生异常: {str(e)} # 示例单独测试这个函数 if __name__ __main__: print(execute_command(notepad)) # Windows 下会打开记事本但capture_output会使其在后台通常GUI程序不这样用。 # 对于打开GUI程序通常不需要捕获输出可以改用 subprocess.Popen 并不等待。 # 下面是一个更适用于打开图形程序的版本 def open_gui_program(program_name: str) - str: SAFE_GUI_PROGRAMS [notepad, calc, mspaint] if program_name not in SAFE_GUI_PROGRAMS: return f错误不允许打开程序 {program_name}。 try: subprocess.Popen(program_name, shellTrue) return f已启动 {program_name}。 except Exception as e: return f启动 {program_name} 失败: {str(e)}3.2 网页浏览与信息提取工具这个工具使用 Selenium 控制浏览器访问网页并使用 BeautifulSoup 解析内容将关键信息返回给 AI。from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup import time # 初始化一个全局浏览器驱动实例也可每次创建 def get_browser_driver(): 创建并返回一个配置好的 Chrome 浏览器驱动实例。 chrome_options Options() chrome_options.add_argument(--headlessnew) # 无头模式不显示图形界面适合服务器。 # chrome_options.add_argument(--window-size1920,1080) # 如果非无头设置窗口大小 # 如果 chromedriver 不在 PATH需要指定路径driver webdriver.Chrome(executable_path/path/to/chromedriver, optionschrome_options) driver webdriver.Chrome(optionschrome_options) return driver def browse_website(url: str, question: str ) - str: 访问指定网址并提取页面文本内容或根据问题查找特定信息。 参数: url (str): 要访问的网址. question (str, optional): 用户想从页面中了解的具体问题. 返回: str: 页面的主要内容或针对问题的答案. driver None try: driver get_browser_driver() driver.get(url) # 等待页面主要内容加载 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, body)) ) time.sleep(2) # 额外等待一下动态内容 # 获取页面 HTML 源码 page_source driver.page_source soup BeautifulSoup(page_source, html.parser) # 移除脚本、样式等无关标签 for script in soup([script, style]): script.decompose() # 获取纯文本内容 text soup.get_text(separator , stripTrue) # 简化文本去除过多空白符 lines (line.strip() for line in text.splitlines()) chunks (phrase.strip() for line in lines for phrase in line.split( )) text .join(chunk for chunk in chunks if chunk) # 如果用户有具体问题可以在此处添加逻辑让一个小型模型或规则从 text 中提取答案。 # 例如如果 question 包含“天气”则查找温度、天气状况等关键词。 # 这里为简单起见我们返回前 1000 个字符的摘要。 if question: # 简化处理返回摘要并提示模型自行理解 result f已访问 {url}。页面内容摘要前1000字符: {text[:1000]}...\n请根据以上内容回答用户问题{question}。 else: result f已访问 {url}。页面内容摘要前1000字符: {text[:1000]}... return result except Exception as e: return f访问网址 {url} 时出错: {str(e)} finally: if driver: driver.quit() # 确保关闭浏览器释放资源 # 示例单独测试这个函数 if __name__ __main__: # 测试访问一个新闻网站 summary browse_website(https://news.baidu.com, 今天有什么重大新闻) print(summary)4. 集成大语言模型与工具调用逻辑现在我们将工具函数和 OpenAI API 连接起来实现完整的代理循环。4.1 定义工具描述供模型识别模型需要知道它可以调用哪些工具以及每个工具的用途和参数。这通过一个特定格式的 JSON Schema 来实现。# tools.py # 将之前定义的工具函数和它们的描述放在一起 # 工具函数定义 (同上略) def execute_command(command: str, args: list None) - str: # ... 实现代码 ... def browse_website(url: str, question: str ) - str: # ... 实现代码 ... # 工具描述列表用于提供给 OpenAI API TOOLS [ { type: function, function: { name: execute_command, description: 在用户电脑上执行一个安全的系统命令例如打开记事本、计算器等应用程序。, parameters: { type: object, properties: { command: { type: string, description: 要执行的基础命令如 notepad, calc. }, args: { type: array, items: {type: string}, description: 命令的参数列表可选。 } }, required: [command] } } }, { type: function, function: { name: browse_website, description: 访问一个指定的网址获取网页的主要内容摘要或根据问题查找信息。, parameters: { type: object, properties: { url: { type: string, description: 需要访问的网址必须以 http:// 或 https:// 开头。 }, question: { type: string, description: 用户想从该网页中了解的具体问题可选。 } }, required: [url] } } } ] # 工具名称到实际函数的映射 TOOL_MAPPING { execute_command: execute_command, browse_website: browse_website }4.2 实现代理对话循环这是整个应用的核心它管理着与模型的对话处理模型发出的工具调用请求并返回结果。# agent.py import openai import json from tools import TOOLS, TOOL_MAPPING # 设置 OpenAI API Key (从环境变量读取) client openai.OpenAI(api_keyos.environ.get(OPENAI_API_KEY)) def run_agent_conversation(user_input: str, conversation_history: list None) - (str, list): 运行一轮代理对话。 参数: user_input (str): 用户本次的输入。 conversation_history (list, optional): 之前的对话历史。 返回: tuple: (模型的最终回复, 更新后的对话历史) if conversation_history is None: conversation_history [] # 将用户输入加入历史 conversation_history.append({role: user, content: user_input}) # 第一步将用户输入和工具描述发送给模型让模型决定是否调用工具。 response client.chat.completions.create( modelgpt-3.5-turbo, # 或 gpt-4 messagesconversation_history, toolsTOOLS, tool_choiceauto, # 让模型自动决定是否调用工具以及调用哪个。 ) response_message response.choices[0].message tool_calls response_message.tool_calls # 将模型的初始回复加入历史 conversation_history.append(response_message) # 第二步如果模型要求调用工具则执行相应的工具函数。 if tool_calls: print(f模型要求调用 {len(tool_calls)} 个工具。) for tool_call in tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) print(f执行工具: {function_name}参数: {function_args}) # 从映射中获取对应的函数并执行 function_to_call TOOL_MAPPING[function_name] function_response function_to_call(**function_args) # 将工具执行结果加入对话历史告知模型 conversation_history.append({ role: tool, tool_call_id: tool_call.id, content: function_response, }) # 第三步将工具执行结果返回给模型让模型生成面向用户的最终回复。 second_response client.chat.completions.create( modelgpt-3.5-turbo, messagesconversation_history, ) final_message second_response.choices[0].message final_response_content final_message.content conversation_history.append(final_message) else: # 如果模型没有调用工具直接使用其初始回复 final_response_content response_message.content return final_response_content, conversation_history # 主程序简单的命令行交互界面 if __name__ __main__: history [] print(AI 助手已启动输入 quit 或 exit 退出) while True: user_query input(\n你: ) if user_query.lower() in [quit, exit]: break response, history run_agent_conversation(user_query, history) print(f助手: {response})5. 运行验证与结果分析现在让我们运行这个程序并测试其核心功能。5.1 测试场景一电脑操控启动程序后输入指令“请帮我打开计算器。”预期行为模型识别出“打开计算器”需要调用execute_command工具。工具函数execute_command(calc)被安全地执行。系统计算器程序calc.exe在后台启动。模型收到“命令执行成功”的反馈后生成回复如“已为您打开计算器。”实际运行日志可能如下你: 请帮我打开计算器。 模型要求调用 1 个工具。 执行工具: execute_command参数: {command: calc} 助手: 已为您打开计算器。同时你会在电脑上看到计算器窗口。5.2 测试场景二内置浏览器查询输入指令“访问百度新闻首页看看今天有什么头条新闻。”预期行为模型识别出“访问百度新闻首页”需要调用browse_website工具。工具函数browse_website(https://news.baidu.com, 今天有什么头条新闻)被执行。Selenium 在无头模式下访问该网址并提取页面文本。模型收到包含新闻摘要的工具响应后分析并提炼出头条新闻生成回复如“根据百度新闻首页今天的头条新闻是关于...[具体内容]。”5.3 结果分析要点成功率在定义好的工具白名单和功能范围内对于清晰的指令模型应能正确调用工具。响应时间工具调用尤其是网页浏览会增加响应延迟。需要优化网络和浏览器初始化。结果准确性网页内容提取的准确性取决于页面结构和解析逻辑复杂页面可能需要更精细的解析规则。6. 常见问题排查与安全实践将这样的 AI 代理投入实际使用即使是个人使用必须高度重视安全和稳定性。6.1 常见问题排查表问题现象可能原因检查与解决方式程序报错ModuleNotFoundError依赖库未正确安装。使用pip list检查是否安装了openai,selenium等库。重新执行pip install命令。selenium报错WebDriverException浏览器驱动未找到或版本不匹配。1. 确认chromedriver已在 PATH 中或路径正确。2. 确认 Chrome 浏览器与chromedriver版本匹配。OpenAI API 调用返回认证错误API 密钥无效或未设置。1. 检查OPENAI_API_KEY环境变量是否设置正确。2. 在 OpenAI 平台检查密钥状态和余额。模型不调用工具直接回答“我无法操作电脑”工具描述不够清晰或模型版本不支持。1. 检查tools.py中的工具描述是否准确描述了功能。2. 尝试使用更新的模型如gpt-4。网页访问工具返回空白或错误内容页面加载太慢或需要 JS 渲染。1. 增加WebDriverWait的等待时间。2. 检查browse_website函数中的异常处理。执行命令工具被拒绝命令不在安全白名单内。检查SAFE_COMMANDS或SAFE_GUI_PROGRAMS列表是否包含了你想测试的命令。6.2 核心安全实践严格的命令白名单这是最重要的安全措施。绝对禁止允许模型执行任意命令如rm -rf /,format C:。白名单应尽可能小。最小权限原则运行此 Python 脚本的用户账户不应具有高级系统权限如 Administrator/root。输入验证与沙箱对工具函数的输入参数如 URL、命令参数进行严格验证。对于更高级的使用考虑在 Docker 容器等沙箱环境中运行整个代理。API 密钥保护切勿在代码或版本控制系统中提交 API 密钥。始终使用环境变量或安全的密钥管理服务。监控与日志记录所有工具调用请求和结果便于审计和故障排除。用户意识明确告知用户该代理的能力边界和潜在风险避免其发出危险指令。7. 最佳实践与扩展方向构建一个健壮的 AI 代理应用远不止于功能实现。7.1 工程化最佳实践使用成熟的代理框架对于生产环境强烈建议使用 LangChain 或 LlamaIndex。它们提供了更强大的工具集成、记忆管理、错误处理和可扩展性。异步处理工具调用如网络请求可能是耗时的。使用异步编程如asyncio可以避免阻塞主线程提升响应速度。配置化管理将模型类型、API 端点、安全白名单等配置项外置到配置文件如config.yaml中。添加对话记忆目前的简单实现使用列表存储历史对于长对话会消耗大量 Token。可以引入向量数据库等进行摘要式记忆管理。7.2 功能扩展方向增加更多工具例如文件读写严格限制路径、发送邮件、查询数据库、控制智能家居等。多模态能力集成视觉模型使代理能够“看到”屏幕截图并进行分析和操作。计划与推理对于复杂任务让模型能够分解为多个步骤并逐步执行ReAct 模式。开发图形界面将命令行交互升级为 Web 界面或桌面应用提升用户体验。实现 ChatGPT 等大模型的电脑操控与网页浏览能力本质上是构建一个安全、可控的“AI 代理”。关键在于清晰地定义工具边界、实现可靠的工具函数以及建立模型与工具间稳健的通信机制。从本文提供的最小可行方案出发你可以逐步迭代打造出功能更强大、更安全的个人AI助手。下一步可以深入探索 LangChain 等框架它们能极大地简化上述流程并提供企业级应用所需的各种组件。