某些模型能够进行工具方法调用:生成用户自定义的方法的签名并调用它。一般来说,此交互过程主要包含下面几个步骤:
LLM(大语言模型)
根据用户的问题推理生成调用工具方法的名称和参数- 调用工具,返回简单的结果
- 再次调用
LLM
,生成自然流畅的回答
下面我们来说明这一过程。
应该是因为
langchian
对deepseek
的支持还完善,使用deepseek-r1
无法生成工具方法签名,使用MFDoom/deepseek-r1-tool-calling:7b
可以生成工具方法签名,但是在调用工具方法时不成功。
所以本文仅使用llama3.1
。
准备
在正式开始撸代码之前,需要准备一下编程环境。
-
计算机
本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:- CPU: Intel i5-8400 2.80GHz
- 内存: 16GB
-
Visual Studio Code 和 venv 这是很受欢迎的开发工具,相关文章的代码可以在
Visual Studio Code
中开发和调试。 我们用python
的venv
创建虚拟环境, 详见:
在Visual Studio Code中配置venv。 -
Ollama 在
Ollama
平台上部署本地大模型非常方便,基于此平台,我们可以让langchain
使用llama3.1
、qwen2.5
等各种本地大模型。详见:
在langchian中使用本地部署的llama3.1大模型 。
定义工具方法
下面我们定义两个简单的工具方法,它可以做加法和乘法运算。
@tool
def add(a: int, b: int) -> int:
"""计算a和b的和。"""
print (f"add is called...{a}+{b}")
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""计算a和b的乘积。"""
print (f"multiply is called...{a}*{b}")
return a * b
tools = [add, multiply]
绑定工具
下面,我们将 LLM
和 工具 关联起来。
llm = ChatOllama(model=model_name,temperature=0.1,verbose=True)
llm_with_tools = llm.bind_tools(tools)
生成工具方法签名
我们出一个题目,让 llm_with_tools 生成工具方法签名。
query = "3 * 12等于多少? 11 + 49等于多少?"
messages = [HumanMessage(query)]
ai_msg = llm_with_tools.invoke(messages)
print(f' tool_calls is:{ai_msg.tool_calls}')
messages.append(ai_msg)
经过上面的一次调用,llm_with_tools 将返回一个 AIMessage
,它的 tool_calls
的内容为:
[
{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': '851a6249-e8da-4f3a-81ba-8377569d2dbe', 'type': 'tool_call'},
{'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': '3929f2b1-e160-4f45-aaeb-eeea876a1b7d', 'type': 'tool_call'}
]
真棒,我们把用户的提问生成了两个 tool_call
。然后把消息合并,为最后大模型可以返回流畅的答案做准备。
调用工具方法
下面我们使用模型生成的参数来调用工具行数,生成答案:
for tool_call in ai_msg.tool_calls:
selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()]
tool_msg = selected_tool.invoke(tool_call)
messages.append(tool_msg)
print(f'now,messages are:{messages}')
上面的循环主要作用是根据 tool_calls
调用工具方法,我们看看现在的消息内容:
[
HumanMessage(content='3 * 12等于多少? 11 + 49等于多少?', additional_kwargs={}, response_metadata={}),
AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'llama3.1', 'created_at': '2025-02-07T06:59:30.4497694Z', 'done': True, 'done_reason': 'stop', 'total_duration': 44403254600, 'load_duration': 13950016100, 'prompt_eval_count': 239, 'prompt_eval_duration': 7522000000, 'eval_count': 43, 'eval_duration': 22923000000, 'message': Message(role='assistant', content='', images=None, tool_calls=None)}, id='run-306ea35c-1b9b-4871-b7f7-4d726892e9bf-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': '851a6249-e8da-4f3a-81ba-8377569d2dbe', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': '3929f2b1-e160-4f45-aaeb-eeea876a1b7d', 'type': 'tool_call'}], usage_metadata={'input_tokens': 239, 'output_tokens': 43, 'total_tokens': 282}),
ToolMessage(content='36', name='multiply', tool_call_id='851a6249-e8da-4f3a-81ba-8377569d2dbe'),
ToolMessage(content='60', name='add', tool_call_id='3929f2b1-e160-4f45-aaeb-eeea876a1b7d')
]
此过程又生成了两个新的 ToolMessage
。
再次调用 LLM
,生成回答
我们将上述生成的消息列表塞给 LLM
,让它给出自然流畅的回答:
result = llm_with_tools.invoke(messages)
print(f'result is:{result.content}')
最终返回的结果为:
So, the answers to your questions are:
* 3 * 12 = 36
* 11 + 49 = 60
总结
在这篇文章里,我们逐步演示了如何使用大语言模型调用外部工具方法并返回结果。当然,目前的方式还比较笨拙,比如:如果用户的提问不涉及到调用工具方法时,上述代码显然会报错。
在后面的部分中我们将让大语言模型与外部工具的交互更加智能,变成 Agent(智能体)
。
代码
本文涉及的所有代码以及相关资源都已经共享,参见:
参考:
🪐祝好运🪐