智能体(Agent) 是一种能够自主规划、决策、执行任务的组件,核心是让大语言模型(LLM)根据任务需求,选择并调用工具,完成单靠模型自身无法解决的复杂问题。

Agent智能体 =大语言模型(大脑)+工具集(手脚)+ 决策逻辑(思维)

Agent智能体是让 LLM 从"只会回答"升级为会做事(影响现实世界)"的智能助手。

  • 没有Agent时,LLM 只能基于自身训练数据回答问题,遇到需要实时数据、复杂计算、外部工具调用的场景就会卡壳。
  • 有了Agent后,LLM 就像一个"指挥官",能思考任务步骤→选择合适工具→执行工具调用→根据结果调整策略,直到完成任务。

核心特点:

  • 目标驱动:围绕用户的具体任务目标展开工作。
  • 工具调用能力:能连接外部工具,弥补LLM 的局限性
  • 自主决策与迭代:不需要人工干预,能根据工具返回的结果,判断是否需要继续调用工具,或直接生成最终答案。

agent与chain的区别

chain agent
执行固定流程,按预设步骤执行 执行流程动态,根据任务和结果自主调整
工具调用链路写死在代码中 工具选择由LLM思考决定
适合简单标准化任务 适合复杂、多步骤、需要决策的任务

创建agent

通过create_agent方法可以创建Agent对象,其也是Runnable接口的子类实现,所以也拥有:

  • invoke,执行,一次型得到完整结果
  • stream,执行,流式得到结果

agent调用invoke、stream时,参数类型:["message":[{"role":"user", "content":query}]]

invoke案例

from langchain.agents import create_agent
from langchain_community.chat_models import ChatTongyi
from langchain_core.output_parsers import StrOutputParser
from langchain_core.tools import tool


api_key = "sk-xxx"

@tool(description="查询天气")
def get_weather(city):
    return "晴天"

agent = create_agent(
    model=ChatTongyi(model="qwen3-max", api_key=api_key),
    tools=[get_weather,],
    system_prompt="你是一个聊天助手,可以回答用户的问题"
)

# agent调用invoke时,参数类型:["message":[{}]]
res = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "明天上海的天气如何?"}
        ]
    }
)

parser = StrOutputParser()

for message in res["messages"]:
    print(f"{type(message).__name__}: {parser.invoke(message)}")

stream案例

from langchain.agents import create_agent
from langchain_community.chat_models import ChatTongyi
from langchain_core.tools import tool

api_key = "sk-xxx"

@tool(description="获取股价,传入股票名称,返回字符串信息")
def get_price(name: str) -> str:
    return f"股票{name}的价格是20元"

@tool(description="获取股票信息,传入股票名称,返回字符串信息")
def get_price_info(name: str) -> str:
    return f"股票{name}是一家上市公司,专注游戏开发"

agent = create_agent(
    model=ChatTongyi(model="qwen3-max", api_key=api_key),
    tools=[get_price, get_price_info],
    system_prompt="你是一个智能助手,可以回答股票相关问题"
)

for chunk in agent.stream(
        {"messages": [{"role":"user", "content":"大米公司的股价多少,介绍一下"}]},
    stream_mode="values"
):
    # print(chunk)
    latest_message = chunk["messages"][-1]
    if latest_message.content:
        print(type(latest_message).__name__, latest_message.content)
    try:
        # 如果有tool_calls说明在调用工具,可以看到调用工具的信息
        if latest_message.tool_calls:
            print(latest_message.tool_calls)
    except AttributeError as e:
        pass

ReAct

Agent ReAct 是大模型智能体的核心思考与行动框架,全称 Reasoning+Acting(推理+行动),是让 Agent 像人类一样「思考问题→制定策略→执行行动→验证结果」的关键逻辑。

简单来说:ReAct让 Agent不再是“直接回答问题”,而是通过“自然语言思考过程”指导工具调用,一步步解决复杂问题完美适配需要多步推理、工具协作的场景(如智能客服、报告生成、任务规划等)

