简单学习 --> LangChain

LangChain

LangChain是 我们 大模型应用 和 大模型之间的桥梁,是目前Agent主流的框架,

复制代码
1. 在25年10月后更新的1.0版本, 才是真正langchain
安装依赖
复制代码
1. 用conda创建环境和下载依赖
​
conda create -n langchain python=3.13
​
conda activate langchain 
​
conda install langchain 
conda install langchain-openai
简单案例
复制代码
agent = create_agent(
    model=llm,
    system_prompt=SYSTEM_PROMPT,
    tools=[get_user_location, get_weather_for_location],
    context_schema=Context,
    response_format=ToolStrategy(ResponseFormat),
    checkpointer=checkpointer
)
​
Checkpointer

Checkpointer存储的是已正式 **State(状态)**的快照,这个状态包含

  1. 聊天记录(message) : 和ai说的每一句话

  2. 上下文变量(Context): 有Content Schema 定义的部分, 就是定义了运行时上下文的结构

  3. 内部节点位置: Agent现在跑到了流程的那一步了

LangChain的核心组件

  1. Agent (智能体)

  2. llm (模型)

  3. Message (消息)

  4. Tools (工具)

  5. 中间件

  6. 短期记忆

  7. Stream 流式输出

  8. 结构化输出

Agent 智能体

Agent将 大模型 和 工具 结合在一起 , 这样我们就有了一个能进行任务推理,又能决定使用那种工具,来解决我们问题的系统

例子1 : Agent的基础使用
复制代码
import json
import os
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from pprint import pprint
​
from dotenv import load_dotenv
​
load_dotenv()
api_key = os.getenv("ARK_API_KEY")
​
# 系统提示词
SYSTEM_PROMPT = "你是一个语文老师"
​
​
api_key = os.environ["ARK_API_KEY"]
# 创建大模型
llm = ChatOpenAI(
    model="doubao-seed-1-6-251015",
    temperature=0,
    api_key=api_key,  
    base_url="https://ark.cn-beijing.volces.com/api/v3",  
)
​
#创建Agent 只有大模型和 系统提示词
agent = create_agent(
    model=llm,             
    system_prompt=SYSTEM_PROMPT,  
)
​
response = agent.invoke(
    {"messages": [{"role": "user", "content": "你是谁?"}]},
)
​
print(json.dumps(response, indent=2, ensure_ascii=False, default=lambda x: str(x)))
返回response
复制代码
{
  "messages": [
    {
      "content": "你是谁?",
      "additional_kwargs": {},
      "response_metadata": {},
      "id": "8db61c08-4444-403a-968e-4093a95edeb0"
    },
    {
      "content": "我是你的语文老师呀~无论是字词含义的解析、经典文章的赏析、作文写作的指导,还是文学常识的科普,都可以随时和我交流探讨 哦!",
      "additional_kwargs": {
        "refusal": null
      },
      "response_metadata": {
        "token_usage": {
          "completion_tokens": 177,
          "prompt_tokens": 45,
          "total_tokens": 222,
          "completion_tokens_details": {
            "accepted_prediction_tokens": null,
            "audio_tokens": null,
            "reasoning_tokens": 139,
            "rejected_prediction_tokens": null
          },
          "prompt_tokens_details": {
            "audio_tokens": null,
            "cached_tokens": 0
          }
        },
        "model_provider": "openai",
        "model_name": "doubao-seed-1-6-251015",
        "system_fingerprint": null,
        "id": "021779109178404d124c43c387314b34f4e9e457720e339368e9c",
        "service_tier": "default",
        "finish_reason": "stop",
        "logprobs": null
      },
      "id": "lc_run--019e3b2b-abd8-74e2-8f54-5d4275d36f39-0",
      "tool_calls": [],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "input_tokens": 45,
        "output_tokens": 177,
        "total_tokens": 222,
        "input_token_details": {
          "cache_read": 0
        },
        "output_token_details": {
          "reasoning": 139
        }
      }
    }
  ]
}
response的各个参数
message (基础识别属性 )

决定了这条信息是谁发的, 怎么找到这条信息

  • content : 核心内容 , 是模型真正说出来的话

  • id : 唯一标识符, 为每个消息生成id, 用来在复杂图流程中(LangGraph) 中定位

  • type : 消息类型 , human 代表用户 , ai代表 ai

  • additional_kwargs : 额外参数, 各家厂家都例如 openai都有自己的一套标准,不一定完完全全用langchain的,所以这里就放langchain中没有的参数

usage_metadata (消耗统计属性 )

记录对话消耗的资源

  • input_tokens : 输入Token数

  • output_tokens : 输出token数

  • total_tokens : 总消耗token数

  • input_token_details : 输入明细

    cache_read : 是否读取了之前的缓存(读取缓存token会少)

  • output_token_details : 输出明细

    reasoning : 推理token, 类似于豆包,DeepSeek都支持思考模式, 模型的思考过程是不展示在正文中的,但是也消耗token

response_metadata (响应详情属性)

这是由大模型供应商返回的原始信息"快照"。

