首页 / 文章 / LangGraph中的ReAct智能体:逐步进行思考、行动与观察。

LangGraph中的ReAct智能体:逐步进行思考、行动与观察。

将ReAct循环实现为带有类型化状态、工具调用以及可测试的停止条件的显式图节点。

4143 词

本指南旨在为《ReAct Agents Explained:使用LangGraph的逐步实现》重建一条可操作的路径。重点介绍合约、检查机制以及可直接放入代码库而无需猜测其用途的代码。在修改代码之前,应先明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。

简介

在开始之前,需先明确输入参数、该步骤的负责人以及终止标准,然后再修改代码。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,杜绝默默完成部分任务的情况。 将规划与工具执行分开。规划者负责提出方案;执行者负责进行修改;验证者则根据目标检查结果。

什么是ReAct智能体?

在回答“什么是ReAct智能体?”这一问题时,应在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间与成本。提前了解这些信息可以避免在从演示环境过渡到共享环境时出现意外费用。应将规划与工具执行分开:规划器负责提出方案,执行者负责实施变更,验证器则根据目标检查最终结果。

为何ReAct优于纯链式思维方法

关于为何 ReAct 比纯链式思维更好,在修改代码之前需明确输入、各步骤的负责人以及终止标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作员无需查看整个流程即可进行审计。 将规划与工具执行分开。规划器负责提出方案,执行器负责实施变更,验证器则根据目标检查结果。 关于为何 ReAct 比纯链式思维更好,在修改代码之前需明确输入、各步骤的负责人以及终止标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 优先选择小型、可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应能指向单一的责任主体。

而不是一个错综复杂的流程。

Thought: I don’t know the answer yet. I should search.
Action: Search("Paris weather this week")
Observation: It will rain on Thursday.
Thought: I should suggest indoor activities.
Final Answer: ...

ReAct提示法

在采用ReAct提示法时,应在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 应将这一阶段视为输入与经过验证的输出之间的契约。为生成物命名,定义成功判定标准,并拒绝默许的半完成状态。 严格限制工具的结构规范。过宽的自由文本参数容易引发注入攻击,还会增加审计成本。

ReAct提示法的目的

在采用 ReAct 提示法时,应在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本,提前了解情况可避免在从演示环境过渡到共享环境时出现意外费用。需严格限制工具的架构,过宽的自由文本参数容易引发注入攻击,并增加审计成本。

ReAct 提示法的核心要素

对于ReAct提示机制的关键要素,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作员无需查看整个流程即可进行审计。 严格限制工具的架构。过大的自由文本参数容易引发注入攻击,且会增加审计成本。 对于ReAct提示机制的关键要素,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,应优先使用小型且可测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个特定责任模块,而非整个复杂的流程。

1. 思考链推理

对于1.思考链推理,在修改代码之前需明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新执行相应步骤,而无需猜测隐藏状态。 将此阶段视为输入与验证后输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 在耗资源较高的模型调用之后设置检查点,以避免重复执行相同任务时再次产生费用。

2. 明确的动作空间

2. 明确的操作空间:在修改代码之前,需先定义输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息,可避免在从演示环境过渡到共享环境时出现意外费用。在耗时的模型调用之后设置检查点,这样重新尝试时就不会重复计算相同的工作量。

3. 观测数据整合

关于3.观测集成,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作人员无需查看整个流程即可进行审计。 在执行耗时的模型调用后设置检查点,这样重试时就不会重复处理相同的工作。 关于3.观测集成,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向某个特定功能,而非整个复杂的流程。

4.迭代循环

在4. 迭代循环中,应在修改代码之前明确输入参数、各步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与验证后输出之间的契约。为相关成果命名,定义成功标准,杜绝无声的半完成状态。 将规划与工具执行分开。规划者负责提出方案;执行者负责进行修改;验证者则根据目标检查最终结果。

5. 最终答案生成

第五点:最终答案生成。在修改代码之前,需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间与成本。提前明确这些信息,可避免在从演示环境过渡到共享环境时出现意外费用。应将规划与工具执行分开:规划者负责提出方案,执行者负责进行操作,验证者则根据目标检查结果。

标准的ReAct提示结构

在采用标准的ReAct提示结构时,应在修改代码之前明确输入参数、各步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作员无需查看整个系统结构即可进行审计。 将规划与工具执行分开。规划器负责提出方案,执行器负责实施操作,验证器则根据目标检查结果。 在采用标准的ReAct提示结构时,应在修改代码之前明确输入参数、各步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向某个具体的责任模块,而非复杂的流程链。

