tools
This commit is contained in:
kennethcheng 2026-04-15 10:45:46 +08:00
parent f1cdf6dd1f
commit eb82b1a174
4 changed files with 171 additions and 2 deletions

2
.env
View File

@ -3,9 +3,11 @@
SILICONFLOW_API_KEY = "sk-sylilrjrtxlvecwhfusjkutclmppzuzhncfcfxtekxrzyjee"
SILICONFLOW_BASE_URL = "https://api.siliconflow.cn/v1"
# gemma4:e2b
OLLAMA_API_KEY = "ollama"
OLLAMA_BASE_URL = "http://localhost:11434/v1"
# MiniMax-M2.7
MINIMAX_API_KEY = "sk-cp-wWkzvRP-BiQia-6izxvqgehEsHSz8v4_PtDJAuT3OI0s8QFcEOsxIHcQoZC2cVQTK3L09EUuu5HDArYMvKXFnf91jk8LuZ0tteS7-Wd4Lk2zDm8RqrKkrd4"
MINIMAX_BASE_URL = "https://api.minimaxi.com/v1"

View File

@ -1,6 +1,6 @@
# LangChain Learning
[![](https://img.shields.io/badge/version-0.0.7-blue.svg)](https://github.com/your-repo/langchain-learning)
[![](https://img.shields.io/badge/version-0.0.10-blue.svg)](https://github.com/your-repo/langchain-learning)
[![](https://img.shields.io/badge/python-3.11+-green.svg)](https://www.python.org/)
[![](https://img.shields.io/badge/LangChain-v1.2-orange.svg)](https://www.langchain.com/)
@ -13,6 +13,7 @@
- **流式响应**:实时流式输出,带来更好的使用体验
- **Prompt 工程**:多种 Prompt 模板构建方式
- **输出解析**:支持 JSON 等格式解析
- **工具调用 (Tool Calling)**:支持 @tool 装饰器和 StructuredTool 定义工具
- **Token 用量追踪**:轻松监控 API 调用消耗
- **内存管理**实现对话历史持久化ConversationBufferMemory, SummaryMemory
- **Rich 终端界面**:支持 Markdown 渲染、多行输入等高级交互
@ -24,7 +25,7 @@
### 1. 安装依赖
```bash
pip install langchain>=1.2.15 langchain-community>=0.4.1 langchain-siliconflow>=1.0.0 requests>=2.33.1 rich openai
pip install langchain>=1.2.15 langchain-community>=0.4.1 langchain-siliconflow>=1.0.0 requests>=2.33.1 rich openai pydantic
```
### 2. 配置环境变量
@ -72,6 +73,13 @@ OLLAMA_API_KEY=ollama
|------|------|------|
| 基础 RAG | `python rag/rag_demo.py` | 基于 FAISS 向量库的检索问答系统 |
**工具调用示例**
| 示例 | 命令 | 说明 |
|------|------|------|
| 工具定义 | `python tools/tool_definition.py` | 演示 @tool 装饰器和 StructuredTool 定义方式 |
| 工具调用 | `python tools/tool_demo.py` | 演示模型如何调用工具并获取结果 |
**Token 用量示例**
| 示例 | 命令 | 说明 |
@ -113,6 +121,9 @@ langchain-learning/
│ └── json_parser_demo.py # JSON 输出解析示例
├── rag/
│ └── rag_demo.py # RAG 检索增强生成示例
├── tools/
│ ├── tool_definition.py # 工具定义方式演示
│ └── tool_demo.py # 工具调用完整流程演示
├── token/
│ └── token_demo.py # Token 用量追踪示例
├── memory/
@ -138,6 +149,7 @@ langchain-learning/
**Ollama (本地)**
- `gemma4:26b`
- `gemma4:e2b`
- `deepseek-v3.1:671b-cloud`
## 技术栈
@ -148,6 +160,7 @@ langchain-learning/
| LLM 提供商 | SiliconFlow, Ollama |
| 向量库 | FAISS |
| 终端美化 | Rich |
| 数据验证 | Pydantic |
| 语言 | Python 3.11+ |
## 许可证

56
tools/tool_definition.py Normal file
View File

@ -0,0 +1,56 @@
import sys
from langchain_core.tools import tool, StructuredTool
from pydantic import BaseModel, Field
from sqlalchemy import True_
# 定义函数/tool
## 方式1使用注解
@tool
def get_weather1(city: str) -> str:
"""查询指定城市的最新天气信息"""
# 通过api调用天气网站的接口得到最新的天气信息
return f"{city}当前的温度为18°C"
class FieldInfo(BaseModel):
city: str = Field(description="要查询的城市名称")
## 注解中可以通过传参覆盖原有函数的描述等信息
@tool(
name_or_callable="get_weather1",
args_schema=FieldInfo,
description="查询某个城市的天气,并返回温度信息",
return_direct=True
)
def get_weather2(city: str) -> str:
"""查询指定城市的最新天气信息"""
# 通过api调用天气网站的接口得到最新的天气信息
return f"{city}当前的温度为18°C"
## 方式2
def get_weather3(city: str) -> str:
"""查询指定城市的最新天气信息"""
# 通过api调用天气网站的接口得到最新的天气信息
return f"{city}当前的温度为18°C"
get_weather3_tool = StructuredTool.from_function(
func=get_weather3,
name="get_weather3",
args_schema=FieldInfo,
description="第三个返回天气的函数"
)
if __name__ == '__main__':
# 调用函数,并打印返回结果
print(get_weather1("北京"))
print(get_weather1("巴黎"))
print(f"name={get_weather3_tool.name}")
print(f"args={get_weather3_tool.args}")
print(f"description={get_weather3_tool.description}")
print(f"return_direct={get_weather3_tool.return_direct}") # 直接返回如果为false就是会将返回值给到大模型让大模型进一步加工后再返回。如果是true则直接返回给用户。

98
tools/tool_demo.py Normal file
View File

@ -0,0 +1,98 @@
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)