9
0

从零理解 Agent:从手写循环到 LangChain、LangGraph 与 Pi SDK

2026-09-04

从零理解 Agent:从手写循环到 LangChain、LangGraph 与 Pi SDK

前言

今天对 Agent 开发进行了一次比较完整的梳理。

一开始,我对 Agent 的理解非常简单:

Agent = 模型 + 工具 + 循环

基于这个思想,我尝试使用:

  • Python

  • LangChain

  • DeepSeek

  • 文件读取工具

  • 文件编辑工具

  • Bash 工具

实现一个最简单的终端 Coding Agent。

随着讨论深入,又进一步理解了:

  • Tool Calling 是怎么工作的

  • Agent 为什么需要循环

  • 上下文是如何传递的

  • LangChain 为什么看不到显式 while

  • LangGraph 解决什么问题

  • Pi SDK 和 LangChain 有什么区别

  • Session Context 和长期 Memory 有什么区别

  • Pi 的自动 Context Compaction 是怎么实现的

最终,我对 Agent 的理解从“模型调用几个 Tool”,逐渐变成了一套比较完整的认知体系。


一、Agent 最核心的公式

今天最重要的认识之一:

Agent = LLM + Tools + State + Loop

分别代表:

LLM
负责思考、判断下一步做什么

Tools
负责真正执行动作

State
记录之前发生过什么

Loop
不断执行:
思考 → 行动 → 观察 → 再思考

经典过程:

Think
  ↓
Action
  ↓
Observation
  ↓
Think
  ↓
Action
  ↓
Observation
  ↓
...

换成程序:

用户
 ↓
LLM
 ↓
Tool Call
 ↓
Python 执行 Tool
 ↓
Tool Result
 ↓
再次交给 LLM
 ↓
继续判断
 ↓
最终回答

这就是一个 Agent 最核心的工作机制。


二、案例:自己实现一个 Mini Coding Agent

为了真正理解 Agent,我没有一开始就使用 LangChain 的高级 Agent API,而是自己写 Agent Loop。

技术栈:

Python
+
LangChain
+
DeepSeek

核心 Tools:

read_file
edit_file
bash

也就是说让模型拥有三个能力:

1. read_file

读取项目文件。

例如:

pom.xml
package.json
src/main.py

2. edit_file

修改或者创建代码文件。

典型过程:

先 read_file
 ↓
找到旧代码
 ↓
old_text → new_text
 ↓
写回文件

3. bash

执行系统命令。

例如:

dir
ls
git status
mvn test
npm run build
python main.py

于是这个 Coding Agent 已经可以完成:

查看项目
 ↓
读取代码
 ↓
修改代码
 ↓
执行测试
 ↓
发现错误
 ↓
继续读取代码
 ↓
再次修改
 ↓
重新测试

这已经形成了一个完整的小型 Coding Agent 闭环。


三、Tool Calling 并不是模型真的在执行工具

这是今天另一个非常重要的认识。

例如:

@tool
def read_file(path: str):
    ...

然后:

model.bind_tools(tools)

并不意味着:

LLM 可以直接执行 Python。

实际上模型只是知道:

现在有一个工具:

read_file(path: str)

作用:
读取文件

当模型需要读文件时,它可能返回:

tool_calls:

name = read_file

args =
{
    "path": "pom.xml"
}

也就是说:

模型只是提出“我要调用 read_file”。

真正执行:

open(...)

的是我们的 Python 程序。

所以正确理解应该是:

LLM
只负责决策

Python / Runtime
负责执行

这是 Agent 开发中非常核心的边界。


四、手写 Agent Loop

今天最值得记住的代码其实只有十几行:

while True:

    ai = model.invoke(messages)

    messages.append(ai)

    if not ai.tool_calls:
        print(ai.content)
        break

    for tool_call in ai.tool_calls:

        tool = tool_map[
            tool_call["name"]
        ]

        result = tool.invoke(
            tool_call
        )

        messages.append(
            result
        )

这段代码基本就是整个 Agent 的灵魂。

翻译成人话:

while True:

    问模型:
        下一步做什么?

    模型:
        我要读 pom.xml

    Python:
        好,我帮你读

    Tool Result:
        pom.xml 内容是……

    再问模型:
        下一步?

    模型:
        我要修改代码

    Python:
        好,修改

    再问:
        下一步?

    模型:
        我要执行 mvn test

    Python:
        执行

    再问模型:
        下一步?

    模型:
        任务完成

    break

这让我真正理解了:

Agent 并不是一个神秘的新技术。

它的基础就是:

模型决策
+
工具执行
+
结果反馈
+
循环

五、Agent 的上下文到底是怎么来的?

之前很容易产生一个误区:

是不是 LLM 自己记住了之前说过的话?

实际上不是。

LLM 本身没有这种持续运行的“记忆”。

真正的原因是:

messages

一直保存着历史消息。

例如:

SystemMessage
HumanMessage
AIMessage
ToolMessage
AIMessage
ToolMessage
...

每一次:

model.invoke(messages)

都会重新把这些上下文发送给模型。

例如第一次:

System
User

第二次:

System
User
Assistant Tool Call
Tool Result

第三次:

System
User
Assistant Tool Call
Tool Result
Assistant Tool Call
Tool Result

所以:

所谓模型“记得之前发生了什么”,本质是应用程序重新把历史 Context 给了模型。

这让我开始真正理解:

Context
Memory
Session

之间的关系。


六、为什么 LangChain Agent 看不到循环?

后来接触 LangChain 高级 API 时产生了一个疑问:

为什么 LangChain 开发 Agent 的代码里没有明显的循环?

例如:

agent = create_agent(
    model=model,
    tools=tools
)

然后:

result = agent.invoke(...)

看起来只调用了一次。

实际上:

agent.invoke()

内部可能已经执行:

Model
 ↓
Tool
 ↓
Model
 ↓
Tool
 ↓
Model
 ↓
Final Answer

也就是说:

model.invoke()

通常代表:

一轮模型调用。

而:

agent.invoke()

通常代表:

整个 Agent Loop 跑完后的结果。

LangChain 只是把我们自己写的:

while True

封装起来了。

这有点像 Java:

JDBC

需要自己操作:

Connection
PreparedStatement
ResultSet

而 MyBatis:

userMapper.selectById(1);

把大量底层工作封装了。

Agent 也是一样。


七、LangChain、LangGraph、Pi SDK 到底怎么选?

今天对三者的定位终于比较清楚了。


LangChain:通用 Agent 开发框架

适合:

智能客服
销售助手
RAG
CRM Agent
订单 Agent
知识库问答
企业微信助手
数据查询 Agent

LangChain 更像:

Agent 开发工具箱。

它提供:

Model
Tool
Message
Prompt
Agent
Memory
Retriever
Middleware

例如智能客服:

用户:
我的订单为什么还没发货?

 ↓

Agent
 ↓
get_order
 ↓
订单信息
 ↓
LLM
 ↓
create_ticket
 ↓
工单结果
 ↓
LLM
 ↓
最终回复

这种场景非常适合 LangChain。


八、LangGraph:复杂 Agent Workflow

如果 Agent 不再只是:

Model
 ↕
Tool

而出现复杂业务流程:

退款申请
 ↓
检查订单
 ↓
金额 > 1000?
 ├─ Yes
 │    ↓
 │ 人工审批
 │    ↓
 │ 暂停
 │    ↓
 │ 审批完成
 │    ↓
 │ 恢复
 │
 └─ No
      ↓
   自动退款

就更适合 LangGraph。

LangGraph 核心概念:

State
Node
Edge
Conditional Edge
Checkpoint
Human-in-the-loop

所以:

LangChain
更偏:
让模型自己决定调用什么 Tool

LangGraph
更偏:
开发者控制整个 Agent 工作流程

两者也并不是互斥关系。

可以理解为:

简单 Agent
↓
LangChain

复杂 Agent
↓
LangChain + LangGraph

复杂 Workflow
↓
LangGraph

九、Pi SDK 是什么?

Pi 和 LangChain、LangGraph 最大的不同:

Pi 更像一个已经完成的 Coding Agent Harness。

它不是主要为了:

客服
销售
订单
CRM

而是天然面向:

Coding Agent

Pi 已经实现了很多 Coding Agent 基础设施,例如:

Agent Loop
Session
Context
Tool Calling
Context Compaction
Streaming
Coding Tools

以及类似:

read
write
edit
bash
grep
find
ls

这些能力。

所以如果我要开发:

自己的 Claude Code
自己的 Codex CLI
Web Coding Agent
IDE Coding Assistant
自动改代码平台

Pi SDK 很有吸引力。

因为很多基础能力不用重新实现。


十、什么时候用 Pi,什么时候用 LangChain?

今天形成了一个非常清晰的判断方式。

先问:

是不是以操作代码为核心?

如果:

YES

优先:

Pi SDK

例如:

Coding Agent
代码修改 Agent
Terminal Agent
IDE Assistant

如果:

NO

继续判断:

是不是普通业务 Agent?

例如:

客服
销售
RAG
CRM
订单

优先:

LangChain

如果流程越来越复杂:

审批
暂停恢复
多节点
条件分支
多 Agent

再考虑:

LangGraph

最终可以总结成:

                    Agent
                      │
             是否操作代码?
              /            \
            Yes             No
             ↓               ↓
          Pi SDK          LangChain
                             │
                         流程复杂?
                         /      \
                       No        Yes
                       ↓          ↓
                  LangChain   LangGraph

十一、Context 和 Memory 不是一回事

这是今天非常重要的一次概念区分。

可以将 Memory 分成三层:

Memory
│
├── Session Context
│
├── User Memory
│
└── Project Memory

Session Context

解决:

当前这次聊天刚才发生了什么?

例如:

刚刚读取了 UserController
刚修改了登录接口
mvn test 报了什么错误

User Memory

解决:

用户长期有什么特点?

例如:

喜欢 Java
回答希望使用中文
代码希望详细注释
不喜欢过度设计

这种东西适合:

MySQL
PostgreSQL
Vector DB

进行长期保存。


Project Memory

解决:

这个项目有哪些长期事实?

例如:

项目使用 Spring Boot
前端 Vue3
数据库 MySQL
部署 Docker Compose

所以最终可以形成:

               Agent
                 │
      ┌──────────┼──────────┐
      ↓          ↓          ↓
Session      User Memory   Project Memory
Context

这是比简单“Memory”这个词更加准确的理解。


十二、Pi SDK 的 Session 保存在哪里?

如果使用 Pi SDK:

SessionManager.inMemory()

意味着:

只保存在内存

程序关闭以后就消失。

如果使用持久化 SessionManager,则可以保存为 Pi 的 Session 数据。

所以:

Pi SDK 已经提供了 Session 管理能力,但是业务系统完全可以额外把用户、Session 元数据、长期 Memory 保存到自己的数据库。

如果做 SaaS:

users
sessions
messages
memories
projects

依然应该是自己的业务数据库负责。

Pi 负责:

Agent Runtime

数据库负责:

业务持久化

两层职责应该分开。


十三、Context Compaction 是什么?

随着 messages 越来越长:

messages
 ↓
Token 越来越多
 ↓
费用越来越高
 ↓
Context Window 接近极限

Agent 就需要:

Context Compression

或者:

Context Compaction

十四、Pi 自动上下文压缩的核心思想

我一开始的猜测是:

设置 Token 阈值,达到以后让 LLM 总结历史。

这个理解基本正确。

核心逻辑:

当前 Context Token
        ↓
是否接近 Context Window?
       / \
     No   Yes
     │     │
 继续运行  Compaction
             ↓
      找出较老的历史
             ↓
           LLM
             ↓
          Summary
             ↓
 Summary + 最近原始消息
             ↓
          继续 Agent

它不是把所有消息全部总结。

而是:

旧消息
 ↓
总结

最近消息
 ↓
继续原样保留

例如:

历史 100K Token

其中:

旧的 80K
 ↓
Summary

最近 20K
 ↓
原样保留

重新组成:

System Prompt

+

History Summary

+

Recent Messages

十五、Context Compaction 是滚动进行的

非常有意思的一点:

第一次:

历史 A
 ↓
Summary A

继续聊以后:

Summary A
+
新增历史 B
 ↓
再次总结
 ↓
Summary B

再继续:

Summary B
+
新增历史 C
 ↓
Summary C

形成:

Summary1
   ↓
Summary2
   ↓
Summary3

这是一种:

Rolling Compaction

也就是滚动式上下文压缩。


十六、Context Compression 是有损的

这里又获得了一个很重要的认识。

Context Compaction 并不是:

100K Token
↓
某个神秘算法
↓
10K Token
而且所有信息一点不丢

实际上它是:

100K 历史
 ↓
LLM Summary
 ↓
更短的语义摘要

所以一定会产生信息损失。

因此:

Context Compaction

和:

长期 Memory

是不同的东西。

Context Compaction 的目的:

防止当前会话 Context Window 爆掉。

Memory 的目的:

长期保存真正重要的信息。


十七、DeepSeek + LangChain

本次 Coding Agent 中最终使用:

DeepSeek
+
LangChain

模型使用 LangChain 统一初始化:

from langchain.chat_models import init_chat_model