复制代码
"model_provider": "openai",            -->模型供应商 , 虽然用的是豆包,但用的是openai的标准
"model_name": "doubao-seed-1-6-251015", -->具体模型型号
"id": "021779109178404d124c43c387314b34f4e9e457720e339368e9c", --> 请求id,方便后续查看日志
"service_tier": "default",       --> 服务层级,default表示标准服务速度
"finish_reason": "stop",      --> 结束原因,stop是理想结束状态,代表模型完整的生成
"logprobs": null              --> 对数概率,代表模型返回回答的信心指数(相当于人说话时,心里默念:
这句话我百分百确定 / 大概七成把握 / 随口乱说logprobs 就是把这份内心把握值记录下来。)
其他核心属性
复制代码
"tool_calls":     --> 工具调用指令, 如果Agent觉得需要查天气,那么这里会放查天气的tool
"invalid_tool_calls":  --> 非法工具调用,如果模型调用工具错误了,这里会记录错误
refusal : 拒绝回复 , 例如:我想造一个炸弹,这种模型会拒绝回复,在这里记录拒绝原因 
例子2 : 动态选择大模型
复制代码
# 模型调度中间件:根据对话轮次自动切换模型
@wrap_model_call
def auto_switch_model(req: ModelRequest, execute_func) -> ModelResponse:
    # 打印请求详情日志
    req_info = vars(req)
    print(json.dumps(req_info, ensure_ascii=False, indent=2, default=str))
    print("====================对话分割线====================")
​
    chat_history = req.messages
    chat_round = len(chat_history)
    print(f"当前对话累计轮次:{chat_round}")
​
    # 核心判断逻辑不变:超过4轮用深度模型,否则轻量模型
    if chat_round > 4:
        print("【调度】对话偏长,启用深度推理模型")
        target_llm = deep_reason_model
    else:
        print("【调度】简短闲聊,启用轻量化对话模型")
        target_llm = light_chat_model
​
    # 覆写请求指定模型并放行
    return execute_func(req.override(model=target_llm))
request里包含哪些内容
  1. Message : 完整的消息列表 包括 human的 , ai的

  2. model : 原本使用的大模型

  3. tools : 工具列表

  4. state : 状态 , 例如 用户 id , 当前处理的步骤,临时变量等

例子3 : 动态选择提示词

可以用来遇到不同的用户身份

复制代码
​
​
# ====================== 动态提示词中间件 ======================
@dynamic_prompt
def identity_based_prompt(req: ModelRequest) -> str:
​
    # 打印请求详情(保留调试逻辑)
    req_detail = vars(req)
    print(json.dumps(req_detail, indent=2, ensure_ascii=False, default=lambda x: str(x)))
    print("----------------------------------\n")
​
    # 从运行时上下文获取用户身份
    runtime_ctx = getattr(req.runtime, "context", {})
    current_identity = runtime_ctx.get("user_identity", "default")
​
    # 基础系统提示
    base_intro = "你是智能助手阿泽。"
​
    # 身份判断逻辑(完全不变)
    if current_identity == "expert":
        print("--- [动态切换] 当前身份:专业模式 ---")
        return f"{base_intro} 你现在是资深技术专家,回答专业、严谨、有深度。"
    
    elif current_identity == "beginner":
        print("--- [动态切换] 当前身份:入门模式 ---")
        return f"{base_intro} 你现在是耐心导师,用最简单通俗的语言解释问题。"
​
    return base_intro
​
​
​
​
# ====================== 创建智能体 ======================
ai_assistant = create_agent(
    model=ai_model,
    middleware=[identity_based_prompt],
    context_schema=ChatContext
)
​
​
# ====================== 上下文结构定义 ======================
class ChatContext(TypedDict):
    user_identity: str
    
# ====================== 测试运行 ======================
if __name__ == "__main__":
    print("\n--- 测试 1:专业模式 ---")
    res_pro = ai_assistant.invoke(
        {"messages": [{"role": "user", "content": "你是谁"}]},
        context={"user_identity": "expert"}
    )
    print(f"专业模式回答:{res_pro['messages'][-1].content}")
  • context

作用:传参、传身份、传场景、传配置 →

你传给 Agent 的 "额外信息" ,不是用户问题,不是对话历史,让 Agent 知道当前用户是谁、处于什么场景、该用什么语气回答。)

  • context_schema

作用:定义 context 的格式 → 让代码更安全、更规范 (context 的格式约束 / 类型定义。)

结构化输出

结构化输出让 Agent 返回 我们指定格式 的数据 , 这样就不需要再经过解析就能拿到 JSON , Pydantic 或者 数据类形式的数据

复制代码
​
# ====================== 定义结构化输出格式 ======================
class PersonInfo(BaseModel):
    """用于存储用户个人信息的数据结构"""
    username: str = Field(description="用户的真实姓名")
    mail_address: str = Field(description="用户的电子邮箱")
    mobile_number: str = Field(description="用户的手机号码")
​
# ====================== 创建结构化提取智能体 ======================
info_extractor = create_agent(
    model=ai_llm,
    system_prompt="你是专业信息提取员,能从任意文本中精准提取用户信息并按格式输出。",
    response_format=ToolStrategy(PersonInfo)
)
​
# ====================== 执行提取任务 ======================
input_content = "请提取信息:李四,邮箱是 lisi@demo.com,联系电话是 13912345678"
print(f"--- 待提取文本内容 ---\n{input_content}\n")
​
# 调用智能体
response = info_extractor.invoke({
    "messages": [{"role": "user", "content": input_content}]
})
响应格式 (Response Format)
复制代码
​
response_format=ToolStrategy(PersonInfo)
​
通过ToolStrategy 设置返回格式为 PersonInfo类 , 也就是
    username: str = Field(description="用户的真实姓名")
    mail_address: str = Field(description="用户的电子邮箱")
    mobile_number: str = Field(description="用户的手机号码")