。

Question: <user question>Thought: <reason about what to do next>

Action: <selected tool>
Action Input: <tool input>
Observation: <tool output>
... (repeat as needed)
Thought: I now know the final answer
Final Answer: <answer to the user>

零样本 ReAct 提示法

在采用零样本 ReAct 提示法时,需在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为生成结果命名,定义成功判定标准,并拒绝默许的半完成状态。 严格限制工具架构。过宽的自由文本参数容易引发注入攻击,也会增加审计成本。

ReAct 提示法与 ReAct 智能体

在比较 ReAct Prompting 与 ReAct Agents 时,应在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息可以避免在从演示环境过渡到共享环境时出现意外费用。需严格限制工具的架构,过宽的自由文本参数容易引发注入攻击,并增加审计成本。

为何选择 LangGraph 构建 ReAct Agents?

对于为何选择 LangGraph 来构建 ReAct Agent,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放,以便操作员无需查看整个流程即可进行审计。 严格限制工具的架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。 对于为何选择 LangGraph 来构建 ReAct Agent,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相比庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个特定功能,而非整个复杂的流程。

核心问题:ReAct是状态机,而非提示词

在探讨“核心问题:ReAct是状态机,而非提示词”时,应在修改代码之前明确输入内容、各步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 应将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 在耗资源较高的模型调用之后设置检查点,以避免重复处理相同任务。

没有LangGraph会出什么问题

对于没有 LangGraph 就会出问题的场景,在修改代码之前需先明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前了解这些信息可以避免在从演示环境切换到共享环境时出现意外费用。在耗时的模型调用之后设置检查点,这样重新尝试时就不会重复计算相同的工作量。

1. 隐式控制流

对于1.隐式控制流,应在修改代码之前明确输入参数、该步骤的负责人以及退出条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作员无需查看整个流程即可进行审计。 在耗时的模型调用之后设置检查点,这样重试时就不会重复处理相同的工作。 对于1.隐式控制流,应在修改代码之前明确输入参数、该步骤的负责人以及退出条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相比庞大的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向某个特定职责,而非整个复杂的流程。

while True:
    llm_output = llm(prompt)
    if "Action:" in llm_output:
        tool_result = call_tool(...)
    else:
        break

2.脆弱的状态管理

第二点:脆弱的状态管理。在修改代码之前,需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。应将此阶段视为输入与已验证输出之间的契约:为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。规划与工具执行应分开处理——规划者负责提出方案,执行者负责进行修改,验证者则根据目标检查结果。

3. 不存在一等循环语义

第三点:没有一流循环语义,在修改代码之前需明确输入参数、步骤执行者以及退出条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息可避免在从演示环境过渡到共享环境时出现意外费用。应将规划与工具执行分开:规划者负责提出方案,执行者负责进行修改,验证者则根据目标检查最终结果。

4. 生产环境适配性差

针对第4点“生产环境就绪性差”的问题,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,以便操作人员无需查看整个流程即可进行审计。 规划与工具执行应分开处理。规划器负责提出方案,执行者负责实际操作,验证器则根据目标检查结果。

LangGraph中的核心概念(以智能体为中心的视角)

在研究LangGraph中的核心概念(以智能体为中心的视角)时,应在修改代码之前明确输入内容、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 应将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,杜绝无声的半完成状态。 需严格限制工具的结构规范。过宽的自由文本参数容易引发注入攻击,也会增加审计成本。

1. 状态:智能体的记忆

对于第1点“状态:智能体的记忆”,在修改代码之前需明确输入内容、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息可避免在从演示环境过渡到共享环境时出现意外费用。需严格限制工具的架构,过宽的自由文本参数容易引发注入攻击,并增加审计成本。

2. 节点:认知与操作单元

对于2.节点:认知单元与操作单元,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作人员无需查看整个流程即可进行审计。 严格限制工具架构的灵活性。过大的自由文本参数容易引发注入攻击,且会增加审计成本。 对于2.节点:认知单元与操作单元,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,应优先选择小型且可测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个特定责任模块,而非整个复杂的流程。

3. 边界条件:显式控制流

在采用“3. 边界条件:显式控制流”时,应在修改代码之前明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 在耗时的模型调用之后设置检查点,以避免重复处理相同任务时再次产生费用。

4. 具有灵活性的确定性执行

