1. 项目概述:当LLM学会“调用函数”
如果你正在开发一个基于大语言模型(LLM)的应用,比如一个智能客服、一个数据分析助手,或者一个自动化工作流,你大概率会遇到一个核心难题:LLM的“幻觉”和“知识截止日期”。你希望它能查询实时天气、操作数据库、调用第三方API,或者执行一个复杂的计算,但LLM本身只是一个语言模型,它无法直接“行动”。它只能“说”,不能“做”。
传统的解决思路是,开发者需要编写大量的胶水代码:解析LLM的回复,判断其意图,然后手动调用对应的函数,再把结果塞回给LLM,让它继续生成回复。这个过程繁琐、易错,且难以维护。而sigoden/llm-functions这个项目,正是为了解决这个痛点而生的。它不是一个庞大的框架,而是一个精巧、实用的工具库,其核心思想是:让函数调用成为LLM的一种原生能力。
简单来说,llm-functions允许你将任意Python函数“描述”给LLM,LLM在理解用户意图后,可以自主决定是否调用、以及调用哪个函数,并自动将函数的执行结果整合到其后续的对话或思考中。这极大地简化了构建“LLM+工具”类应用(即所谓的Agent或工具调用)的流程。对于开发者而言,你不再需要写复杂的意图识别和调度逻辑,只需要定义好函数和它的自然语言描述,剩下的交给llm-functions和LLM去协作完成。
这个项目特别适合那些希望快速为LLM应用添加外部能力,但又不想陷入复杂架构设计的开发者。无论是想给聊天机器人加一个查股票的功能,还是让AI助手能帮你操作本地文件,llm-functions都能提供一个清晰、直接的实现路径。
2. 核心设计思路与架构解析
2.1 从“意图识别”到“函数即能力”的范式转变
在llm-functions出现之前,主流的LLM工具调用方案可以概括为“两步走”:
- 意图识别与参数提取:首先,通过一个独立的LLM调用或规则引擎,分析用户输入,判断用户想做什么(意图),并提取出必要的参数(如城市名、股票代码)。
- 手动调用与结果回填:开发者根据识别出的意图,在代码中手动调用对应的函数或API,获取结果后,再将这个结果作为新的上下文,发送给LLM生成最终回复。
这种方式的问题在于,它把本应由LLM完成的“理解-决策-执行”链条人为地切断了。llm-functions的设计哲学是反其道而行之:将函数的定义(包括名称、描述、参数schema)直接暴露给LLM,让LLM在生成回复的“思考过程”中,自主决定是否需要“外援”。这更贴近人类解决问题的过程——我们在思考时,如果意识到需要查资料或使用计算器,会自然地暂停思考,去执行那个动作,然后再继续。
2.2 项目架构与核心组件
llm-functions的架构非常简洁,核心是围绕Function和FunctionRegistry两个概念构建的。
Function类:这是能力的载体。你定义的每一个Python函数,都需要被包装成一个Function对象。这个包装过程不仅仅是简单的封装,关键在于你需要为这个函数提供丰富的“元数据”:- 名称(name):函数的唯一标识。
- 描述(description):用自然语言清晰描述这个函数是做什么的。这是LLM理解该函数用途的关键。例如,“获取指定城市的当前天气情况”就比“get_weather”好得多。
- 参数Schema(parameters):严格定义函数每个参数的类型、描述、是否必需等。这通常遵循JSON Schema格式,确保了LLM能精确地生成符合要求的调用参数。
FunctionRegistry类:这是能力的仓库。它是一个中心化的注册表,用于管理所有已注册的Function对象。当LLM需要决定调用哪个函数时,它会查询这个注册表。注册表的设计支持动态添加和移除函数,使得应用的能力可以灵活扩展。与LLM的集成层:这是项目的“魔法”发生地。
llm-functions并不绑定于某个特定的LLM提供商(如OpenAI、Anthropic)。它通过适配器模式,将FunctionRegistry中的函数信息,转换成特定LLM API所要求的“工具定义”(Tool Definitions)格式。例如,对于OpenAI的Chat Completions API,它会生成符合其tools参数要求的JSON对象。然后,在LLM的回复中,它会解析出tool_calls字段,自动执行对应的函数,并将结果以tool消息的形式追加到对话历史中,供LLM进行下一轮推理。
注意:
llm-functions本身不包含LLM的调用逻辑,它只负责“定义函数”和“解析调用”。你需要结合像openai、anthropic或litellm这样的SDK来完成与LLM的实际通信。这使得它非常轻量且易于集成到现有项目中。
2.3 对比主流方案:与LangChain、LlamaIndex的异同
你可能会问,LangChain的Tools和Agents,LlamaIndex的Query Engines with Tools,不也是做类似的事情吗?是的,但它们的设计目标和复杂度不同。
- LangChain:是一个功能极其丰富的大框架,涵盖了从数据加载、向量存储、记忆管理到链式调用、代理的方方面面。它的Tool调用功能是其中一环,但学习曲线陡峭,抽象层次多。如果你需要一个“全家桶”式的解决方案,LangChain很强大。但如果你只想解决“让LLM调用我的几个函数”这个具体问题,LangChain可能显得过于重型。
- LlamaIndex:核心优势在于数据索引和检索(RAG)。它的工具调用功能更多是服务于其检索流程的增强。如果你的应用核心是文档问答,需要结合工具调用,LlamaIndex是不错的选择。
llm-functions:专注且极简。它只解决“函数调用”这一件事,并且力求API设计直观、侵入性低。你不需要理解复杂的“Agent执行循环”、“ToolExecutor”等概念。它的代码量小,依赖少,更容易理解和调试。对于快速原型开发、中小型项目,或者希望将工具调用能力嵌入到现有非LLM框架应用中的场景,llm-functions的优势非常明显。
选择建议:如果你的项目复杂度高,需要记忆、复杂的工作流、多种数据连接器,选LangChain。如果你的核心是文档检索,选LlamaIndex。如果你需要的是一个轻量、专注、可以像导入普通库一样轻松使用的函数调用工具,llm-functions是你的理想选择。
3. 核心细节解析与实操要点
3.1 如何定义一个合格的“LLM可调用函数”
定义一个能被LLM良好理解和调用的函数,远不止写一个def那么简单。以下是需要关注的几个核心细节:
1. 函数签名与类型提示(Type Hints):强烈建议使用Python的类型提示。这不仅能让你的代码更清晰,llm-functions也能利用这些信息自动生成更准确的参数Schema。例如:
def get_weather(city: str, country_code: str = “CN”) -> str: …这里的str类型和默认值”CN”都会被捕获,帮助LLM理解country_code是一个可选的字符串参数。
2. 描述(Description)的撰写艺术:描述是LLM理解函数功能的唯一自然语言入口。一个好的描述应该:
- 准确:明确说明函数的作用。例如,“计算两个数的和”就比“处理数字”准确。
- 完整:提及关键的限制或前提条件。例如,“根据股票代码查询实时股价,仅支持沪深A股和部分美股代码”。
- 用户意图导向:从用户可能提出的问题角度来描述。例如,“解答关于Python标准库的用法问题”可以对应函数
search_python_docs(query: str)。
3. 参数Schema的精细化定义:虽然llm-functions可以自动从类型提示生成基础Schema,但对于复杂参数,手动定义是必要的。
- 枚举类型:如果参数只能取几个特定值,一定要定义为枚举。这能极大提高LLM调用的准确性。
在注册函数时,from enum import Enum class Unit(Enum): CELSIUS = “celsius” FAHRENHEIT = “fahrenheit” def get_temperature(city: str, unit: Unit = Unit.CELSIUS) -> float: …llm-functions会将这个枚举信息传递给LLM,LLM就知道unit只能填 “celsius” 或 “fahrenheit”。 - 复杂对象:如果参数是一个字典或Pydantic模型,需要详细定义其内部结构。
4. 函数的健壮性与错误处理:LLM调用的函数必须非常健壮。因为用户的输入是开放域的,LLM提取的参数可能五花八门。
- 输入验证:在函数内部开头,务必对参数进行二次验证。例如,即使Schema要求
city是字符串,LLM也可能传一个不存在的城市名。你的函数需要能处理这种情况,并返回一个友好的错误信息,如“未找到该城市信息”,这个信息会被传回给LLM,让它向用户解释。 - 异常捕获:函数内部所有可能抛出异常的地方(如网络请求、数据库查询)都必须用
try…except包裹,返回一个结构化的错误结果,而不是让异常直接抛出导致整个调用链崩溃。
3.2 FunctionRegistry 的管理策略
FunctionRegistry作为中央仓库,其管理方式直接影响应用的灵活性和性能。
- 按模块/功能分组注册:不要把所有函数都堆在一个地方。可以按功能模块创建多个注册表,或者使用一个主注册表,但通过命名前缀来分组,例如
weather.get、stock.query、file.read。这有助于管理和避免命名冲突。 - 动态注册与热更新:
llm-functions支持在运行时动态添加或移除函数。这个特性非常有用,例如,你可以实现一个插件系统,用户上传的插件可以将其函数动态注册到主应用中。或者在长期运行的服务中,根据配置热加载新的工具函数。 - 注册表的序列化与持久化:虽然项目本身可能不直接提供,但你可以很容易地将
FunctionRegistry中的函数定义(主要是元数据)序列化为JSON或YAML保存。这在需要将函数配置与代码分离,或者进行版本管理时非常有用。
3.3 与不同LLM供应商的适配实战
llm-functions通过bind方法或类似的适配器来连接不同的LLM。这里以OpenAI和Anthropic为例,说明关键配置。
OpenAI (gpt-3.5-turbo, gpt-4):OpenAI的API原生支持tools和tool_choice参数。llm-functions的适配器会将你的Function列表转换成OpenAI要求的格式。关键点在于tool_choice参数:
”auto”:让模型自行决定是否调用函数以及调用哪个。这是最常用的模式。”none”:禁止模型调用任何函数。{“type”: “function”, “function”: {“name”: “get_weather”}}:强制模型调用指定的函数。这在调试或构建确定性工作流时有用。
实操心得:使用OpenAI时,务必在系统提示(System Prompt)中给予模型清晰的指令,引导它积极使用工具。例如:“你是一个有帮助的助手,可以调用各种工具来获取信息。当用户的问题需要实时数据、计算或外部信息时,你应该调用相应的工具。”
Anthropic (Claude):Anthropic的API设计略有不同,它使用tools参数定义工具,并在消息中通过特定的结构来触发工具调用。llm-functions的Anthropic适配器会处理这些转换。需要注意的是,Claude对工具描述的格式可能更敏感,确保描述简洁明了。
通用建议:无论使用哪个供应商,在首次集成后,一定要用一组典型的用户query进行测试,观察LLM是否正确理解了函数描述、是否在合适的时机发起了调用、参数提取是否准确。这是一个迭代优化的过程。
4. 完整实操流程:构建一个天气查询助手
下面我们通过一个完整的例子,一步步展示如何使用llm-functions构建一个能查询天气的智能助手。我们将使用OpenAI的GPT-4作为LLM引擎。
4.1 环境准备与安装
首先,创建一个新的Python虚拟环境并安装必要的包。
# 创建并激活虚拟环境(以conda为例) conda create -n llm-functions-demo python=3.10 conda activate llm-functions-demo # 安装核心库和OpenAI SDK pip install llm-functions openai python-dotenv requestsllm-functions是核心库,openai用于调用GPT,python-dotenv用于管理API密钥,requests用于在我们的天气函数中发起HTTP请求。
接下来,在项目根目录创建.env文件,存放你的OpenAI API密钥:
OPENAI_API_KEY=sk-your-secret-key-here4.2 定义工具函数与注册
我们创建weather_tool.py文件,定义天气查询函数。
# weather_tool.py import os import requests from typing import Optional from enum import Enum from dotenv import load_dotenv from llm_functions import Function # 加载环境变量 load_dotenv() # 模拟一个天气API的密钥(这里用OpenWeatherMap示例,你需要自己申请) WEATHER_API_KEY = os.getenv(“WEATHER_API_KEY”, “your_fallback_key”) class TemperatureUnit(Enum): """温度单位枚举""" CELSIUS = “metric” # 对应API中的摄氏度参数 FAHRENHEIT = “imperial” def get_current_weather( location: str, unit: TemperatureUnit = TemperatureUnit.CELSIUS ) -> str: """ 获取指定城市的当前天气情况。 参数: location: 城市名称,例如“北京”、“New York”。支持中英文。 unit: 温度单位,可选“CELSIUS”(摄氏度)或“FAHRENHEIT”(华氏度),默认为摄氏度。 返回: 一个描述天气的字符串,包括温度、天气状况和湿度。 如果查询失败,返回错误信息。 """ # 在实际项目中,这里应调用真实的天气API,如OpenWeatherMap # 此处为模拟逻辑 if not location: return “错误:请提供有效的城市名称。” # 模拟API调用和响应解析 # 假设我们有一个模拟的API响应 mock_data = { “Beijing”: {“temp”: 22, “condition”: “晴朗”, “humidity”: 65}, “New York”: {“temp”: 68, “condition”: “多云”, “humidity”: 80}, } city_key = location.strip() if city_key in mock_data: data = mock_data[city_key] temp = data[“temp”] condition = data[“condition”] humidity = data[“humidity”] unit_str = “°C” if unit == TemperatureUnit.CELSIUS else “°F” return f”{location}的当前天气:{condition},温度 {temp}{unit_str},湿度 {humidity}%。” else: return f”抱歉,暂时无法获取 {location} 的天气信息。请检查城市名称是否正确。” # 将函数包装成LLM可识别的Function对象 weather_function = Function.from_callable( get_current_weather, name=“get_current_weather”, # 给函数一个明确的调用名 description=“查询指定城市的实时天气。”, # 简洁的描述 )4.3 集成LLM并创建对话循环
现在,创建主程序main.py,将函数注册,并与OpenAI GPT进行交互。
# main.py import os from openai import OpenAI from dotenv import load_dotenv from llm_functions import FunctionRegistry from weather_tool import weather_function # 加载环境变量和初始化 load_dotenv() client = OpenAI(api_key=os.getenv(“OPENAI_API_KEY”)) # 1. 创建函数注册表并注册天气函数 registry = FunctionRegistry() registry.register(weather_function) # 2. 将注册表绑定到OpenAI客户端,生成工具定义 # llm_functions的OpenAI集成器会处理转换 from llm_functions.integrations.openai import OpenAIIntegration integration = OpenAIIntegration(registry) # 这个 `tools` 变量就是可以直接传给OpenAI API的格式 tools = integration.get_tools_for_openai() # 3. 系统提示,引导模型使用工具 system_prompt = “”” 你是一个智能天气助手,专门回答与天气相关的问题。 你可以调用“get_current_weather”工具来查询全球城市的实时天气。 当用户询问某个地方的天气时,你应该主动调用这个工具来获取准确信息。 如果工具返回了信息,请用友好、自然的方式告诉用户。 如果工具返回错误,请向用户解释可能的原因(如城市名不对)。 对于与天气无关的问题,请礼貌地告知你的能力范围。 “”” # 4. 简单的对话循环 def chat_with_weather_assistant(): messages = [{“role”: “system”, “content”: system_prompt}] print(“天气助手已启动!输入‘退出’或‘quit’结束对话。\n”) while True: try: user_input = input(“\n你: “).strip() if user_input.lower() in [“退出”, “quit”, “exit”]: print(“助手: 再见!”) break # 将用户输入加入消息历史 messages.append({“role”: “user”, “content”: user_input}) # 调用OpenAI API,传入工具定义 response = client.chat.completions.create( model=“gpt-4-turbo-preview”, # 或 gpt-3.5-turbo messages=messages, tools=tools, tool_choice=“auto”, # 让模型自行决定是否调用工具 temperature=0.2, # 较低的温度使输出更确定 ) # 获取模型的回复 assistant_message = response.choices[0].message messages.append(assistant_message) # 先将模型的回复(可能包含tool_calls)加入历史 # 5. 检查模型是否想要调用工具 if assistant_message.tool_calls: print(f”助手: (正在查询天气信息…)”) # 处理每一个工具调用(本例中只有一个工具) for tool_call in assistant_message.tool_calls: function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) # 通过集成器执行工具调用 # integration.execute_tool_call 会自动找到对应函数并传入参数 tool_result = integration.execute_tool_call(tool_call) # 将工具执行结果作为一条新消息追加到历史中 messages.append({ “role”: “tool”, “tool_call_id”: tool_call.id, “content”: str(tool_result), # 结果需要是字符串 }) # 工具调用并添加结果后,需要让模型基于新信息生成最终回复 second_response = client.chat.completions.create( model=“gpt-4-turbo-preview”, messages=messages, # 此时历史已包含工具执行结果 ) final_message = second_response.choices[0].message messages.append(final_message) print(f”助手: {final_message.content}”) else: # 模型没有调用工具,直接输出回复 print(f”助手: {assistant_message.content}”) except Exception as e: print(f”发生错误: {e}”) # 可以选择清空当前轮次的消息,避免错误上下文影响 messages.pop() # 移除导致错误的用户输入 print(“助手: 抱歉,处理你的请求时出了点问题,请再试一次。”) if __name__ == “__main__”: import json # 需要导入json来解析参数 chat_with_weather_assistant()4.4 运行与测试
运行python main.py,你就可以开始与你的天气助手对话了。
测试用例1:正常查询
你: 北京今天天气怎么样? 助手: (正在查询天气信息…) 助手: 北京的当前天气:晴朗,温度 22°C,湿度 65%。测试用例2:带单位的查询
你: What‘s the weather in New York in Fahrenheit? 助手: (正在查询天气信息…) 助手: New York的当前天气:多云,温度 68°F,湿度 80%。测试用例3:无关问题
你: 讲个笑话吧。 助手: 我是一个专注于天气查询的助手,目前还没有学习讲笑话的功能。你可以问我关于任何城市天气的问题!测试用例4:错误处理(模拟)
你: 火星的天气如何? 助手: (正在查询天气信息…) 助手: 抱歉,暂时无法获取 火星 的天气信息。请检查城市名称是否正确。通过这个完整的例子,你可以看到,核心的“意图识别-函数调用-结果整合”循环完全由llm-functions和 OpenAI API 协作处理,我们只需要定义好函数和清晰的描述即可。这大大简化了开发流程。
5. 高级应用与性能优化
5.1 构建复杂多工具Agent
一个实用的助手通常不止一个功能。我们可以轻松扩展注册表,加入更多工具。
示例:添加股票查询和计算器
# finance_tool.py from llm_functions import Function from typing import List def get_stock_price(symbol: str) -> str: “”“查询指定股票代码的实时股价。仅支持部分主要股票代码,如 AAPL, GOOGL。”“” # 模拟数据 mock_prices = {“AAPL”: 172.3, “GOOGL”: 151.2, “MSFT”: 406.5} price = mock_prices.get(symbol.upper()) if price: return f”{symbol} 的最新股价为 ${price}。” else: return f”未找到股票代码 {symbol} 的信息。” def calculate_expression(expression: str) -> str: “”“计算一个数学表达式的结果。支持加减乘除和括号,例如 ‘(2+3)*4’。”“” try: # 警告:使用eval有安全风险,仅作演示。生产环境应用安全计算库如`ast.literal_eval`或自定义解析器。 result = eval(expression) return f”表达式 ‘{expression}’ 的计算结果是:{result}。” except Exception as e: return f”无法计算表达式 ‘{expression}’:{e}。” stock_function = Function.from_callable(get_stock_price, name=“query_stock”, description=“查询美股的实时股价。”) calc_function = Function.from_callable(calculate_expression, name=“calculate”, description=“计算一个数学表达式。”) # 在主程序中注册 registry.register(stock_function) registry.register(calc_function) # 记得更新 tools = integration.get_tools_for_openai()现在,你的助手就能回答“苹果股价多少?”和“计算一下(15+27)/3等于多少?”这类问题了。关键在于系统提示要更新,概括所有可用工具。
5.2 流式输出(Streaming)与函数调用
在真实应用中,我们往往希望获得流式响应,让用户更快地看到结果。当结合函数调用时,流程会稍微复杂一些。
核心思路:
- 发起一个开启流式输出的API调用。
- 监听返回的数据块(chunks)。
- 当检测到一个完整的
tool_calls结构时,暂停流式输出,去执行函数。 - 执行完毕后,将结果作为新的消息,再次发起一个(可能是非流式的)API调用,让模型生成最终回复,或者继续流式输出最终回复。
llm-functions本身不处理流式逻辑,但它的设计允许你在流式处理的回调函数中,使用integration.execute_tool_call来执行工具。你需要根据OpenAI流式返回的数据结构,自己解析出完整的tool_calls对象。这是一个进阶话题,需要对OpenAI的流式响应格式有深入了解。
5.3 错误处理与用户反馈优化
1. 函数执行失败:函数内部应返回结构化的错误信息,而不是抛出异常。LLM可以理解这些错误信息并生成友好的用户提示。例如,在天气函数中返回“网络请求超时,请稍后重试”。
2. LLM参数提取错误:有时LLM可能误解用户意图,提取出错误的参数。例如,用户问“上海和北京的天气”,LLM可能只调用了get_weather(location=”上海和北京”)。为了处理这种情况,你有两个策略:
- 在函数内部做兼容处理:例如,尝试分割字符串,或调用模糊搜索API。
- 优化函数描述和系统提示:在描述中明确“一次只能查询一个城市”,并在系统提示中强调这一点。
3. 工具选择不当:如果LLM频繁错误地选择工具,或者该用工具时不用,需要调整:
- 优化工具描述:使其更精确,与其他工具区分度更高。
- 调整系统提示:更明确地指导模型何时使用工具。
- 提供少量示例(Few-Shot):在系统提示或初始消息中,给出一两个正确使用工具的对话示例,这是引导LLM行为最有效的方法之一。
6. 常见问题与排查技巧实录
在实际使用llm-functions的过程中,你可能会遇到一些典型问题。下面是一个快速排查指南。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| LLM完全不调用函数 | 1. 系统提示未引导使用工具。 2. 函数描述不清晰或与用户问题不匹配。 3. tool_choice参数被误设为”none”。 | 1. 检查并强化系统提示,明确要求模型在需要时使用工具。 2. 重写函数描述,使其更贴近用户自然提问方式。 3. 确认API调用中 tool_choice=”auto”。 |
| LLM调用了函数,但参数总是错误 | 1. 参数Schema定义不准确(如类型、枚举值)。 2. 函数描述未明确参数格式(如日期格式“YYYY-MM-DD”)。 3. LLM模型能力不足(可尝试换更强模型)。 | 1. 使用Python类型提示和Pydantic模型精确定义Schema。 2. 在参数描述中写明格式要求。 3. 在系统提示中通过示例(Few-Shot)展示正确参数格式。 |
| 函数被调用,但返回结果后LLM的回复不理想 | 1. 函数返回的结果不是纯文本或格式混乱。 2. LLM未能正确理解函数返回的上下文。 | 1. 确保函数返回字符串。复杂数据应格式化成易读的自然语言。 2. 在函数返回的字符串中,可以包含一些引导性语句,如“查询结果显示:…”。 |
遇到KeyError或AttributeError,提示找不到函数 | 1. 函数注册的名称与LLM调用时使用的名称不一致。 2. 函数未正确注册到 FunctionRegistry。 | 1. 检查Function.from_callable的name参数,确保与LLMtool_calls中的function.name完全一致(大小写敏感)。2. 确认 registry.register()已成功执行,并且注册表实例被正确传递给了集成器。 |
| 流式输出模式下,工具调用逻辑混乱 | 流式响应中,tool_calls是分块传输的,需要组装完整后才能执行。 | 1. 参考OpenAI官方文档,编写代码累积tool_calls的增量数据,直到获得一个完整对象。2. 考虑在简单场景下先使用非流式模式,功能稳定后再升级到流式。 |
| 性能问题:简单问题也触发函数调用,增加延迟和成本 | LLM过于“积极”地使用工具。 | 1. 调整系统提示,说明“仅在必要时”使用工具。 2. 对于非常简单的、模型自身知识就能完美回答的问题(如“你好”),可以在客户端逻辑中做前置过滤,不触发工具调用流程。 |
实操心得一:描述即契约把函数的自然语言描述看作是与LLM签订的“契约”。描述越精准,LLM履约就越准确。花时间反复打磨每个函数的描述,并让不同的人阅读,看是否会产生歧义,这项投入的回报率极高。
实操心得二:测试驱动开发不要等到所有功能都写完再测试。为每个工具函数编写单元测试,模拟LLM可能传入的各种边界和错误参数。同时,构建一个端到端的测试集,包含各种典型的用户提问,自动化运行并检查LLM的最终输出是否符合预期。这能帮你快速发现描述不清、Schema定义错误等问题。
实操心得三:成本与延迟监控每次函数调用都意味着至少两次LLM API请求(第一次决定调用,第二次基于结果生成回复)。对于高频应用,这会使成本和响应时间翻倍。务必监控token消耗和响应延迟。对于一些结果稳定、耗时的查询(如复杂计算),可以考虑引入缓存机制,将(函数名+参数)哈希后缓存结果,在一定时间内相同的请求直接返回缓存,避免重复调用真实函数和后续的LLM生成。
llm-functions就像给LLM这个“大脑”装上了可随意插拔的“手脚”。它剥离了复杂框架的厚重外壳,将函数调用这一核心能力以最简洁的方式呈现出来。这种设计使得开发者能够更专注于定义“做什么”(函数逻辑),而将“何时做”和“如何调用”的复杂决策交给了更擅长此道的LLM。对于追求开发效率、热爱简洁设计的开发者来说,这个项目无疑提供了一条构建智能应用的快车道。