​
最后输出==> 
{'mail_address': 'lisi@demo.com',
 'mobile_number': '13912345678',
 'username': '李四'}

流式输出

LangChain 实现了一个流式传输系统来提供实时更新。 不需要等完整的响应构造完,就可以渐进式的显示输出,能很好的提高用户体验。

复制代码
​
# 1. 系统提示词
SYSTEM_PROMPT = "你是专业AI助手,回答清晰、简洁、有条理,不啰嗦"
​
# 2. 用户问题
USER_QUESTION = "请简单介绍一下人工智能的日常应用场景,50字以内."
​
​
​
# 创建智能体
agent = create_agent(
    model=llm,
    system_prompt=SYSTEM_PROMPT,
)
​
# ===================== 1. 完整整体输出(一次性,不流式) =====================
print("\n===== 完整整体响应 =====\n")
full_response = llm.invoke(USER_QUESTION)  # 直接用 llm 调用,不卡 agent
print(full_response.content)
​
# ===================== 2. 流式输出(保留你原来的逻辑) =====================
print("\n\n===== 流式分段响应 =====\n")
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="messages"
):
    print("----------------------------------\n")
    print(json.dumps(chunk, indent=2, ensure_ascii=False, default=lambda x: str(x)))
​
​
流式输出模式
values

: 返回完整的State对象(整个对话),是步骤级的,用于 状态追踪,持久化快照

复制代码
=====> stream_mode="values"
​
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="values"
):
    print("----------------------------------\n")
    print(json.dumps(chunk, indent=2, ensure_ascii=False, default=lambda x: str(x)))
​
==================
{
  "messages": [
    "content='请简单介绍一下人工智能的日常应用场景,50字以内.' additional_kwargs={} response_metadata={} id='804cfb13-c11b-42a3-a307-5de220b401c0'"
  ]
}
----------------------------------
​
{
  "messages": [
    "content='请简单介绍一下人工智能的日常应用场景,50字以内.' additional_kwargs={} response_metadata={} id='804cfb13-c11b-42a3-a307-5de220b401c0'",
    "content='语音助手、电商推荐、人脸识别支付、智能导航、AI客服等是人工智能在日常生活中的常见应用场景。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 68, 'total_tokens': 173, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 81, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'doubao-seed-1-6-251015', 'system_fingerprint': None, 'id': '021779239369242b47c0529c98d6cfd28de18ec7839083b7fbaeb', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019e42ee-3a52-7253-9cf1-184822ec65cc-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 68, 'output_tokens': 105, 'total_tokens': 173, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 81}}"
  ]
}
updates

: 返回当前节点产生的增量, 是节点级的, 用于监控节点流水线执行

