99 lines
3.2 KiB
Python
99 lines
3.2 KiB
Python
import sys
|
||
|
||
from langchain_core.messages import HumanMessage, ToolMessage
|
||
from langchain_core.tools import tool, StructuredTool
|
||
from pydantic import BaseModel, Field
|
||
|
||
import logging
|
||
|
||
from langchain_openai import ChatOpenAI
|
||
import os
|
||
import dotenv
|
||
|
||
|
||
# 定义函数/tool
|
||
|
||
## 方式1:使用注解
|
||
class FieldInfo(BaseModel):
|
||
city: str = Field(description="要查询的城市名称")
|
||
|
||
|
||
## 注解中可以通过传参覆盖原有函数的描述等信息
|
||
@tool(
|
||
name_or_callable="get_weather",
|
||
args_schema=FieldInfo,
|
||
description="查询某个城市的天气,并返回温度信息",
|
||
return_direct=True
|
||
)
|
||
def get_weather(city: str) -> str:
|
||
"""查询指定城市的最新天气信息"""
|
||
# 通过api调用天气网站的接口,得到最新的天气信息
|
||
return f"{city}当前的温度为18°C"
|
||
|
||
@tool
|
||
def test(city: str) -> str:
|
||
"""这个是一个测试函数"""
|
||
# 通过api调用天气网站的接口,得到最新的天气信息
|
||
return f"{city}当前的温度为18°C"
|
||
######################################################################
|
||
|
||
logging.basicConfig(
|
||
level=logging.DEBUG,
|
||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||
)
|
||
|
||
dotenv.load_dotenv()
|
||
|
||
## 设置环境变量
|
||
# os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
|
||
# os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL")
|
||
os.environ['OPENAI_API_KEY'] = os.getenv("OLLAMA_API_KEY")
|
||
os.environ['OPENAI_BASE_URL'] = os.getenv("OLLAMA_BASE_URL")
|
||
# os.environ['OPENAI_API_KEY'] = os.getenv("MINIMAX_API_KEY")
|
||
# os.environ['OPENAI_BASE_URL'] = os.getenv("MINIMAX_BASE_URL")
|
||
|
||
# 如果要使用函数调用,需要选择支持的模型
|
||
# 如果不支持,会报错:Error code: 400 - {'code': 20037, 'message': 'Function call is not supported for this model.', 'data': None}
|
||
# llm = ChatOpenAI(model="Qwen/Qwen2.5-7B-Instruct")
|
||
llm = ChatOpenAI(model="gemma4:e2b")
|
||
# llm = ChatOpenAI(model="MiniMax-M2.7")
|
||
|
||
|
||
## 将模型与工具进行绑定
|
||
llm_with_tools = llm.bind_tools([get_weather,test],tool_choice="auto")
|
||
|
||
messages = [HumanMessage(content="巴黎现在的气温是多少")]
|
||
|
||
## 使用哪个工具,由大模型自己选择
|
||
response = llm_with_tools.invoke(messages)
|
||
print(response)
|
||
|
||
## 模型会返回:需要调用get_weather,并且识别出来传参为 Paris,但是不会进行真正的调用
|
||
|
||
# 如果需要调用工具,则执行并把结果回传
|
||
for call in getattr(response, "tool_calls", []) or []:
|
||
# print(call)
|
||
# {'name': 'get_weather', 'args': {'city': 'Paris'}, 'id': '019a305388b40ad0d961da5696e9fd2f', 'type': 'tool_call'}
|
||
if call["name"] == "get_weather":
|
||
args = call["args"] # 例如 {"city": "Paris"}
|
||
result = get_weather.invoke(args)
|
||
messages.append(response) # 把模型消息加入对话
|
||
messages.append(
|
||
ToolMessage(
|
||
content=result,
|
||
tool_call_id=call["id"], # 必须把这次tool调用的id对上
|
||
)
|
||
)
|
||
# 5) 第二轮:把工具结果交回给模型,让它产出最终回答
|
||
final_msg = llm.invoke(messages)
|
||
print("FINAL:", final_msg.content)
|
||
break
|
||
else:
|
||
# 模型未请求调用工具,直接给出回答
|
||
print("FINAL(no-tool):", response.content)
|
||
|
||
|
||
|
||
|
||
|