第四点:兼具确定性与灵活性的执行方式。在修改代码之前,需明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间与成本。提前了解这些信息,可避免在从演示环境切换到共享环境时出现意外费用。在耗时的模型调用之后设置检查点,这样重新尝试时就不会重复计算相同的工作量。

ReAct + LangGraph:天然的组合

对于 ReAct + LangGraph 这一天然契合的组合,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作员无需查看整个流程即可进行审核。 在执行成本较高的模型调用后设置检查点,这样重试时就不会重复处理相同的工作。 对于 ReAct + LangGraph 这一天然契合的组合,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向某个特定职责,而非整个复杂的流程。

应用场景:酒店取消辅助系统(政策规则与退款计算)

对于“酒店取消辅助系统(政策规则与退款计算)”这一应用场景,在修改代码之前需明确输入参数、各步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与验证后输出之间的契约。为相关成果命名,定义成功标准,杜绝无声的半完成状态。 将规划与工具执行分开处理:规划者负责提出方案,执行者负责进行操作,验证者则根据目标检查结果。

问题描述

在修改代码之前,针对问题描述需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。规划与工具执行应分开处理:规划器负责提出方案,执行器负责进行操作,验证器则根据目标检查结果。

步骤1:安装依赖项

规划与工具执行应分开处理:规划器负责提出方案,执行器负责进行操作,验证器则根据目标检查结果。

pip install -U langgraph langchain langchain-openai
export OPENAI_API_KEY="..."

步骤2:定义工具(即“动作”)

需严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

from typing import TypedDict, Annotated
from datetime import datetime
import json

from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
    BaseMessage,
    HumanMessage,
    ToolMessage,
    SystemMessage
)
from langchain_core.tools import tool
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import tools_condition

@tool
def get_cancellation_policy(rate_plan: str) -> str:
    """
    Returns cancellation policy text for a given rate plan.
    """
    policies = {
        "flexible": "Free cancellation until 24 hours before check-in. After that, first night is charged.",
        "semi-flex": "Free cancellation until 72 hours before check-in. After that, 50% of the stay is charged.",
        "non-refundable": "No refund after booking. Full stay amount is charged on cancellation."
    }
    key = rate_plan.strip().lower()
    return policies.get(key, "Policy not found. Supported: flexible, semi-flex, non-refundable.")
@tool
def calculate_refund(
    rate_plan: str,
    check_in: str,
    cancel_date: str,
    nightly_rate: float,
    nights: int
) -> str:
    """
    Calculates refund amount based on a simplified policy model.
    Dates format: YYYY-MM-DD
    """
    rp = rate_plan.strip().lower()
    ci = datetime.strptime(check_in, "%Y-%m-%d").date()
    cd = datetime.strptime(cancel_date, "%Y-%m-%d").date()
    total = nightly_rate * nights
    days_before = (ci - cd).days
    if rp == "non-refundable":
        refund = 0.0
        charged = total
        rule = "Non-refundable: no refund."
    elif rp == "flexible":
        if days_before >= 1:
            refund = total
            charged = 0.0
            rule = "Flexible: cancelled >= 24h before check-in, full refund."
        else:
            charged = nightly_rate  # 1 night penalty
            refund = max(total - charged, 0.0)
            rule = "Flexible: late cancel, 1 night charged."
    elif rp == "semi-flex":
        if days_before >= 3:
            refund = total
            charged = 0.0
            rule = "Semi-flex: cancelled >= 72h before check-in, full refund."
        else:
            charged = 0.5 * total
            refund = total - charged
            rule = "Semi-flex: late cancel, 50% charged."
    else:
        return "Unsupported rate plan. Use: flexible, semi-flex, non-refundable."
    return (
        f"Rule: {rule}\n"
        f"Days before check-in: {days_before}\n"
        f"Total: ${total:.2f}\n"
        f"Charged: ${charged:.2f}\n"
        f"Refund: ${refund:.2f}"
    )

步骤3:在LangGraph中构建ReAct循环(推理→工具→再推理)

需严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
from langgraph.prebuilt import ToolNode, tools_condition
# 1) Define state
class AgentState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    booking_id: str
# 2) Define structured Output schema
class RefundDecision(BaseModel):
    booking_id: str
    rate_plan: str
    total_amount: float
    charged_amount: float
    refund_amount: float
    policy_summary: str
    explanation: str