复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="updates"
):
==============
​
===== 流式分段响应 =====
​
----------------------------------
​
{
  "model": {
{
  "model": {
  "model": {
    "messages": [
      "content='AI日常应用包括语音助手、电商推荐、地图导航、人脸识别支付、智能客服等场景。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 134, 'prompt_tokens': 68, 'total_tokens': 202, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 113, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'doubao-seed-1-6-251015', 'system_fingerprint': None, 'id': '021779239610884c2ac42b5b86809233f1159b66476f323e06698', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019e42f1-ea30-7de3-9aac-abbca9e41630-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 68, 'output_tokens': 134, 'total_tokens': 202, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 113}}"
    ]
  }
}
messages

: 返回消息块(token) , 是token级(一个词一个词的), 用于实时对话,UI渲染

复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="messages"
):
    -------------------------
    
[
  "content='等' additional_kwargs={} response_metadata={'model_provider': 'openai'} id='lc_run--019e42f3-2d75-78f0-b2ea-2663d81b7b9d' tool_calls=[] invalid_tool_calls=[] tool_call_chunks=[]",
  {
    "ls_integration": "langchain_chat_model",
    "langgraph_step": 1,
    "langgraph_node": "model",
    "langgraph_triggers": [
      "branch:to:model"
    ],
    "langgraph_path": [
      "__pregel_pull",
      "model"
    ],
    "langgraph_checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "ls_provider": "openai",
    "ls_model_name": "doubao-seed-1-6-251015",
    "ls_model_type": "chat",
    "ls_temperature": 0.0
  }
]
----------------------------------
​
[
  "content='是' additional_kwargs={} response_metadata={'model_provider': 'openai'} id='lc_run--019e42f3-2d75-78f0-b2ea-2663d81b7b9d' tool_calls=[] invalid_tool_calls=[] tool_call_chunks=[]",
  {
    "ls_integration": "langchain_chat_model",
    "langgraph_step": 1,
    "langgraph_node": "model",
    "langgraph_triggers": [
      "branch:to:model"
    ],
    "langgraph_path": [
      "__pregel_pull",
      "model"
    ],
    "langgraph_checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "ls_provider": "openai",
    "ls_model_name": "doubao-seed-1-6-251015",
    "ls_model_type": "chat",
    "ls_temperature": 0.0
  }
]
----------------------------------
​
[
  "content='人工智能' additional_kwargs={} response_metadata={'model_provider': 'openai'} id='lc_run--019e42f3-2d75-78f0-b2ea-2663d81b7b9d' tool_calls=[] invalid_tool_calls=[] tool_call_chunks=[]",
  {
    "ls_integration": "langchain_chat_model",
    "langgraph_step": 1,
    "langgraph_node": "model",
    "langgraph_triggers": [
      "branch:to:model"
    ],
    "langgraph_path": [
      "__pregel_pull",
      "model"
    ],
    "langgraph_checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "ls_provider": "openai",
    "ls_model_name": "doubao-seed-1-6-251015",
    "ls_model_type": "chat",
    "ls_temperature": 0.0
  }
]
----------------------------------
​
[
  "content='的' additional_kwargs={} response_metadata={'model_provider': 'openai'} id='lc_run--019e42f3-2d75-78f0-b2ea-2663d81b7b9d' tool_calls=[] invalid_tool_calls=[] tool_call_chunks=[]",
  {
    "ls_integration": "langchain_chat_model",
    "langgraph_step": 1,
    "langgraph_node": "model",
    "langgraph_triggers": [
      "branch:to:model"
    ],
    "langgraph_path": [
      "__pregel_pull",
      "model"
    ],
    "langgraph_checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "checkpoint_ns": "model:25fb39ad-5029-0f46-c4ff-3d9a41a1ede4",
    "ls_provider": "openai",
    "ls_model_name": "doubao-seed-1-6-251015",
    "ls_model_type": "chat",
    "ls_temperature": 0.0
  }
]
----------------------------------
​
debug

: 返回完整的执行链路日志, 非常详细,能看到执行过程中是否正常执行,用于性能分析和调试

复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="debug"
):
===============
​
{
  "step": 1,
  "timestamp": "2026-05-20T01:17:10.162649+00:00",
  "type": "task",
  "payload": {
    "id": "c76d884a-1ad5-fd91-aa32-3927eeb6a4ee",
    "name": "model",
    "input": {
      "messages": [
        "content='请简单介绍一下人工智能的日常应用场景,50字以内.' additional_kwargs={} response_metadata={} id='37e0b1ac-53c5-4a0f-80fb-c4745c622d10'"
      ]
    },
    "triggers": [
      "branch:to:model"
    ]
  }
}
-----
​
  "step": 1,
  "timestamp": "2026-05-20T01:17:14.938457+00:00",
  "type": "task_result",
  "payload": {
    "id": "c76d884a-1ad5-fd91-aa32-3927eeb6a4ee",
    "name": "model",
    "print("\n\n===== 流式分段响应 =====\n")
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": USER_QUESTION}]}, 
    stream_mode="debug"
):": null,
    "result": {
      "messages": [
        "content='语音助手(如Siri)、智能推荐(电商/视频)、导航、人脸识别(解锁/支付)、智能家电等是AI日常常见应用。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 127, 'prompt_tokens': 68, 'total_tokens': 195, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 93, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'doubao-seed-1-6-251015', 'system_fingerprint': None, 'id': '0217792398301087d9c2387c122bdc91a342e02a9471447b7bafc', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019e42f5-4293-70c1-b8a6-527d582afd96-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 68, 'output_tokens': 127, 'total_tokens': 195, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 93}}"
      ]
    },
    "interrupts": []
  }
}
​

工具

模型本身闭源的QA,所以有些事情例如 : 查今天天气,查热点新闻等这些大模型是做不到的。工具就类似于一些大模型可以用的组件,通过这些组件模型就可以做到一些做不到的事情。

(例如: 让模型帮我看看今天北京的天气,那么就可以创建一个可以用来查天气的工具,那么模型就可以通过这个工具拿到天气情况,再给我们返回结果)

通过@tool定义工具
复制代码
@tool
def get_current_city(runtime: ToolRuntime[Context]) -> str:
    """根据用户标识获取用户常用居住城市"""
    user_id = runtime.context.user_id
    return "杭州" if user_id == "1" else "武汉"
​
创建Agent时把 工具传给tools
复制代码
# Create agent
agent = create_agent(
    model=llm,
    system_prompt=SYSTEM_PROMPT,
    tools=[get_current_city, get_traffic_info],
    context_schema=Context,
)

在执行时模型就可以根据问题,来调用需要的工具获取答案

复制代码
 Define system prompt
SYSTEM_PROMPT = """你是专业出行规划助手,语言通俗易懂,善于贴心规划出行方案。
​
你可调用两个实用工具:
​
- get_traffic_info:查询指定城市出行交通状况
- get_current_city:自动获取用户常驻城市
​
用户询问出行相关问题时,优先确认城市信息,未明确地点则自动调取用户所在城市再作答。"""
​
# Define context schema
@dataclass
class Context:
    """Custom runtime context schema."""
    user_id: str
​
# Define tools
@tool
def get_traffic_info(city: str) -> str:
    """查询指定城市实时出行交通情况"""
    return f"{city}当前路况顺畅,公交地铁出行便捷,适合短途游玩出行!"
​
@tool
def get_current_city(runtime: ToolRuntime[Context]) -> str:
    """根据用户标识获取用户常用居住城市"""
    user_id = runtime.context.user_id
    return "杭州" if user_id == "1" else "武汉"
​
​
​
# Create agent
agent = create_agent(
    model=llm,
    system_prompt=SYSTEM_PROMPT,
    tools=[get_current_city, get_traffic_info],
    context_schema=Context,
)
​
# Run agent
config = {"configurable": {"thread_id": "1"}}
​
response = agent.invoke(
    {"messages": [{"role": "user", "content": "最近适合出门游玩吗"}]},
    config=config,
    context=Context(user_id="1")
)

message消息

消息是 LangChain 中模型上下文的基本单位。它们代表模型的输入和输出,携带内容和元数据,用于在与 LLM 交互时表示对话状态。

