笔记转载自:L站
Agent基础入门实战教程(2):从 0 到 1 手写一个最小 Agent
Agent基础入门实战教程(4):LangChain聊天机器人小练习
Agent基础入门实战教程(5):图结构可视化与调试框架:LangGraph Studio
Agent基础入门实战教程(6):LangChain 中间件【做项目去了,暂停更新】
仅做个人学习收藏,如有侵权,请联系删除
Agent基础入门实战教程(1):初识Agent
1.1 Agent的定义
AI Agent(智能体) 是一个以大语言模型为核心决策模块,在给定目标、工具和约束条件下,能够读取上下文、规划下一步动作、调用外部工具,并根据执行结果持续调整的任务执行系统。
1.2 Agent的能力
从表格可以看出 Agent 更强调围绕目标进行多步执行,并能根据中间结果调整后续动作。他的价值在于:处理那些需要多步推理、工具协作、动态决策的复杂任务。但并不意味着所有场景下Agent 都是最优解,如你只想翻译文字,或者生成一些简单的内容,直接只用普通的LLM调用是更优的。
1.3 Agent的核心架构-TAO循环
(为了方便理解,我们可以把 Agent 的核心运行过程概括为 TAO 循环:Think、Act、Observe。)
Agent 内部运转的核心为 - TAO循环(Think:思考 → Act:行动 → Observe:观察)
Think(思考):LLM 作为"大脑",分析当前状态和用户目标,决定下一步行动。这一步可能包括:判断任务是否完成、确定需要调用的工具、规划执行顺序、评估风险等。
Act(行动):根据 Think 阶段的决策,调用相应的工具或生成回答。如果决定调用工具,就执行工具调用;如果判断任务已完成,就生成最终回答。
Observe(观察):收集 Act 阶段的结果——如果是工具调用,收集工具返回的数据;如果是生成回答,观察用户的反馈。将观察结果纳入上下文,为下一轮 Think 提供输入。
这三个环节形成一个闭环,循环往复直到任务完成。无论一个agent有多复杂,这个就是一个agent的内部核心运转流程。举例一个我们经常使用到的场景:使用codex 查询一个知识点:LangChain 最新版本的 Agent 怎么使用??
用户输入问题
LLM思考这个问题是否需要外部工具
需要调用工具查询最新文档
查询到的文档返回给LLM:本质是将用户输入+查询的结果 统一成一个上下文,在塞给LLM
LLM思考:不需要再调用工具了,可以直接输出
输出结果
TAO 循环的精妙之处在于它的自终止性——Agent 在每一轮的 Think 阶段都会判断"任务是否已经完成",如果完成就输出最终答案并退出循环,如果未完成就继续下一轮。这意味着 Agent 可以根据任务复杂度自动调整执行步数:简单任务一轮就结束,复杂任务可能需要五轮、十轮甚至更多。
1.4 新手常见问题 Q&A
Q1:Agent 是不是就是 ChatGPT?
不是。
ChatGPT 更像是一个可以对话的大语言模型应用,而 Agent 是一种围绕目标执行任务的系统设计方式。Agent 通常会在 LLM 的基础上接入工具、状态管理和执行循环,让它可以根据任务进展决定下一步做什么。
简单理解:
普通聊天:你问一句,它答一句
Agent:你给一个目标,它拆步骤、用工具、看结果,再继续执行
Q2:Agent 是不是一定要调用工具?
不一定。
如果任务很简单,Agent 可能不需要调用工具,直接回答就可以。但工具是 Agent 能力扩展的关键。没有工具的 Agent,能力基本只来自模型本身;接入工具后,它才能搜索资料、查询数据库、执行代码、读取文件、调用 API。
Q3:Agent 和工作流 Workflow 有什么区别?
Workflow:开发者提前规定好每一步
Agent:开发者提供目标、工具和约束,由模型动态决定下一步
Q4:Agent 是不是一定比普通 LLM 更强?
不是。
Agent 的优势是能处理复杂任务,但它也会带来额外成本,比如执行时间更长、调用费用更高、结果不稳定性增加、调试难度变大。
如果一个任务一次 LLM 调用就能完成,就没必要强行做成 Agent。
Q5:学习 Agent 需要先学很多框架吗?
不需要。
刚开始学习 Agent,最重要的是先理解核心思想:
目标 → 思考 → 行动 → 观察 → 再思考 → 最终回答
Q6:在企业项目中,Agent 和后端服务有什么区别?
简单理解:
后端服务:负责确定性的业务逻辑
Agent:负责不确定性较高的智能决策和任务执行
在真实企业项目中,Agent 往往是接入后端系统的一部分,而不是单独替代后端。后端提供接口、权限、数据和业务规则,Agent 在这些约束下调用工具完成任务。
企业里的合理架构通常不是“用 Agent 替代后端”,而是:
前端 → 后端服务 → Agent → 工具/API/数据库
如:用户说:帮我查一下这个客户最近三个月的订单异常情况
后端负责:
1. 校验用户是否有权限查看该客户数据
2. 提供订单查询接口
3. 提供异常订单规则
4. 记录操作日志
Agent 负责:
1. 理解用户想查什么
2. 决定需要调用哪些接口
3. 分析接口返回的数据
4. 总结异常原因并生成自然语言结果
到这里,我们已经基本理解了什么是 Agent:它是一个围绕目标进行思考、行动、观察,并不断迭代的任务执行系统。
Agent基础入门实战教程(2):从 0 到 1 手写一个最小 Agent
2.1 环境准备
创建虚拟环境:
python -m venv .venv
激活虚拟环境:
.venv/Scripts/activate
激活成功后,终端前面一般会出现:(.venv)
安装依赖
pip install openai python-dotenv requests notebook jupyterlab ipykernel
偷懒做法:一键安装环境提示词
请帮我在当前项目目录中搭建 Python + Jupyter Notebook 的学习环境,要求如下:
1. 检查当前电脑是否安装 Python,并确认版本是否为 3.10 或以上。
2. 在当前项目根目录创建 Python 虚拟环境,目录名为 .venv。
3. 激活 .venv 虚拟环境。
4. 在虚拟环境中安装以下依赖:
- openai
- python-dotenv
- requests
- notebook
- jupyterlab
- ipykernel
5. 将当前 .venv 注册为 Jupyter Notebook 内核,内核显示名称为 ".venv"。
6. 在项目根目录创建 .env.example 文件,内容如下:
OPENAI_API_KEY=你的_api_key
OPENAI_BASE_URL=BASE_URL
MODEL_NAME=模型名称
7. 不要创建真实 .env 文件,也不要替我填写 API Key。
8. 安装完成后,请告诉我:
- Python 版本
- pip 版本
- Jupyter 是否安装成功
- Notebook 内核是否注册成功
- 下一步我应该如何打开 .ipynb 文件
2.2 第一次调用大模型 API
# 导入 os 模块,用于读取环境变量
import os
# 导入 dotenv,用于从 .env 文件中加载配置
from dotenv import load_dotenv
# 导入 OpenAI 客户端
from openai import OpenAI
# 读取项目根目录下的 .env 文件
load_dotenv(override=True)
# 创建大模型客户端
# api_key:你的 API Key,用于身份认证
# base_url:模型服务地址
client = OpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL")
)
# 调用大模型的 Chat Completions 接口
response = client.chat.completions.create(
# 指定要调用的模型名称
# .env 配置的模型名称
model=os.getenv("MODEL_NAME"),
# messages 是传给大模型的对话上下文
messages=[
# system 用来设定模型的角色和行为
{"role": "system", "content": "你是一个有用的 AI 助手。"},
# user 表示用户输入的问题
{"role": "user", "content": "请用一句话解释什么是 Agent。"}
]
)
# 取出模型回复的正文内容
answer = response.choices[0].message.content
# 打印模型回复
print(answer)
到这里,我们完成了最普通的一次 LLM 调用。注意,此时它还不是 Agent,因为它只是根据输入生成回答,并没有调用工具,也没有进入多轮执行循环。他只能按照模型训练的数据进行回答,不具备外部感知能力。如:查询今天的天气。LLM就做不到,我们必须写一个工具,让LLM具备外部感知能力。
2.3 给大模型准备第一个工具
对开发者来说,工具本质上就是我们自己写的普通函数。
# 定义一个天气获取工具
# 这里先不调用真实天气 API,只返回固定的北京天气
def get_weather():
"""返回北京天气信息。"""
return "北京天气:晴,气温 25℃,适合出门。"
# 手动调用工具,查看返回结果
weather = get_weather()
print(weather)
2.4 把工具交给大模型使用
才我们只是手动调用了 get_weather()。接下来要让大模型知道:它可以使用这个工具。
需要注意:大模型不会真的执行 Python 函数。它只会返回一个“我想调用哪个工具”的请求,真正执行工具的仍然是我们的代码。
# 1. 定义工具说明书
# 这份说明书是给大模型看的,不是给 Python 执行的
tools = [
{
"type": "function",
"function": {
# 工具名称,需要和后面的工具注册表保持一致
"name": "get_weather",
# 工具描述:告诉大模型这个工具什么时候该用
"description": "获取北京当前天气。当用户询问北京天气时使用这个工具。",
# 工具参数:这个工具暂时不需要参数,所以 properties 为空
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
]
# 2. 建立工具注册表
# 注册表的作用:把大模型返回的工具名称,映射到真正的 Python 函数
tool_registry = {
"get_weather": get_weather
}
# 3. 准备用户问题
messages = [
{"role": "system", "content": "你是一个有用的 AI 助手。"},
{"role": "user", "content": "北京今天天气怎么样?"}
]
# 4. 第一次调用大模型
# 这一次调用会把 tools 一起传给大模型,让它自己判断是否需要调用工具
response = client.chat.completions.create(
model=os.getenv("MODEL_NAME"),
messages=messages,
tools=tools,
tool_choice="auto"
)
# 5. 取出大模型的回复
assistant_message = response.choices[0].message
# 6. 判断大模型是否想调用工具
if assistant_message.tool_calls:
print("大模型决定调用工具")
# 先把大模型的工具调用请求加入消息历史
messages.append(assistant_message)
# 遍历大模型请求调用的工具
for tool_call in assistant_message.tool_calls:
tool_name = tool_call.function.name
print("工具名称:", tool_name)
# 从注册表中找到真正的 Python 函数
tool_func = tool_registry[tool_name]
# 执行工具,拿到结果
tool_result = tool_func()
print("工具返回:", tool_result)
# 把工具结果加入消息历史,交还给大模型
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_result
})
# 7. 第二次调用大模型
# 这一次大模型会根据工具结果,组织成自然语言回答
final_response = client.chat.completions.create(
model=os.getenv("MODEL_NAME"),
messages=messages
)
print("最终回答:")
print(final_response.choices[0].message.content)
else:
# 如果大模型认为不需要工具,就直接输出它的回答
print("大模型没有调用工具")
print(assistant_message.content)
核心逻辑是这 4 步:
1. 写一个 Python 工具函数 get_weather()
2. 写 tools 工具说明书,让大模型知道有这个工具
3. 大模型返回 tool_calls,表示“我想调用 get_weather”
4. Python 执行 get_weather,再把结果交还给大模型生成最终回答
用户问题
↓
LLM 读取 tools 说明书
↓
LLM 生成 tool_calls
↓
Python 执行真实函数
↓
工具结果写回 messages
↓
LLM 生成最终回答
注意:大模型不会直接执行 get_weather,它只是提出调用请求;真正执行函数的是我们的 Python 代码。
它解决了一个关键问题:让大模型从“只能回答”变成“可以借助外部工具获得信息”
2.5 Agent Loop
到这里,我们已经让大模型成功使用了 get_weather 工具。它能够根据用户问题判断是否需要调用工具,并把工具返回结果整理成自然语言回答。但严格来说,这还不是一个完整的 Agent。
而 Agent 的关键在于:它不是只判断一次,而是可以在拿到工具结果后继续判断下一步要做什么。
在我们写的这个示例中,只是在现有流程外面套一个 for 循环
2.6 Q&A
Q1:为什么我写了 get_weather(),大模型就能用它?
不是大模型“自动发现”了这个函数,而是我们通过 tools 把工具说明传给了大模型。get_weather() 是真正执行的 Python 函数,tools 是给大模型看的工具说明书。大模型根据工具说明判断是否需要调用工具。
Q2:大模型真的执行了 get_weather() 吗?
没有。
大模型只返回了一个工具调用请求,比如:
我要调用 get_weatherQ3:为什么工具执行完后,还要再调用一次大模型?
第二次调用大模型,是让它基于工具结果,整理成更自然的回答。
完整流程是:用户问题 -> LLM 决定调用工具 -> Python 执行工具 -> LLM 整理最终回答
Q4:如何知道模型会返回什么?
可以将所有的字段打印出来,然后查看相关的字段,你就能知道是如何请求大模型,如果封装请求参数等。
Q5: 工具为什么现在固定返回北京天气,而不是调用真实天气 API?
等流程理解清楚后,再把 get_weather() 替换成真实天气 API,就只是工具内部实现的变化。
固定返回数据可以减少干扰,让我们先看清楚:
LLM 如何决定调用工具
Python 如何执行工具
工具结果如何回到 LLM
LLM 如何生成最终回答在真实开发中,我们会根据业务场景封装各种类型的工具,让大模型能够和外部世界交互。比如:
天气查询工具
搜索工具
数据库查询工具
文件读写工具
订单查询工具
代码执行工具
企业内部 API 工具
工具调用流程不变,变化的是工具内部具体怎么实现。
Agent基础入门实战教程(3):初识LangChain
3.1 环境准备
安装命令
pip install -U langchain langchain-core python-dotenv langchain-openai
注意:
本教程使用的 langchain 版本为 1.2x;各版本可能会有差异
3.2 LangChain 是什么?
LangChain 是一个用于开发大模型应用的框架。它不是大模型本身,而是帮助我们把大模型、提示词、工具、数据、工作流组织起来。
如果不用langchain,你需要自己处理:
API 调用
多轮对话历史
Prompt 拼接
输出格式解析
工具调用
流式输出
错误重试
Agent 流程控制
LangChain 把这些能力封装成统一组件,让我们可以更快搭建 AI 应用。
注意:在学习langChain中,非常有必要和上一篇连起来看,在使用langChain的功能时,一定要理解使用langChain实现的功能,对应的是上节课中,使用纯py+sdk是如何实现的。这样你才能理解langChain到底封装了什么。
3.3 第一次调用大模型
from dotenv import load_dotenv
import os
from langchain_openai import ChatOpenAI
load_dotenv(override=True)
llm = ChatOpenAI(
model="gpt-5.5",
temperature=0,
# 在根目录建一个.env,把base_url和apikay放到环境变量中
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY")
)
# invoke():执行一次调用
response = llm.invoke("请用一句话介绍 LangChain")
print(response)
我们通过langchain,仅仅只需要几行代码,即可调用大模型。大大节约了我们的时间。使用langchain的话,就不需要在用模型官方提供的SDK了。
3.4 Message 消息机制
真实聊天不是简单的一句话,而是一组消息。
常见消息类型:
SystemMessage:系统设定,就是我们常说的系统提示词
HumanMessage:用户输入
AIMessage:模型回复
from langchain.messages import SystemMessage, HumanMessage
messages = [
SystemMessage("你是一名耐心的 Python 老师,回答要适合新手。"),
HumanMessage("什么是函数?")
]
response = llm.invoke(messages)
ai_message = response.content
print(response.content)
这段代码的含义就是,有一个messages 的消息列表,消息列表中有系统提示词和用户输入的提问。
模拟多轮对话示例:
from langchain.messages import AIMessage
messages.append(AIMessage(ai_message))
messages.append(HumanMessage("能不能再举一个例子?"))
response = llm.invoke(messages)
print(response.content)
关键点:多轮对话需要把历史消息一起传给模型。
3.5 Prompt 模板
PromptTemplate
from langchain_core.prompts import PromptTemplate
prompt = PromptTemplate.from_template(
"请用新手能听懂的方式解释:{topic}"
)
formatted_prompt = prompt.invoke({"topic": "LangChain"})
print(formatted_prompt.text)
response = llm.invoke(formatted_prompt.text)
print(response.content)
ChatPromptTemplate
聊天模型更推荐使用 ChatPromptTemplate。
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名 AI 应用开发老师,回答要清晰、简洁。"),
("human", "请解释这个概念:{topic}")
])
messages = prompt.invoke({"topic": "Prompt 模板"})
response = llm.invoke(messages)
print(response.content)
注意看,我们这里的from_messages,使用的是system和human会被langchain转化为 SystemMessage, HumanMessage。然后将ai返回的内容,再塞回去:(“ai”, “xxx”) 这样的话,就可以进行下一轮对话了。
3.6 Runnable 与链式组合
LangChain 里很多组件都可以被“执行”,例如:
Prompt
Model
Output Parser
Tool
Agent
这些可执行对象被称为 Runnable。
LangChain 支持用 | 把多个步骤串起来:
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名新手教程作者。"),
("human", "请用三句话解释:{topic}")
])
parser = StrOutputParser()
chain = prompt | llm | parser
result = chain.invoke({"topic": "LangChain 的 Runnable"})
print(result)
这段代码的执行流程是:
输入变量 -> Prompt 生成消息 -> 模型生成回复 -> 解析成字符串
我们可以理解为语法糖
3.7 结构化输出
很多时候,我们不希望模型只返回一段自然语言,而是希望它返回程序能处理的数据。
例如从一句话中提取姓名、年龄和城市。
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str = Field(description="姓名")
age: int = Field(description="年龄")
city: str = Field(description="所在城市")
structured_model = llm.with_structured_output(
Person,
method="function_calling"
)
result = structured_model.invoke(
"张三今年 18 岁,住在北京。"
)
print(result)
print(result.name)
print(result.age)
print(result.city)
3.8 流式输出
就是像打字机一样,一个一个输出
for chunk in llm.stream("请用 100 字介绍 LangChain"):
print(chunk.content, end="", flush=True)
这里的 stream 就是流式输出的方法。对比上面的 invoke 的话,invoke就是一次性执行完,就是等接口返回全部结果,一次性输出。
3.9 工具调用 与 Agent入门
上面我们初步使用langchain,简单实验了langchain常用的方法,但是也都是只能回答问题。接下来,我们将使用langchain来创建agnet,agent可以根据任务,来决定是否需要调用工具。
先定义一个工具:
from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询城市天气的本地示例工具。"""
weather_map = {
"北京": "晴,25 摄氏度",
"上海": "多云,27 摄氏度",
"广州": "小雨,29 摄氏度",
}
return weather_map.get(city, "暂未查询到该城市天气")
在langchain中,使用:@tool 就是定义一个工具的方法,注意,一定要在函数内部写注释,不然模型不能很准确的知道这个工具函数的具体作用。
创建 Agent:
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="你是一个会使用工具解决问题的助手。"
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "上海的天气如何?"}
]
})
print(result["messages"][-1].content)
上面这个 agent 的流程:
用户提出问题 -> 模型判断是否需要工具 -> 调用工具 -> 读取工具结果 -> 生成最终回答
我们只需要将定义好的工具名,放入到tools参数中,agent就会自动决定当前用户提问是否需要调用工具。
如果你将上面这个agent最终的输出完整打印:
[HumanMessage(content='上海的天气如何?'), AIMessage(content='', , tool_calls=[{'name': 'get_weather', 'args': {'city': '上海'}}), ToolMessage(content='多云,27 摄氏度', name='get_weather'), AIMessage(content='上海现在多云,气温约 27°C。')]
上面这个是我删除了多余的打印,只保留了比较有用的信息:
1: HumanMessage: 用户输入
2: AIMessage: ai回答
3: tool_calls: ai抛出要调用工具请求
4: ToolMessage: 工具调用结果
5: AIMessage: 调用完工具后,AI的输出
从打印出的日志,对比我们上节课讲解的agent的流程。现在就完全对得上了,只是langchain将这内部复杂的逻辑封装好了,不用我们管内部到底是如何实现的。不过,只要你上节课手敲了agent,到这里就可以一眼就理解。
Q&A
Q1: 什么时候用字符串,什么时候用 messages?
# 简单任务可以直接传字符串:
model.invoke("解释一下 Python 变量")
# 需要角色设定、多轮历史时,使用 messages:
model.invoke([
SystemMessage("你是一名 Python 老师"),
HumanMessage("什么是变量?")
])
Q2: 什么时候需要 Agent?
如果只是问答、总结、改写,不一定需要 Agent。
如果任务需要模型自己判断是否调用工具,例如计算、搜索、查数据库、调用 API,就可以考虑 Agent。
Q3: LangChain 和 LangGraph 是什么关系?
LangChain 负责提供大模型应用开发的常用组件
LangGraph 负责管理更复杂的 Agent / 工作流执行过程。
在 LangChain 1.0 之后,很多 Agent 能力底层其实是基于 LangGraph 构建的。
例如:
你用 create_agent() 创建 Agent 时
表面上是在使用 LangChain 的高级接口;
但底层执行流程、状态流转、工具调用等能力,很多是由 LangGraph 提供支撑的。
在 Agent 和复杂工作流这部分,可以理解为 LangChain 是对 LangGraph 的高层封装。
Q4:是否有必要学langchain?
分场景:
1:如果你只是做一个很简单的大模型调用:没必要
2:如果你要做的是一个真正的 AI 应用,LangChain 就很有价值了
对新手来说,学习 LangChain 的好处是:
能理解大模型应用的标准开发流程
能快速接触 Prompt、Message、Tool、Agent、RAG 等核心概念
能少写很多重复代码
能更容易从简单 Demo 过渡到真实项目
总结:
如果你只是玩 API,可以不学 LangChain;
如果你想系统开发 AI 应用,LangChain 很值得学。Q5:那我目前阶段是否只要学你上面讲到的知识点?我还需要课后扩展吗?
入门阶段,先学本教程这些知识点就够了。Agent基础入门实战教程(4):LangChain聊天机器人小练习
上一节我们已经初步认识了langchain的基本用法,但是我们学习的都是一个一个知识点片段,如果不加以练习的话,会很容易忘记,所以这节课,我们一起来练习巩固一下。
1. 先准备好环境变量
from dotenv import load_dotenv
import os
load_dotenv(override=True)
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_BASE_URL")
model_name = os.getenv("MODEL_NAME", "gpt-5.4-mini")
2. 初始化聊天模型
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model=model_name,
temperature=0,
api_key=api_key,
base_url=base_url,
)
print("模型初始化完成")
3. 使用langchain创建Agent实例
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=[],
system_prompt=(
"你叫小智,是一名乐于助人的智能助手。"
"请在对话中保持温和、有耐心的语气。"
"回答新手问题时,要尽量解释清楚,并适当举例。"
),
)
print("Agent 创建完成")
前3步其实是我们在写langchain应用时,比较常见的固定初始化流程,可理解为前期准备工作。
4. 调用一次
result = agent.invoke({
"messages": [
{"role": "user", "content": "你好,你是谁?"}
]
})
final_message = result["messages"][-1]
print(final_message.content)
5. 保存多轮对话
聊天机器人如何想要能连续对话,就需要保存历史信息。
下面先手动维护一个聊天列表
messages = []
messages.append({
"role": "user", "content": "我叫小明"
})
result = agent.invoke({"messages": messages})
reply = result["messages"][-1].content
print("小智:", reply)
messages.append({"role": "assistant", "content": reply})
6. 流式输出
stream_messages = [
{"role": "user", "content": "请用三句话解释什么是 LangChain。"}
]
full_reply = ""
print("小智:", end="", flush=True)
for part in agent.stream(
{"messages": stream_messages},
stream_mode="messages",
version="v2",
):
if part["type"] != "messages":
continue
token, metadata = part["data"]
if metadata.get("langgraph_node") != "model":
continue
if token.text:
print(token.text, end="", flush=True)
full_reply += token.text
print("\n\n完整回复:")
print(full_reply)
上面6步,我们已经创建了单轮对话的聊天机器人,机器人的输出方式:流式输出和非流式输出我们都已经练习到了。但是我这里推荐大家一定要每一步都将结果打印一下,仔细看看langchain响应的结果的结构是咋样的。
7. 封装流式输出
上面的流式输出流程,我们也可以理解为是一个固定的流程,因为langchain返回的结构,我们如何封装聊天列表等,都是固定流程,所以我们可以将他封装成一个函数
def chat_once(user_input: str, messages: list[dict]) -> str:
messages.append({"role": "user", "content": user_input})
print("小智:", end="", flush=True)
full_reply = ""
for part in agent.stream(
{"messages": messages},
stream_mode="messages",
version="v2",
):
if part["type"] != "messages":
continue
token, metadata = part["data"]
if metadata.get("langgraph_node") != "model":
continue
if token.text:
print(token.text, end="", flush=True)
full_reply += token.text
print()
messages.append({"role": "assistant", "content": full_reply})
return full_reply
8. 多轮对话
上面我们写好了单轮对话,那我们只要能将用户提问能够输入,然后加一个无限循环,那就是多轮对话了。
messages = []
print("输入 exit 或 quit 退出对话\n")
while True:
user_input = input("你:")
if user_input.lower() in {"exit", "quit"}:
print("对话结束,再见!")
break
if not user_input.strip():
print("请输入内容。")
continue
print(f"你:{user_input}")
chat_once(user_input, messages)
print("-" * 40)
# 避免历史消息无限增长。这里保留最近 100 条消息。
messages = messages[-100:]
9. 测试

这是我本地运行后的结果,我先输入我叫小明,后续我再提问的时,AI已经能知道我的名字。这说明我们做的这个小机器人已经能拥有记忆了,这个记忆就是我们手动维护的聊天列表。
当然我们这个是一个最简单的聊天机器人,只简单维护了聊天列表。在真实的企业级的agent中,会围绕这个最小agent去扩展他的各方面的能力。列如:工具调用,长期记忆,知识库检索,权限控制等。
掌握这个最小版本之后,后面再学习复杂 Agent,就会更容易理解它们是怎么一步步扩展出来的。接下来我们将深入学习langchain,加油!
Agent基础入门实战教程(5):图结构可视化与调试框架:LangGraph Studio
1. 图结构可视化与调试框架:LangGraph Studio
LangGraph Studio 是一个Graph 可视化引擎,能够实时观察每个节点的执行状态、输入输出数据和中间计算结果。

如图所示,这个就是LangGraph Studio项目启动后,可以很直观的看到,我们这个agent的所有节点的流程,所以我们需要先创建好LangGraph Studio项目,才能让我们的前期的学习更加的容易理解。
1.1 一个 LangGraph CLI 项目的最小骨架

如图所示,这个最小骨架即可启动一个LangGraph Studio的项目。
1.2 langgraph.json
{
"dependencies": ["."],
"graphs": {
"agent": "./src/agent/graph.py:graph"
},
"env": ".env"
}
我们先来看看第一个文件,它是整个项目的配置中枢,langgraph 命令启动时第一个读的就是它。
graphs:登记你的图,告诉Studio你要按照哪个agent来画图。 解释:agent 这个图,在src/agent/graph.py文件里,变量名叫graph"。
1.3 graph.py
from dotenv import load_dotenv
import os
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
load_dotenv(override=True)
def get_weather(city: str) -> str:
"""查询指定城市的天气。"""
return f"{city} 现在晴,25°C。"
llm = ChatOpenAI(
model="gpt-5.5",
temperature=0,
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY")
)
graph = create_agent(
model=llm,
tools=[get_weather],
system_prompt="你是一个乐于助人的助手,需要时调用工具来回答问题。",
)
这是我们前面学习过的agent的代码,大家应该都很熟悉了,我们主要看 graph 这个变量名,需要和上面的graphs下定义的graph的变量名一致。studio启动时,就可以找到这个agent,将它绘制成图。
1.4 启动
在启动前需要先安装依赖,如果没有安装的话,就先去把最后的附件下载,让AI安装好依赖,安装完依赖后启动:
langgraph dev

启动完成后,终端会打印出这个图案,然后访问Studio UI这个链接,就可以得到agent的所有节点流程图。
1.5 Studio图

我们再来看看这张图,这张图有4个节点
开始:这是这个agent的入口,所有对话都从这里开始
model:这是模型节点,这一步就是调用大模型
tools:工具节点,工具执行就在这个节点
end:流程结束
流程:
start → model:实线。说明是固定的流程,消息一进来,必定会走这条路。
model → tools:虚线。说明不是固定流程。由model节点来判断是否要走tools节点。
model → end:虚线。和tools一样,需要有model来判断。如果需要调用工具,就走tools节点,如果不需要,就走end节点。整个流程结束。
连起来看一次完整对话
问:北京天气怎样?
__start__
│
▼
model ← 模型看到问题,决定"得调 get_weather"
│ (虚线,选了 tools 这条岔路)
▼
tools ← 执行 get_weather("北京") → "北京 现在晴,25°C"
│ (虚线,把结果送回去)
▼
model ← 模型拿到工具结果,组织成人话:"北京现在晴,25度"
│ (虚线,这次没工具要调了,选 __end__)
▼
__end__ ← 返回最终答复
大家先把自己的图启动好,能可视化自己的节点,下节课我们在这张图的基础上讲解 langchain中间件。中间件可以理解为是langchain agent的生命周期,在不同的生命周期内创建好后,就会在图上画出,学习中间件才会事半功倍。所以一定要先启动好自己的studio图,才能到下节课。
Agent基础入门实战教程(6):LangChain 中间件
1.1 什么是中间件
中间件就是在agent的流程中,插入节点,在这个节点上,可以做额外的事情。比如在Model前,插入鉴权、安全校验等。
1.2 中间件的生命周期钩子
before_agent: agent开始前,用途:鉴权、初始化
before_model: 每次调用模型前,用途:动态修改prompt
wrap_model_call: 包裹模型掉调用(前后都可),用途:重试、改请求、改响应
after_model: 每次调用模型后,用途:记录输出、统计token、校验
warp_tool_call: 包裹工具调用(前后),用途:校验、修改工具调用参数
after_agent: agent结束后,用途:计时,清理,收尾
1.3 中间件练习
在上节课的graph.py中练习
# 导入
from langchain.agents.middleware import AgentMiddleware
# 自定义中间件,所有中间件钩子都写在这里
# 继承AgentMiddleware
# 没实现的话,就不会触发,也不会在 studio 图中画出
class MyMiddleware(AgentMiddleware):
# 把定义好的中间件传到 graph 中
graph = create_agent(
model=llm,
tools=[get_weather],
system_prompt="你是一个乐于助人的助手,需要时调用工具来回答问题。",
middleware=[MyMiddleware()],
)
1:before_agent
class MyMiddleware(AgentMiddleware):
def before_model(self, state, runtime):
print("agent开始前")
messages = state["messages"]
print("消息列表:", messages)
return None

如图,在model之前定义了钩子,钩子就会在图上画出,他的流程就非常清晰了。
然后再说说这几个参数:
self:指向类本身,可以使用self在多个钩子之间共享数据
state:状态,存有整个agent的共享信息,数据会发生改变,比如maeesges。
runtime:运行是的上下文,数据不会发生改变,如环境变量,配置
return:
None:什么都不改变
一个 dict: 比如想修改state,就return一个dict,如修改后的消息列表
2: after_model
def after_model(self, state, runtime):
last = state["messages"][-1]
print(f"🟢 [after_model] 模型已返回{last.content!r}")
return None

3: before_agent

剩下的大家自己一个一个练习,把每个参数都打印出来看看,尝试修改一些参数。
2.1 langchain 封装好的中间件
langchain 把一堆常见需求做成了开箱即用的中间件,直接 import 进来塞 middleware=[...] 就行,不用自己写。
举例:
from langchain.agents.middleware import ModelCallLimitMiddleware
graph = create_agent(
model=llm,
tools=[get_weather],
middleware=[
MyMiddleware(), # 自定义
ModelCallLimitMiddleware(max_calls=3), # 封装好的,直接配参数
],
)
SummarizationMiddleware: 自动总结压缩历史消息
ModelCallLimitMiddleware: 限制调模型的最大次数, 防止 agent 陷入无限循环
ToolCallLimitMiddleware: 限制调工具的最大次数
ModelRetryMiddleware: 模型调用失败时自动重试
ToolRetryMiddleware: 工具执行失败时自动重试
ModelFallbackMiddleware: 主模型挂了自动切备用模型(比如 gpt 失败转 claude)
PIIMiddleware: 检测/脱敏隐私信息(邮箱、电话、身份证等)
HumanInTheLoopMiddleware: 关键操作(如删库、转账)前暂停,等人工审批再继续
ContextEditingMiddleware: 编辑上下文,比如清理掉旧的工具调用记录,精简上下文
LLMToolSelectorMiddleware: 工具太多时,先用 LLM 筛出相关工具再给模型(避免工具列表太长)
LLMToolEmulator: 用 LLM 模拟工具返回,不真执行(测试/开发时好用)
TodoListMiddleware: 给 agent 加一个待办清单能力,适合多步骤任务
ShellToolMiddleware: 给 agent 加执行 shell 命令的能力
FilesystemFileSearchMiddleware: 给 agent 加文件搜索能力
上面是我让AI给我找的,我就懒得一个一个试了,大家有兴趣的话就试一下
自定义 vs 封装好的,怎么选?
底层是同一套机制——这些封装好的,内部也就是实现了 before_model / wrap_model_call 这些钩子,跟你写的没本质区别,只是帮你写好了。
评论