一个典型的ReAct范式的Agent如上图所示:

  • 思考Reasoning:模型分析问题,判断现有信息是否足够,明确下一步即模型决策是否需要调用外部工具获取更多信息用来回答
  • 行动Action:执行思考阶段指定的策略即基于模型决策结果,调用工具获取信息
  • 观察observation:获取行动的结果,提取有效信息即获取工具返回值即判断工具是否正常工作位下一轮思考提供信息
  • 思考->行动->观察->思考->行动->观察... 循环往复直到结束

Langchain的Agent对象遵循ReAct框架要求,在执行的过程中会持续的自我思考、自我行动、自我观察。

案例

from langchain.agents import create_agent
from langchain_community.chat_models import ChatTongyi
from langchain_core.tools import tool

api_key = "sk-xxx"

@tool(description="获取体重,返回值是整数,单位千克")
def get_weight() -> int:
    return 70

@tool(description="获取身高,返回值是整数,单位是厘米")
def get_height() -> int:
    return 175

agent = create_agent(
    model=ChatTongyi(model="qwen3-max", api_key=api_key),
    tools=[get_weight, get_height],
    system_prompt="""你是一个严格遵循ReAct框架的智能体,必须按照[思考->行动->观察->再思考]的流程解决问题,
    且**每轮仅能思考调用1个工具**,禁止单次调用多个工具。
    并告知我你的思考过程,工具的调用原因,按思考、行动、观察按个结构告知我"""
)

for chunk in agent.stream(
        {"messages": [{"role":"user", "content":"计算我的BMI"}]},
    stream_mode="values"
):
    # print(chunk)
    latest_message = chunk["messages"][-1]
    if latest_message.content:
        print(type(latest_message).__name__, latest_message.content)
    try:
        # 如果有tool_calls说明在调用工具,可以看到调用工具的信息
        if latest_message.tool_calls:
            print(latest_message.tool_calls)
    except AttributeError as e:
        pass
HumanMessage 计算我的BMI
AIMessage [思考]  
要计算BMI(身体质量指数),需要知道体重(千克)和身高(米)。公式为:  
$$ \text{BMI} = \frac{\text{体重(kg)}}{\text{身高(m)}^2} $$  

目前我缺少用户的体重和身高数据,因此需要先获取这两个数值。根据工具列表,我可以分别调用 `get_weight` 和 `get_height` 来获取数据。由于每轮只能调用一个工具,我首先选择获取体重。

[行动]  

[{'name': 'get_weight', 'args': {}, 'id': 'call_dd1510e1647f4346946ea383', 'type': 'tool_call'}]
ToolMessage 70
AIMessage [观察]  
获取到的体重是 70 千克。

[思考]  
现在我已经有了体重数据(70 kg),接下来需要获取身高数据(单位:厘米)。之后,我可以将身高转换为米,并计算 BMI。根据规则,我每轮只能调用一个工具,因此现在调用 `get_height` 来获取身高数据。

[行动]  


[{'name': 'get_height', 'args': {}, 'id': 'call_46c534accd4843bfa84b47d9', 'type': 'tool_call'}]
ToolMessage 175
AIMessage [观察]  
获取到的身高是 175 厘米。

[思考]  
现在我已获取到体重(70 kg)和身高(175 cm)。接下来,我可以计算 BMI。首先将身高从厘米转换为米:175 cm = 1.75 m。然后使用 BMI 公式:  
$$ \text{BMI} = \frac{70}{1.75^2} = \frac{70}{3.0625} \approx 22.86 $$  

因此,用户的 BMI 约为 22.86,属于正常范围。

[最终回答]  
您的 BMI 是约 22.86,属于正常范围。

Plan-and-Execute

ReAct 模式:是“思考 - 行动 - 观察”的单步循环,适合短任务,通过 create_agent (LangChain 1.0+) 或 create_react_agent 可以直接创建agent。

Plan-and-Execute 模式:是将任务拆分为“先制定完整计划,再按步骤执行”的双阶段模式,更适合长程、复杂的多步任务。

其核心逻辑由三个组件组成:

  1. Planner(规划器):一个 LLM Chain,负责接收用户输入,生成一个步骤列表(Plan)。
  2. Executor(执行器):通常是一个 ReAct Agent,负责接收“当前步骤”,调用工具执行,并返回结果。
  3. Controller/Replanner(控制/重规划器):负责协调两者,将执行结果反馈给 Planner,或者判断是否完成任务。

