work hard work smart

专注于AI+Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

LangChain Agent以及ReAct模式

Posted on 2026-10-05 07:29  work hard work smart  阅读(2)  评论(0)    收藏  举报

什么是Tool: 工具/能力的封装

什么是Agent

1、Agent : 决策者 = 如何使用这些能力

2、Agent 是一个决策引擎,决定什么时候调用哪个Tool, 根据上下文决定下一步做什么,处理Tool的返回结果,并确定是否继续调用其它Tool。

3、Agent的核心是推理+行动(Reason+Act),也就是ReAct模式

image

 

Agent = LLM + Memory + Tools + Planning + Action 

 

二、Agent使用

import os
import json
import httpx
from typing import TypedDict

from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()

# 1.Tool 定义
@tool
def get_weather(loc: str) -> dict:
    """
    查询即时天气函数

    :param loc: 必要参数,字符串类型,用于表示查询天气的具体城市名称。
                注意,中国的城市需要用对应城市的英文名称代替,例如如果需要查询北京市天气,
                则 loc 参数需要输入 'Beijing'/'shanghai'。
    :return: OpenWeather API 查询即时天气的结果。
    """
    url = "https://api.openweathermap.org/data/2.5/weather"
    params = {
        "q": loc,
        "appid": os.getenv("OPENWEATHER_API_KEY"),
        "units": "metric",
        "lang": "zh_cn"
    }
    response = httpx.get(url, params=params, timeout=30)
    data = response.json()
    #print(json.dumps(data, ensure_ascii=False, indent=2))
    return json.dumps(data, ensure_ascii=False)



# 2 结构化输出(推荐)
class WeatherCompareOutput(TypedDict):
    beijing_temp: float
    shanghai_temp: float
    hotter_city: str
    summary: str


# 3 模型(OpenAI Compatible)
model = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


# 4 创建Agent
agent = create_agent(
    model=model,
    tools=[get_weather],
    system_prompt=(
        "你是天气助手。"
        "当用户询问多个城市天气时,"
        "你需要分别调用工具获取数据,并进行比较分析。"
    ),
    response_format=WeatherCompareOutput,
)

# 5 调用Agent
result = agent.invoke(
    {"input": "请问今天北京和上海的天气怎么样,哪个城市更热?"}
)
print(result)

print()

print(json.dumps(result["structured_response"], ensure_ascii=False, indent=2))

  

ReAct Agent (推理+Action过程)

import os

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.tools import tool

load_dotenv()

# 模拟产品数据库
PRODUCT_DATABASE = {
    "无线耳机": [
        {"id": "WH-1000XM5", "name": "索尼 WH-1000XM5", "popularity": 95, "price": 299},
        {"id": "QC45", "name": "Bose QuietComfort 45", "popularity": 88, "price": 329},
        {"id": "AIRMAX", "name": "苹果 AirPods Max", "popularity": 92, "price": 549},
        {"id": "PXC550", "name": "森海塞尔 PXC 550", "popularity": 76, "price": 299},
        {"id": "HT450", "name": "JBL Tune 760NC", "popularity": 82, "price": 99}
    ],
    "游戏鼠标": [
        {"id": "GPW", "name": "罗技 G Pro 无线", "popularity": 90, "price": 129},
        {"id": "VIPER", "name": "雷蛇 Viper V2 Pro", "popularity": 87, "price": 149},
        {"id": "DAV3", "name": "雷蛇 DeathAdder V3", "popularity": 85, "price": 119}
    ],
    "笔记本电脑": [
        {"id": "MBP14", "name": "MacBook Pro 14英寸", "popularity": 94, "price": 1999},
        {"id": "XPS13", "name": "戴尔 XPS 13", "popularity": 89, "price": 1299},
        {"id": "TPX1", "name": "ThinkPad X1 Carbon", "popularity": 86, "price": 1499}
    ]
}

# 模拟库存数据库
INVENTORY_DATABASE = {
    "WH-1000XM5": {"stock": 10, "location": "仓库-A"},
    "QC45": {"stock": 0, "location": "仓库-B"},
    "AIRMAX": {"stock": 5, "location": "仓库-C"},
    "PXC550": {"stock": 15, "location": "仓库-A"},
    "HT450": {"stock": 25, "location": "仓库-B"},
    "GPW": {"stock": 8, "location": "仓库-C"},
    "VIPER": {"stock": 12, "location": "仓库-A"},
    "DAV3": {"stock": 3, "location": "仓库-B"},
    "MBP14": {"stock": 7, "location": "仓库-C"},
    "XPS13": {"stock": 0, "location": "仓库-A"},
    "TPX1": {"stock": 4, "location": "仓库-B"}
}


# 工具1:搜索产品
@tool
def search_products(query: str) -> str:
    """搜索产品并返回按受欢迎度排序的结果"""
    print(f"🔍 [工具调用] search_products('{query}')")

    # 关键词映射,支持多种中文表达方式
    keyword_mapping = {
        "无线耳机": ["无线耳机", "蓝牙耳机", "头戴式耳机", "耳机"],
        "游戏鼠标": ["游戏鼠标", "电竞鼠标", "鼠标"],
        "笔记本电脑": ["笔记本电脑", "笔记本", "手提电脑", "电脑"]
    }

    # 查找匹配的类别
    matched_category = None
    for category, keywords in keyword_mapping.items():
        if any(keyword in query for keyword in keywords):
            matched_category = category
            break

    if matched_category and matched_category in PRODUCT_DATABASE:
        products = PRODUCT_DATABASE[matched_category]
        # 按受欢迎度排序
        sorted_products = sorted(products, key=lambda x: x['popularity'], reverse=True)
        result = f"找到 {len(sorted_products)} 个匹配 '{query}' 的产品:\n"

        for i, product in enumerate(sorted_products, 1):
            result += f"{i}. {product['name']} (ID: {product['id']}) - 受欢迎度: {product['popularity']}% - ¥{product['price']}\n"

        return result