model = init_chat_model(
    model="deepseek-v4-flash",
    model_provider="deepseek",
    api_key="...",
    temperature=0
)

然后:

model_with_tools = model.bind_tools(
    tools
)

这里让我理解:

init_chat_model
↓
创建模型

bind_tools
↓
把工具描述告诉模型

tool_calls
↓
模型决定调用工具

tool.invoke
↓
程序真正执行工具

这些步骤之间的职责已经非常清晰。


十八、本次涉及的技术栈

整个学习过程中涉及:

编程语言

Python
TypeScript

Agent Framework

LangChain
LangGraph
Pi SDK

LLM

DeepSeek

Agent 基础概念

LLM
Tools
Agent Loop
Tool Calling
Tool Result
State
Messages
Session
Context
Memory
Context Window
Context Compaction

Coding Agent Tools

read_file
edit_file
bash

以及后续可以增加:

grep
find
list_files
search_code

Memory / 数据层

MySQL
PostgreSQL
Vector DB

后续业务 Agent 技术栈

例如智能客服:

Python
+
FastAPI
+
LangChain
+
DeepSeek
+
MySQL / PostgreSQL
+
RAG

十九、今天我最大的几个收获

收获 1:真正理解了 Agent Loop

以前:

Agent = 模型 + Tool

现在:

Agent =
LLM
+
Tools
+
State
+
Loop

更加完整。


收获 2:LLM 不会真的执行 Tool

LLM:

负责决定 Action

程序:

负责执行 Action

Tool Result:

重新作为 Observation 给 LLM

收获 3:所谓上下文,是程序重新发送历史

模型不是一直活在那里。

每次:

model.invoke(messages)

实际上都是:

把历史
+
当前问题
+
Tool Result

重新发给模型。

收获 4:理解 LangChain 为什么没有显式循环

不是循环消失了。

而是:

while True

被框架封装了。

所以:

agent.invoke()

背后可能已经执行了很多轮:

Model → Tool → Model → Tool → Model

收获 5:LangChain、LangGraph、Pi 不再混淆

现在可以快速判断:

Coding Agent
→ Pi SDK

业务 Agent
→ LangChain

复杂 Agent Workflow
→ LangGraph

收获 6:Context 和 Memory 是两个问题

Context
解决:
“刚才聊了什么?”

Memory
解决:
“长期应该记住什么?”

收获 7:理解了 Context Compaction

它本质不是神秘技术。

而是:

Token 接近阈值
 ↓
旧 Context
 ↓
LLM 总结
 ↓
Summary
+
Recent Messages
 ↓
继续运行

并且:

Compaction 是有损的。

二十、目前对 Agent 的整体认知

经过今天的学习,我现在可以把一个 Agent 系统理解成:

                         Agent
                           │
             ┌─────────────┼─────────────┐
             ↓             ↓             ↓
            LLM          Tools          State
             │             │             │
             │             │          Context
             │             │          Session
             │             │          Memory
             │             │
             └──────┬──────┘
                    ↓
                 Agent Loop
                    │
            ┌───────┴────────┐
            ↓                ↓
         Action          Observation
            │                │
            └──────→ LLM ←───┘

如果 Context 越来越长:

Context
 ↓
Token Threshold
 ↓
Compaction
 ↓
Summary + Recent Messages

如果要长期保存用户信息:

User Memory
Project Memory
 ↓
Database

如果是 Coding Agent:

Pi SDK

如果是通用业务 Agent:

LangChain

如果存在复杂流程:

LangGraph

结语

今天最大的变化,不是学会了某一个 LangChain API,而是开始理解:

Agent Framework 到底替开发者封装了什么。

自己写过:

while True

以后再看到:

create_agent(...)

就知道它背后是什么。

自己理解过:

messages

以后再看到:

Session
Memory
Checkpoint
Compaction

也知道这些东西究竟在解决什么问题。

所以目前最值得继续深入的不是马上堆更多框架,而是继续围绕:

Model
Tool
State
Loop
Context
Memory

这几个最核心的概念做几个真实的小 Agent。

当这些基础真正理解以后,再去看 LangGraph、Pi SDK、MCP、Multi-Agent、RAG、Context Engineering,就不会只是“会调用 API”,而是真的知道整个 Agent 系统为什么这样设计。

从零理解 Agent:从手写循环到 LangChain、LangGraph 与 Pi SDK
/archives/wei-ming-ming-wen-zhang-cJOwDqk9
作者
Administrator
发布于
2026-09-04
许可协议
CC BY-NC-SA 4.0

评论