用 LangGraph 构建 Plan-and-Execute,应通过 LangGraph 的状态机(State Graph)来显式定义。这种方式让你能清晰看到“规划”和“执行”之间的状态跳转。

核心设计思路

  1. State(状态):包含 input(用户输入), plan(生成的计划列表), past_steps(已执行的步骤及结果), response(最终回答)。
  2. Nodes(节点)
    • planner_node: 调用 LLM 生成计划。
    • agent_node: 调用 ReAct Agent 执行当前计划步骤。
    • replan_node (可选): 根据执行结果调整后续计划。
  3. Edges(边):控制流程,例如:开始 -> 规划 -> 执行步骤1 -> 执行步骤2 ... -> 结束。
# 确保安装最新版: pip install langchain langchain-openai langgraph
import os
from typing import Annotated, Sequence, TypedDict
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain.agents import create_agent
from langgraph.graph import StateGraph, END
from langchain_community.tools import DuckDuckGoSearchResults
from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
os.environ["DASHSCOPE_API_KEY"] = "sk-xxx"


# 初始化搜索工具
wrapper = DuckDuckGoSearchAPIWrapper(
    region="zh-cn",  # 中文结果
    time="y",        # 最近一年
    max_results=1    # 最多1条
)
duck_search_tool = DuckDuckGoSearchResults(api_wrapper=wrapper, endpoint="http://api.wlai.vip")

# --- 1. 定义状态 (State) ---
class AgentState(TypedDict):
    input: str  # 用户原始输入
    plan: list[str]  # 生成的计划步骤列表
    past_steps: list[tuple]  # 已执行的步骤 [(step_name, result), ...]
    response: str  # 最终输出
    current_step_index: int  # 当前执行到的步骤索引


# --- 2. 初始化组件 ---
llm = ChatOpenAI(
    model="qwen3-max",
    api_key=os.getenv("DASHSCOPE_API_KEY", ""),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    # streaming=True,  # 支持流式输出
    temperature=0.3  # 降低随机性,让工具调用更稳定
)

tools = [duck_search_tool]

# 创建一个用于执行单步任务的 ReAct Agent (作为子节点)
agent_executor = create_agent(
    model=llm,
    tools=tools,
    system_prompt="你是一个负责执行具体子任务的助手。请根据给定的任务步骤调用工具。"
)


# --- 3. 定义节点函数 ---

def plan_node(state: AgentState):
    """规划节点:将大任务拆解为步骤"""
    messages = [
        HumanMessage(
            content=f"""
            请为以下目标制定一个分步计划。
            目标:{state['input']}
            要求:
            1. 输出一个 JSON 格式的列表,包含具体的步骤字符串。
            2. 每个步骤必须是可独立执行的任务。
            3. 只输出 JSON 列表,不要其他废话。例如:["步骤1", "步骤2"]
            """
        )
    ]
    # 为了简化演示,这里直接让 LLM 输出文本,实际生产中建议强制 JSON Output
    response = llm.invoke(messages)

    # 简单解析 (实际需用 JSON Parser)
    # 假设 LLM 返回了类似 ["step1", "step2"] 的字符串
    import json
    try:
        # 清理可能的 markdown 标记
        content = response.content.replace("```json", "").replace("```", "").strip()
        plan_list = json.loads(content)
    except:
        # 降级处理,如果解析失败,将其视为单步任务
        plan_list = [state['input']]
    print(f"plan_list: {plan_list}")
    return {"plan": plan_list, "current_step_index": 0}


def execute_step_node(state: AgentState):
    """执行节点:执行当前步骤"""
    if state["current_step_index"] >= len(state["plan"]):
        return {"past_steps": state["past_steps"]}  # 没有更多步骤,直接跳过

    current_step = state["plan"][state["current_step_index"]]
    print(f"current_step_index: {state["current_step_index"]} - {current_step}")
    # 调用内部的 Agent 执行当前步骤
    # 我们将“当前步骤”作为输入传给内部 agent
    result = agent_executor.invoke(
        {"messages": [HumanMessage(content=current_step)]}
    )
    print(result)
    output = result.get("messages")[-1]

    # 更新状态
    new_past_steps = state.get("past_steps", []) + [(current_step, output.content)]
    new_index = state["current_step_index"] + 1

    return {
        "past_steps": new_past_steps,
        "current_step_index": new_index
    }