# 工具2:检查库存
@tool
def check_inventory(product_id: str) -> str:
    """检查特定产品的库存状态"""
    print(f"📦 [工具调用] check_inventory('{product_id}')")

    if product_id in INVENTORY_DATABASE:
        stock_info = INVENTORY_DATABASE[product_id]
        status = "有库存" if stock_info['stock'] > 0 else "缺货"
        return f"产品 {product_id}: {status} ({stock_info['stock']} 件库存) - 位置: {stock_info['location']}"
    else:
        return f"未找到产品ID: {product_id}"


# 创建代理
model = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

agent = create_agent(
    model,
    tools=[search_products, check_inventory],
    system_prompt="""你是电商助手,遵循ReAct模式:
    1. 先推理用户需求
    2. 选择合适的工具执行操作
    3. 基于工具结果进行下一步推理
    4. 重复直到获得完整答案

    保持推理步骤简洁明了。"""
)

# 测试案例1:无线耳机搜索
result1 = agent.invoke({
    "messages": [{"role": "user", "content": "查找当前最受欢迎的无线耳机并检查是否有库存"}]
})

print("\n" + "=" * 40)
print("📊 最终结果:")
for msg in result1['messages']:
    if hasattr(msg, 'content'):
        print(f"{msg.__class__.__name__}: {msg.content}")
print("=" * 40)

print()
print()

# # 详细追踪ReAct循环过程
def track_react_cycle(messages):
    print("ReAct循环步骤分析:")
    step = 1
    for i, msg in enumerate(messages):
        msg_type = msg.__class__.__name__
        if msg_type == "AIMessage" and hasattr(msg, 'tool_calls') and msg.tool_calls:
            print(f"\n🔄 步骤{step}: Reasoning + Acting")
            for tool_call in msg.tool_calls:
                print(f"   🛠️  工具调用: {tool_call['name']}({tool_call['args']})")
            step += 1
        elif msg_type == "ToolMessage":
            print(f"   📋  观察结果: {msg.content[:80]}...")
        elif msg_type == "AIMessage" and not (hasattr(msg, 'tool_calls') and msg.tool_calls):
            print(f"\n✅ 最终回答: {msg.content}")

# 追踪案例1的ReAct循环
track_react_cycle(result1['messages'])

  

输出如下:

🔍 [工具调用] search_products('无线耳机')
📦 [工具调用] check_inventory('WH-1000XM5')

========================================
📊 最终结果:
HumanMessage: 查找当前最受欢迎的无线耳机并检查是否有库存
AIMessage: 1. 首先需要搜索当前最受欢迎的无线耳机。
2. 然后从搜索结果中获取最受欢迎的产品ID,并检查其库存状态。

现在我开始第一步,搜索最受欢迎的无线耳机:


ToolMessage: 找到 5 个匹配 '无线耳机' 的产品:
1. 索尼 WH-1000XM5 (ID: WH-1000XM5) - 受欢迎度: 95% - ¥299
2. 苹果 AirPods Max (ID: AIRMAX) - 受欢迎度: 92% - ¥549
3. Bose QuietComfort 45 (ID: QC45) - 受欢迎度: 88% - ¥329
4. JBL Tune 760NC (ID: HT450) - 受欢迎度: 82% - ¥99
5. 森海塞尔 PXC 550 (ID: PXC550) - 受欢迎度: 76% - ¥299

AIMessage: 1. 搜索结果显示,最受欢迎的无线耳机是索尼 WH-1000XM5(受欢迎度95%)。
2. 接下来检查该产品的库存状态。

现在我执行库存检查:



ToolMessage: 产品 WH-1000XM5: 有库存 (10 件库存) - 位置: 仓库-A
AIMessage: 索尼 WH-1000XM5 是当前最受欢迎的无线耳机,且有库存(10件,位于仓库-A)。  
如需下单或了解其他选项,请告诉我!
========================================


ReAct循环步骤分析:

🔄 步骤1: Reasoning + Acting
   🛠️  工具调用: search_products({'query': '无线耳机'})
   📋  观察结果: 找到 5 个匹配 '无线耳机' 的产品:
1. 索尼 WH-1000XM5 (ID: WH-1000XM5) - 受欢迎度: 95% - ¥299
2. 苹果 ...

🔄 步骤2: Reasoning + Acting
   🛠️  工具调用: check_inventory({'product_id': 'WH-1000XM5'})
   📋  观察结果: 产品 WH-1000XM5: 有库存 (10 件库存) - 位置: 仓库-A...

✅ 最终回答: 索尼 WH-1000XM5 是当前最受欢迎的无线耳机,且有库存(10件,位于仓库-A)。  
如需下单或了解其他选项,请告诉我!