从零理解 Agent:从手写循环到 LangChain、LangGraph 与 Pi SDK
从零理解 Agent:从手写循环到 LangChain、LangGraph 与 Pi SDK
前言
今天对 Agent 开发进行了一次比较完整的梳理。
一开始,我对 Agent 的理解非常简单:
Agent = 模型 + 工具 + 循环
基于这个思想,我尝试使用:
Python
LangChain
DeepSeek
文件读取工具
文件编辑工具
Bash 工具
实现一个最简单的终端 Coding Agent。
随着讨论深入,又进一步理解了:
Tool Calling 是怎么工作的
Agent 为什么需要循环
上下文是如何传递的
LangChain 为什么看不到显式
whileLangGraph 解决什么问题
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 系统为什么这样设计。