def should_continue(state: AgentState):
    """路由逻辑:判断是否还有步骤要执行"""
    if state["current_step_index"] < len(state["plan"]):
        return "continue"
    else:
        return "end"


def finalize_node(state: AgentState):
    """汇总节点:将所有步骤结果汇总为最终答案"""
    steps_summary = "\n".join([f"Step: {s[0]}\nResult: {s[1]}" for s in state["past_steps"]])

    messages = [
        HumanMessage(
            content=f"""
            基于以下执行步骤和结果,回答用户的原始问题。
            原始问题:{state['input']}

            执行历史:
            {steps_summary}

            请给出一个综合的最终回答。
            """
        )
    ]
    response = llm.invoke(messages)
    return {"response": response.content}


# --- 4. 构建图 (Graph) ---
workflow = StateGraph(AgentState)

# 添加节点
workflow.add_node("planner", plan_node)
workflow.add_node("executor", execute_step_node)
workflow.add_node("finalizer", finalize_node)

# 设置入口
workflow.set_entry_point("planner")

# 定义边
# 从 planner 到 executor
workflow.add_edge("planner", "executor")

# 条件边:executor 完成后,判断是继续执行下一步,还是去 finalize
workflow.add_conditional_edges(
    "executor",
    should_continue,
    {
        "continue": "executor",  # 循环回 executor 执行下一步
        "end": "finalizer"  # 结束,去汇总
    }
)

# 从 finalizer 到 END
workflow.add_edge("finalizer", END)

# 编译图
app = workflow.compile()

# --- 5. 运行测试 ---
print("开始执行 Plan-and-Execute Agent...")
inputs = {"input": "查询 2026 年华为手机的销量,并对小米手机的销量数据,最后总结谁更有优势。"}
result = app.invoke(inputs)

print("\n--- 最终回答 ---")
print(result["response"])


'''
开始执行 Plan-and-Execute Agent...
plan_list: [
    '确认2026年是否已结束,以判断是否有实际销量数据可供查询。', 
    '若2026年尚未结束,查找权威市场研究机构(如IDC、Counterpoint、Canalys)对2026年华为手机销量的预测报告。', 
    '若2026年已结束,检索上述机构发布的2026年全年华为手机实际销量数据。', 
    '同样方法查询2026年小米手机的销量预测或实际销量数据。', 
    '将华为与小米在2026年的销量数据进行对比,包括总量、同比增长率及市场份额。', 
    '分析影响销量的关键因素(如产品发布、市场策略、供应链状况等),评估各自优势。', 
    '基于销量数据和相关分析,总结2026年华为与小米谁在销量上更具优势。'
    ]
current_step_index: 0 - 确认2026年是否已结束,以判断是否有实际销量数据可供查询。
current_step_index: 1 - 若2026年尚未结束,查找权威市场研究机构(如IDC、Counterpoint、Canalys)对2026年华为手机销量的预测报告。
current_step_index: 2 - 若2026年已结束,检索上述机构发布的2026年全年华为手机实际销量数据。
current_step_index: 3 - 同样方法查询2026年小米手机的销量预测或实际销量数据。
current_step_index: 4 - 将华为与小米在2026年的销量数据进行对比,包括总量、同比增长率及市场份额。
current_step_index: 5 - 分析影响销量的关键因素(如产品发布、市场策略、供应链状况等),评估各自优势。
current_step_index: 6 - 基于销量数据和相关分析,总结2026年华为与小米谁在销量上更具优势。

--- 最终回答 ---
截至目前(2024年),2026年尚未到来,因此**没有华为或小米手机在2026年的实际销量数据**,权威市场研究机构(如IDC、Canalys、Counterpoint)也**未发布针对2026年两家手机品牌销量的明确预测报告**。

不过,结合现有趋势和相关信息,可以做出以下综合判断:

### 一、关于手机业务
- **华为**:近年来依托鸿蒙生态和自研芯片逐步恢复手机业务,但受制于供应链和地缘政治因素,其全球市场份额仍处于恢复阶段。目前尚无2026年手机销量的具体预测。
- **小米**:2025年第一季度在中国市场手机销量同比增长40%,市场份额达19%,位居第一,显示出强劲复苏势头。若该趋势延续,2026年其手机销量有望保持竞争力。

但由于缺乏2026年双方手机销量的直接预测或数据,**无法在手机领域明确判断谁更具销量优势**。

### 二、关于智能汽车(常被误纳入“销量”讨论)
部分信息提及2026年小米汽车交付量(如2月达23,728辆)及华为赋能车型年销量或超200万辆,但需注意:
- **小米汽车**是小米集团直接销售的产品;
- **华为不造车**,其“销量”实为合作车企(如问界、智界等)搭载华为技术的车型总和。

若将智能汽车纳入广义“终端销量”考量:
- **小米**在自有品牌电动车上增长迅速;
- **华为**通过技术合作覆盖更广泛的车型和销量基数,整体生态影响力更大。

### 三、综合结论
- **就手机业务而言**:2026年销量尚无可靠预测,无法断定谁更有优势,但小米近期表现更稳健,华为则依赖技术突破与供应链恢复。
- **若泛化到智能终端整体生态**:华为凭借“手机+汽车+鸿蒙生态”的多端协同,在2026年可能具备更广泛的市场影响力;小米则在手机与汽车双线发力,增长潜力显著。

> **最终回答**:  
由于2026年尚未到来,**目前没有华为或小米手机在该年度的销量数据或权威预测**,因此无法准确比较两者在手机销量上的优劣。现有信息更多反映其在智能汽车等新领域的布局。建议在2026年临近或结束后,参考IDC、Canalys等机构发布的正式报告进行客观对比。
'''