消息是包含以下内容的对象:

  • 角色 - 标识消息类型(例如 systemuser

  • 内容 - 表示消息的实际内容(例如文本、图像、音频、文档等)

  • 元数据 - 可选字段,例如响应信息、消息 ID 和令牌使用情况

消息对象
复制代码
1.系统消息
SystemMessage(content=SYSTEM_PROMPT),
2. 人的消息
HumanMessage(content="Can you help me learn Python?"),
3. AI消息
AIMessage("I'd be happy to help you with that question!")
4. 工具消息
ToolMessage

使用消息的最简单方式是创建消息对象,并在调用时将它们传递给模型。

复制代码
SYSTEM_PROMPT = "你是专业编程答疑助手,简洁清晰、一步到位解答问题"
​
​
ai_msg = AIMessage(content="Sure! I can help you with any programming problems.")
​
messages = [
    SystemMessage(content=SYSTEM_PROMPT),
    HumanMessage(content="Can you help me learn Python?"),
    ai_msg,
    HumanMessage(content="Perfect! Please explain what a list is."),
]
​
for msg in messages:
    print(f"{msg.__class__.__name__}: {msg.content}")
​
response = llm.invoke(messages)
​
print("="*50)
print(json.dumps(response, indent=2, ensure_ascii=False, default=lambda x: str(x)))

通过dict的方式创建消息列表 (但是这种方式最终也是会转成 上面的消息对象格式)

复制代码
messages = [
    {"role": "system", "content": "You are a poetry expert"},
    {"role": "user", "content": "Write a haiku about spring"},
    {"role": "assistant", "content": "Cherry blossoms bloom..."}
]
response = model.invoke(messages)

llm模型

llm是强大的 AI 工具,能够像人类一样理解和生成文本。它们用途广泛,可用于撰写内容、翻译语言、总结信息和回答问题,而无需针对每项任务进行专门训练

除了文本生成之外,许多模型还支持:

  • 工具调用 - 调用外部工具(如数据库查询或 API 调用)并将结果用于响应中。

  • 结构化输出 - 模型的响应被限制为遵循定义的格式。

  • 多模态 - 处理和返回非文本数据,如图像、音频和视频。

  • 推理 - 模型执行多步推理以得出结论。

模型是 Agent代理 的推理引擎。它们驱动代理的决策过程,决定调用哪些工具、如何解释结果以及何时提供最终答案。

llm的基本用法
复制代码
import os
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
​
load_dotenv()
api_key = os.getenv("ARK_API_KEY")
​
# 初始化模型
llm = ChatOpenAI(
    model="doubao-seed-1-8-251228",
    temperature=0,
    api_key=api_key,
    base_url="https://ark.cn-beijing.volces.com/api/v3",
)
​
# 基础调用
res = llm.invoke("简单介绍一下Python语言")
​
# 打印结果
print(res.content)

llm和Agent的区别

llm
复制代码
llm.invoke(message) ==>生成单词的 LLm 响应
  • 输入 : massage列表 或 prompt

  • 输出 : AIMessage (单条响应)

  • 功能 :

    • 简单对话生成

    • 支持消息历史

    • 无工具调用,无循环,无状态管理

agent
复制代码
agent.invoke({"messages": [...]}) → 可以多步工具调用 + 最终回答
  • 输入 : 消息列表

  • 输出 : 完整的对话历史

  • 功能 :

    • 可以工具调用

    • 可以ReAct (思考(Thought) → 行动(Action) → 观察(Observation) 循环,直到解完题。)

    • 支持状态管理(LangGraph)

    • 可以持久化、流式、结构化

    • 支持 HITL (AI 干活,关键步骤停下来等人审核 / 确认 / 修正,再继续)

短期记忆

短期记忆 让您的应用程序能够记住单个线程对话中的先前交互。

通过 checkpointer=InMemorySaver(), 让Agent带有短期记忆,就可以通过config获取state

state = Agent 的记忆 + 当前进度 + 全部上下文

复制代码
@dataclass
class Context:
    user_id: str
​
@tool
def get_weather_info(runtime: ToolRuntime[Context], city: str = None) -> str:
    """获取天气信息"""
    if not city:
        user_id = runtime.context.user_id
        city = "佛罗里达" if user_id == "1" else "旧金山"
    return f"{city} 总是阳光明媚!"
​
llm = ChatOpenAI(
    model="doubao-seed-1-8-251228",
    temperature=0,
    api_key=os.getenv("ARK_API_KEY"),
    base_url="https://ark.cn-beijing.volces.com/api/v3",
)
​
# 核心:带短期记忆的 Agent
agent = create_agent(
    model=llm,
    system_prompt="你是专业气象预报员,使用工具获取天气。",
    tools=[get_weather_info],
    context_schema=Context,
    checkpointer=InMemorySaver(),
)
​
# 第一次对话
config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
    {"messages": [{"role": "user", "content": "今天什么天气?"}]},
    config=config,
    context=Context(user_id="1")
)
​
# 查看记忆(短期记忆内容)
state = agent.get_state(config)
print("最终回答:", response["messages"][-1].content)
print("\n记忆中存储的消息:")
for i, msg in enumerate(state.values["messages"]):
    print(f"[{i}] {msg.content[:50]}...")
state
复制代码
state = agent.get_state(config) ==># 使用 config 获取当前 thread_id 的最新状态

state.values : 核心数据,主要包含了mseeage列表(对话历史)

