Skip to content

内置中间件(上):调用限制与模型回退 ​

3、其它内置中间件 ​

这里为大部分中间件提供测试代码和输出,感兴趣的同学自行研究。

3.1 ModelCallLimitMiddleware中间件 ​

限制模型调用次数,避免无限循环,控制调用成本。

举例1:整个会话限制-优雅退出 ​

python
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os

# 从.env文件中加载环境变量
load_dotenv(override=True)

CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")

model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)
python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage

from typing import List

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ModelCallLimitMiddleware(
            thread_limit=2,  # 每个线程最多2次模型调用
            # run_limit=5,   # 每次运行最多5次
            exit_behavior="end", # 达到限制后退出
        ),
    ],
)

def pretty_iterate_msg(messages: List[SystemMessage | HumanMessage | AIMessage | ToolMessage]):
    for msg in messages:
        msg.pretty_print()

config = {"configurable": {"thread_id": "1"}}
response_first = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)
print("=" * 30, "> first <", "=" * 30)
pretty_iterate_msg(response_first["messages"])

response_second = agent.invoke({
    "messages": [HumanMessage("你是谁?")]},
    config=config
)
print("=" * 30, "> second <", "=" * 30)
pretty_iterate_msg(response_second["messages"])

response_third = agent.invoke({
    "messages": [HumanMessage("你能帮我做什么?")]},
    config=config
)
print("=" * 30, "> third <", "=" * 30)
pretty_iterate_msg(response_third["messages"])

输出

text
============================== > first < ==============================
================================ Human Message =================================

你好
================================== Ai Message ==================================

你好!有什么我可以帮你的吗?
============================== > second < ==============================
================================ Human Message =================================

你好
================================== Ai Message ==================================

你好!有什么我可以帮你的吗?
================================ Human Message =================================

你是谁?
================================== Ai Message ==================================

我是 ChatGPT,一个由 OpenAI 训练的 AI 助手。
我可以帮你回答问题、写作、翻译、总结、编程、头脑风暴等。

如果你愿意,也可以直接告诉我你现在想做什么。
============================== > third < ==============================
================================ Human Message =================================

你好
================================== Ai Message ==================================

你好!有什么我可以帮你的吗?
================================ Human Message =================================

你是谁?
================================== Ai Message ==================================

我是 ChatGPT,一个由 OpenAI 训练的 AI 助手。
我可以帮你回答问题、写作、翻译、总结、编程、头脑风暴等。

如果你愿意,也可以直接告诉我你现在想做什么。
================================ Human Message =================================

你能帮我做什么?
================================== Ai Message ==================================

Model call limits exceeded: thread limit (2/2)

举例2:整个会话限制-抛异常 ​

python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage

from typing import List

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ModelCallLimitMiddleware(
            thread_limit=2,
            # run_limit=5,
            exit_behavior="error",
        ),
    ],
)

def pretty_iterate_msg(messages: List[SystemMessage | HumanMessage | AIMessage | ToolMessage]):
    for msg in messages:
        msg.pretty_print()

config = {"configurable": {"thread_id": "1"}}
response_first = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)
print("=" * 30, "> first <", "=" * 30)
pretty_iterate_msg(response_first["messages"])

response_second = agent.invoke({
    "messages": [HumanMessage("你是谁?")]},
    config=config
)
print("=" * 30, "> second <", "=" * 30)
pretty_iterate_msg(response_second["messages"])

response_third = agent.invoke({
    "messages": [HumanMessage("你能帮我做什么?")]},
    config=config
)
print("=" * 30, "> third <", "=" * 30)
pretty_iterate_msg(response_third["messages"])

输出

text
============================== > first < ==============================
================================ Human Message =================================

你好
================================== Ai Message ==================================

你好!有什么我可以帮你的吗?
============================== > second < ==============================
================================ Human Message =================================

你好
================================== Ai Message ==================================

你好!有什么我可以帮你的吗?
================================ Human Message =================================

你是谁?
================================== Ai Message ==================================

我是 ChatGPT,一个由 OpenAI 训练的 AI 助手。
我可以帮你回答问题、写作润色、翻译、总结、编程、头脑风暴等。
你想聊点什么?

Traceback...

ModelCallLimitExceededError: Model call limits exceeded: thread limit (2/2)
During task with name 'ModelCallLimitMiddleware.before_model' and id '42468735-c5e0-c935-6c91-26d7d4f04f95'

举例3:单次调用限制-优雅退出 ​

需要fake-server重复触发工具调用,代码如下

python
import json
import random
import time
from http.server import BaseHTTPRequestHandler, HTTPServer

class FakeDeepSeekHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        content_length = int(self.headers.get("Content-Length", 0))
        raw_body = self.rfile.read(content_length).decode("utf-8")

        print("\n" + "=" * 100)

        json_body = None
        try:
            json_body = json.loads(raw_body)
            print("[JSON BODY]")
            print(json.dumps(json_body, ensure_ascii=False, indent=2))
        except Exception as e:
            print("[JSON PARSE ERROR]")
            print(repr(e))

        response = {
            "id": "chatcmpl-test",
            "object": "chat.completion",
            "created": int(time.time()),
            "model": "any",
            "choices": [
                {
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": "",
                        "tool_calls": [
                            {
                                "id": "call_1",
                                "type": "function",
                                "function": {
                                    "name": json_body["tools"][0] ["function"]["name"],
                                    "arguments": json.dumps(
                                        {'name': '康师傅', 'email': 'songhongkang@atguigu.cn', 'phone': '12345678912'},
                                        ensure_ascii=False
                                    )
                                }
                            }
                        ]
                    },
                    "finish_reason": "stop"
                }
            ],
            "usage": {
                "prompt_tokens": 1,
                "completion_tokens": 1,
                "total_tokens": 2
            }
        }
        append_val = {
            "id": "call_2",
            "type": "function",
            "function": {
                "name": json_body["tools"][1]["function"]["name"],
                "arguments": json.dumps(
                    {'event_name': '问数项目启动会', 'date': '2026-03-27'},
                    ensure_ascii=False
                )
            }
        }

        if random.randint(1, 10) > 2:
            response["choices"][0]["message"] ["tool_calls"].append(append_val)
        # response["choices"][0]["message"]["tool_calls"][0]["function"] ["arguments"] = json.dumps(
        #     {'name1': '康师傅', 'email2': 'songhongkang@atguigu.cn', 'phone': '12345678912'},
        #     ensure_ascii=False
        # )

        print("\n" + "=" * 100)
        print("[RESPONSE]")
        print(json.dumps(response, ensure_ascii=False, indent=2))

        body = json.dumps(response, ensure_ascii=False).encode("utf-8")

        self.send_response(200)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, format, *args):
        pass

def main():
    server = HTTPServer(("127.0.0.1", 8889), FakeDeepSeekHandler)
    print("Fake DeepSeek server running at http://127.0.0.1:8889")
    server.serve_forever()
if __name__ == "__main__":
    main()

注意:服务端代码逻辑是80%概率输出非法响应,所以不一定会导致单次请求的工具调用超过限制,尝试几次即可看到效果。

客户端代码

python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
from langchain_deepseek import ChatDeepSeek

from pydantic import BaseModel, Field, SecretStr
from typing import List, Union
from dotenv import load_dotenv

load_dotenv()
model = ChatDeepSeek(
    model="any",
    api_base="http://localhost:8889",
    api_key=SecretStr("<KEY>")
)

class ContactInfo(BaseModel):
    """用户的联系方式"""
    name: str = Field(description="用户姓名")
    email: str = Field(description="用户邮箱地址")
    phone: str = Field(description="用户的手机号")

class EventInfo(BaseModel):
    event_name: str = Field(description="事件名称")
    date: str = Field(description="事件发生日期")

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ModelCallLimitMiddleware(
            # thread_limit=2,
            run_limit=3,
            exit_behavior="end",
        ),
    ],
    response_format=Union[ContactInfo, EventInfo]
)

config = {"configurable": {"thread_id": "1"}}
response = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)

for msg in response["messages"]:
    msg.pretty_print()

输出