middleware

中间件的作用是对智能体的每一步工作进行控制和自定义的执行作用场景:

  • 日志记录、分析、调试
  • 转换提示词、工具选择
  • 重试、备用、提前终止等逻辑控制
  • 安全防护、个人身份检测等

Langchain中内置了一些基础的中间件,参见:https://docs.langchain.com/oss/python/langchain/middleware/built-in
中间件通过Hooks钩子来实现拦截,自定义中间件可以简单的使用装饰器来定义。

节点式钩子(执行点顺序拦截)

  • before_agent:agent执行之前拦截
  • after_agent:agent执行后拦截
  • before_model:模型执行前拦截
  • after model:模型执行后拦截

针对工具和模型的包装式钩子

  • wrap_modelcall:每个模型调用时候拦截
  • wrap_tool cal:每个工具调用时候拦截

案例

from typing import Callable

from langchain.agents import create_agent
from langchain_community.chat_models import ChatTongyi
from langchain_core.output_parsers import StrOutputParser
from langchain_core.tools import tool
from langchain.agents.middleware import before_agent, after_agent, \
    before_model, after_model, wrap_model_call, wrap_tool_call, \
    dynamic_prompt
from langchain.agents import AgentState
from langgraph.runtime import Runtime
from langgraph.types import Command
from langchain.tools.tool_node import ToolCallRequest
from langchain_core.messages import ToolMessage
from langchain.agents.middleware import ModelRequest

api_key = "sk-xxx"

@tool(description="查询天气,传入城市名称,返回天气字符串信息")
def get_weather(city: str) -> str:
    return f"{city}天气:晴天"

"""
1.agent执行前
2.agent执行后
3.model执行前
4.model执行后
5.工具执行中
6.模型执行中
7.dynamic_prompt每次生成提示词前调用

request.runtime.context 是上下文字典,可以添加标志位信息,在其他中间件中获取
如 中间件监控到A工具被调用后添加标志信息,提示词中间件调用时判断这个信息,从而动态切换提示词
"""

@before_agent
def log_before_agent(
        # 整个agent智能体中状态记录
        state: AgentState,
        # 记录了整个执行过程中上下文状态信息
        runtime: Runtime
) -> None:
    # agent执行前会调用此函数,并传入AgentState,Runtime两个对象
    print(f"[before_agent] agent启动,并附带{len(state['messages'])}条消息")