主要作用

  • 上下文记忆 : 让ai知道前面说了什么,工具的返回结果是什么

  • 数据传递 : 在state中定义的其他变量

  • 业务逻辑

state.next (流程控制)

是一个元组,记录了图接下来要执行的节点名称 ,如果下一个节点是工具那么就是 ('tool',)

作用

  • 断点续传 : 如果agent流程需要'人工审批',那么程序就会停下来,next会指向审批节点,直到你允许他继续

  • 状态监控 : 通过他你可以判断 ai当前的状态是在 '思考'(agent节点),还是在调用工具(tools节点)

state.metadata

状态本身的数据

包含

  • source : 状态来源, 告诉你当前的快照是怎么产生的 ,(loop:表示自动运行,input:表示初始输入,update:表示开发者收手动调用修改了数据)

  • step : 步数,记录当前是第几次节点转换

  • parents : 父级状态映射(这个字段用于记录"当前状态是从哪个状态演变而来的"。)

作用

  • 通过setp判断模型是不是陷入死循环

  • 在复杂的分支流程中,parents 用于最终逻辑是怎么合并的

state.config

用于再次定位掉 当前这个状态的 Id

包含:thread_id 和唯一的 Checkpoint_id

作用:

  • 如果保存了10个步骤的快照,那么可以通过checkpoint_id 调用 agent.invoke ,让ai回到那个"时间段"

  • 多线程隔离,确保不同用户对话不会搞混

中间件

中间件负责Agent流程中,拦截、过滤、转发、统一处理流程 ,不直接干活,只管流程管控,可以更精细地控制代理(Agent)内部的执行流程

SummarizationMiddleware 自动压缩上下文
复制代码
1. 在创建agent时通过 middleware传中间件
2. SummarizationMiddleware : 当token到达4000时,保留最后20调数据,20条前面的都给压缩成摘要
​
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
​
​
agent = create_agent(
    model="openai:gpt-4o",
    tools=[weather_tool, calculator_tool],
    middleware=[
        SummarizationMiddleware(
            model="openai:gpt-4o-mini",
            max_tokens_before_summary=4000,  # 在 4000 个 token 时触发摘要
            messages_to_keep=20,  # 摘要后保留最近 20 条消息
            summary_prompt="Custom prompt for summarization...",  # 可选
        ),
    ],
)
ModelCallLimitMiddleware 模型限制

给 AI 模型调用次数 "上锁" ,限制模型最多推理多少次,防止 AI 无限循环、死循环、疯狂调用、浪费 token

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
​
​
agent = create_agent(
    model="openai:gpt-4o",
    tools=[...],
    middleware=[
        ModelCallLimitMiddleware(
            thread_limit=10,  # 每个线程(跨多次运行)最多 10 次调用
            run_limit=5,  # 每次运行(单次调用)最多 5 次调用
            exit_behavior="end",  # 或者 "error" 以引发异常
        ),
    ],
)
ToolCallLimitMiddleware 工具限制

限制智能体调用工具的最大次数,防止频繁反复调用工具、死循环调用,节约资源 + 控制流程

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ToolCallLimitMiddleware
​
​
# 限制所有工具调用
global_limiter = ToolCallLimitMiddleware(thread_limit=20, run_limit=10)
​
# 限制特定工具
search_limiter = ToolCallLimitMiddleware(
    tool_name="search", #要限制的特定工具。如果未提供,则限制适用于所有工具。
    thread_limit=5,  #线程中所有运行的最大工具调用次数。默认为无限制。
    run_limit=3,   #单次调用中最大工具调用次数。默认为无限制
)
​
agent = create_agent(
    model="openai:gpt-4o",
    tools=[...],
    middleware=[global_limiter, search_limiter],
)
ModelFallbackMiddleware模型错误兜底

主模型挂了、报错、超时、限流时,自动切换到备用模型继续运行,保证服务不中断。

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ModelFallbackMiddleware
​
agent = create_agent(
    model="gpt-4o",      #主模型
    tools=[],
    middleware=[
        ModelFallbackMiddleware(
            "gpt-4o-mini",        # 备用模型
            "claude-3-5-sonnet-20241022",
        ),
    ],
)
PIIMiddleware 敏感信息屏蔽

自动识别并脱敏屏蔽隐私敏感信息,防止手机号、身份证、住址等私密数据泄露

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
​
agent = create_agent(
    model="gpt-4o",
    tools=[],
    middleware=[         #邮箱 和信用卡 数据脱敏
        PIIMiddleware("email", strategy="redact", apply_to_input=True),
        PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
    ],
)
LLMToolSelectorMiddleware 模型选择工具

如果有多个工具时,根据用户提问语义,自动筛选匹配的工具,只把相关工具下发给模型,过滤无关工具。

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware
​
agent = create_agent(
    model="gpt-4o",         #主模型
    tools=[tool1, tool2, tool3, tool4, tool5, ...],
    middleware=[
    LLMToolSelectorMiddleware(
     model="gpt-4o-mini",    # 用轻量模型专门负责选工具(省钱、快)
     max_tools=3,            # 最多只选 3 个工具
     always_include=["search"],  # 不管什么情况,都带上 search 工具
        ),
    ],
)
ToolRetryMiddleware工具重试

工具调用失败时,自动重试 N 次,避免因为网络波动、接口超时、临时故障导致任务失败。

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ToolRetryMiddleware
​
agent = create_agent(
    model="gpt-4o",
    tools=[search_tool, database_tool],
    middleware=[
        ToolRetryMiddleware(
           max_retries=3,      # 最多重试 3 次
           backoff_factor=2.0, # 每次重试等待时间翻倍
           initial_delay=1.0,  # 第一次重试等 1 秒
        ),
    ],
)
ModelRetryMiddleware模型重试