text
================================•[1m Human Message •[0m=================================

你好
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
==================================•[1m Ai Message •[0m==================================

Model call limits exceeded: run limit (3/3)

举例4:单次调用限制-抛异常 ​

python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
from langchain_deepseek import ChatDeepSeek

from pydantic import BaseModel, Field, SecretStr
from typing import List, Union
from dotenv import load_dotenv
load_dotenv(override=True)
model = ChatDeepSeek(
    model="any",
    api_base="http://localhost:8889",
    api_key=SecretStr("<KEY>")
)

class ContactInfo(BaseModel):
    """用户的联系方式"""
    name: str = Field(description="用户姓名")
    email: str = Field(description="用户邮箱地址")
    phone: str = Field(description="用户的手机号")

class EventInfo(BaseModel):
    event_name: str = Field(description="事件名称")
    date: str = Field(description="事件发生日期")

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ModelCallLimitMiddleware(
            # thread_limit=2,
            run_limit=3,
            exit_behavior="error",
        ),
    ],
    response_format=Union[ContactInfo, EventInfo]
)

config = {"configurable": {"thread_id": "1"}}
response = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)

for msg in response["messages"]:
    msg.pretty_print()

输出

text
Traceback...
ModelCallLimitExceededError: Model call limits exceeded: run limit (3/3)
During task with name 'ModelCallLimitMiddleware.before_model' and id 'e731473b-d9c6-d07d-2392-40d38f0f7219'

3.2 ToolCallLimitMiddleware中间件 ​

限制工具调用次数,可以限制所有工具调用的总次数,也可以限制特定工具的调用次数。

作用如下:

  • 避免过多调用某些昂贵的外部API
  • 限制网络爬虫或数据库查询请求的数量
  • 避免Agent陷入无限循环

退出行为有三种模式:

  • error:直接抛异常
  • end:结束整个会话
  • continue:继续运行Agent,这是默认行为,此时Agent会将工具调用超出限制的信息传递给模型,后者自主决定后续行为,如果模型能力不足,可能导致死循环,为了避免这种情况,我们实现的fake server会以20%的概率输出正确响应,从而能终止循环。

举例1:整个会话限制-优雅结束 ​

python
from langchain.agents import create_agent
from langchain.agents.middleware import ToolCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
from langchain_deepseek import ChatDeepSeek

from pydantic import BaseModel, Field, SecretStr
from typing import List, Union
from dotenv import load_dotenv

load_dotenv(override=True)
model = ChatDeepSeek(
    model="any",
    api_base="http://localhost:8889",
    api_key=SecretStr("<KEY>")
)

class ContactInfo(BaseModel):
    """用户的联系方式"""
    name: str = Field(description="用户姓名")
    email: str = Field(description="用户邮箱地址")
    phone: str = Field(description="用户的手机号")

class EventInfo(BaseModel):
    event_name: str = Field(description="事件名称")
    date: str = Field(description="事件发生日期")

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ToolCallLimitMiddleware(
            # thread_limit=2,  # 每个线程最多2次工具调用
            run_limit=2,       # 每次运行最多2次
            exit_behavior="end",
        ),
    ],
    response_format=Union[ContactInfo, EventInfo]
)

def pretty_iterate_msg(messages: List[SystemMessage | HumanMessage | AIMessage | ToolMessage]):
    for msg in messages:
        msg.pretty_print()

config = {"configurable": {"thread_id": "1"}}
response = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)
pretty_iterate_msg(response["messages"])

输出

text
================================•[1m Human Message •[0m=================================

你好
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================

Tool call limit reached: run limit exceeded (4/2 calls).

举例2:整个会话限制-抛异常 ​

python
from langchain.agents import create_agent
from langchain.agents.middleware import ToolCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
from langchain_deepseek import ChatDeepSeek

from pydantic import BaseModel, Field, SecretStr
from typing import List, Union
from dotenv import load_dotenv

load_dotenv(override=True)
model = ChatDeepSeek(
    model="any",
    api_base="http://localhost:8889",
    api_key=SecretStr("<KEY>")
)

class ContactInfo(BaseModel):
    """用户的联系方式"""
    name: str = Field(description="用户姓名")
    email: str = Field(description="用户邮箱地址")
    phone: str = Field(description="用户的手机号")

class EventInfo(BaseModel):
    event_name: str = Field(description="事件名称")
    date: str = Field(description="事件发生日期")

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ToolCallLimitMiddleware(
            # thread_limit=2,
            run_limit=2,
            exit_behavior="error",
        ),
    ],
    response_format=Union[ContactInfo, EventInfo]
)

def pretty_iterate_msg(messages: List[SystemMessage | HumanMessage | AIMessage | ToolMessage]):
    for msg in messages:
        msg.pretty_print()

config = {"configurable": {"thread_id": "1"}}
response = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)
pretty_iterate_msg(response["messages"])

输出

text
Traceback...
ToolCallLimitExceededError: Tool call limit reached: run limit exceeded (4/2 calls).
During task with name 'ToolCallLimitMiddleware.after_model' and id '065d5983-8a8a-2c1c-0b6f-d9f365c122ba'

案例3:单次调用限制-继续运行 ​

python
from langchain.agents import create_agent
from langchain.agents.middleware import ToolCallLimitMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
from langchain_deepseek import ChatDeepSeek

from pydantic import BaseModel, Field, SecretStr
from typing import List, Union
from dotenv import load_dotenv

load_dotenv(override=True)
model = ChatDeepSeek(
    model="any",
    api_base="http://localhost:8889",
    api_key=SecretStr("<KEY>")
)

class ContactInfo(BaseModel):
    """用户的联系方式"""
    name: str = Field(description="用户姓名")
    email: str = Field(description="用户邮箱地址")
    phone: str = Field(description="用户的手机号")