@after_agent
def log_after_agent(state: AgentState, runtime: Runtime) -> None:
    # agent执行后会调用此函数,并传入AgentState,Runtime两个对象
    print(f"[after_agent] agent结束,并附带{len(state['messages'])}条消息")

@before_model
def log_before_model(state: AgentState, runtime: Runtime) -> None:
    # model执行前会调用此函数,并传入AgentState,Runtime两个对象
    print(f"[before_model] model即将调用,并附带{len(state['messages'])}条消息")

@after_model
def log_after_model(state: AgentState, runtime: Runtime) -> None:
    # model执行后会调用此函数,并传入AgentState,Runtime两个对象
    print(f"[after_model] model调用结束,并附带{len(state['messages'])}条消息")

@wrap_model_call
def model_call_hook(
        # 请求的数据封装
        request: ToolCallRequest,
        # 执行的工具函数
        handler: Callable[[ToolCallRequest], ToolMessage | Command]
) -> ToolMessage | Command:
    print("模型被调用")
    return handler(request)

@wrap_tool_call
def tool_call_hook(request, handler):
    print(f"执行工具:{request.tool_call['name']}")
    print(f"工具参数:{request.tool_call['args']}")
    if request.tool_call['name'] == "xxx":  # xxx工具被调用
        # 注入状态信息
        request.runtime.context["xxx"] = True
    return handler(request)


@dynamic_prompt  # 每次生成提示词前调用
def prompt_switch(request: ModelRequest) -> str:  # 需要返回提示词字符串
    print("[prompt_switch] 即将生成提示词")
    if request.runtime.context.get("xxx"):
        # 切换提示词
        return "明天北京的天气如何"
    return request.messages[-1].content


agent = create_agent(
    model=ChatTongyi(model="qwen3-max", api_key=api_key),
    tools=[get_weather,],
    system_prompt="你是一个聊天助手,可以回答用户的问题",
    middleware=[log_before_agent, log_after_agent, log_before_model, log_after_model, model_call_hook, tool_call_hook, prompt_switch]
)


res = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "明天上海的天气如何?"}
        ]
    },
    context={"xxx": False}  # request.runtime.context 上下文字典
)

parser = StrOutputParser()

for message in res["messages"]:
    print(f"{type(message).__name__}: {parser.invoke(message)}")
[before_agent] agent启动,并附带1条消息
[before_model] model即将调用,并附带1条消息
模型被调用
[after_model] model调用结束,并附带2条消息
执行工具:get_weather
工具参数:{'city': '上海'}
[before_model] model即将调用,并附带3条消息
模型被调用
[after_model] model调用结束,并附带4条消息
[after_agent] agent结束,并附带4条消息
HumanMessage: 明天上海的天气如何?
AIMessage: 
ToolMessage: 上海天气:晴天
AIMessage: 明天上海的天气是晴天,适合外出活动!记得做好防晒哦~

ChatOpenai + bind_tools

# 导入LLM(以Deepseek为例,可替换为百度千帆/硅基流动等,用法一致)
from langchain_openai import ChatOpenAI
import os

# 2. 初始化LLM(需提前配置环境变量 DEEPSEEK_API_KEY)
llm = ChatOpenAI(
    model="deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY", ""),
    base_url="https://api.deepseek.com/v1",
    streaming=True,  # 支持流式输出
    temperature=0.3  # 降低随机性,让工具调用更稳定
)

# 3. 定义工具:用@tool装饰器将普通函数转为AI可识别的工具
@tool  # 核心装饰器:自动生成工具描述,供LLM判断是否调用
def get_weather(query: str) -> List[str]:
    """
    用于获取指定地区、指定时间的天气信息(工具描述很重要!LLM靠这个判断是否调用)
    :param query: 查询条件,格式示例:"北京 今明两天天气"、"上海 明天是否下雨"
    :return: 天气信息列表,包含每天的天气描述
    """
    # 这里是模拟工具返回(真实场景替换为调用天气API,如高德/百度天气接口)
    if "今明两天" in query or "今天" in query or "明天" in query:
        return ["今天天气晴朗,温度20~28℃,微风", "明天天气多云转晴,温度22~30℃,南风3级"]
    elif "后天" in query:
        return ["后天天气小雨,温度18~25℃,东北风2级"]
    else:
        return [f"已查询到:{query} 的天气为晴朗,温度20~28℃(模拟数据)"]