模型调用失败时,自动重试,解决模型临时报错、超时、限流问题,让 AI 更稳。

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ModelRetryMiddleware
​
agent = create_agent(
    model="gpt-4o",
    tools=[search_tool, database_tool],
    middleware=[
        ModelRetryMiddleware(
            max_retries=3,  #模型调用失败,最多自动重试 3 次
            backoff_factor=2.0, #指数退避(等待时间越来越长)
            initial_delay=1.0,  #第一次失败后 等 1 秒 再重试
        ),
    ],
)
ContextEditingMiddleware清除tools上下文

允许你在对话过程中,手动修改、插入、删除 AI 记忆里的消息(上下文),相当于直接编辑 AI 的大脑记忆。

ContextEditingMiddleware + ClearToolUsesEdit = 自动清理工具调用记录

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ContextEditingMiddleware, ClearToolUsesEdit
​
agent = create_agent(
    model="gpt-4o",
    tools=[],
    middleware=[
        ContextEditingMiddleware(
            edits=[
                ClearToolUsesEdit(
                    trigger=100000,  # 触发清理的长度阈值,把旧的工具调用消息删掉,防止上下文太长、爆 token
                    keep=3,    # 保留最近3条工具记录
                ),
            ],
        ),
    ],
)
ShellToolMiddleware 命令

ShellToolMiddleware = 给 AI 命令行执行加 "安全沙箱"

限制 AI 只能在指定文件夹里操作,不能乱跑、不能乱删、不能破坏系统。

复制代码
当我的agent tools能力不够的时候,它自己写一个python脚本,脚本写在workspace中,理论上只要权限够,它爱干啥干啥。
​
from langchain.agents import create_agent
from langchain.agents.middleware import (
    ShellToolMiddleware,
    HostExecutionPolicy,
)
​
agent = create_agent(
    model="gpt-4o",
    tools=[search_tool],
    middleware=[
        ShellToolMiddleware(
            workspace_root="/workspace",  #AI 只能在这个文件夹里干活!
            execution_policy=HostExecutionPolicy(), #允许在当前机器(本机)执行命令
        ),
    ],
)
FilesystemFileSearchMiddleware 文件搜索
复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import FilesystemFileSearchMiddleware
​
agent = create_agent(
    model="gpt-4o",
    tools=[],
    middleware=[
        FilesystemFileSearchMiddleware(
            root_path="/workspace",      #这个目录下放了很多文件,可以让agent在里面搜索文件
            use_ripgrep=True,
        ),
    ],
)

Agent生命周期

Agent运行的固定流程
  1. 代理开始

  2. 准备调用模型

  3. 模型正在调用

  4. 模型调用完成

  5. 工具正在调用

  6. 代理结束

这就是 Agent 的生命周期

中间件是怎么嵌入到Agent的生命周期的

插在生命周期各个阶段的 "钩子函数"

  • @before_agent → 代理启动前

  • @before_model → 模型调用前

  • @wrap_model_call → 包裹模型调用(进入+退出)

  • @after_model → 模型调用后

  • @wrap_tool_call → 包裹工具调用(进入+退出)

  • @after_agent → 代理结束后

1. before_agent

执行时机 :整个 Agent 流程正式启动最开始

作用:

  • 初始化全局上下文、用户信息、环境变量
  • 权限校验、登录鉴权、会话初始化
  • 前置统一日志、全局参数注入
  • 提前拦截非法请求,直接终止流程

场景:加载用户身份、初始化会话 ID、全局配置初始化

2. before_model

执行时机 :准备调用大模型之前一刻

作用

  • 预处理对话上下文、裁剪冗余消息

  • 动态修改本轮入参、临时追加提示词

  • 提前做隐私脱敏、敏感词过滤

  • 支持跳转流程(can_jump_to 直接跳到结束)

场景:精简对话历史、临时补全指令、前置风控

3. wrap_model_call

执行时机环绕包裹整个模型调用全过程(进入 + 执行 + 退出)

作用

  • 切面监听:调用前埋点、调用后统计

  • 计算 LLM 响应耗时、监控调用耗时

  • 拦截模型请求、篡改请求入参

  • 拦截模型返回结果、修改输出内容

  • 实现模型重试、熔断、降级、备用模型切换

4. after_model

执行时机 :大模型返回结果之后,还没走工具逻辑

作用

  • 校验模型输出是否合规、过滤幻觉内容

  • 修正 AI 错误回答、统一格式化输出

  • 提取模型意图、提前解析工具调用参数

  • 统计模型输出长度、缓存本轮结果

场景:结果校验、内容修正、意图解析

5. wrap_tool_call

执行时机环绕包裹所有工具调用全过程

作用

  • 工具调用安全审计、黑白名单拦截

  • 统计工具执行耗时、记录调用日志

  • 工具入参校验、出参统一封装

  • 捕获工具异常、自动重试、异常兜底

  • 禁止高危工具执行、权限管控

特点:管控所有 Tool,业务安全、限流、重试全靠它

6. after_agent

执行时机 :Agent 整轮对话 / 任务全部跑完结束

作用

  • 会话收尾、清理临时缓存与临时数据

  • 汇总全流程日志、上报运行指标

  • 持久化对话记忆、归档聊天记录

  • 释放资源、关闭连接、结束会话