class EventInfo(BaseModel):
    event_name: str = Field(description="事件名称")
    date: str = Field(description="事件发生日期")

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver(),  # Required for thread limiting
    tools=[],
    middleware=[
        ToolCallLimitMiddleware(
            # thread_limit=2,
            run_limit=2,
            exit_behavior="continue",
        ),
    ],
    response_format=Union[ContactInfo, EventInfo]
)

def pretty_iterate_msg(messages: List[SystemMessage | HumanMessage | AIMessage | ToolMessage]):
    for msg in messages:
        msg.pretty_print()

config = {"configurable": {"thread_id": "1"}}

# seen = set()
response = agent.invoke({
    "messages": [HumanMessage("你好")]},
    config=config
)
pretty_iterate_msg(response["messages"])

输出

text
================================•[1m Human Message •[0m=================================

你好
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo
Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
EventInfo (call_2)
Call ID: call_2
Args:
 event_name: 问数项目启动会
 date: 2026-03-27
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventInfo) when only one is expected.
Please fix your mistakes.
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.
=================================•[1m Tool Message •[0m=================================
Name: EventInfo

Tool call limit exceeded. Do not make additional tool calls.
==================================•[1m Ai Message •[0m==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
 name: 康师傅
 email: songhongkang@atguigu.cn
 phone: 12345678912
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Returning structured response: name='康师傅' email='songhongkang@atguigu.cn' phone='12345678912'
=================================•[1m Tool Message •[0m=================================
Name: ContactInfo

Tool call limit exceeded. Do not make additional tool calls.

3.3 ModelFallbackMiddleware中间件 ​

用于故障转移,当主模型无法访问时,启用备用模型。

举例1:

python
from langchain.agents.middleware import ModelFallbackMiddleware
from langchain.chat_models import init_chat_model

# 定义主模型和备用模型
primary_model = init_chat_model("openai:gpt-5.4-mini")

fallback = ModelFallbackMiddleware(
    fallback_models=[
                    init_chat_model("openai:gpt-4o-mini"),
                    init_chat_model("anthropic:claude-3-haiku")
    ]
)

agent = create_agent(
    model=primary_model,
    tools=[],
    middleware=[fallback],
)

举例2:

python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelFallbackMiddleware
from langchain.messages import HumanMessage

from dotenv import load_dotenv

load_dotenv(override=True)
agent = create_agent(
    model="deepseek:fake_model",
    tools=[],
    middleware=[
        ModelFallbackMiddleware(
            "deepseek-v4-flash",
            "deepseek-v4-pro",
        ),
    ],
)

response = agent.invoke({
    "messages": [HumanMessage("你是谁?")]
})

last_msg = response["messages"][-1]
print(last_msg)

print('=' * 30, '-> model_name <-', '=' * 30)
print(last_msg.response_metadata.get("model_name"))

输出

text
content='你好呀!我是DeepSeek,由深度求索公司创造的AI助手。😊\n\n我的主要特点包
括:\n- **免费使用**:随时为你提供帮助,不收费\n- **强大的上下文能力**:拥有1M的上下 文窗口,可以一次性处理像《三体》三部曲那样的长篇内容\n- **文件处理**:支持上传图片、 PDF、Word、Excel、PPT等文件,我能读取其中的文字信息\n- **联网搜索**:虽然需要你手 动开启,但我可以帮你搜索最新信息\n- **语音交互**:在App端支持语音输入\n\n我的知识截 止于2025年5月,会尽我所能用热情、细腻的方式回答你的问题。无论是学习、工作还是日常闲 聊,我都很乐意陪伴你!\n\n有什么我可以帮你的吗?🌟' additional_kwargs= {'refusal': None, 'reasoning_content': '好的,用户问了一个非常基础的自我介绍问 题:“你是谁?”。这是一个新对话的典型开场,用户可能想确认我的身份和能力,以便后续提出更 具体的问题。\n\n我需要给出一个清晰、全面的自我介绍,说明我的身份、创造者、核心功能和一 些关键特点(比如免费、长上下文、文件处理等),让用户快速了解我能提供什么帮助。最后应该 以开放式的邀请结束,引导用户提出进一步的需求。\n\n想到了用热情友好的语气开头,然后分点 (虽然回复里是段落形式,但思考时是分块想)介绍核心信息,最后表达乐于助人的态度。'} response_metadata={'token_usage': {'completion_tokens': 296, 'prompt_tokens': 6, 'total_tokens': 302, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 126, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 6}, 'model_provider': 'deepseek', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '618d4649-1de7-439d-b1cc-fe2bafb06afe', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019eac53-2db9-75c2-8493-b351c34ae7ae-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 6, 'output_tokens': 296, 'total_tokens': 302, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 126}}
============================== -> model_name <- ==============================
deepseek-v4-flash