# 工具列表:可添加多个工具(如搜索、计算等)
tools = [get_weather]

# 4. 绑定工具到LLM:告诉模型"你现在拥有这些工具"
# bind_tools:LangChain的工具绑定方法,让LLM能识别工具并生成工具调用指令
llm_with_tools = llm.bind_tools(tools)

response = llm_with_tools.invoke("北京气温乘2是多少?")

if response.tool_calls:
    # 手动处理每个工具调用
    for tool_call in response.tool_calls:
        tool_result = execute_tool(tool_call)
        messages.append(AIMessage(content=str(tool_result)))
    # 再次调用获取最终回答
    final_response = llm.invoke(messages)

ChatOpenai.bind_tools 与 LangChain agent 区别:
 

1. 调用机制不同

  • bind_tools():直接绑定工具到LLM,让模型自主决定是否调用工具

  • langchain Agent:有一个专门的"思考-行动-观察"循环(ReAct模式),可以多次调用工具

2. 执行流程差异

bind_tools()流程:

# 单次调用,模型可能返回工具调用,也可能直接返回文本
response = llm.bind_tools(tools).invoke(messages)
# 如果需要执行工具,需要手动处理:
if response.tool_calls:
    # 手动执行工具
    # 手动将结果传回给模型
    # 再次调用模型获取最终回答

langchain Agent流程:

# Agent自动处理整个循环
agent.invoke(messages)
# Agent内部会自动:
# 1. 判断是否需要调用工具
# 2. 如果需要,自动调用工具
# 3. 将工具结果传回给LLM
# 4. 重复1-3直到得到最终答案

3. 决策能力差异

# bind_tools() - 单次决策
tools = [get_weather, search_web, calculate]
llm.bind_tools(tools).invoke("北京天气怎么样?并计算明天比今天高几度?")
# 可能只调用get_weather,不会自动进行后续计算

# langchain Agent - 多步推理
agent.invoke("北京天气怎么样?并计算明天比今天高几度?")
# 可能:
# 1. 调用get_weather获取今明两天温度
# 2. 调用calculate计算温差
# 3. 整合信息回答用户

实际应用场景

使用bind_tools()适合:

# 简单场景,只需要一次工具调用
tools = [get_weather]
llm = ChatOpenAI(model="deepseek-chat")
llm_with_tools = llm.bind_tools([get_weather])

# 翻译任务,可能不需要工具
response = llm.invoke("翻译'hello'到中文")

使用langchain Agent适合:

# 复杂场景,需要多步推理
from langchain.agents import create_tool_calling_agent

tools = [search_web, calculator, database_query]
agent = create_tool_calling_agent(llm, tools, prompt)

# 需要多次工具调用的复杂问题
agent.invoke("查询公司去年Q4的销售数据,计算同比增长率,并查找相关行业新闻")

关键对比表格

特性 bind_tools() Agent
调用次数 单次 可多次
自动执行工具
推理循环 有(ReAct)
复杂度
适用场景 简单工具调用 复杂任务分解
控制粒度 细(手动控制) 粗(自动控制)

代码示例对比

# bind_tools() 完整手动流程
llm_with_tools = llm.bind_tools([get_weather, calculator])
response = llm_with_tools.invoke("北京气温乘2是多少?")

if response.tool_calls:
    # 手动处理每个工具调用
    for tool_call in response.tool_calls:
        tool_result = execute_tool(tool_call)
        messages.append(AIMessage(content=str(tool_result)))
    # 再次调用获取最终回答
    final_response = llm.invoke(messages)

# langchain Agent 自动流程
agent = create_agent(llm, [get_weather, calculator], prompt)
# 一步完成所有操作
final_response = agent.invoke({"input": "北京气温乘2是多少?"})

总结bind_tools() 是底层工具绑定,给你更多控制权;而 Agent 是高层抽象,自动处理完整的工具调用循环。选择哪个取决于你的具体需求!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