场景:数据落库、日志上报、资源回收、会话收尾

@dynamic_prompt动态修改提示词

动态生成 / 修改系统提示词(system prompt)

根据当前对话状态、用户信息、上下文实时自动改 prompt,而不是写死固定的。

普通的 system prompt 是写死的

复制代码
system_prompt="你是一个助手"  # 永远不变

但真实场景需要灵活变化

  • 用户是 VIP → 提示词变成 "你是 VIP 专属助手"

  • 时间是晚上 → 提示词变成 "现在是晚上,说话温柔点"

  • 用户在查订单 → 提示词自动加上订单相关规则

  • 不同用户 → 不同语气、不同权限

@dynamic_prompt 就是干这个的:自动、实时改提示词。

复制代码
@dynamic_prompt的执行时机:
​
1.@before_agent /agent 启动前
​
2.自动生成最新的 system prompt 再进入模型。
复制代码
1. 根据时间和用户id动态修改提示词
​
@dynamic_prompt
def make_dynamic_prompt(state: AgentState, runtime: ToolRuntime):
    user_id = runtime.context.user_id
    time_now = "晚上" if 18 < time.localtime().tm_hour else "白天"
​
    # 动态返回提示词!
    return f"""
    你是智能助手。
    当前用户ID:{user_id}
    当前时间:{time_now}
    请根据时间调整语气。
    """
​
2. 然后注册到 agent:
​
agent = create_agent(
    model=llm,
    middleware=[make_dynamic_prompt],  # 👈 加这里
)

MCP服务

MCP(模型上下文协议) 是 Anthropic 推出的开放标准协议 ,用来统一大模型(LLM)和外部世界(工具 / 数据 / 服务)的连接方式

复制代码
类比:电脑的USB接口,一套标准,即插即用。
​
核心目标:标准化、安全、可扩展 地让模型调用外部能力、读取外部数据。

我们就可以把tools换了个地方单独定义,做成MCP服务,这样很多ai都能直接调用

不同 LLM、不同框架,调用同一套工具,写法、格式、调用逻辑全都不一样,重复写适配代码累死。

所有工具统一打包做成 MCP 服务,对外只暴露一套标准接口。

这样不管用什么llm都可以直接调用MCP,就能调用所有工具。

复制代码
MCP = 工具统一中转站 / 通用接口层
所有工具只写一次,部署成 MCP Server
所有大模型、所有 AI 框架,统一一套调用方式拿工具
彻底解决:工具多、模型杂、适配麻烦
安装依赖
复制代码
pip install mcp langchain-mcp-adapters langchain-openai python-dotenv
创建本地mcp服务
复制代码
from mcp.server.fastmcp import FastMCP
​
# 1. 初始化 FastMCP
mcp = FastMCP("MyWeatherService")
​
​
# 2. 定义查天气工具
@mcp.tool()
def query_weather(city: str) -> str:
    """根据城市名称查询天气。"""
    city = city.strip()
​
    if "北京" in city:
        return "北京天气:晴,气温 25°C,微风。"
    elif "上海" in city:
        return "上海天气:阴,气温 22°C,局部有小雨。"
    elif "深圳" in city:
        return "深圳天气:多云,气温 28°C,体感较热。"
    else:
        return f"暂时查不到 {city} 的天气,可能那里是世外桃源吧。"
​
​
if __name__ == "__main__":
    # 直接运行后会启动 MCP 服务,便于被 LangChain 客户端通过 stdio 连接
    mcp.run(transport="stdio")
调用本地mcp服务
复制代码
import asyncio
import os
import sys
from pathlib import Path
​
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
​
load_dotenv()
​
​
async def main() -> None:
    # 1. 连接本地 MCP 服务
    client = MultiServerMCPClient(
        {
            "weather": {
                "command": sys.executable,
                "args": [str(Path(__file__).with_name("17.py"))],
                "transport": "stdio",
            }
        }
    )
​
    # 2. 从 MCP 服务加载工具
    tools = await client.get_tools()
​
    # 3. 初始化模型
    llm = ChatOpenAI(
        model="doubao-seed-1-8-251228",
        temperature=0,
        api_key=os.getenv("ARK_API_KEY"),
        base_url="https://ark.cn-beijing.volces.com/api/v3",
    )
​
    # 4. 创建 Agent
    agent = create_agent(
        model=llm,
        tools=tools,
        system_prompt="你是一个会调用天气工具的助手。",
    )
​
    # 5. 发起一次查询
    result = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "帮我查一下北京天气"}]}
    )
​
    print(result["messages"][-1].content)
​
​
if __name__ == "__main__":
    asyncio.run(main())
相关推荐
麻雀飞吧11 小时前
期货历史行情与实时数据一体化:主流平台维护负担对照
python
前端若水11 小时前
【无标题】
java·人工智能·python·机器学习
吃好睡好便好12 小时前
在Matlab中绘制阶梯图
开发语言·人工智能·学习·算法·机器学习·matlab
Restart-AHTCM12 小时前
LangChain学习之提示词模板 (Prompts) - 练习(2/8)
学习·langchain
databook12 小时前
填充与积累:积分与面积的可视化
python·数学·动效
YangYang9YangYan12 小时前
2026产品专员学习数据分析的价值与路径
学习·数据挖掘·数据分析
TechWayfarer12 小时前
AI大模型时代:IP数据云如何适配智能体场景需求
开发语言·人工智能·python·网络协议·tcp/ip·langchain