系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解 Tool 封装原则 → 掌握@tool装饰器 → 构建“查订单 + 发通知 + 执行脚本”三大自定义工具!
🔧 一、为什么需要自定义 Tool?
内置工具(如搜索、维基)很好用,但真实业务需求千奇百怪:
- “查一下订单号 12345 的物流状态”
- “给用户张三发一条短信提醒”
- “重启测试环境的 Web 服务”
这些能力不存在于公开 API,必须对接你自己的系统。
而 自定义 Tool 就是 LangChain 提供的“能力插槽” —— 你只需写一个函数,Agent 就能像调用计算器一样使用它!
✅ 今天,我们就来把三个典型企业场景封装成 Tool!
🧱 二、自定义 Tool 的两种方式
表格
| 方式 | 代码量 | 适用场景 |
|---|---|---|
Tool(name=..., func=..., description=...) | 稍多 | 需要精细控制 |
@tool 装饰器(推荐 ✅) | 极简 | 快速开发、类型提示 |
🔔 Day 12 重点:
@tool装饰器 + Pydantic 参数校验
🛠️ 三、实战 1:查订单状态(模拟数据库查询)
场景
用户提供订单号,AI 返回状态(发货中 / 已签收)。
# day12_custom_tools.py
from langchain.agents import AgentExecutor, create_react_agent
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
# 模拟数据库
ORDERS_DB = {
"1001": {"status": "已发货", "carrier": "顺丰", "tracking": "SF123456789"},
"1002": {"status": "已签收", "carrier": "京东物流", "tracking": "JD987654321"},
}
@tool
def query_order_status(order_id: str) -> str:
"""
根据订单号查询物流状态。
如果订单不存在,返回“未找到该订单”。
"""
order = ORDERS_DB.get(order_id)
if not order:
return "未找到该订单"
return f"订单 {order_id} 状态:{order['status']},快递公司:{order['carrier']},单号:{order['tracking']}"
# 测试
print(query_order_status("1001"))
# 输出:订单 1001 状态:已发货,快递公司:顺丰,单号:SF123456789
💡 函数签名中的
order_id: str会被自动解析为 Tool 的输入参数!
🛠️ 四、实战 2:发送通知(模拟企业微信/钉钉)
场景
AI 可主动触发通知(需权限控制!)。
NOTIFICATION_LOG = []
@tool
def send_notification(user_name: str, message: str) -> str:
"""
向指定用户发送通知消息(仅限内部系统使用)。
返回发送结果。
"""
# 模拟发送
log_entry = f"[通知] 发送给 {user_name}:{message}"
NOTIFICATION_LOG.append(log_entry)
print(f"📢 {log_entry}") # 模拟实际发送
return "通知已发送"
# 测试
print(send_notification("张三", "您的订单已发货!"))
⚠️ 安全提示:生产环境中,此处应调用企业微信/钉钉 SDK,并校验用户权限!
🛠️ 五、实战 3:执行运维脚本(谨慎使用!)
场景
AI 可请求“重启服务”、“清理缓存”等操作(高危! )。
@tool
def run_maintenance_command(command: str) -> str:
"""
执行预定义的运维命令(仅支持:'restart_web', 'clear_cache')。
禁止执行任意 shell 命令!
"""
allowed_commands = {
"restart_web": "正在重启 Web 服务...",
"clear_cache": "正在清理缓存..."
}
if command not in allowed_commands:
return "不支持的命令。可用命令:restart_web, clear_cache"
return allowed_commands[command]
# 测试
print(run_maintenance_command("restart_web"))
🔒 关键原则:
- 绝不直接执行用户输入的 shell 命令
- 只允许白名单内的操作
- 记录所有操作日志
🤖 六、组装自定义 Tool Agent
# 初始化 LLM
llm = ChatOllama(model="qwen:7b", temperature=0)
# 收集所有自定义工具
tools = [
query_order_status,
send_notification,
run_maintenance_command
]
# 创建 ReAct Agent
prompt = create_react_agent.get_default_prompt()
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=5
)
# 测试复杂任务
questions = [
"订单 1002 到哪了?",
"如果订单已签收,请通知用户李四:‘您的订单已送达,感谢购买!’",
"重启一下 Web 服务"
]
for q in questions:
print(f"\n👤 用户:{q}")
response = agent_executor.invoke({"input": q})
print(f"🤖 AI:{response['output']}")
▶️ 输出示例:
Thought: 我需要先查询订单状态。
Action: query_order_status
Action Input: {"order_id": "1002"}
Observation: 订单 1002 状态:已签收...
Thought: 现在需要发送通知。
Action: send_notification
Action Input: {"user_name": "李四", "message": "您的订单已送达,感谢购买!"}
Observation: 通知已发送
Final Answer: 已通知用户李四:您的订单已送达,感谢购买!
📢 [通知] 发送给 李四:您的订单已送达,感谢购买!
✅ Agent 成功完成“查询 → 决策 → 通知”多步操作!
⚠️ 七、安全与最佳实践
表格
| 风险 | 防御措施 |
|---|---|
| 工具被滥用 | 所有 Tool 必须做输入校验和权限控制 |
| 敏感数据泄露 | Tool 返回内容避免包含密码、身份证等 |
| 操作不可逆 | 高危操作(如删除)应加二次确认或审批流程 |
| 日志缺失 | 记录所有 Tool 调用(谁、何时、调用了什么) |
| 模型幻觉导致错误调用 | 在 description 中明确说明使用场景和限制 |
💡 建议:
- 开发阶段开启
verbose=True- 生产环境增加中间件拦截危险调用
- 对 Tool 进行单元测试
📦 八、配套代码结构
langchain-30-days/
└── day12/
└── custom_business_tools.py # 查订单 + 发通知 + 运维脚本
📝 九、今日小结
- ✅ 理解了自定义 Tool 的价值:连接 AI 与业务系统
- ✅ 学会了用
@tool装饰器快速封装函数 - ✅ 实现了三大典型企业场景工具
- ✅ 掌握了 Tool 的安全设计原则
- ✅ 构建了能自主调用内部系统的智能体
🎯 明日预告:Day 13 —— OutputParser 进阶!让 Agent 的输出自动转为结构化对象(Pydantic + Retry)!