# 2) Choose model and System prompt
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
SYSTEM_PROMPT = SystemMessage(
    content="""
You are a hotel cancellation assistant.
Rules:
- Use tools when needed.
- Never guess policy or refund.
- Final answer MUST be valid JSON with this schema:
{
  "booking_id": "...",
  "rate_plan": "...",
  "total_amount": number,
  "charged_amount": number,
  "refund_amount": number,
  "policy_summary": "...",
  "explanation": "..."
}
"""
)
# 3) Register tools
tools = [get_cancellation_policy, calculate_refund]
def safe_tool_node(state):
    last_msg = state["messages"][-1]
    if not hasattr(last_msg, "tool_calls") or not last_msg.tool_calls:
        return {}
    tool_call = last_msg.tool_calls[0]
    tool_name = tool_call["name"]
    if tool_name not in ALLOWED_TOOLS:
        return {
            "messages": [
                ToolMessage(
                    content=f"Tool '{tool_name}' is not allowed.",
                    tool_call_id=tool_call["id"]
                )
            ]
        }
    for tool in tools:
        if tool.name == tool_name:
            result = tool.invoke(tool_call["args"])
            return {
                "messages": [
                    ToolMessage(
                        content=result,
                        tool_call_id=tool_call["id"]
                    )
                ]
            }
# 4)Before reasoning, check if we already processed this booking.
REFUND_MEMORY = {}
def memory_lookup_node(state):
    booking_id = state["booking_id"]
    if booking_id in REFUND_MEMORY:
        return {
            "messages": [
                HumanMessage(
                    content=f"Cached decision found:\n{REFUND_MEMORY[booking_id]}"
                )
            ]
        }
    return {}
# 5) Reasoning node: LLM decides next action (tool call) or final answer
def agent_node(state: AgentState):
    # Bind tools so the model can produce tool calls
    llm_with_tools = llm.bind_tools(tools)
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}
# 6) After final decision, store it.
def memory_write_node(state):
    booking_id = state["booking_id"]
    final_answer = state["messages"][-1].content
    REFUND_MEMORY[booking_id] = final_answer
    return {}

构建并编译图表

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

# 7) Build the graph
builder = StateGraph(AgentState)

# Nodes
builder.add_node("memory_lookup", memory_lookup_node)
builder.add_node("agent", agent_node)
builder.add_node("tools", safe_tool_node)
builder.add_node("memory_write", memory_write_node)
# Flow
builder.add_edge(START, "memory_lookup")
builder.add_edge("memory_lookup", "agent")
builder.add_conditional_edges(
    "agent",
    tools_condition,
    {
        "tools": "tools",   # model wants to act
        END: "memory_write" # model finished reasoning
    }
)
builder.add_edge("tools", "agent")
builder.add_edge("memory_write", END)
graph = builder.compile()

第4步:在用例上运行代理

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

query = """
Booking details:
Rate plan: Non-Refundable
Check-in: 2026-01-20
Nights: 2
Nightly rate: 120
Cancelled on: 2026-01-18
"""

result = graph.invoke({
    "booking_id": "BKG-12345",
    "messages": [
        SYSTEM_PROMPT,
        HumanMessage(content=query)
    ]
})
final_output = result["messages"][-1].content
print(final_output)

输出结果

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

{
  "booking_id": "BKG-12345",
  "rate_plan": "Non-Refundable",
  "total_amount": 240.0,
  "charged_amount": 240.0,
  "refund_amount": 0.0,
  "policy_summary": "Non-refundable bookings do not allow refunds after confirmation.",
  "explanation": "The booking was made under a non-refundable rate plan, which charges the full stay amount regardless of cancellation timing."
}

内部处理流程(ReAct行为模式)

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

1) 思考(推理)

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

2) 行动(工具调用)

需严格限制工具架构。过长的自由文本参数容易引发注入攻击,且会增加审计成本。

3) 观察(工具输出)

在耗资源模型调用后设置检查点,以避免重试时重复计费。

4) 最终答案

在耗资源模型调用后设置检查点,以避免重试时重复计费。

为何这属于“ReAct”(而不仅仅是工具)

在耗资源模型调用后设置检查点,以避免重试时重复计费。

结论

将规划与工具执行分开。规划器负责提出方案,执行器负责修改状态,验证器则根据目标检查结果。

操作清单

严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

条件路径应通过命名函数来体现业务规则,而非隐藏在提示文本中。

将类型与组件放在一起,并保持属性数量较少。过多的属性会成为TypeScript旨在避免的问题根源。

编写一份简短的操作手册:如何轮换密钥、如何清空队列、如何回滚最近的更改。