Post

Agent学习笔记转载

笔记转载自:L站 Agent基础入门实战教程(1):初识Agent Agent基础入门实战教程(2):从 0 到 1 手写一个最小 Agent Agent基础入门实战教程(3):初识LangChain Agent基础入门实战教程(4):LangChain聊天机器人小练习<

先搞技术 阅读 2 点赞 0 评论 0

笔记转载自:L站

Agent基础入门实战教程(1):初识Agent

Agent基础入门实战教程(2):从 0 到 1 手写一个最小 Agent

Agent基础入门实战教程(3):初识LangChain

Agent基础入门实战教程(4):LangChain聊天机器人小练习

Agent基础入门实战教程(5):图结构可视化与调试框架:LangGraph Studio

Agent基础入门实战教程(6):LangChain 中间件【做项目去了,暂停更新】

仅做个人学习收藏,如有侵权,请联系删除

Agent基础入门实战教程(1):初识Agent

1.1 Agent的定义

  AI Agent(智能体) 是一个以大语言模型为核心决策模块,在给定目标、工具和约束条件下,能够读取上下文、规划下一步动作、调用外部工具,并根据执行结果持续调整的任务执行系统。

1.2 Agent的能力

维度

AI Agent

交互模式

多轮自主:接收目标后自主规划、执行、迭代

工具使用

动态选择并调用任意工具(搜索、数据库、API、代码执行)

环境感知

感知对话 + 工具返回结果 + 外部环境状态

任务复杂度

适合多步复杂任务(调研、分析、报告生成)

状态管理

有状态:维护工作记忆、长期记忆、任务进度

错误处理

工具失败或结果不符合预期时,可以重试、换工具、调整计划

  从表格可以看出 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 怎么使用??

  1. 用户输入问题

  2. LLM思考这个问题是否需要外部工具

  3. 需要调用工具查询最新文档

  4. 查询到的文档返回给LLM:本质是将用户输入+查询的结果 统一成一个上下文,在塞给LLM

  5. LLM思考:不需要再调用工具了,可以直接输出

  6. 输出结果

  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

推荐 Python 3.10 及以上版本

VS Code

用于打开项目和运行 Notebook

Jupyter Notebook

用于运行 .ipynb 文件

Python 虚拟环境 .venv

用于隔离本教程所需依赖

大模型 API Key

用于调用大模型接口

创建虚拟环境:

python -m venv .venv

激活虚拟环境:

.venv/Scripts/activate
激活成功后,终端前面一般会出现:(.venv)

安装依赖

pip install openai python-dotenv requests notebook jupyterlab ipykernel

依赖

说明

openai

调用大模型 API

python-dotenv

读取 .env 配置文件

requests

调用 HTTP 接口

notebook / jupyterlab

运行 .ipynb 文件

ipykernel

让 Jupyter 使用当前 .venv 环境

偷懒做法:一键安装环境提示词

请帮我在当前项目目录中搭建 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_weather

Q3:为什么工具执行完后,还要再调用一次大模型?
第二次调用大模型,是让它基于工具结果,整理成更自然的回答。
完整流程是:
用户问题 -> LLM 决定调用工具 -> Python 执行工具 -> LLM 整理最终回答

Q4:如何知道模型会返回什么?
可以将所有的字段打印出来,然后查看相关的字段,你就能知道是如何请求大模型,如果封装请求参数等。

Q5: 工具为什么现在固定返回北京天气,而不是调用真实天气 API?
等流程理解清楚后,再把 get_weather() 替换成真实天气 API,就只是工具内部实现的变化。

固定返回数据可以减少干扰,让我们先看清楚:

LLM 如何决定调用工具
Python 如何执行工具
工具结果如何回到 LLM
LLM 如何生成最终回答

在真实开发中,我们会根据业务场景封装各种类型的工具,让大模型能够和外部世界交互。比如:

  • 天气查询工具

  • 搜索工具

  • 数据库查询工具

  • 文件读写工具

  • 订单查询工具

  • 代码执行工具

  • 企业内部 API 工具

工具调用流程不变,变化的是工具内部具体怎么实现。

Agent基础入门实战教程(3):初识LangChain

3.1 环境准备

环境

说明

langchain

LangChain 主包,提供 Agent、Chain、模型初始化等常用入口,是开发 LangChain 应用的核心依赖。

langchain-core

LangChain 核心抽象包,包含消息类型、Prompt 模板、Runnable、输出解析器等基础组件。

python-dotenv

用于读取 .env 文件中的环境变量,常用来管理 API Key,避免把密钥直接写进代码。

langchain-openai

OpenAI 模型集成包,提供 ChatOpenAIOpenAIEmbeddings 等组件,也可用于兼容 OpenAI API 格式的第三方模型。

安装命令

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. 测试

image

这是我本地运行后的结果,我先输入我叫小明,后续我再提问的时,AI已经能知道我的名字。这说明我们做的这个小机器人已经能拥有记忆了,这个记忆就是我们手动维护的聊天列表。

当然我们这个是一个最简单的聊天机器人,只简单维护了聊天列表。在真实的企业级的agent中,会围绕这个最小agent去扩展他的各方面的能力。列如:工具调用,长期记忆,知识库检索,权限控制等。

掌握这个最小版本之后,后面再学习复杂 Agent,就会更容易理解它们是怎么一步步扩展出来的。接下来我们将深入学习langchain,加油!

Agent基础入门实战教程(5):图结构可视化与调试框架:LangGraph Studio

1. 图结构可视化与调试框架:LangGraph Studio

LangGraph Studio 是一个Graph 可视化引擎,能够实时观察每个节点的执行状态、输入输出数据和中间计算结果。

image


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

1.1 一个 LangGraph CLI 项目的最小骨架

image


如图所示,这个最小骨架即可启动一个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
image


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

1.5 Studio图

image

我们再来看看这张图,这张图有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
image


如图,在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
image

3: before_agent

image

image466×596 21.6 KB

剩下的大家自己一个一个练习,把每个参数都打印出来看看,尝试修改一些参数。

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 这些钩子,跟你写的没本质区别,只是帮你写好了。

评论