Agent 动态调控:模型、工具、提示词、输出、流模式

文章目录

    • [1. 指定模型](#1. 指定模型)
      • [1.1 静态模型](#1.1 静态模型)
      • [1.2 动态模型](#1.2 动态模型)
    • [2. 指定工具](#2. 指定工具)
      • [2.1 静态工具](#2.1 静态工具)
      • [2.2 动态工具](#2.2 动态工具)
        • [2.2.1 运行时,根据条件动态选择预先注册好的工具](#2.2.1 运行时,根据条件动态选择预先注册好的工具)
        • [2.2.2 运行时,动态加入新工具](#2.2.2 运行时,动态加入新工具)
      • [2.3 工具错误处理](#2.3 工具错误处理)
    • [3. 指定提示词](#3. 指定提示词)
      • [3.1 静态系统提示](#3.1 静态系统提示)
      • [3.2 动态系统提示](#3.2 动态系统提示)
    • [4. 结构化输出](#4. 结构化输出)
      • [4.1 说明](#4.1 说明)
      • [4.2 默认行为](#4.2 默认行为)
    • [5. 定义State](#5. 定义State)
      • [5.1 通过 Middleware 定义](#5.1 通过 Middleware 定义)
      • [5.2 通过 state_schema 参数定义](#5.2 通过 state_schema 参数定义)
    • [6. 人机协作(Human-in-the-loop)](#6. 人机协作(Human-in-the-loop))
      • [6.1 三种中断决策类型](#6.1 三种中断决策类型)
      • [6.2 配置与响应中断](#6.2 配置与响应中断)
        • [6.2.1 添加中间件与检查点](#6.2.1 添加中间件与检查点)
        • [6.2.2 响应中断](#6.2.2 响应中断)
    • [7. 支持的流模式](#7. 支持的流模式)
      • [7.1 模式一:updates - 流式传输代理进度](#7.1 模式一:updates - 流式传输代理进度)
      • [7.2 模式二:messages - 流式传输LLM Token](#7.2 模式二:messages - 流式传输LLM Token)
      • [7.3 模式三:custom - 流式传输自定义更新](#7.3 模式三:custom - 流式传输自定义更新)
      • [7.4 模式四:组合模式 - 同时使用多种流模式](#7.4 模式四:组合模式 - 同时使用多种流模式)
      • [7.5 模式实践](#7.5 模式实践)
        • [7.5.1 流式传输推理 / 思考 Token](#7.5.1 流式传输推理 / 思考 Token)
        • [7.5.2 流式传输工具调用](#7.5.2 流式传输工具调用)
        • [7.5.3 流式传输与人机协作 (Human-in-the-Loop)](#7.5.3 流式传输与人机协作 (Human-in-the-Loop))

1. 指定模型

模型是Agent的推理引擎,其配置方式分为静态模型和动态模型两种。

1.1 静态模型

在创建Agent时一次性配置,执行期间保持不变。有两种指定方式:

  • 使用模型标识符字符串(最直接):
python 复制代码
agent = create_agent("openai:gpt-4o-mini", tools=tools)

支持自动推断(如 "gpt-5" 自动映射为 "openai:gpt-5")。

  • 使用模型实例(更精细控制):
python 复制代码
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
    model="gpt-5.4",
    temperature=0,
    api_key=os.getenv("PACKYAPI_API_KEY"),
    base_url="https://www.packyapi.com/v1",
)
agent = create_agent(model, tools=tools)

适合设置 temperaturemax_tokenstimeout 等特定参数。

1.2 动态模型

在运行时根据当前状态或上下文动态选择模型。通过中间件(middleware)配合 @wrap_model_call 装饰器实现:

python 复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from langchain.tools import tool
from langchain_openai import ChatOpenAI
model1 = ChatOpenAI(
    model="gpt-5.4",
    temperature=0,
    api_key=os.getenv("PACKYAPI_API_KEY"),
    base_url="https://www.packyapi.com/v1",
)

model2 = ChatOpenAI(
    model="gpt-5.5",
    temperature=0,
    api_key=os.getenv("PACKYAPI_API_KEY"),
    base_url="https://www.packyapi.com/v1",
)

@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息"""
    return f"{city}的天气是晴天,温度25摄氏度。"

@wrap_model_call
# request为发送给模型的完整请求对象,handler为可调用函数,负责继续执行后面中间件,最终真正调用模型
def retry_model(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    """根据消息数量来选择模型"""
    count=len(request.state["messages"])
    if count < 5:
        final_model = model1
    else:
        final_model = model2

    return handler(request.override(model=final_model)) # 动态选择进行调用


# 构造agent
agent=create_agent(
    model=model1,
    tools=[get_weather_for_location],
    middleware=[retry_model],
)
print(
    agent.invoke(
        {
            "messages": [
                {
                    "role": "user",
                    "content": "What is the weather in Beijing?"
                }
            ]
        }
    )
)

打印:

plain 复制代码
{'messages': [HumanMessage(content='What is the weather in Beijing?', additional_kwargs={}, response_metadata={}, id='ea5f09d3-e982-408a-b6ce-939cc3f234af'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 4427, 'total_tokens': 4448, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-adbbb889-71aa-4112-9895-ba118', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019fa6cc-963a-7b42-a1c2-4c73caf7d3cd-0', tool_calls=[{'name': 'get_weather_for_location', 'args': {'city': 'Beijing'}, 'id': 'call_64T5LpfKwgHwcDFzwm3lw523', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 4427, 'output_tokens': 21, 'total_tokens': 4448, 'input_token_details': {}, 'output_token_details': {}}), ToolMessage(content='Beijing的天气是晴天,温度25摄氏度。', name='get_weather_for_location', id='0518d319-0742-4e87-847a-eb9bd10ae3db', tool_call_id='call_64T5LpfKwgHwcDFzwm3lw523'), AIMessage(content='北京现在是晴天,气温 `25°C`。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 4476, 'total_tokens': 4493, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-f2af79d1-db7f-44e5-8df9-50eb4', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019fa6cc-9e54-7510-80de-7df6dfa58215-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 4476, 'output_tokens': 17, 'total_tokens': 4493, 'input_token_details': {}, 'output_token_details': {}})]}

2. 指定工具

工具赋予Agent执行具体操作的能力。Agent不仅支持模型直接调用工具,还提供以下增强功能:

  • 顺序多工具调用(一次提示词触发多次工具调用)
  • 并行工具调用(适用时同时执行)
  • 基于前序结果动态选择工具
  • 工具重试逻辑与错误处理
  • 跨工具调用的状态持久化

2.1 静态工具

在创建Agent时通过 tools 参数传入工具列表,执行期间保持不变。工具可以定义为:

  • 普通Python函数
  • 使用 @tool 装饰器(可自定义名称、描述、参数schema等)
python 复制代码
from langchain.tools import tool
from langchain.agents import create_agent

@tool
def get_weather_for_location(city: str) -> str:
	"""获取指定城市的天气信息。"""
	return f"在{city}总是阳光明媚!"

# 定义 agent
agent = create_agent(
model="gpt-o-mini",
tools=[get_weather_for_location],
system_prompt="你是一位乐于助人的助手。",
)

若提供空列表,Agent将仅包含一个不带工具调用能力的LLM节点。

2.2 动态工具

静态工具适用于大多数场景,但在运行时,有些场景需要灵活调整工具集如:

  • 根据认证状态、用户权限、功能开关或对话阶段,再决定哪些工具可用。
  • 避免工具过多导致模型上下文过载或出错,同时避免工具过少限制能力。

这就需要用到动态工具,动态工具有两种实现方式:

  1. 运行时,根据条件动态选择预先注册好的工具
  2. 运行时,动态加入新工具
2.2.1 运行时,根据条件动态选择预先注册好的工具
python 复制代码
from typing import Callable
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from langchain.tools import tool

@tool
def public_search(query: str) -> str:
    """公开搜索: 无需认证即可使用,返回基础信息。"""
    print(f"[公开搜索结果] 关于 '{query}' 的基础信息: 这是公开可获取的内容。")
    return f"[公开搜索结果] 关于 '{query}' 的基础信息: 这是公开可获取的内容。"


@tool
def private_search(query: str) -> str:
    """私有搜索: 仅已认证用户可用,返回敏感或个性化数据。"""
    print(f"[私有搜索结果] 关于 '{query}' 的私密数据: 仅限认证用户查看。")
    return f"[私有搜索结果] 关于 '{query}' 的私密数据: 仅限认证用户查看。"


@tool
def advanced_search(query: str) -> str:
    """高级搜索: 提供深度分析。"""
    print(f"[高级搜索结果] 关于 '{query}' 的深度分析报告: 包含详细统计和趋势。")
    return f"[高级搜索结果] 关于 '{query}' 的深度分析报告: 包含详细统计和趋势。"


class State(AgentState):
    authenticated: bool


@wrap_model_call(state_schema=State)
def state_based_tools(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """基于对话状态的过滤工具。"""

    # 获取状态: 检查用户是否已认证(举例)
    state = request.state
    is_authenticated = state.get("authenticated", False)

    # 未认证用户只能使用以 "public_" 开头的工具
    if not is_authenticated:
        tools = [
            t for t in request.tools
            if t.name.startswith("public_")
        ]

        request = request.override(tools=tools)

    else:
        # 其他条件
        pass

    return handler(request)


# 定义 agent
agent = create_agent(
    model=model1,
    tools=[public_search,private_search,advanced_search],
    system_prompt="你是一位乐于助人的客服助手。根据用户的问题,选择合适的工具来提供答案。",
    middleware=[state_based_tools],
)


# 执行 agent
response = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "北京的天气如何?"
            }
        ],
        "authenticated": False,
    }
)

运行代码后,根据过滤条件,会打印 public_search 中的日志。除了可以从state中获取数据进行过滤,还可以从 storerequest.runtime.store)、contextrequest.runtime.context)中获取并基于获取到的数据进行筛选。

2.2.2 运行时,动态加入新工具
python 复制代码
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ToolCallRequest

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import (
    AgentMiddleware,
    ModelRequest,
    ToolCallRequest
)

@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息。"""
    return f"在{city}总是阳光明媚!"


# 该工具将在运行时动态添加的工具
@tool
def calculate_tip(
    bill_amount: float,
    tip_percentage: float = 0.1
) -> str:
    """计算一笔账单的小费金额。"""
    print("计算账单中...")

    tip = bill_amount * tip_percentage

    return (
        f"小费: {tip:.2f}元, "
        f"一共: {bill_amount + tip:.2f}元"
    )


class DynamicToolMiddleware(AgentMiddleware):
    """能够注册并处理动态工具的中间件。"""
    def wrap_model_call(
        self,
        request: ModelRequest,
        handler
    ):
        # 在请求中添加工具
        updated = request.override(
            tools=[
                *request.tools, # 列表展开,只添加了函数名
                calculate_tip
            ]
        )

        return handler(updated)

    def wrap_tool_call(
        self,
        request: ToolCallRequest,
        handler
    ):
        # 处理动态工具的执行过程
        if request.tool_call["name"] == "calculate_tip":
            try:
                return handler(
                    request.override(
                        tool=calculate_tip  # 真正调用工具
                    )
                )
            except Exception as e:
                return ToolMessage(
                    content=f"工具错误: 请检查您的输入并重新尝试。({str(e)})",
                    tool_call_id=request.tool_call["id"]
                )

        return handler(request)


agent = create_agent(
    model=model1,
    tools=[get_weather_for_location],  # 只注册天气工具
    system_prompt=(
        "你是一位乐于助人的客服助手。"
        "根据用户的问题,必须用合适的工具来提供答案。"
    ),
    middleware=[DynamicToolMiddleware()],
)


# agent 可以同时使用这两个工具
result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "计算100元的账单小费是多少"
            }
        ]
    }
)


print(result["messages"][-1].content)

2.3 工具错误处理

错误处理中间件可提高Agent的鲁棒性,避免因工具调用失败而中断流程。通过 @wrap_tool_call 装饰器创建中间件,自定义工具执行失败时的错误响应:

python 复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage


@wrap_tool_call
def handle_tool_errors(request, handler):
    """使用自定义消息来处理工具执行过程中的错误。"""

    try:
        return handler(request)

    except Exception as e:
        # 向模型返回自定义错误消息
        return ToolMessage(
            content=f"工具错误: 请检查您的输入并重新尝试。({str(e)})",
            tool_call_id=request.tool_call["id"]
        )


agent = create_agent(
    model="gpt-4o-mini",
    tools=[
        search,
        get_weather
    ],
    middleware=[
        handle_tool_errors
    ]
)

错误发生时,Agent会向模型返回定制的 ToolMessage,帮助模型更好地恢复。

3. 指定提示词

通过 system_prompt 参数控制Agent的初始行为,支持静态字符串和动态生成两种方式。

3.1 静态系统提示

字符串形式或SystemMessage形式:直接传入提示文本。

python 复制代码
from langchain.messages import SystemMessage


agent = create_agent(
    model="anthropic:claude-sonnet-4-5",
    system_prompt=SystemMessage(
        content=[
            {
                "type": "text",
                "text": "你是一名负责分析文学作品的人工智能助手。"
            }
        ]
    )
)

若未提供 system_prompt,Agent会根据输入消息自行推断任务。

3.2 动态系统提示

通过 @dynamic_prompt 中间件,可以根据请求参数(如用户角色、对话阶段)动态生成系统提示。

python 复制代码
from typing import TypedDict

from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest


class Context(TypedDict):
    user_role: str

@dynamic_prompt # 动态系统提示
def user_role_prompt(request: ModelRequest) -> str:
    """根据用户角色生成系统提示。"""
    user_role = request.runtime.context.get("user_role","初学者")
    base_prompt = "你是一位乐于助人的助手。"
    if user_role == "专家":
        return f"{base_prompt} 提供详尽的技术解答。"
    elif user_role == "初学者":
        return (
            f"{base_prompt} "
            "用简单易懂的语言解释概念,避免使用专业术语。"
        )

    return base_prompt


agent = create_agent(
    model=model1,
    middleware=[user_role_prompt],
    context_schema=Context
)


# 系统提示将根据具体情境动态设定
result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "解释机器学习"
            }
        ]
    },
    context={
        "user_role": "初学者" #设置静态下上文
    }
)

print(
    result["messages"][-1].content
)

输出结果:

plain 复制代码
机器学习,可以理解成:

**让电脑"自己从经验里学规律"**,而不是每一步都由人提前写死。

### 举个简单例子
比如你想让电脑分辨"这是猫"还是"这不是猫"。

传统做法是:
- 人告诉电脑:猫有耳朵、胡须、尾巴......
- 然后按这些规则去判断

机器学习的做法是:
- 给电脑看很多猫的图片,也给它看很多不是猫的图片
- 电脑会慢慢找到一些"共同特点"
- 以后再来一张新图片,它就能猜这是不是猫

### 它是怎么"学"的?
可以把它想成一个学生:

1. **先看很多例子**
2. **从例子里找规律**
3. **用找到的规律去判断新东西**
4. **如果判断错了,就继续调整**

### 常见用途
机器学习现在很常见,比如:

- 垃圾邮件过滤
- 短视频推荐
- 语音识别
- 人脸识别
- 商品推荐
- 自动驾驶里的部分功能

### 它和普通程序有什么不同?
普通程序像是:
- 人写好明确规则
- 电脑照着执行

机器学习像是:
- 人给很多数据和答案
- 电脑自己总结规则

### 说得再简单一点
**机器学习 = 用大量数据训练电脑,让它学会做预测或判断。**

### 一个生活化比喻
像教小朋友认水果:

- 你不给他背一大堆死规则
- 而是让他看很多苹果、香蕉、橙子
- 看多了以后,他自己就能认出来

机器学习也是这样,只不过"看"的是数据。

如果你愿意,我还可以继续用**"做菜"**或者**"教小孩"**的比喻,解释一下:
- 深度学习是什么
- 训练数据是什么
- 模型是什么

4. 结构化输出

LangChain通过 create_agent 中的 response_format 参数提供了实现此功能的策略。核心步骤:

  1. 使用Pydantic定义期望的输出格式(BaseModel)。
  2. 在创建Agent时,将 response_format 参数设置为对应的策略,并传入定义好的格式模型。

4.1 说明

LangChain提供了两种主要策略,可以根据模型的支持情况和具体需求进行选择。

策略 原理 适用场景 特点
ToolStrategy (工具策略) 利用模型的工具调用(Tool Calling)能力,通过创建一个"虚拟工具"来迫使模型以调用该工具参数的形式输出结构化数据。 任何支持工具调用的模型。当模型不支持原生结构化输出或原生输出不可靠时使用。 通用性强,兼容性好。
ProviderStrategy (提供者策略) 直接使用模型提供商提供的原生结构化输出功能。 仅限支持原生结构化输出的模型(如GPT-4O、Claude 3等)。 更可靠、效率更高,是首选方案。

示例:从一段文本中提取联系人信息(姓名、邮箱、电话)。

定义输出格式

python 复制代码
from pydantic import BaseModel

class ContactInfo(BaseModel):
name: str
email: str
phone: str

使用 ToolStrategy

python 复制代码
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

# 创建Agent, 指定使用ToolStrategy
agent = create_agent(
model = "gpt-xx", # 任何支持工具调用的模型
tools=[],           # 此处为简化, 可以无工具或传入其他工具
response_format=ToolStrategy(ContactInfo) # 传入ToolStrategy
)

使用 ProviderStrategy

python 复制代码
from langchain.agents.structured_output import ProviderStrategy

# 创建Agent, 指定使用ProviderStrategy
agent = create_agent(
model="gpt-xx", # 必须使用支持原生结构化输出的模型
response_format=ProviderStrategy(ContactInfo) # 传入ProviderStrategy
)

4.2 默认行为

从 langchain 1.0 开始,提供了一个便捷的简化写法。可以直接将定义好的Pydantic模型传给 response_format

python 复制代码
# 简化写法: 直接传递模型
agent = create_agent(
model="gpt-.",
response_format=ContactInfo  # 直接传入模型, 而不是策略对象
)

默认行为:

  • 当直接传入模型(如 ContactInfo)时,LangChain会自动处理:
    a. 优先尝试使用 ProviderStrategy(如果模型支持原生结构化输出)。
    b. 若不支持,则自动回退使用 ToolStrategy
  • 这种"自动选择"的策略兼顾了便捷性和兼容性。

重要注意事项:预绑定工具(pre-bound)的模型不支持与结构化输出一起使用。如果需要动态模型选择与结构化输出结合,请确保传入中间件的模型没有预先调用 bind_tools

5. 定义State

LangChain Agent会自动维护完整的对话历史。历史信息以消息列表(messages)的形式存储在Agent的状态中,开发者无需额外配置即可实现基本的对话上下文跟踪。

在某些场景下,仅靠对话历史不够,Agent需要记住额外的信息(例如:用户偏好、临时标志、中间结果)。这可以通过自定义状态实现。

核心概念:

  • 自定义状态是Agent的短期记忆,仅在当前会话生命周期内有效。
  • 自定义状态必须继承自 AgentState 并定义为 TypedDict(在LangChain 1.0及之后版本)。

两种实现方式:

方式 适用场景 特点
通过Middleware定义 自定义状态需要在中间件钩子或特定工具中被访问时使用。 推荐方式。作用域清晰,状态与相关中间件、工具绑定。
通过 state_schema 参数定义 自定义状态仅需在工具中使用,无需复杂中间件逻辑时的简化用法。 快速实现,但作用域较广。仅用于向后兼容,不推荐新项目使用。

5.1 通过 Middleware 定义

官方推荐优先使用通过Middleware定义的方式,因为它能将状态的扩展与特定中间件、工具的作用域绑定,结构更清晰。

步骤:

  1. 定义一个继承自 AgentStateTypedDict
  2. 创建一个继承自 AgentMiddleware 的类,设置 state_schema 为该 TypedDict
    类型要求:从LangChain 1.0开始,自定义状态必须是 TypedDict 类型,不再支持Pydantic模型或dataclass。
  3. create_agent 时通过 middleware 参数传入该中间件。

代码示例:

python 复制代码
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import AgentMiddleware
from typing import Any


# 定义自定义状态
class CustomState(AgentState):
    user_preferences: dict


# 创建中间件,关联状态
class CustomMiddleware(AgentMiddleware):
    state_schema = CustomState

    tools = []  # 可选:为该中间件绑定工具

    def before_model(
        self,
        state: CustomState,
        runtime
    ) -> dict[str, Any] | None:
        # 在模型调用前可以访问和修改 state
        print(
            f"User preferences: {state.get('user_preferences')}"
        )

        return None


# 创建 Agent 并传入中间件
agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[],
    middleware=[
        CustomMiddleware()
    ]  # 关键:通过 middleware 注入自定义状态
)


# 调用时传入额外状态
result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "什么是大模型?"
            }
        ],
        "user_preferences": {
            "style": "技术性",
            "verbosity": "详细的"
        },
    }
)

5.2 通过 state_schema 参数定义

步骤:

  1. 定义一个继承自 AgentStateTypedDict
  2. create_agent 时直接通过 state_schema 参数传入该类型。

代码示例:

python 复制代码
from langchain.agents import AgentState, create_agent

# 定义自定义状态
class CustomState(AgentState):
    user_preferences: dict


# 创建 Agent 时传入 state_schema
agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[],  # 工具可以访问此状态
    state_schema=CustomState
)


# 调用时传入额外状态
result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "什么是大模型?"
            }
        ],
        "user_preferences": {
            "style": "技术性",
            "verbosity": "详细的"
        }
    }
)

通过灵活运用自定义状态,可以为Agent赋予更丰富的短期记忆能力,使其在多轮交互中表现更智能、更贴合用户需求。

6. 人机协作(Human-in-the-loop)

Human-in-the-loop(HITL)为AI Agent添加人工审核与干预能力。HITL中间件允许在Agent执行敏感工具调用前,暂停流程并等待人工审批。HITL在自动化与风险控制之间建立平衡,适用于写文件、执行SQL等需要人工确认的操作。

其实现依赖LangGraph的持久化层(checkpointer),实现执行状态的保存与恢复。

6.1 三种中断决策类型

当Agent触发中断时,人工可做出以下三种响应(由策略配置决定哪些可用):

决策类型 说明 示例用例
approve 完全批准,工具按原参数执行 发送邮件草稿
edit 允许修改工具参数后再执行 修改邮件收件人后发送
reject 拒绝执行,将反馈添加到对话中 拒绝SQL删除操作并说明原因

注意:当多个工具调用同时中断时,需按顺序逐一决策;编辑参数时请保守修改,避免影响模型后续判断。

6.2 配置与响应中断

6.2.1 添加中间件与检查点

使用 HumanInTheLoopMiddleware 中间件完成人机协作,其配置选项主要包含以下两部分:

参数一:interrupt_on(必选)

一个字典,用于定义哪些工具需要触发人工中断以及允许哪些决策类型。键为工具名称(字符串),值可以是以下三种形式:

值类型 含义 示例
True 中断该工具,允许全部三种决策(approveeditreject "工具1": True
False 不中断,自动批准(工具调用直接执行) "工具2": False
InterruptOnConfig 对象 精细控制:可指定允许的决策列表和自定义描述 自己配

InterruptOnConfig 对象的属性:

  • allowed_decisions:列表,可选值 "approve""edit""reject"。决定人工可用的操作类型。
  • description:字符串或可调用函数,用于覆盖该工具的中断提示消息。若未指定,则使用全局 description_prefix 拼接而成。

示例:

python 复制代码
interrupt_on={
	"write_file": True,                    # 全决策可用
	"execute_sql": {"allowed_decisions": ["approve", "reject"]},  # 禁止编辑
	"read_data": False,                    # 自动通过
}

参数二:description_prefix(可选)

字符串。作为中断消息的全局前缀,默认会在每个中断请求的描述前加上此文本。

最终消息格式:description_prefix + "\n\nTool: <tool_name>\nArgs: <arguments>"

工具级覆盖:若在 InterruptOnConfig 中指定了 description,则忽略全局前缀。

示例:

python 复制代码
description_prefix="工具执行尚待批准"

中断时用户看到的消息开头为:

python 复制代码
工具执行尚待批准
Tool: execute_sql
Args: {...}

通过合理配置 interrupt_ondescription_prefix,开发者可以灵活控制哪些操作需要人工介入,以及如何向审核者展示信息。

示例如下:

python 复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver


@tool
def write_file(
    file_path: str,
    content: str
) -> str:
    """
    写入文件内容。
    该操作会修改外部文件,需要人工审核。
    """

    print(f"正在写入文件: {file_path}")

    # 模拟写文件
    return f"文件 {file_path} 写入成功"


@tool
def execute_sql(
    sql: str
) -> str:
    """
    执行SQL语句。

    支持:
    - SELECT 查询
    - INSERT 插入
    - UPDATE 修改
    - DELETE 删除
    """

    print(f"执行SQL: {sql}")

    # 模拟执行SQL
    return f"SQL执行成功: {sql}"


@tool
def read_data(
    table_name: str
) -> str:
    """
    查询数据库中的数据。
    只读操作,不会修改数据。
    """

    print(f"读取数据表: {table_name}")

    # 模拟查询
    return f"{table_name} 查询结果: 数据列表..."

agent = create_agent(
    model=model1,
    tools=[
        write_file,
        execute_sql,
        read_data
    ],
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={
                "write_file": True,
                # 允许 approve / edit / reject 三种决策
                "execute_sql": {
                    "allowed_decisions": [
                        "approve",
                        "reject"
                    ]
                },
                # 禁止 edit,只允许批准或拒绝
                "read_data": False, # 自动批准,不触发中断
            },
            description_prefix="工具执行尚待批准",# 中断提示前缀
        ),
    ],
    checkpointer=InMemorySaver(),
)

关键条件:

  • 必须配置 checkpointer 以支持中断恢复。
  • 调用时需传入 thread_id 以标识会话线程。
  • 策略设计:对只读工具(如 read_data)可设 False 自动放行;对写操作建议至少配置 approvereject

代码中 HumanInTheLoopMiddleware 配置表明:

  • write_file 会中断,并允许批准、编辑、拒绝;
  • execute_sql 会中断,但仅允许批准或拒绝(不可编辑);
  • read_data 不中断,自动执行;
  • 中断提示将以"工具执行尚待批准"开头。
6.2.2 响应中断
  • 触发中断后获取待审核动作:
python 复制代码
config = {
    "configurable": {
        "thread_id": "1"
    }
}


result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "删除数据库中的data表中id=的旧记录。"
            }
        ]
    },
    config=config,
    version="v2",  # 必须使用 v2 版本获取中断信息
)


# result.interrupts 包含待审核的工具调用详情
print(result.interrupts)

输出包含 action_requests(工具名和参数)与 review_configs(允许的决策类型),如下所示:

python 复制代码
# (Interrupt(value={'action_requests': [{
# 'name': 'execute_sql',
# 'args': {'sql': 'DELETE FROM data WHERE id = 123;'},
# 'description': "工具执行尚待批准\n\nTool: execute_sql\nArgs: {'sql': 'DELETE FROM data WHERE id = 123;'}"}], 'review_configs': [{'action_name': 'execute_sql', 'allowed_decisions': ['approve', 'reject']}]}, id='78c7c01b95f755b13888d95b025d8060'),)
  • 提交决策继续执行:
plain 复制代码
print(
    agent.invoke(
        Command(
            resume={
                "decisions": [
                    {
                        "type": "approve"
                    }
                ]
            }
        ),
        config=config,
        version="v2",
    )
)

输出如下:

plain 复制代码
# GraphOutput(value={'messages':
# [HumanMessage(content='请调用 execute_sql 工具,执行DELETE语句,删除数据库中的data表中id=123的旧记录。', additional_kwargs={}, response_metadata={}, id='af891d1e-ca8c-49fd-a93e-bcba2e6e32f0'),
# AIMessage(content='我先直接执行删除语句,删掉 `data` 表里 `id=123` 的旧记录。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 57, 'prompt_tokens': 4532, 'total_tokens': 4589, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-8c7d246c-32aa-424b-8147-94118', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019fa803-122c-7610-b40d-4f1e8da0eb62-0', tool_calls=[{'name': 'execute_sql', 'args': {'sql': 'DELETE FROM data WHERE id = 123;'}, 'id': 'call_kDVD1c0GmTDbUMY0nR4oSNEf', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 4532, 'output_tokens': 57, 'total_tokens': 4589, 'input_token_details': {}, 'output_token_details': {}}),
# ToolMessage(content='SQL执行成功: DELETE FROM data WHERE id = 123;', name='execute_sql', id='8b1d30d5-2a43-41f6-9ccd-f26317010bf5', tool_call_id='call_kDVD1c0GmTDbUMY0nR4oSNEf'),
# AIMessage(content='已执行删除:`DELETE FROM data WHERE id = 123;`\n\n- 结果:删除语句执行成功\n- 目标表:`data`\n- 条件:`id = 123`\n\n如果你需要,我也可以继续帮你查询确认该记录是否已不存在。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 64, 'prompt_tokens': 4613, 'total_tokens': 4677, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-10c50646-18e0-43d0-9476-9c683', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019fa803-208a-7cd0-9743-e69c1b907aaa-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 4613, 'output_tokens': 64, 'total_tokens': 4677, 'input_token_details': {}, 'output_token_details': {}})]}, interrupts=())

7. 支持的流模式

LangChain Agent流式系统能做什么?

  • 流式传输代理进度:在每个代理步骤后获取状态更新。
  • 流式传输LLM token:实时生成语言模型产生的token。
  • 流式传输推理token:输出模型的内部思考过程。
  • 流式传输自定义更新:在代码中定义并发出用户自定义的信号(如"已获取10/100条记录")。
  • 支持多种流模式 :可根据需要选择 updates(代理进度)、messages(LLM消息块)、custom(自定义数据)。

7.1 模式一:updates - 流式传输代理进度

在每个代理步骤(如一次LLM调用、一次工具执行)完成后,流式传输完整的状态更新。

python 复制代码
from langchain.agents import create_agent

def get_weather(city: str) -> str:
    """获取城市天气"""
    return f"{city} 天气晴朗!"

agent = create_agent(
    model="gpt-5-nano",
    tools=[get_weather],
)

# 使用 stream_mode="updates"
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="updates",
    version="v2",
):
    if chunk["type"] == "updates":
        for step, data in chunk["data"].items():
            print(f"步骤: {step}")
            print(f"内容: {data['messages'][-1].content_blocks}")

输出:

复制代码
步骤: model
内容: [{'type': 'tool_call', 'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_Ayr5Q9GL7EwHHAi0vBdzZg9'}]
步骤: tools
内容: [{'type': 'text', 'text': '上海 天气晴朗!'}]
步骤: model
内容: [{'type': 'text', 'text': '上海的天气晴朗!'}]

注意:将 version="v2"(需要LangGraph >= 1.1)传递给 stream() 或 astream() 以获取统一的输出格式。每个数据块都是一个具有 type、ns 和 data 键的 StreamPart 字典

7.2 模式二:messages - 流式传输LLM Token

从任何调用了LLM的节点中,流式传输token级别的增量消息块,实现类似"逐字输出"效果。每块是一个元组 (token, metadata)

同上,使用 stream_mode="messages"

python 复制代码
# ... (代理定义与上文相同)
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="messages",
    version="v2",
):
    if chunk["type"] == "messages":
        token, metadata = chunk["data"]
        print(f"节点: {metadata['langgraph_node']}")
        print(f"内容块: {token.content_blocks}\n")

输出解读(部分):

  • model 节点逐块输出工具调用的JSON参数(如 '{', 'city', '":"', '上海', ...`)。
  • 然后 tools 节点输出工具执行结果。
  • 最后 model 节点逐块输出最终回复文本(如 '上海', '的', '天气' ...)。

7.3 模式三:custom - 流式传输自定义更新

与LangGraph用法一致,通过 get_stream_writer() 函数获取写入器,可以主动向流中推送任意自定义数据。

适用场景:报告工具执行进度(如"正在查询数据库...")、中间计算结果、调试信息等。

python 复制代码
from langchain.agents import create_agent
from langgraph.config import get_stream_writer

def get_weather(city: str) -> str:
    """获取天气,并发送自定义更新"""
    writer = get_stream_writer()
    writer(f"正在查询 {city} 的天气数据...")
    writer(f"成功获取 {city} 的天气数据。") 
    return f"{city} 天气晴朗!"

agent = create_agent(
    model="gpt-4o-mini",
    tools=[get_weather],
)

for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="custom",
    version="v2",
):
    if chunk["type"] == "custom":
        print(chunk["data"])  # 直接输出自定义消息

输出:

复制代码
正在查询 上海 的天气数据...
成功获取 上海 的天气数据。

7.4 模式四:组合模式 - 同时使用多种流模式

将模式作为列表传入,如 stream_mode=["updates", "custom"]。每次迭代返回一个包含 type(标识模式)和 data(对应负载)的字典。

示例:同时使用 updatescustom 模式

python 复制代码
# ... (代理定义与 custom 模式示例相同)
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode=["updates", "custom"],
    version="v2",
):
    print(f"流模式: {chunk['type']}")
    print(f"内容: {chunk['data']}\n")
plain 复制代码
model {'messages': [AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 76, 'total_tokens': 96, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-aebb1d63-403a-4379-9e3b-8f910', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019fa870-b63e-7032-96f9-64a7310c0aab-0', tool_calls=[{'name': 'get_weather_for_location', 'args': {'city': '北京'}, 'id': 'call_3PewB69q4pfgl2cwgQcW8add', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 76, 'output_tokens': 20, 'total_tokens': 96, 'input_token_details': {}, 'output_token_details': {}})]}
正在查询 北京 的天气数据...
成功获取 北京 的天气数据。
tools {'messages': [ToolMessage(content='北京的天气是晴天,温度25摄氏度。', name='get_weather_for_location', id='fe0f1dc5-796f-4a27-8bbb-c02184954a11', tool_call_id='call_3PewB69q4pfgl2cwgQcW8add')]}
model {'messages': [AIMessage(content='北京现在是晴天,气温 25°C。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 123, 'total_tokens': 139, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'chatcmpl-2657453a-058b-47cc-8b35-4386c', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019fa870-c512-7c50-a2b1-264e470513c5-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 123, 'output_tokens': 16, 'total_tokens': 139, 'input_token_details': {}, 'output_token_details': {}})]}

会交替输出 type: updates(包含代理状态更新)和 type: custom(包含自定义消息)。

7.5 模式实践

7.5.1 流式传输推理 / 思考 Token

某些模型(如Claude、OpenAI系列)在生成最终答案前会进行内部推理。将这些推理内容实时输出,可以增强应用的透明度与可解释性。

实现原理:LangChain 将不同提供商的推理内容(Anthropic的thinking块、OpenAI的reasoning摘要等)统一规范为 content_blockstype: "reasoning" 的块。

代码示例:

python 复制代码
from langchain.agents import create_agent
from langchain.messages import AIMessageChunk
from langchain_openai import ChatOpenAI

# 1. 配置模型,启用推理输出
model = ChatOpenAI(
    model="gpt-5.4",
    temperature=0,
    api_key=os.getenv("PACKYAPI_API_KEY"),
    base_url="https://www.packyapi.com/v1",
    reasoning={            # 关键配置:
        "effort": "medium",  # 推理程度: 'low', 'medium', or 'high'
        "summary": "detailed",   # 推理摘要: 'detailed', 'auto', or None
    }
)

@tool
def get_weather(city: str) -> str:
    """获取天气"""
    return f"{city} 天气晴朗!"

agent = create_agent(model=model, tools=[get_weather])

# 2. 使用 stream_mode="messages" 并过滤 reasoning 块
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海的天气如何?"}]},
    stream_mode="messages",
    version="v2"
):
    if chunk["type"] == "messages":
        token,metadata=chunk["data"]
        # 推理内容
        reasoning = [b for b in token.content_blocks if b["type"] == "reasoning"]
        # 响应内容
        text = [b for b in token.content_blocks if b["type"] == "text"]
        if reasoning and 'reasoning' in reasoning[0]:
            print(f"{reasoning[0]['reasoning']}", end="")
        if text:
            print(text[0]["text"], end="")

关键点

  • 必须确保模型支持并开启了推理输出(如 reasoning 参数)。注意:模型不同,参数不同(Anthropic 系列参数为 thinking)
  • 无论使用哪个提供商,都可通过 content_blocks 中的 type: "reasoning" 统一访问推理内容。
  • 通常配合 stream_mode="messages" 使用,逐块输出推理文本。
7.5.2 流式传输工具调用

在代理调用工具时,你可能希望:

  • 实时显示工具调用的参数JSON片段(增量)。
  • 最终获取完整解析后的工具调用消息(用于后续逻辑)。

实现原理:

  • stream_mode="messages":提供 AIMessageChunk,其中包含 tool_call_chunks(增量参数)。
  • stream_mode="updates":在步骤(如 model 节点)完成后,提供完整的 AIMessage,其中包含已解析的 tool_calls

代码示例(组合模式):

python 复制代码
from langchain.agents import create_agent
from langchain.messages import AIMessage, AIMessageChunk, ToolMessage
from langchain_core.messages import AnyMessage

def get_weather(city: str) -> str:
    """获取天气"""
    return f"{city} 天气晴朗!"

agent = create_agent(model=model, tools=[get_weather])

def render_chunk(token: AIMessageChunk):
    if token.text:  # 普通的文本
        print(token.text, end="|")
    if token.tool_call_chunks:  # 工具调用碎片
        print(token.tool_call_chunks)  # 增量块

def render_completed(msg: AnyMessage):
    if isinstance(msg, AIMessage) and msg.tool_calls: # ai消息中有工具调用
        print(f"完整工具调用: {msg.tool_calls}")
    if isinstance(msg, ToolMessage): # 工具调用结果
        print(f"工具响应: {msg.content_blocks}")

input_msg = [{"role": "user", "content": "上海天气如何?"}]
for chunk in agent.stream(
    {"messages": input_msg},
    stream_mode=["messages", "updates"],
    version="v2",
):
    if chunk["type"] == "messages":
        token, meta = chunk["data"]
        if isinstance(token, AIMessageChunk): # 如果是AIMessageChunk类型,则渲染增量块
            render_chunk(token)
    elif chunk["type"] == "updates":
        for source, update in chunk["data"].items():
            if source in ("model", "tools"):  # 关注模型和工具节点
                render_completed(update["messages"][-1])
plain 复制代码
[{'name': 'get_weather', 'args': '', 'id': 'call_o7zUpe9xzl8ieKTB3biAF9HT', 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '{"', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': 'city', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '":"', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '上海', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '"}', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
完整工具调用: [{'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_o7zUpe9xzl8ieKTB3biAF9HT', 'type': 'tool_call'}]
工具响应: [{'type': 'text', 'text': '上海 天气晴朗!'}]
上海|天气|晴|朗|。|

关键点

  • tool_call_chunks 是增量数据,可用于实时显示(如"正在输入参数...")。
  • 完整的 tool_calls 需从 updates 模式中获取,通常在 model 节点完成后出现。
  • 如果消息未保存在状态中,可通过累积 AIMessageChunk(使用 + 操作符)来重构完整消息。
7.5.3 流式传输与人机协作 (Human-in-the-Loop)

代理执行到某些关键操作(如调用工具)时,需要暂停并等待人工审批或编辑,然后继续执行。

实现原理:

  • 使用 HumanInTheLoopMiddleware 中间件,指定需要中断的工具。
  • 流式传输时,捕获 updates 模式中的 __interrupt__ 节点。
  • 构造 Command(resume=...) 来恢复执行。

代码示例:

python 复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain_core.messages import AIMessageChunk, AnyMessage, AIMessage, ToolMessage
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command, Interrupt

def get_weather(city: str) -> str:
    """获取天气"""
    return f"{city} 天气晴朗!"

agent = create_agent(
    "gpt-5-mini",
    tools=[get_weather],
    # 允许全部三种决策 (approve、edit、reject)
    middleware=[HumanInTheLoopMiddleware(interrupt_on={"get_weather": True})],
    checkpointer=InMemorySaver(),
)

def render_interrupt(interrupt: Interrupt) -> None:
    interrupts = interrupt.value
    for request in interrupts["action_requests"]:
        print(request["description"])

def render_chunk(token: AIMessageChunk):
    if token.text:
        print(token.text, end="|")
    if token.tool_call_chunks:
        print(token.tool_call_chunks)  # 增量块

def render_completed(msg: AnyMessage):
    if isinstance(msg, AIMessage) and msg.tool_calls:
        print(f"完整工具调用: {msg.tool_calls}")
    if isinstance(msg, ToolMessage):
        print(f"工具响应: {msg.content_blocks}")

config = {"configurable": {"thread_id": "1"}}
interrupts = []

# 第一次流式: 遇到中断
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "查询北京和上海的天气"}]},
    config=config,
    stream_mode=["updates"],
    version="v2",
):
    if chunk["type"] == "updates":
        for source, update in chunk["data"].items():
            if source == "__interrupt__":
                interrupts.extend(update)
                # 解析中断, 展示给用户, 收集决策...
                render_interrupt(update[0])

输出:

复制代码
Tool execution requires approval    # 获取北京天气被拦列
Tool: get_weather
Args: {'city': '北京'}
Tool execution requires approval    # 获取上海天气被拦列
Tool: get_weather
Args: {'city': '上海'}

批准第一个调用,编辑第二个调用(城市改为西安):

python 复制代码
decisions = {
    interrupts[0].id: {
        "decisions": [
            {"type": "approve"},   # 第一个工具调用批准
            {                       # 第二个工具调用编辑
                "type": "edit",
                "edited_action": {
                    "name": "get_weather",
                    "args": {"city": "西安"},
                },
            }
        ]
    }
}

# 第二次流式: 恢复执行
for chunk in agent.stream(
    Command(resume=decisions),
    config=config,
    stream_mode=["messages", "updates"],
    version="v2",
):
    if chunk["type"] == "messages":
        token, metadata = chunk["data"]
        if isinstance(token, AIMessageChunk):
            render_chunk(token)
    elif chunk["type"] == "updates":
        for source, update in chunk["data"].items():
            if source in ("model", "tools"):
                render_completed(update["messages"][-1])
            if source == "__interrupt__":
                interrupts.extend(update)
                render_interrupt(update[0])

输出:

复制代码
工具响应: [{'type': 'text', 'text': '西安 天气晴朗!'}]
工具响应: [{'type': 'text', 'text': '北京 天气晴朗!'}]
[{'name': 'get_weather', 'args': '', 'id': 'call_EV8v7AALzzyjpj4cYWqJHPrO', 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '{', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': 'city', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '":"', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
[{'name': None, 'args': '上海', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]

[{'name': None, 'args': '"}', 'id': None, 'index': 0, 'type': 'tool_call_chunk'}]
完整工具调用: [{'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_EV8v7AALzzyjpj4cYWqJHPrO', 'type': 'tool_call'}]
Tool execution requires approval    # 这里由于Agent遵循ReAct模式, 发现问题是咨询上海天气, 由于没有拿到答案, 则继续循环。
                                    # 实际上与模型也有关系, 可以换成gpt-4o-mini看结果
Tool: get_weather
Args: {'city': '上海'}

Human-in-the-loop 中间件通过可配置的策略、灵活的决策类型和状态持久化,使 Agent 能够在关键操作上获得人工监督,既保持了自动化效率,又增加了安全性和可控性。结合流式处理,开发者可以构建出既流畅又可靠的交互体验。

相关推荐
m沐沐1 小时前
【自然语言处理】NLP分词与Word2Vec词向量——从原理到实战
人工智能·深度学习·opencv·机器学习·计算机视觉·自然语言处理·word2vec
临床数据科学和人工智能兴趣组2 小时前
R语言版本检查与扩展包自动更新
机器学习·数据分析·r语言·r语言-4.2.1
是上好佳佳佳呀2 小时前
【机器学习|DAY06】集成学习(Ensemble Learning)笔记
笔记·机器学习·集成学习
喜欢的名字被抢了2 小时前
langgraph教程系列-02-让流程分叉-条件边与路由
教程·langgraph
学习中.........2 小时前
并行 BPE 训练 与 手写 `Tokenizer`
人工智能·算法·机器学习·语言模型
AINative软件工程3 小时前
LLM 应用的 Graceful Degradation 工程实践:5 层降级策略让你的 AI 功能永不完全崩溃
架构·llm·ai编程
To_OC3 小时前
对接大模型流式接口,我被一个 ReadableStream 卡了半小时
前端·node.js·llm
_itgo11 小时前
LangGraph 主要3 种核心模式
ai·langchain·langgraph
糖果店的幽灵12 小时前
人已经用 WorkBuddy 找工作拿了面试,你还在一份份手工改简历
人工智能·面试·职场和发展·langgraph