Agent核心能力详解

文章目录

  • [1. 指定模型](#1. 指定模型)
    • [1.1 静态模型](#1.1 静态模型)
    • [1.1 动态模型](#1.1 动态模型)
  • [2. 指定工具](#2. 指定工具)
    • [2.1 静态工具](#2.1 静态工具)
    • [2.1 动态工具](#2.1 动态工具)
    • [2.3 工具错误处理](#2.3 工具错误处理)
  • [3. 指定提示词](#3. 指定提示词)
    • [3.1 静态系统提示](#3.1 静态系统提示)
    • [3.2 动态系统提示](#3.2 动态系统提示)
  • [4. 指定结构化输出策略](#4. 指定结构化输出策略)
  • [5. 定义State](#5. 定义State)
  • [6. 人机协作(Human-in-the-loop)](#6. 人机协作(Human-in-the-loop))
    • [6.1 配置](#6.1 配置)
    • [6.2 HITL一般流程:](#6.2 HITL一般流程:)
    • [6.3 响应中断与恢复](#6.3 响应中断与恢复)
  • [7. 流模式](#7. 流模式)
    • [模式一: updates - 流式传输代理进度](#模式一: updates - 流式传输代理进度)
    • [模式二: messages - 流式传输LLM Token](#模式二: messages - 流式传输LLM Token)
    • [模式三: custom - 流式传输自定义更新](#模式三: custom - 流式传输自定义更新)
    • [模式四: 组合模式 - 同时使用多种流模式](#模式四: 组合模式 - 同时使用多种流模式)

1. 指定模型

1.1 静态模型

在创建 Agent 时一次性配置,执行期间保持不变:

方式一: 使用模型标识符字符串: 这是一种快捷方式,create_agent 内部会调用 init_chat_model 来初始化模型。你只能指定模型标识,虽然也能配置部分参数, 但是能配置的参数有限

python 复制代码
agent = create_agent("openai:gpt-4o-mini", tools=tools)

方式二: 使用模型实例: 精细的模型参数:像 temperature、max_tokens、timeout、max_retries 这些生成参数,以及更底层的 use_responses_api、store 等提供商特定配置,只有创建模型实例时才能设定

python 复制代码
model_5 = ChatOpenAI(model="gpt-5-mini", tempeture=0.1, max_tokens=1000)
# 定义 agent
agent = create_agent(
    model=model_5,
    tools=[get_weather_for_location],
    system_prompt="你是一位乐于助人的助手。",
    middleware=[dynamic_model_selection],
)

1.1 动态模型

在运行时根据当前状态或上下文动态选择模型。通过中间件(middleware)配合

实现方式: @wrap_model_call 装饰器

python 复制代码
@wrap_model_call
def dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse:
    """根据对话的复杂程度(如消息数量)来选择模型。"""
    message_count = len(request.state["messages"])
    if message_count > 1:
        # 使用高级模型进行更长时间的对话
        final_model = model
    else:
        final_model = model_mini
    return handler(request.override(model=final_model))

2. 指定工具

工具赋予 Agent 执行具体操作的能力

2.1 静态工具

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

普通的python函数
使用@tool的装饰器(可自定义名称、描述、参数schema等)

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

python 复制代码
agent = create_agent(
    model="gpt-5-mini",
    tools=[get_weather_for_location],
)

2.1 动态工具

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

根据认证状态、用户权限、功能开关或对话阶段,再决定哪些工具可用

避免工具过多导致模型上下文过载或出错,同时避免工具过少限制能力。

动态工具有两种实现方式

  1. 运行时,根据条件动态选择预先注册好的工具
  2. 运行时,动态加入新工具

动态选择: 运行时,根据条件动态选择预先注册好的工具

python 复制代码
@wrap_model_call(state_schema=State)
def state_based_tools(
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    """根据条件筛选已存在的工具,例如根据状态筛选"""
    state = request.state
    is_auth = state.get("auth", False)

    # 未认证用户只能使用public_的工具
    if not is_auth:
        # 筛选
        tools = [t for t in request.tools if t.name.startswith("public_")]
        request = request.override(tools=tools) # 更新替换
    else:
        tools = [t for t in request.tools if t.name.startswith("private_")]
        request = request.override(tools=tools)  # 更新替换

    return handler(request)   # llm 执行,这一步是为了选择工具,因此要提前筛选工具

动态加入: 运行时,动态加入新工具

python 复制代码
class DynamicToolMiddleware(AgentMiddleware):
    """能够注册并处理动态工具的中间件。"""

    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
    ) -> ModelResponse:
        # 将新的工具添加进来
        updated_request = request.override(tools=[*request.tools, calculate_tip])
        return handler(updated_request)  # llm调用(选择工具)

    def wrap_tool_call(
        self,
        request: ToolCallRequest,
        handler: Callable[[ToolCallRequest], ToolMessage | Command],
    ) -> ToolMessage | Command:
        # 处理动态工具的执行过程
        # 走到此处已经把tool选好了,只是根据tool_calls知道要调用哪个工具和参数

        try:
            if request.tool_call["name"] == "calculate_tip":
                return handler(request.override(tool=calculate_tip))  # calculate_tip.invoke()

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

*request.tools 是Python 列表解包,把原工具列表展开,再拼上新工具

2.3 工具错误处理

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

python 复制代码
@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"]
        )

3. 指定提示词

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

3.1 静态系统提示

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

字符串写法日常使用就足够了

python 复制代码
agent = create_agent(model, tools, system_prompt="你是一位乐于助人的助手。请简洁准确地表达。")
python 复制代码
agent = create_agent(
    model="gpt-5-mini",
    system_prompt=SystemMessage(
        content=[
            {"type": "text",
             "text": "你是一名乐于助人的人工智能助手."}
        ]
    ),
)

3.2 动态系统提示

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

python 复制代码
@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="gpt-5-mini",
    context_schema=Context,
    # system_prompt=  # 静态提示词
    middleware=[user_role_prompt]
)

4. 指定结构化输出策略

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

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

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

ToolStrategy : 利用模型的工具调用(Tool Calling) 能力,通过创建一个"虚拟工具"来迫使模型以调用该工具参数的形式输出结构化数据

ProviderStrategy : 直接使用模型提供商提供的原生结构化输出功能, 是首选方案

这两种策略感兴趣可自行学习

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

python 复制代码
class ContactInfo(BaseModel):
    first_name: str
    last_name: str
    
agent = create_agent(
    model="gpt-4.1",
    response_format=ContactInfo # 直接传入模型,而不是策略对象
)

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

5. 定义State

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

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

注意:

自定义状态是 Agent 的短期记忆,仅在当前会话生命周期 内有效.

自定义状态必须继承自 AgentState定义为 TypedDict (在 LangChain 1.0 及之后版本)

两种实现方法:

  1. 通过 Middleware 定义: 推荐方式。自定义状态需要在中间件钩子或特定工具中被访问时使用
  2. 通过 state_schema 参数定义: 快速实现,但作用域较广, 不推荐新项目使用。自定义状态仅需在工具中使用,无需复杂中间件逻辑时的简化用法

这里介绍下通过Middleware定义的方式(推荐):

步骤:

  1. 定义一个继承自 AgentState 的 TypedDict
  2. 创建一个继承自 AgentMiddleware 的类,设置 state_schema 为该 TypedDict

从 LangChain 1.0 开始,自定义状态必须是 TypedDict 类型,不再支持Pydantic 模型或 dataclass。

  1. 在 create_agent 时通过 middleware 参数传入该中间件
python 复制代码
# 1. 定义TypedDict
class CustomState(AgentState):
    user_preferences: dict

# 2. 创建中间件(类),关联状态
class CustomMiddleware(AgentMiddleware):
    state_schema = CustomState # 设置 state_schema 为该 TypedDict
    tools = []  # 可选:为该中间件绑定工具

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

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

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

通过state_schema参数定义

步骤:

  1. 定义一个继承自 AgentState 的 TypedDict
  2. create_agent 时 直接通过 state_schema 参数传入该类型
python 复制代码
# 1. 定义自定义状态
class CustomState(AgentState):
    user_preferences: dict
    
# 2. 创建Agent时传入 state_schema
agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[], # 工具可以访问此状态
    state_schema=CustomState # 快捷定义
)

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

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

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

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

中断策略:

  1. approve: 完全批准,工具按原参数执行
  2. edit: 允许修改工具参数后再执行
  3. reject: 拒绝执行,将反馈添加到对话中

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

6.1 配置

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

参数一: interrupt_on (必选)

这是一个字典,用于定义哪些工具需要触发人工中断 以及允许哪些决策类型

键为工具名称(字符串),值可以是以下三种形式:

  1. True: 中断该工具,允许全部三种决策( approve 、edit 、 reject), "工具1": True
  2. False: 不中断,自动批准(工具调用直接执行)
  3. InterruptOnConfig 对象: 精细控制, 可指定允许的决策列表和自定义描述 , "工具2": False

InterruptOnConfig 对象的属性:

allowed_decisions :列表,可选值 "approve" 、 "edit" 、 "reject" 。决定人工可用的操作类型

description :字符串或可调用函数,用于覆盖该工具的中断提示消息。若未指定,则使用全局 description_prefix 拼接而成


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

参数二: description_prefix (可选)

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

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

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

若在 InterruptOnConfig 中指定了 description ,则忽略全局前缀


关键条件:

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

6.2 HITL一般流程:

  1. LLM 提议调用某工具(命中 interrupt_on 的工具)
  2. 中间件拦截,发出中断,等待你输入决策
  3. 用同一个 thread_id 再次调用 agent,传入 Command(resume={"decisions": ...}) 恢复执行(同一个thread是通过config参数确定的)
  4. 中间件校验决策合法性,并按决策执行对应动作(approve/edit/reject)
  5. LLM 收到一条 ToolMessage(真实结果或拒绝说明),再决定下一步

6.3 响应中断与恢复

触发中断后获取待审核动作

python 复制代码
config = {"configurable": {"thread_id": "11"}}
# 中断
response1 = agent.invoke(
    {"messages": [{"role": "user", "content": "删除数据库中data表中id=1的旧数据"}]},
    config=config,
    version="v2",  # LangChain v1.1后必须使用v2版本获取中断信息
)
# result.interrupts 包含待审核的工具调用详情
print(response1.interrupts)

打印出来的审核工具调用详情

python 复制代码
(
    Interrupt(
        value={
            'action_requests': [  # 被中断的工具信息
                {
                    'name': 'execute_sql',
                    'args': {'sql': 'DELETE FROM data WHERE id = 1;'},
                    'description': "工具执行尚待批准\n\nTool: execute_sql\nArgs: {'sql': 'DELETE FROM data WHERE id = 1;'}"
                }
            ],
            'review_configs': [    # 运行的操作
                {'action_name': 'execute_sql', 'allowed_decisions': ['approve', 'edit']}
            ]
        },
        id='005232c15e9a9dad9d48580dbd93acaa'
    ),
)

提交决策继续执行

python 复制代码
# approve
response2 = agent.invoke(
    Command(resume={"decisions": [{"type": "approve"}]}),
    config=config,
    version="v2"
)
print(response2)
python 复制代码
# reject: 目前不支持(因为该工具在interrupt_on没配置这个决策)
response2 = agent.invoke(
    Command(
        resume={
            "decisions": [
                {
                    "type": "reject",
                    "message": "这是错误的,因为...."  # 会被构造成ToolMessage返回
                }
            ]
        }
    ),
    config=config,
    version="v2"
)
print(response2)
python 复制代码
# edit
response2 = agent.invoke(
    Command(
        resume={
            "decisions": [{
                "type": "edit",
                "edited_action": {
                    "name": "execute_sql",
                    "args": {'sql': 'DELETE FROM data WHERE id = 2;'}
                }
            }],
        }
    ),
    config=config,
    version="v2"
)
print(response2.value)

7. 流模式

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

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

python 复制代码
# 定义工具
@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息"""
    return f"在{city}总是阳光明媚!"


agent = create_agent(
    model="gpt-5-mini",
    tools=[get_weather_for_location],
    system_prompt="你是一位乐于助人的客服助手。",
)
# 模式1:updates
for chunk in agent.stream(
    {
        "messages": [{
            "role": "user",
            "content": "北京的天气如何?"
        }]
    },
    stream_mode="updates",
    version="v2",  # v1.1. 版本以上
):
    if chunk["type"] == "updates":
        for step, data in chunk["data"].items():
            print(f"步骤:{step}")
            print(f"内容:{data}")

此处将 version="v2" (需要 LangGraph >= 1.1.)传递给 stream() 或 astream() 以获取统一的

输出格式。每个数据块都是一个具有 type、ns 和 data 键的 StreamPart 字典, 比较方便

如果用"v1"版本必须使用(mode, data)元组接收.stream才行

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

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

python 复制代码
# messages
for chunk in agent.stream(
    {
        "messages": [{
            "role": "user",
            "content": "北京的天气如何?"
        }]
    },
    stream_mode="messages",
    version="v2",  # v1.1. 版本以上
):
    if chunk["type"] == "messages":
        token, metadata = chunk["data"]
        print(f"节点:{metadata['langgraph_node']}")
        print(token.content_blocks)

你会看到 model 节点逐块输出工具调用的JSON参数(如 '{"' , 'city' , '":"' , '上海' ,

...)

然后 tools 节点输出工具执行结果。

最后 model 节点逐块输出最终回复文本(如 '上海' , '的' , ' 天气' ...)

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

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

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

python 复制代码
# 定义工具
@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息"""
    writer = get_stream_writer()
    writer(f"正在查询{city}的天气")
    writer(f"查询{city}的天气完成")
    return f"在{city}总是阳光明媚!"


agent = create_agent(
    model="gpt-5-mini",
    tools=[get_weather_for_location],
    system_prompt="你是一位乐于助人的客服助手。",
)

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

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

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

示例:同时使用 updates 和 custom 模式

python 复制代码
# 组合
for chunk in agent.stream(
    {
        "messages": [{
            "role": "user",
            "content": "北京的天气如何?"
        }]
    },
    stream_mode=["updates", "custom"],
    version="v2",  # v1.1. 版本以上
):
    print(f"流模式:{chunk['type']}")
    print(f"内容:{chunk['data']}\n")

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

相关推荐
染指111017 小时前
119.Agent-LangChain核心组件-Runtime运行时
人工智能·langchain·agent
用户31346721435420 小时前
Agent 开发学习笔记(六):LCEL:把组件串成链
langchain·agent
艾醒(AiXing-w)21 小时前
LangChain 1.0 智能体开发(三):Agent 记忆管理——从短期对话到跨会话长期记忆
数据库·人工智能·langchain
KimLiu1 天前
LCODER之AI Agent开发实战一 :问数项目智能体搭建(3)元数据知识库的构建
langchain·llm·agent
烛之武1 天前
LangChain笔记
langchain·大模型·agent·mcp
运维@小兵2 天前
LangChain系列——中间件(Middleware)
langchain·langchain中间件
hyunbar2 天前
LangChain 实战:中间件Summarization详解
langchain·agent·harness
归去来 兮2 天前
LangChain从入门到精通
python·langchain·langgraph
沉下心来学鲁班2 天前
初识DeepAgents搭建第一个智能体
人工智能·python·langchain