🌟 LangChain 30 天保姆级教程 · Day 12|自定义 Tool 实战!把你的 API、数据库、脚本变成 AI 的“超能力”

3 阅读4分钟

系列目标: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)!