1. 项目概述一个为ChatGPT“开刃”的代码库如果你深度使用过ChatGPT的API尤其是它的Function Calling函数调用功能可能会和我有同样的感受它很强大但用起来总有点“钝”。你需要自己写大量的胶水代码来处理对话历史、管理函数注册、解析API返回的复杂JSON、处理可能的错误还要考虑如何将自然语言的用户请求精准地路由到你那一大堆功能各异的函数上。这个过程繁琐、重复而且容易出错。这就是我最初发现johniwasz/whetstone.chatgpt这个项目时的感受——它像一块“磨刀石”Whetstone旨在把ChatGPT API这把本就锋利的“刀”打磨得更加趁手、高效。这个开源库的核心目标非常明确简化基于ChatGPT Function Calling构建复杂AI应用的开发流程。它不是一个全新的AI框架而是一个轻量级、高抽象的“脚手架”或“编排层”让你能专注于定义“做什么”你的业务函数而把“怎么做”与ChatGPT的交互、上下文管理、路由调度交给它。简单来说它帮你处理了所有与OpenAI API对话的脏活累活。你只需要用装饰器定义你的工具函数Whetstone就能自动将它们“翻译”成ChatGPT能理解的Function描述并在对话中智能地调用它们。无论是构建一个能查询数据库、发送邮件、控制智能家居的智能助手还是创建一个能自动执行代码、分析数据、生成报告的分析工具Whetstone都能大幅降低你的开发门槛。我花了相当一段时间在实际项目中集成和测试它从简单的命令行工具到带有Web界面的复杂应用。这篇文章我就来为你彻底拆解whetstone.chatgpt不仅告诉你它怎么用更会深入分享其设计哲学、核心机制、我在实战中踩过的坑以及一些能让你事半功倍的进阶技巧。无论你是想快速给自己的应用加上AI大脑还是希望深入理解AI Agent的底层运作这里都有你需要的干货。2. 核心设计哲学与架构拆解在直接敲代码之前理解一个库为什么这样设计比知道它怎么用更重要。这能帮助你在遇到复杂场景时做出更合理的架构决策。2.1 为什么需要Whetstone从原始API的痛点说起让我们先回顾一下如果不使用任何辅助库直接用OpenAI Python SDK实现一个简单的“天气查询”Function Calling需要哪些步骤定义函数描述手动构造一个符合OpenAI Function Calling规范的JSON字典描述你的函数名、参数和说明。发起对话将用户消息和这个函数描述列表传给ChatCompletion.create。解析响应检查API返回的finish_reason是否为“function_call”。提取参数从message[“function_call”]中解析出函数名和参数字符串通常是JSON格式。调用函数根据函数名找到你本地对应的Python函数并将参数字符串反序列化后传入。处理结果执行函数获取结果。继续对话将函数执行结果作为新的消息再次调用API让ChatGPT生成面向用户的自然语言回复。管理历史你需要自己维护一个消息历史列表并在每次交互中正确传递。这还只是一个函数的情况。当你有几十个函数需要处理多轮对话、函数链式调用、错误处理时代码会迅速变得难以维护。Whetstone的核心价值就是将上述第1、3、4、5、6、7步全部自动化。2.2 核心架构装饰器、注册中心与对话引擎Whetstone的架构非常清晰主要包含三个核心部分tool装饰器这是你与Whetstone交互的主要方式。你只需要在普通的Python函数上加上tool并填写描述信息Whetstone就会在背后自动为你生成OpenAI所需的JSON Schema并注册这个函数。from whetstone import tool tool(name“get_weather”, description“获取指定城市的当前天气”) def fetch_weather(city: str) - str: # 你的业务逻辑 return f“{city}的天气是晴25摄氏度。”注意它利用Python的类型注解city: str来自动推断参数类型这比手动写JSON Schema优雅和安全得多。工具注册中心一个全局的、隐式的注册表。所有被tool装饰的函数都会被自动收集到这里。当你初始化对话引擎时Whetstone会从这个注册中心获取所有工具并一次性生成完整的工具描述列表供ChatGPT使用。这意味着你不需要手动维护一个工具列表代码组织更灵活。Chat引擎这是Whetstone的大脑也是你进行对话的主要接口。它封装了与OpenAI API的整个交互循环。内部状态管理自动维护消息历史messages。工具调用循环自动处理function_call响应定位对应工具执行函数并将结果作为新的消息追加到历史中然后继续调用API直到ChatGPT给出最终的自然语言回复finish_reason为“stop”。流式响应支持可以处理流式输出实现打字机效果。可扩展性允许注入自定义的OpenAI客户端方便你配置代理、自定义基础URL等。这种架构带来的最大好处是“关注点分离”。你作为开发者只需要关心工具函数的实现业务逻辑而复杂的AI交互逻辑被封装在Chat引擎内部。这极大地提升了开发效率和代码的可读性。注意Whetstone目前主要围绕OpenAI的Chat Completion API设计。虽然理论上其设计模式可以适配其他支持类似Function Calling功能的模型如Claude、DeepSeek等但需要一定的改造工作。社区也有相关讨论和分支但在官方未明确支持前将其视为一个针对OpenAI API的优化工具更为稳妥。3. 从零开始快速上手与基础配置理论说得再多不如动手跑一遍。让我们从一个最简单的例子开始搭建一个能运行的环境。3.1 环境准备与安装首先确保你的Python版本在3.8以上。然后通过pip安装Whetstone和OpenAI官方库。pip install whetstone-chatgpt openai接下来你需要一个OpenAI的API密钥。如果你还没有需要去OpenAI平台注册并获取。安全提醒永远不要将API密钥硬编码在代码中或提交到版本控制系统。最推荐的方式是使用环境变量# 在终端中设置临时 export OPENAI_API_KEY‘sk-your-secret-key-here’ # 或者在 .bashrc, .zshrc 或 .env 文件中设置永久3.2 第一个“Hello Agent”程序我们来创建一个最简单的智能助手它只有一个功能打招呼。# hello_agent.py import os from whetstone import Chat, tool from openai import OpenAI # 1. 初始化OpenAI客户端Whetstone内部会使用 client OpenAI(api_keyos.environ.get(“OPENAI_API_KEY”)) # 2. 使用tool装饰器定义一个工具函数 tool(name“greet_user”, description“向用户问好”) def say_hello(user_name: str) - str: “”“一个简单的打招呼函数。”“” return f“你好{user_name}很高兴为你服务。” # 3. 创建Chat引擎实例 chat_engine Chat( clientclient, # 传入自定义客户端 model“gpt-3.5-turbo”, # 指定模型gpt-4系列也可用 # tools参数无需手动传入tool装饰的函数会自动注册 ) # 4. 发起对话 if __name__ “__main__”: # 用户输入 user_message “请用中文向小明打个招呼。” # 调用run方法Whetstone会处理所有中间步骤 final_response chat_engine.run(user_message) # 打印最终结果 print(“AI回复:”, final_response)运行这个脚本python hello_agent.py你应该会看到类似这样的输出AI回复: 你好小明很高兴为你服务。发生了什么Whetstone自动将say_hello函数及其描述、参数信息转换成了ChatGPT能理解的格式。ChatGPT理解了用户的意图是“调用greet_user函数且参数user_name为‘小明’”。Whetstone接收到function_call后自动调用本地的say_hello(“小明”)函数。获取函数结果“你好小明很高兴为你服务。”后Whetstone将其作为新消息再次发送给ChatGPT。ChatGPT这次直接生成了最终的自然语言回复在我们的简单例子里它可能直接返回了函数结果或者稍作润色。这个简单的流程背后是Whetstone替你完成的所有复杂编排。你已经成功创建了一个具备工具调用能力的AI Agent3.3 核心配置参数详解Chat类的初始化参数是你控制Agent行为的关键。以下是几个最常用且重要的参数client必填。一个openai.OpenAI或openai.AsyncOpenAI的实例。这是你配置API密钥、基础URL如果你使用Azure OpenAI或第三方代理、超时时间等的地方。model必填。字符串指定使用的模型如“gpt-3.5-turbo”、“gpt-4-turbo-preview”。system_message可选。系统提示词用于设定AI的角色和行为准则。例如“你是一个乐于助人的数据分析助手请用简洁的语言回答。”强烈建议设置一个清晰的system_message这对引导AI行为至关重要。temperature和top_p控制生成文本的随机性。对于需要稳定执行工具调用的场景建议将temperature设得较低如0.1或0.2以减少不可预测性。stream布尔值是否启用流式响应。如果为Truerun方法将返回一个生成器用于逐块获取响应实现打字机效果。verbose布尔值。如果设为TrueWhetstone会在控制台打印详细的调试信息包括它发送给API的消息、接收到的function_call等。在开发调试阶段这是极其有用的功能。一个更完整的初始化示例chat_engine Chat( clientclient, model“gpt-4-turbo-preview”, system_message“你是一个专业的IT运维助手擅长解释技术概念和调用工具解决问题。请分步骤思考并在调用工具前确认用户需求。”, temperature0.1, top_p0.9, verboseTrue # 打开调试输出 )4. 进阶实战构建多功能AI助手现在让我们构建一个更实用的例子一个集成了天气查询、时间查询和简单计算的桌面助手。4.1 定义多样化的工具函数我们将创建三个工具并展示如何处理不同的参数类型和更复杂的逻辑。# desktop_assistant.py import os import requests from datetime import datetime from typing import Optional from whetstone import Chat, tool from openai import OpenAI client OpenAI(api_keyos.environ.get(“OPENAI_API_KEY”)) # 工具1天气查询模拟 tool(name“get_weather”, description“查询城市天气。返回天气状况和温度。”) def query_weather(city: str, country_code: Optional[str] “CN”) - str: “”“模拟天气查询。在实际应用中这里会调用真实的天气API。”“” # 模拟API调用延迟 import time time.sleep(0.5) # 模拟返回数据 weather_data { (“北京”, “CN”): (“晴”, 22), (“上海”, “CN”): (“多云”, 25), (“纽约”, “US”): (“雨”, 18), } condition, temp weather_data.get((city, country_code), (“未知”, “N/A”)) return f“{city}({country_code})的天气是{condition}气温{temp}摄氏度。” # 工具2获取当前时间 tool(name“get_current_time”, description“获取当前的日期和时间。”) def current_time(timezone: str “Asia/Shanghai”) - str: “”“返回指定时区的当前时间。这里简化处理实际应使用pytz等库。”“” now datetime.now() # 简化处理忽略时区转换 return now.strftime(f“%Y-%m-%d %H:%M:%S (时区: {timezone})”) # 工具3简单计算器 tool(name“calculate”, description“执行简单的数学计算。”) def calculator(expression: str) - str: “”“计算一个数学表达式。警告使用eval有安全风险仅作演示。”“” # 重要安全提示在生产环境中绝对不要使用eval直接执行用户输入 # 这里仅为演示应替换为安全的表达式解析器如ast.literal_eval限制操作。 try: # 极其危险仅用于本地演示。 result eval(expression, {“__builtins__”: None}, {}) return f“表达式 {expression} 的计算结果是: {result}” except Exception as e: return f“计算错误: {e}。请检查表达式格式。” # 初始化Chat引擎 assistant Chat( clientclient, model“gpt-3.5-turbo”, system_message“你是一个桌面助手可以查询天气、时间和进行简单计算。请直接回答问题必要时调用工具。”, verboseFalse ) # 交互循环 if __name__ “__main__”: print(“桌面助手已启动。输入‘退出’或‘quit’结束对话。”) while True: try: user_input input(“\n你: “) if user_input.lower() in [“退出”, “quit”, “exit”]: print(“助手: 再见”) break response assistant.run(user_input) print(f“助手: {response}”) except KeyboardInterrupt: print(“\n对话被中断。”) break except Exception as e: print(f“出错: {e}”)运行这个程序你就可以通过命令行与你的AI助手对话了。试试以下问题“北京现在天气怎么样”“帮我计算一下 (15 27) * 3 等于多少”“现在几点了”你会发现ChatGPT能够很好地理解你的意图并选择正确的工具进行调用。Whetstone在幕后默默地处理了所有的路由和执行工作。4.2 工具描述的艺术让AI更懂你工具函数的description和参数描述至关重要它们直接决定了ChatGPT是否以及如何调用你的工具。以下是一些编写技巧清晰准确用一句话简明扼要地说明工具的功能。例如“查询城市天气”比“获取气象信息”更好。说明约束如果参数有特定格式或范围在描述中说明。例如country_code的描述可以是“国家代码使用ISO 3166-1 alpha-2标准如CN, US”。利用类型注解Python的类型注解str,int,Optional,List[str]会被Whetstone转换为JSON Schema类型帮助AI理解参数格式。函数名有意义name参数应该是一个动词短语清晰表达动作如search_web,send_email,create_calendar_event。一个反面教材是描述过于模糊比如一个名为process的工具描述为“处理数据”。这会让ChatGPT困惑不知道何时该调用它。4.3 处理复杂参数与嵌套对象Whetstone支持通过Pydantic模型来定义复杂的工具参数这能生成更精确、结构化的JSON Schema极大提升AI调用的准确性。首先安装Pydanticpip install pydantic然后在工具函数中使用from pydantic import BaseModel, Field from typing import List # 定义一个复杂的参数模型 class MeetingDetails(BaseModel): title: str Field(description“会议主题”) participants: List[str] Field(description“参会者邮箱列表”) duration_minutes: int Field(ge15, le240, description“会议时长分钟范围15-240”) online: bool Field(defaultTrue, description“是否为线上会议”) tool(name“schedule_meeting”) def create_meeting(details: MeetingDetails) - str: “”“根据提供的详情创建日历会议。”“” # 在这里调用你的日历API如Google Calendar或Outlook return f“已成功创建会议‘{details.title}’时长{details.duration_minutes}分钟参会者{‘ ‘.join(details.participants)}。” # 初始化Chat时Whetstone会自动将MeetingDetails模型转换为详细的参数schema。当用户说“帮我安排一个明天下午2点、时长1小时、关于项目评审的线上会议邀请张三和李四”ChatGPT就能更准确地提取出结构化信息并调用create_meeting函数。使用Pydantic是构建可靠生产级Agent的推荐做法。5. 深入核心消息历史管理与对话状态一个强大的助手需要记住对话上下文。Whetstone的Chat类内部维护了一个messages列表这就是它的“记忆”。5.1 理解消息流在verboseTrue模式下你可以看到Whetstone与API交互的完整消息流。通常包括system系统提示词仅首次。user用户输入。assistantfunction_callAI决定调用工具。function工具执行结果。assistantAI生成的最终回复。Whetstone会自动将所有这些消息追加到历史中从而实现多轮对话的连贯性。5.2 手动管理历史与持久化虽然Whetstone自动管理内部历史但有时你需要更精细的控制比如清空历史开始一个新会话。持久化历史将会话保存到数据库或文件以便下次恢复。修改历史删除或修改某些消息例如移除过长的上下文以节省token。Chat实例的messages属性就是一个标准的OpenAI消息字典列表你可以直接操作它。# 获取当前对话历史 history assistant.messages print(f“当前有 {len(history)} 条消息。”) # 清空历史开始新对话 assistant.messages [] # 或者 assistant.reset() # 手动添加消息 assistant.messages.append({“role”: “user”, “content”: “记住我更喜欢用摄氏度而不是华氏度。”}) # 持久化示例保存到JSON文件 import json def save_chat_session(chat_engine, filename): with open(filename, ‘w’, encoding‘utf-8’) as f: json.dump({“model”: chat_engine.model, “messages”: chat_engine.messages}, f, ensure_asciiFalse, indent2) def load_chat_session(filename, client): with open(filename, ‘r’, encoding‘utf-8’) as f: data json.load(f) new_chat Chat(clientclient, modeldata[“model”]) new_chat.messages data[“messages”] return new_chat重要提示消息历史会消耗token尤其是使用GPT-4时成本较高。长时间对话后历史可能变得很长。OpenAI的模型有上下文长度限制如gpt-3.5-turbo是16k。你需要实现逻辑来截断或总结过长的历史。Whetstone本身不提供自动截断功能这需要你根据业务逻辑手动管理messages列表。5.3 系统提示词System Prompt的实战技巧system_message是塑造AI行为的“宪法”。一个好的系统提示词能显著提升工具调用的准确性和回复质量。明确角色和边界告诉AI它是什么能做什么不能做什么。示例“你是一个代码安全审查助手。你只能调用‘代码静态分析’和‘依赖漏洞检查’这两个工具。对于用户提出的其他请求如运行代码或访问网络你必须明确拒绝。”引导思考过程鼓励AI进行链式思考Chain-of-Thought特别是在复杂任务中。示例“在回答用户问题前请先逐步推理你需要调用哪些工具以及调用顺序。确保你理解了用户的所有要求。”控制输出格式如果你希望回复是特定的格式如Markdown、JSON在这里说明。示例“请用简洁的列表形式总结工具执行结果。如果涉及数据请用Markdown表格呈现。”处理不确定性指导AI在信息不足时如何应对。示例“如果用户请求的信息不完整导致无法调用工具例如查询天气但未提供城市请主动向用户提问以澄清。”你可以动态修改system_message但注意修改后Whetstone会在下次run时将其作为新的系统消息插入历史。如果历史中已存在系统消息这可能会导致混乱。更稳妥的做法是在初始化时设定好或在清空历史后修改。6. 错误处理、调试与性能优化在实际使用中事情不会总是一帆风顺。工具可能出错AI可能误解意图网络可能不稳定。一个健壮的Agent必须能妥善处理这些问题。6.1 工具函数内部的错误处理你的工具函数应该自己捕获和处理可能发生的异常并返回友好的错误信息而不是抛出异常导致整个对话中断。tool(name“fetch_webpage”) def get_webpage_content(url: str) - str: “““获取网页内容。””” import requests try: response requests.get(url, timeout10) response.raise_for_status() # 检查HTTP错误 # 简单提取文本实际应用可能需要BeautifulSoup return response.text[:2000] # 限制返回长度 except requests.exceptions.Timeout: return “错误请求超时请检查网络或稍后重试。” except requests.exceptions.RequestException as e: return f“错误无法获取网页原因{e}” except Exception as e: return f“工具内部发生未知错误{e}”返回结构化的错误信息如JSON{“status”: “error”, “message”: “...”}也是一个好习惯这样AI或后续流程可以程序化地处理错误。6.2 处理AI的“失误”有时ChatGPT可能会调用不存在的工具Whetstone会抛出ValueError。你需要在调用chat.run()的地方进行捕获。提供的参数格式错误例如要求数字却提供了字符串。Whetstone在调用函数前会尝试进行参数类型转换和验证如果用了Pydantic验证会更严格如果失败函数可能接收到错误的值或调用失败。误解意图在不需要时调用工具这通常需要通过优化工具描述和系统提示词来解决。一个简单的全局错误处理框架def safe_run(chat_engine, user_input): try: response chat_engine.run(user_input) return response except ValueError as e: # 可能是工具名错误或参数错误 return f“抱歉在处理你的请求时遇到了问题{e}。请尝试重新表述你的需求。” except Exception as e: # 其他错误如网络问题、API限额等 # 可以在这里加入日志记录 print(f“严重错误: {e}”) return “系统暂时出了点问题请稍后再试。”6.3 使用Verbose模式进行深度调试将verboseTrue是调试Whetstone工作流程的最强武器。它会打印出每次API调用的请求和响应详情让你清晰地看到AI收到了哪些工具描述AI决定调用哪个工具参数是什么工具返回的结果是什么AI基于结果生成了什么回复这对于排查“为什么AI不调用我的工具”或“为什么参数解析错了”这类问题至关重要。6.4 性能优化与成本控制选择合适的模型对于工具调用场景gpt-3.5-turbo在大多数情况下已经足够聪明且成本低廉。只有在需要复杂推理或理解非常精细的指令时才考虑gpt-4系列。精简工具描述工具的名称、描述和参数描述都会占用token。在清晰的前提下尽量保持简洁。管理对话历史这是控制成本的关键。定期清理chat.messages或者实现一个“滑动窗口”机制只保留最近N条消息。对于超长对话可以考虑使用“总结摘要”技术让AI将之前的长篇对话总结成一段摘要然后用摘要替换旧历史。设置超时和重试通过自定义openai.OpenAI客户端可以配置网络超时和重试策略避免因临时网络问题导致程序挂起。from openai import OpenAI client OpenAI( api_key“...”, timeout30.0, # 请求超时时间 max_retries2, # 最大重试次数 )异步调用如果你的应用是异步的如FastAPI Web服务Whetstone支持异步的AsyncChat和atool装饰器可以避免阻塞事件循环提升并发性能。7. 常见问题与排查技巧实录在这一部分我汇总了在实战中使用Whetstone时最常遇到的一些“坑”以及解决方法。7.1 问题速查表问题现象可能原因解决方案AI完全不调用工具1. 工具描述不够清晰。2. 系统提示词未引导AI使用工具。3. 用户请求过于模糊AI认为无需工具。1. 优化工具name和description使其更精准。2. 在system_message中明确指令如“请优先使用可用工具来回答问题”。3. 使用verboseTrue查看AI的思考过程。AI调用了错误的工具工具之间的功能描述有重叠或歧义。重新设计工具职责确保每个工具功能单一、描述独特。使用更具体的名称和参数。参数解析失败1. AI提取的参数格式与函数签名不匹配。2. 使用了复杂类型但未用Pydantic模型。1. 检查verbose日志看AI提供了什么参数。确保函数参数有明确的类型注解。2.强烈建议对复杂参数使用Pydantic模型它能生成严格的Schema。tool装饰的函数未被注册1. 函数定义在Chat实例化之后。2. 函数定义所在的模块未被正确导入。1. 确保所有tool装饰的函数在Chat()调用之前被执行即模块被导入。2. 在主程序入口显式导入包含工具函数的模块。流式响应 (streamTrue) 不工作对run方法的返回值处理不当。streamTrue时run返回一个生成器。你需要迭代它for chunk in chat.run(...): print(chunk, end“”)。对话历史过长导致API错误或成本高未对chat.messages进行管理。实现历史截断逻辑。例如只保留最近10轮对话或当token数预估超过阈值时删除最早的消息。在多线程/多进程环境中工具注册混乱Whetstone的默认工具注册中心是全局的。考虑为每个线程/进程创建独立的工具注册方式可能需要修改或封装Whetstone源码。或者将Agent设计为无状态每次请求新建Chat实例并传入所需的工具列表需手动管理工具。7.2 独家避坑技巧“预热”你的Agent在正式处理用户请求前可以先让Agent进行一两次简单的、确定会调用工具的对话。例如先问“现在几点了”确保工具调用流程是通的。这能提前发现配置问题。为工具函数编写单元测试不要因为工具会被AI调用就忽略测试。单独测试每个工具函数确保其逻辑正确、异常处理完备。这能避免AI“背锅”。对AI的输出保持“怀疑”AI可能会在工具返回的结果上“添油加醋”或产生幻觉。对于关键信息如计算结果、查询到的数据可以考虑让工具函数返回结构化数据如字典并在系统提示词中要求AI“严格依据工具返回的数据进行回答不要编造”。限制工具权限如果一个工具函数执行的是危险操作如删除文件、发送邮件一定要在函数内部进行权限校验例如检查调用者、确认操作。不要完全信任由AI解析出来的参数。使用temperature0进行确定性测试在开发和调试阶段将temperature设为0可以让AI的输出尽可能确定便于复现和排查问题。8. 超越基础模式扩展与高级集成Whetstone本身是一个轻量级核心但它的设计允许你在此基础上构建更复杂的模式。8.1 实现链式调用Sequential Function Calling链式调用是指AI根据第一个工具的结果决定调用第二个工具。Whetstone原生支持这一点因为它在一次run调用中会自动循环处理多个function_call直到AI决定停止。关键在于你的工具函数返回的结果要能成为下一个工具函数所需的上下文。例如一个“分析需求”的工具返回{“action”: “search”, “keywords”: [“...”]}然后AI可以据此调用“网页搜索”工具。8.2 与Web框架集成如FastAPI将Whetstone驱动的Agent封装成一个HTTP API服务是非常常见的需求。# app.py (FastAPI示例) from fastapi import FastAPI, HTTPException from pydantic import BaseModel import os from your_assistant_module import assistant # 导入你之前定义好的Chat实例和工具 app FastAPI(title“AI助手API”) class ChatRequest(BaseModel): message: str session_id: str | None None # 用于区分不同会话 class ChatResponse(BaseModel): reply: str session_id: str app.post(“/chat”, response_modelChatResponse) async def chat_endpoint(request: ChatRequest): try: # 这里可以根据session_id从数据库加载不同的消息历史 # 本例简化处理使用全局助手 reply assistant.run(request.message) return ChatResponse(replyreply, session_idrequest.session_id or “default”) except Exception as e: raise HTTPException(status_code500, detailf“处理请求时出错: {e}”) # 注意全局的assistant实例不是线程安全的。 # 对于生产环境你需要为每个请求创建新的Chat实例或使用线程局部存储。8.3 自定义工具执行层Whetstone的tool装饰器在默认情况下会在被调用时直接执行函数。但有时你可能需要注入额外的逻辑例如权限检查在执行工具前验证用户身份。日志记录记录每个工具的调用参数和结果。性能监控统计工具执行耗时。模拟/测试在不执行真实操作的情况下返回模拟数据。你可以通过创建一个自定义的“工具执行器”来包装Whetstone。一种更直接的方式是在你自己的工具函数内部实现这些横切关注点或者使用Python的装饰器叠加在tool之上。def with_logging(func): “”“一个记录工具调用的装饰器。”“” def wrapper(*args, **kwargs): print(f“[LOG] 调用工具 {func.__name__}参数: args{args}, kwargs{kwargs}”) result func(*args, **kwargs) print(f“[LOG] 工具 {func.__name__} 返回: {result}”) return result return wrapper tool(name“secure_operation”) with_logging # 注意with_logging要放在tool下面先应用业务装饰器 def do_something_secure(user_id: str, action: str): # 这里可以加入权限检查 if not has_permission(user_id, action): return “错误权限不足” # ... 实际业务逻辑 return “操作成功”经过这样一番从理论到实践从基础到进阶的梳理相信你已经对johniwasz/whetstone.chatgpt这个项目有了全面而深入的理解。它就像一副精心设计的“鞍具”让开发者能更轻松、更安全地驾驭ChatGPT Function Calling这匹“骏马”快速奔向构建复杂AI应用的目的地。剩下的就是发挥你的想象力去创造那些真正解决实际问题的智能体了。如果在实践中遇到新的问题不妨再回头看看文中提到的调试方法和排查技巧那都是我从一次次“踩坑”中积累下来的经验。
Whetstone.chatgpt:简化ChatGPT Function Calling开发的AI Agent框架
1. 项目概述一个为ChatGPT“开刃”的代码库如果你深度使用过ChatGPT的API尤其是它的Function Calling函数调用功能可能会和我有同样的感受它很强大但用起来总有点“钝”。你需要自己写大量的胶水代码来处理对话历史、管理函数注册、解析API返回的复杂JSON、处理可能的错误还要考虑如何将自然语言的用户请求精准地路由到你那一大堆功能各异的函数上。这个过程繁琐、重复而且容易出错。这就是我最初发现johniwasz/whetstone.chatgpt这个项目时的感受——它像一块“磨刀石”Whetstone旨在把ChatGPT API这把本就锋利的“刀”打磨得更加趁手、高效。这个开源库的核心目标非常明确简化基于ChatGPT Function Calling构建复杂AI应用的开发流程。它不是一个全新的AI框架而是一个轻量级、高抽象的“脚手架”或“编排层”让你能专注于定义“做什么”你的业务函数而把“怎么做”与ChatGPT的交互、上下文管理、路由调度交给它。简单来说它帮你处理了所有与OpenAI API对话的脏活累活。你只需要用装饰器定义你的工具函数Whetstone就能自动将它们“翻译”成ChatGPT能理解的Function描述并在对话中智能地调用它们。无论是构建一个能查询数据库、发送邮件、控制智能家居的智能助手还是创建一个能自动执行代码、分析数据、生成报告的分析工具Whetstone都能大幅降低你的开发门槛。我花了相当一段时间在实际项目中集成和测试它从简单的命令行工具到带有Web界面的复杂应用。这篇文章我就来为你彻底拆解whetstone.chatgpt不仅告诉你它怎么用更会深入分享其设计哲学、核心机制、我在实战中踩过的坑以及一些能让你事半功倍的进阶技巧。无论你是想快速给自己的应用加上AI大脑还是希望深入理解AI Agent的底层运作这里都有你需要的干货。2. 核心设计哲学与架构拆解在直接敲代码之前理解一个库为什么这样设计比知道它怎么用更重要。这能帮助你在遇到复杂场景时做出更合理的架构决策。2.1 为什么需要Whetstone从原始API的痛点说起让我们先回顾一下如果不使用任何辅助库直接用OpenAI Python SDK实现一个简单的“天气查询”Function Calling需要哪些步骤定义函数描述手动构造一个符合OpenAI Function Calling规范的JSON字典描述你的函数名、参数和说明。发起对话将用户消息和这个函数描述列表传给ChatCompletion.create。解析响应检查API返回的finish_reason是否为“function_call”。提取参数从message[“function_call”]中解析出函数名和参数字符串通常是JSON格式。调用函数根据函数名找到你本地对应的Python函数并将参数字符串反序列化后传入。处理结果执行函数获取结果。继续对话将函数执行结果作为新的消息再次调用API让ChatGPT生成面向用户的自然语言回复。管理历史你需要自己维护一个消息历史列表并在每次交互中正确传递。这还只是一个函数的情况。当你有几十个函数需要处理多轮对话、函数链式调用、错误处理时代码会迅速变得难以维护。Whetstone的核心价值就是将上述第1、3、4、5、6、7步全部自动化。2.2 核心架构装饰器、注册中心与对话引擎Whetstone的架构非常清晰主要包含三个核心部分tool装饰器这是你与Whetstone交互的主要方式。你只需要在普通的Python函数上加上tool并填写描述信息Whetstone就会在背后自动为你生成OpenAI所需的JSON Schema并注册这个函数。from whetstone import tool tool(name“get_weather”, description“获取指定城市的当前天气”) def fetch_weather(city: str) - str: # 你的业务逻辑 return f“{city}的天气是晴25摄氏度。”注意它利用Python的类型注解city: str来自动推断参数类型这比手动写JSON Schema优雅和安全得多。工具注册中心一个全局的、隐式的注册表。所有被tool装饰的函数都会被自动收集到这里。当你初始化对话引擎时Whetstone会从这个注册中心获取所有工具并一次性生成完整的工具描述列表供ChatGPT使用。这意味着你不需要手动维护一个工具列表代码组织更灵活。Chat引擎这是Whetstone的大脑也是你进行对话的主要接口。它封装了与OpenAI API的整个交互循环。内部状态管理自动维护消息历史messages。工具调用循环自动处理function_call响应定位对应工具执行函数并将结果作为新的消息追加到历史中然后继续调用API直到ChatGPT给出最终的自然语言回复finish_reason为“stop”。流式响应支持可以处理流式输出实现打字机效果。可扩展性允许注入自定义的OpenAI客户端方便你配置代理、自定义基础URL等。这种架构带来的最大好处是“关注点分离”。你作为开发者只需要关心工具函数的实现业务逻辑而复杂的AI交互逻辑被封装在Chat引擎内部。这极大地提升了开发效率和代码的可读性。注意Whetstone目前主要围绕OpenAI的Chat Completion API设计。虽然理论上其设计模式可以适配其他支持类似Function Calling功能的模型如Claude、DeepSeek等但需要一定的改造工作。社区也有相关讨论和分支但在官方未明确支持前将其视为一个针对OpenAI API的优化工具更为稳妥。3. 从零开始快速上手与基础配置理论说得再多不如动手跑一遍。让我们从一个最简单的例子开始搭建一个能运行的环境。3.1 环境准备与安装首先确保你的Python版本在3.8以上。然后通过pip安装Whetstone和OpenAI官方库。pip install whetstone-chatgpt openai接下来你需要一个OpenAI的API密钥。如果你还没有需要去OpenAI平台注册并获取。安全提醒永远不要将API密钥硬编码在代码中或提交到版本控制系统。最推荐的方式是使用环境变量# 在终端中设置临时 export OPENAI_API_KEY‘sk-your-secret-key-here’ # 或者在 .bashrc, .zshrc 或 .env 文件中设置永久3.2 第一个“Hello Agent”程序我们来创建一个最简单的智能助手它只有一个功能打招呼。# hello_agent.py import os from whetstone import Chat, tool from openai import OpenAI # 1. 初始化OpenAI客户端Whetstone内部会使用 client OpenAI(api_keyos.environ.get(“OPENAI_API_KEY”)) # 2. 使用tool装饰器定义一个工具函数 tool(name“greet_user”, description“向用户问好”) def say_hello(user_name: str) - str: “”“一个简单的打招呼函数。”“” return f“你好{user_name}很高兴为你服务。” # 3. 创建Chat引擎实例 chat_engine Chat( clientclient, # 传入自定义客户端 model“gpt-3.5-turbo”, # 指定模型gpt-4系列也可用 # tools参数无需手动传入tool装饰的函数会自动注册 ) # 4. 发起对话 if __name__ “__main__”: # 用户输入 user_message “请用中文向小明打个招呼。” # 调用run方法Whetstone会处理所有中间步骤 final_response chat_engine.run(user_message) # 打印最终结果 print(“AI回复:”, final_response)运行这个脚本python hello_agent.py你应该会看到类似这样的输出AI回复: 你好小明很高兴为你服务。发生了什么Whetstone自动将say_hello函数及其描述、参数信息转换成了ChatGPT能理解的格式。ChatGPT理解了用户的意图是“调用greet_user函数且参数user_name为‘小明’”。Whetstone接收到function_call后自动调用本地的say_hello(“小明”)函数。获取函数结果“你好小明很高兴为你服务。”后Whetstone将其作为新消息再次发送给ChatGPT。ChatGPT这次直接生成了最终的自然语言回复在我们的简单例子里它可能直接返回了函数结果或者稍作润色。这个简单的流程背后是Whetstone替你完成的所有复杂编排。你已经成功创建了一个具备工具调用能力的AI Agent3.3 核心配置参数详解Chat类的初始化参数是你控制Agent行为的关键。以下是几个最常用且重要的参数client必填。一个openai.OpenAI或openai.AsyncOpenAI的实例。这是你配置API密钥、基础URL如果你使用Azure OpenAI或第三方代理、超时时间等的地方。model必填。字符串指定使用的模型如“gpt-3.5-turbo”、“gpt-4-turbo-preview”。system_message可选。系统提示词用于设定AI的角色和行为准则。例如“你是一个乐于助人的数据分析助手请用简洁的语言回答。”强烈建议设置一个清晰的system_message这对引导AI行为至关重要。temperature和top_p控制生成文本的随机性。对于需要稳定执行工具调用的场景建议将temperature设得较低如0.1或0.2以减少不可预测性。stream布尔值是否启用流式响应。如果为Truerun方法将返回一个生成器用于逐块获取响应实现打字机效果。verbose布尔值。如果设为TrueWhetstone会在控制台打印详细的调试信息包括它发送给API的消息、接收到的function_call等。在开发调试阶段这是极其有用的功能。一个更完整的初始化示例chat_engine Chat( clientclient, model“gpt-4-turbo-preview”, system_message“你是一个专业的IT运维助手擅长解释技术概念和调用工具解决问题。请分步骤思考并在调用工具前确认用户需求。”, temperature0.1, top_p0.9, verboseTrue # 打开调试输出 )4. 进阶实战构建多功能AI助手现在让我们构建一个更实用的例子一个集成了天气查询、时间查询和简单计算的桌面助手。4.1 定义多样化的工具函数我们将创建三个工具并展示如何处理不同的参数类型和更复杂的逻辑。# desktop_assistant.py import os import requests from datetime import datetime from typing import Optional from whetstone import Chat, tool from openai import OpenAI client OpenAI(api_keyos.environ.get(“OPENAI_API_KEY”)) # 工具1天气查询模拟 tool(name“get_weather”, description“查询城市天气。返回天气状况和温度。”) def query_weather(city: str, country_code: Optional[str] “CN”) - str: “”“模拟天气查询。在实际应用中这里会调用真实的天气API。”“” # 模拟API调用延迟 import time time.sleep(0.5) # 模拟返回数据 weather_data { (“北京”, “CN”): (“晴”, 22), (“上海”, “CN”): (“多云”, 25), (“纽约”, “US”): (“雨”, 18), } condition, temp weather_data.get((city, country_code), (“未知”, “N/A”)) return f“{city}({country_code})的天气是{condition}气温{temp}摄氏度。” # 工具2获取当前时间 tool(name“get_current_time”, description“获取当前的日期和时间。”) def current_time(timezone: str “Asia/Shanghai”) - str: “”“返回指定时区的当前时间。这里简化处理实际应使用pytz等库。”“” now datetime.now() # 简化处理忽略时区转换 return now.strftime(f“%Y-%m-%d %H:%M:%S (时区: {timezone})”) # 工具3简单计算器 tool(name“calculate”, description“执行简单的数学计算。”) def calculator(expression: str) - str: “”“计算一个数学表达式。警告使用eval有安全风险仅作演示。”“” # 重要安全提示在生产环境中绝对不要使用eval直接执行用户输入 # 这里仅为演示应替换为安全的表达式解析器如ast.literal_eval限制操作。 try: # 极其危险仅用于本地演示。 result eval(expression, {“__builtins__”: None}, {}) return f“表达式 {expression} 的计算结果是: {result}” except Exception as e: return f“计算错误: {e}。请检查表达式格式。” # 初始化Chat引擎 assistant Chat( clientclient, model“gpt-3.5-turbo”, system_message“你是一个桌面助手可以查询天气、时间和进行简单计算。请直接回答问题必要时调用工具。”, verboseFalse ) # 交互循环 if __name__ “__main__”: print(“桌面助手已启动。输入‘退出’或‘quit’结束对话。”) while True: try: user_input input(“\n你: “) if user_input.lower() in [“退出”, “quit”, “exit”]: print(“助手: 再见”) break response assistant.run(user_input) print(f“助手: {response}”) except KeyboardInterrupt: print(“\n对话被中断。”) break except Exception as e: print(f“出错: {e}”)运行这个程序你就可以通过命令行与你的AI助手对话了。试试以下问题“北京现在天气怎么样”“帮我计算一下 (15 27) * 3 等于多少”“现在几点了”你会发现ChatGPT能够很好地理解你的意图并选择正确的工具进行调用。Whetstone在幕后默默地处理了所有的路由和执行工作。4.2 工具描述的艺术让AI更懂你工具函数的description和参数描述至关重要它们直接决定了ChatGPT是否以及如何调用你的工具。以下是一些编写技巧清晰准确用一句话简明扼要地说明工具的功能。例如“查询城市天气”比“获取气象信息”更好。说明约束如果参数有特定格式或范围在描述中说明。例如country_code的描述可以是“国家代码使用ISO 3166-1 alpha-2标准如CN, US”。利用类型注解Python的类型注解str,int,Optional,List[str]会被Whetstone转换为JSON Schema类型帮助AI理解参数格式。函数名有意义name参数应该是一个动词短语清晰表达动作如search_web,send_email,create_calendar_event。一个反面教材是描述过于模糊比如一个名为process的工具描述为“处理数据”。这会让ChatGPT困惑不知道何时该调用它。4.3 处理复杂参数与嵌套对象Whetstone支持通过Pydantic模型来定义复杂的工具参数这能生成更精确、结构化的JSON Schema极大提升AI调用的准确性。首先安装Pydanticpip install pydantic然后在工具函数中使用from pydantic import BaseModel, Field from typing import List # 定义一个复杂的参数模型 class MeetingDetails(BaseModel): title: str Field(description“会议主题”) participants: List[str] Field(description“参会者邮箱列表”) duration_minutes: int Field(ge15, le240, description“会议时长分钟范围15-240”) online: bool Field(defaultTrue, description“是否为线上会议”) tool(name“schedule_meeting”) def create_meeting(details: MeetingDetails) - str: “”“根据提供的详情创建日历会议。”“” # 在这里调用你的日历API如Google Calendar或Outlook return f“已成功创建会议‘{details.title}’时长{details.duration_minutes}分钟参会者{‘ ‘.join(details.participants)}。” # 初始化Chat时Whetstone会自动将MeetingDetails模型转换为详细的参数schema。当用户说“帮我安排一个明天下午2点、时长1小时、关于项目评审的线上会议邀请张三和李四”ChatGPT就能更准确地提取出结构化信息并调用create_meeting函数。使用Pydantic是构建可靠生产级Agent的推荐做法。5. 深入核心消息历史管理与对话状态一个强大的助手需要记住对话上下文。Whetstone的Chat类内部维护了一个messages列表这就是它的“记忆”。5.1 理解消息流在verboseTrue模式下你可以看到Whetstone与API交互的完整消息流。通常包括system系统提示词仅首次。user用户输入。assistantfunction_callAI决定调用工具。function工具执行结果。assistantAI生成的最终回复。Whetstone会自动将所有这些消息追加到历史中从而实现多轮对话的连贯性。5.2 手动管理历史与持久化虽然Whetstone自动管理内部历史但有时你需要更精细的控制比如清空历史开始一个新会话。持久化历史将会话保存到数据库或文件以便下次恢复。修改历史删除或修改某些消息例如移除过长的上下文以节省token。Chat实例的messages属性就是一个标准的OpenAI消息字典列表你可以直接操作它。# 获取当前对话历史 history assistant.messages print(f“当前有 {len(history)} 条消息。”) # 清空历史开始新对话 assistant.messages [] # 或者 assistant.reset() # 手动添加消息 assistant.messages.append({“role”: “user”, “content”: “记住我更喜欢用摄氏度而不是华氏度。”}) # 持久化示例保存到JSON文件 import json def save_chat_session(chat_engine, filename): with open(filename, ‘w’, encoding‘utf-8’) as f: json.dump({“model”: chat_engine.model, “messages”: chat_engine.messages}, f, ensure_asciiFalse, indent2) def load_chat_session(filename, client): with open(filename, ‘r’, encoding‘utf-8’) as f: data json.load(f) new_chat Chat(clientclient, modeldata[“model”]) new_chat.messages data[“messages”] return new_chat重要提示消息历史会消耗token尤其是使用GPT-4时成本较高。长时间对话后历史可能变得很长。OpenAI的模型有上下文长度限制如gpt-3.5-turbo是16k。你需要实现逻辑来截断或总结过长的历史。Whetstone本身不提供自动截断功能这需要你根据业务逻辑手动管理messages列表。5.3 系统提示词System Prompt的实战技巧system_message是塑造AI行为的“宪法”。一个好的系统提示词能显著提升工具调用的准确性和回复质量。明确角色和边界告诉AI它是什么能做什么不能做什么。示例“你是一个代码安全审查助手。你只能调用‘代码静态分析’和‘依赖漏洞检查’这两个工具。对于用户提出的其他请求如运行代码或访问网络你必须明确拒绝。”引导思考过程鼓励AI进行链式思考Chain-of-Thought特别是在复杂任务中。示例“在回答用户问题前请先逐步推理你需要调用哪些工具以及调用顺序。确保你理解了用户的所有要求。”控制输出格式如果你希望回复是特定的格式如Markdown、JSON在这里说明。示例“请用简洁的列表形式总结工具执行结果。如果涉及数据请用Markdown表格呈现。”处理不确定性指导AI在信息不足时如何应对。示例“如果用户请求的信息不完整导致无法调用工具例如查询天气但未提供城市请主动向用户提问以澄清。”你可以动态修改system_message但注意修改后Whetstone会在下次run时将其作为新的系统消息插入历史。如果历史中已存在系统消息这可能会导致混乱。更稳妥的做法是在初始化时设定好或在清空历史后修改。6. 错误处理、调试与性能优化在实际使用中事情不会总是一帆风顺。工具可能出错AI可能误解意图网络可能不稳定。一个健壮的Agent必须能妥善处理这些问题。6.1 工具函数内部的错误处理你的工具函数应该自己捕获和处理可能发生的异常并返回友好的错误信息而不是抛出异常导致整个对话中断。tool(name“fetch_webpage”) def get_webpage_content(url: str) - str: “““获取网页内容。””” import requests try: response requests.get(url, timeout10) response.raise_for_status() # 检查HTTP错误 # 简单提取文本实际应用可能需要BeautifulSoup return response.text[:2000] # 限制返回长度 except requests.exceptions.Timeout: return “错误请求超时请检查网络或稍后重试。” except requests.exceptions.RequestException as e: return f“错误无法获取网页原因{e}” except Exception as e: return f“工具内部发生未知错误{e}”返回结构化的错误信息如JSON{“status”: “error”, “message”: “...”}也是一个好习惯这样AI或后续流程可以程序化地处理错误。6.2 处理AI的“失误”有时ChatGPT可能会调用不存在的工具Whetstone会抛出ValueError。你需要在调用chat.run()的地方进行捕获。提供的参数格式错误例如要求数字却提供了字符串。Whetstone在调用函数前会尝试进行参数类型转换和验证如果用了Pydantic验证会更严格如果失败函数可能接收到错误的值或调用失败。误解意图在不需要时调用工具这通常需要通过优化工具描述和系统提示词来解决。一个简单的全局错误处理框架def safe_run(chat_engine, user_input): try: response chat_engine.run(user_input) return response except ValueError as e: # 可能是工具名错误或参数错误 return f“抱歉在处理你的请求时遇到了问题{e}。请尝试重新表述你的需求。” except Exception as e: # 其他错误如网络问题、API限额等 # 可以在这里加入日志记录 print(f“严重错误: {e}”) return “系统暂时出了点问题请稍后再试。”6.3 使用Verbose模式进行深度调试将verboseTrue是调试Whetstone工作流程的最强武器。它会打印出每次API调用的请求和响应详情让你清晰地看到AI收到了哪些工具描述AI决定调用哪个工具参数是什么工具返回的结果是什么AI基于结果生成了什么回复这对于排查“为什么AI不调用我的工具”或“为什么参数解析错了”这类问题至关重要。6.4 性能优化与成本控制选择合适的模型对于工具调用场景gpt-3.5-turbo在大多数情况下已经足够聪明且成本低廉。只有在需要复杂推理或理解非常精细的指令时才考虑gpt-4系列。精简工具描述工具的名称、描述和参数描述都会占用token。在清晰的前提下尽量保持简洁。管理对话历史这是控制成本的关键。定期清理chat.messages或者实现一个“滑动窗口”机制只保留最近N条消息。对于超长对话可以考虑使用“总结摘要”技术让AI将之前的长篇对话总结成一段摘要然后用摘要替换旧历史。设置超时和重试通过自定义openai.OpenAI客户端可以配置网络超时和重试策略避免因临时网络问题导致程序挂起。from openai import OpenAI client OpenAI( api_key“...”, timeout30.0, # 请求超时时间 max_retries2, # 最大重试次数 )异步调用如果你的应用是异步的如FastAPI Web服务Whetstone支持异步的AsyncChat和atool装饰器可以避免阻塞事件循环提升并发性能。7. 常见问题与排查技巧实录在这一部分我汇总了在实战中使用Whetstone时最常遇到的一些“坑”以及解决方法。7.1 问题速查表问题现象可能原因解决方案AI完全不调用工具1. 工具描述不够清晰。2. 系统提示词未引导AI使用工具。3. 用户请求过于模糊AI认为无需工具。1. 优化工具name和description使其更精准。2. 在system_message中明确指令如“请优先使用可用工具来回答问题”。3. 使用verboseTrue查看AI的思考过程。AI调用了错误的工具工具之间的功能描述有重叠或歧义。重新设计工具职责确保每个工具功能单一、描述独特。使用更具体的名称和参数。参数解析失败1. AI提取的参数格式与函数签名不匹配。2. 使用了复杂类型但未用Pydantic模型。1. 检查verbose日志看AI提供了什么参数。确保函数参数有明确的类型注解。2.强烈建议对复杂参数使用Pydantic模型它能生成严格的Schema。tool装饰的函数未被注册1. 函数定义在Chat实例化之后。2. 函数定义所在的模块未被正确导入。1. 确保所有tool装饰的函数在Chat()调用之前被执行即模块被导入。2. 在主程序入口显式导入包含工具函数的模块。流式响应 (streamTrue) 不工作对run方法的返回值处理不当。streamTrue时run返回一个生成器。你需要迭代它for chunk in chat.run(...): print(chunk, end“”)。对话历史过长导致API错误或成本高未对chat.messages进行管理。实现历史截断逻辑。例如只保留最近10轮对话或当token数预估超过阈值时删除最早的消息。在多线程/多进程环境中工具注册混乱Whetstone的默认工具注册中心是全局的。考虑为每个线程/进程创建独立的工具注册方式可能需要修改或封装Whetstone源码。或者将Agent设计为无状态每次请求新建Chat实例并传入所需的工具列表需手动管理工具。7.2 独家避坑技巧“预热”你的Agent在正式处理用户请求前可以先让Agent进行一两次简单的、确定会调用工具的对话。例如先问“现在几点了”确保工具调用流程是通的。这能提前发现配置问题。为工具函数编写单元测试不要因为工具会被AI调用就忽略测试。单独测试每个工具函数确保其逻辑正确、异常处理完备。这能避免AI“背锅”。对AI的输出保持“怀疑”AI可能会在工具返回的结果上“添油加醋”或产生幻觉。对于关键信息如计算结果、查询到的数据可以考虑让工具函数返回结构化数据如字典并在系统提示词中要求AI“严格依据工具返回的数据进行回答不要编造”。限制工具权限如果一个工具函数执行的是危险操作如删除文件、发送邮件一定要在函数内部进行权限校验例如检查调用者、确认操作。不要完全信任由AI解析出来的参数。使用temperature0进行确定性测试在开发和调试阶段将temperature设为0可以让AI的输出尽可能确定便于复现和排查问题。8. 超越基础模式扩展与高级集成Whetstone本身是一个轻量级核心但它的设计允许你在此基础上构建更复杂的模式。8.1 实现链式调用Sequential Function Calling链式调用是指AI根据第一个工具的结果决定调用第二个工具。Whetstone原生支持这一点因为它在一次run调用中会自动循环处理多个function_call直到AI决定停止。关键在于你的工具函数返回的结果要能成为下一个工具函数所需的上下文。例如一个“分析需求”的工具返回{“action”: “search”, “keywords”: [“...”]}然后AI可以据此调用“网页搜索”工具。8.2 与Web框架集成如FastAPI将Whetstone驱动的Agent封装成一个HTTP API服务是非常常见的需求。# app.py (FastAPI示例) from fastapi import FastAPI, HTTPException from pydantic import BaseModel import os from your_assistant_module import assistant # 导入你之前定义好的Chat实例和工具 app FastAPI(title“AI助手API”) class ChatRequest(BaseModel): message: str session_id: str | None None # 用于区分不同会话 class ChatResponse(BaseModel): reply: str session_id: str app.post(“/chat”, response_modelChatResponse) async def chat_endpoint(request: ChatRequest): try: # 这里可以根据session_id从数据库加载不同的消息历史 # 本例简化处理使用全局助手 reply assistant.run(request.message) return ChatResponse(replyreply, session_idrequest.session_id or “default”) except Exception as e: raise HTTPException(status_code500, detailf“处理请求时出错: {e}”) # 注意全局的assistant实例不是线程安全的。 # 对于生产环境你需要为每个请求创建新的Chat实例或使用线程局部存储。8.3 自定义工具执行层Whetstone的tool装饰器在默认情况下会在被调用时直接执行函数。但有时你可能需要注入额外的逻辑例如权限检查在执行工具前验证用户身份。日志记录记录每个工具的调用参数和结果。性能监控统计工具执行耗时。模拟/测试在不执行真实操作的情况下返回模拟数据。你可以通过创建一个自定义的“工具执行器”来包装Whetstone。一种更直接的方式是在你自己的工具函数内部实现这些横切关注点或者使用Python的装饰器叠加在tool之上。def with_logging(func): “”“一个记录工具调用的装饰器。”“” def wrapper(*args, **kwargs): print(f“[LOG] 调用工具 {func.__name__}参数: args{args}, kwargs{kwargs}”) result func(*args, **kwargs) print(f“[LOG] 工具 {func.__name__} 返回: {result}”) return result return wrapper tool(name“secure_operation”) with_logging # 注意with_logging要放在tool下面先应用业务装饰器 def do_something_secure(user_id: str, action: str): # 这里可以加入权限检查 if not has_permission(user_id, action): return “错误权限不足” # ... 实际业务逻辑 return “操作成功”经过这样一番从理论到实践从基础到进阶的梳理相信你已经对johniwasz/whetstone.chatgpt这个项目有了全面而深入的理解。它就像一副精心设计的“鞍具”让开发者能更轻松、更安全地驾驭ChatGPT Function Calling这匹“骏马”快速奔向构建复杂AI应用的目的地。剩下的就是发挥你的想象力去创造那些真正解决实际问题的智能体了。如果在实践中遇到新的问题不妨再回头看看文中提到的调试方法和排查技巧那都是我从一次次“踩坑”中积累下来的经验。