📋 摘要
Claude Code 采用极简的 Agent Loop 架构,适合单用户 CLI 场景,追求简单高效;DeerFlow 选用 LangGraph 构建企业级多用户 Web 应用,解决状态管理、并发、可观测性等复杂问题。架构选型的核心是场景匹配:简单场景用简单方案,复杂场景需要系统化框架。
📑 目录
前言
在 AI 应用开发领域,一个永恒的话题是:如何在简单性和扩展性之间找到平衡?
Claude Code 作为 Anthropic 官方的 CLI 编程助手,以其简洁优雅的实现赢得了开发者的青睐。然而,当我们面对更复杂的生产级应用场景时,是否还能延续这种简单?DeerFlow 项目给出了答案——它选择了 LangGraph 作为 AI Agent 运行时的核心引擎。
本文将从架构演进的视角,深入剖析为什么 Claude Code 不需要 LangGraph,而 DeerFlow 却离不开它。通过核心源码的分析,帮助你理解 LangGraph 的设计哲学,以及在实际项目中如何做出正确的架构选型。
一、AI Agent 开发的核心挑战
在深入架构对比之前,让我们先明确 AI Agent 开发面临的核心挑战:
1.1 状态管理的困境
传统的 LLM 应用是无状态的——每次调用都是独立的,模型不会记住你上一句说了什么。这对于简单的问答场景或许足够,但对于需要多轮对话、任务分解、工具调用的 Agent 应用来说,状态管理是绕不开的难题。
状态管理需要解决的核心问题:
- 对话历史:如何高效存储和检索历史消息?
- 上下文维护:如何在对话过程中保持必要的上下文信息?
- 断点恢复:如果服务重启或网络中断,如何从上次中断的地方继续?
- 并发隔离:多用户场景下,如何确保每个用户的对话互不干扰?
1.2 工具调用的复杂性
现代 AI Agent 的核心能力之一是工具调用——模型能够自主决定何时调用工具、调用哪个工具、如何处理工具返回的结果。
一个典型的工具调用流程:
用户输入 → 模型推理 → 决定调用工具 → 执行工具 → 返回结果 → 模型继续推理 → ...
这个循环可能执行多次,直到模型认为任务完成。在这个过程中,需要处理:
- 工具调用的编排与调度
- 工具执行的超时与错误处理
- 工具结果的格式化与注入
1.3 可观测性与调试
生产环境中的 AI Agent 需要具备可观测性——开发者需要知道:
- Agent 的决策过程是什么?
- 调用了哪些工具?参数是什么?
- 为什么会出现某个错误?
- Token 消耗情况如何?
二、Claude Code 的极简哲学
2.1 架构概览
Claude Code 是 Anthropic 官方推出的命令行 AI 编程助手。作为一个 CLI 工具,它采用了极简的架构设计——Agent Loop 模式。
┌─────────────────────────────────────────────────────────────┐
│ Claude Code 架构 │
├─────────────────────────────────────────────────────────────┤
│ CLI 层 │
│ ├── 用户输入处理 │
│ ├── 输出渲染 (Markdown、语法高亮) │
│ └── 交互式确认 │
├─────────────────────────────────────────────────────────────┤
│ Agent Loop (核心循环) │
│ while True: │
│ 1. 构建消息 (system + history + user input) │
│ 2. 调用 Anthropic API │
│ 3. 解析响应: │
│ - text → 输出给用户 │
│ - tool_use → 执行工具 → 添加 tool_result → 继续 │
│ 4. 持久化到本地文件 │
├─────────────────────────────────────────────────────────────┤
│ 工具层 │
│ ├── Read/Write/Edit 文件操作 │
│ ├── Bash 命令执行 │
│ └── Glob/Grep 文件搜索 │
├─────────────────────────────────────────────────────────────┤
│ 持久化层 │
│ ~/.claude/projects/{project}/ │
│ ├── memory.md # 项目记忆 │
│ └── history.json # 对话历史 │
└─────────────────────────────────────────────────────────────┘
2.2 核心实现:Agent Loop
Claude Code 的核心是一个简洁的循环结构:
# Claude Code 核心循环 (概念简化版,实际使用流式响应)
class ClaudeCode:
def __init__(self):
self.messages = [] # 对话历史(内存中)
self.tools = [
{"name": "read_file", "description": "...", "input_schema": {...}},
{"name": "write_file", "description": "...", "input_schema": {...}},
{"name": "bash", "description": "...", "input_schema": {...}},
# ... 工具定义需包含完整的 JSON Schema
]
def run(self, user_input: str):
# 1. 添加用户消息到历史
self.messages.append({
"role": "user",
"content": user_input
})
# 2. Agent 主循环(实际使用 stream=True 流式处理)
while True:
# 调用 Anthropic Messages API
with anthropic.messages.stream(
model="claude-sonnet-4",
system=self.build_system_prompt(),
messages=self.messages,
tools=self.tools,
max_tokens=8192,
) as stream:
response = stream.get_final_message()
# 添加助手响应到历史
self.messages.append({
"role": "assistant",
"content": response.content
})
# 检查是否有工具调用
tool_calls = [
block for block in response.content
if block.type == "tool_use"
]
if not tool_calls:
# 没有工具调用,直接输出文本并结束
self.render_output(response.content)
break
# 执行工具并收集结果
tool_results = []
for tool_call in tool_calls:
result = self.execute_tool(tool_call)
tool_results.append({
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result,
})
# 将工具结果添加到消息历史
self.messages.append({
"role": "user",
"content": tool_results
})
# 3. 持久化到本地文件
self.save_history()
def build_system_prompt(self) -> str:
"""构建系统提示,包含工作目录、项目记忆等上下文"""
return f"""
You are Claude Code, an AI programming assistant.
# 当前环境
Working directory: {os.getcwd()}
# 项目记忆
{self.load_memory()}
# 可用工具说明
{self.format_tools_description()}
"""
def save_history(self):
"""保存对话历史到本地 JSON 文件"""
history_path = Path.home() / ".claude" / "projects" / self.project_name / "history.json"
with open(history_path, "w") as f:
json.dump(self.messages, f)
注意:实际 Claude Code 使用 stream=True 流式响应,而非同步阻塞调用,以提供实时反馈。工具定义需要完整的 JSON Schema 格式。
2.3 为什么 Claude Code 不需要 LangGraph?
Claude Code 选择极简架构的原因在于它的场景特点:
| 特点 | 说明 | 对架构的影响 |
|---|---|---|
| 单用户 CLI | 一次只有一个用户使用 | 无需并发处理、无需会话隔离 |
| 同步阻塞交互 | 用户等待 AI 响应 | 无需复杂的异步架构 |
| 本地持久化 | 数据保存在用户机器上 | JSON 文件足够,无需数据库 |
| 直接 API 调用 | 只用 Anthropic API | 原生支持工具调用、流式响应 |
| 功能固定 | 不需要扩展插件 | 无需可插拔架构 |
这种极简设计带来的优势:
- 易于理解:整个核心逻辑一目了然
- 易于调试:没有复杂的抽象层,问题定位直接
- 启动快速:无需初始化复杂的运行时环境
- 资源占用低:没有额外的中间件开销
三、DeerFlow 的企业级架构
3.1 场景差异
DeerFlow 是一个多用户 Web 应用,其场景与 Claude Code 有着本质区别:
┌─────────────────────────────────────────────────────────────┐
│ DeerFlow 的场景挑战 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 多用户并发 │
│ └── 需要同时处理多个用户的对话请求 │
│ │
│ 2. 服务化架构 │
│ └── 前后端分离、HTTP API、SSE 流式响应 │
│ │
│ 3. 复杂状态管理 │
│ ├── ThreadState 包含 messages、sandbox、artifacts... │
│ └── 每个线程有独立的工作空间、上传文件、生成文件 │
│ │
│ 4. 可扩展中间件 │
│ ├── 14 个中间件处理不同关注点 │
│ └── 需要支持自定义中间件注入 │
│ │
│ 5. 多模型支持 │
│ ├── OpenAI、Anthropic、DeepSeek、Google 等 │
│ └── 需要统一的模型抽象层 │
│ │
│ 6. 子 Agent 系统 │
│ └── 并发执行多个子任务,需要线程池和状态追踪 │
│ │
│ 7. 可观测性 │
│ └── 集成 LangSmith/Langfuse 进行追踪 │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 LangGraph 的核心价值
面对上述挑战,LangGraph 提供了系统性的解决方案:
┌─────────────────────────────────────────────────────────────┐
│ LangGraph 提供的能力 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ 有状态管理 │
│ ThreadState 自动维护对话上下文、沙箱状态、artifacts 等 │
│ │
│ ✅ 持久化引擎 │
│ Checkpointer 自动保存状态,支持 SQLite、Redis 等后端 │
│ │
│ ✅ 工具编排 │
│ 自动处理 tool_calls → tool_messages 循环 │
│ │
│ ✅ 中间件系统 │
│ 可插拔的中间件链处理跨切面逻辑 │
│ │
│ ✅ 流式响应 │
│ 原生支持 SSE,提供实时反馈 │
│ │
│ ✅ 断线重连 │
│ 支持从任意检查点恢复执行 │
│ │
│ ✅ 可观测性 │
│ 集成 LangSmith/Langfuse 进行全链路追踪 │
│ │
└─────────────────────────────────────────────────────────────┘
四、核心源码剖析
4.1 LangGraph 服务配置
DeerFlow 通过 langgraph.json 配置 LangGraph Server:
{
"$schema": "https://langgra.ph/schema.json",
"python_version": "3.12",
"dependencies": ["."],
"env": ".env",
"graphs": {
"lead_agent": "deerflow.agents:make_lead_agent"
},
"checkpointer": {
"path": "./packages/harness/deerflow/agents/checkpointer/async_provider.py:make_checkpointer"
}
}
注意:dependencies 是字符串数组格式,支持指定多个依赖路径。
关键配置解析:
| 配置项 | 说明 |
|---|---|
graphs.lead_agent | 定义 Agent 入口点,指向 make_lead_agent 工厂函数 |
checkpointer | 状态持久化后端,用于保存和恢复对话状态 |
4.2 Agent 工厂函数
当 LangGraph Server 接收到请求时,会调用 make_lead_agent 创建 Agent 实例:
def make_lead_agent(config: RunnableConfig):
"""
LangGraph Server 调用此函数创建 Agent 实例
参数:
config: 运行时配置,包含 model_name、thinking_enabled 等
返回:
CompiledStateGraph: 编译后的 Agent 图
"""
cfg = config.get("configurable", {})
# 解析运行时配置
thinking_enabled = cfg.get("thinking_enabled", True)
reasoning_effort = cfg.get("reasoning_effort", None)
requested_model_name = cfg.get("model_name") or cfg.get("model")
is_plan_mode = cfg.get("is_plan_mode", False)
subagent_enabled = cfg.get("subagent_enabled", False)
max_concurrent_subagents = cfg.get("max_concurrent_subagents", 3)
is_bootstrap = cfg.get("is_bootstrap", False) # 特殊模式:创建自定义 Agent
agent_name = cfg.get("agent_name")
# 解析模型名称(支持自定义 Agent 配置)
agent_config = load_agent_config(agent_name) if not is_bootstrap else None
agent_model_name = agent_config.model if agent_config and agent_config.model else _resolve_model_name()
model_name = requested_model_name or agent_model_name
# 验证模型能力
app_config = get_app_config()
model_config = app_config.get_model_config(model_name) if model_name else None
if thinking_enabled and not model_config.supports_thinking:
logger.warning(f"Thinking mode enabled but model '{model_name}' does not support it")
thinking_enabled = False
# 注入运行元数据(用于 LangSmith 追踪)
if "metadata" not in config:
config["metadata"] = {}
config["metadata"].update({
"agent_name": agent_name or "default",
"model_name": model_name or "default",
"thinking_enabled": thinking_enabled,
"is_plan_mode": is_plan_mode,
"subagent_enabled": subagent_enabled,
})
# Bootstrap 模式:特殊 Agent 用于初始自定义 Agent 创建流程
if is_bootstrap:
return create_agent(
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled),
tools=get_available_tools(model_name=model_name, subagent_enabled=subagent_enabled) + [setup_agent],
middleware=_build_middlewares(config, model_name=model_name),
system_prompt=apply_prompt_template(subagent_enabled=subagent_enabled, max_concurrent_subagents=max_concurrent_subagents, available_skills=set(["bootstrap"])),
state_schema=ThreadState,
)
# 默认 Lead Agent
return create_agent(
model=create_chat_model(
name=model_name,
thinking_enabled=thinking_enabled,
reasoning_effort=reasoning_effort
),
tools=get_available_tools(
model_name=model_name,
groups=agent_config.tool_groups if agent_config else None,
subagent_enabled=subagent_enabled
),
middleware=_build_middlewares(config, model_name=model_name, agent_name=agent_name),
system_prompt=apply_prompt_template(
subagent_enabled=subagent_enabled,
max_concurrent_subagents=max_concurrent_subagents,
agent_name=agent_name,
available_skills=set(agent_config.skills) if agent_config and agent_config.skills is not None else None
),
state_schema=ThreadState,
)
4.3 线程状态定义
LangGraph 的核心概念之一是状态。DeerFlow 定义了 ThreadState 来承载对话过程中的所有状态信息:
from typing import Annotated, NotRequired, TypedDict
from langchain.agents import AgentState
class SandboxState(TypedDict):
"""沙箱状态"""
sandbox_id: NotRequired[str | None]
class ThreadDataState(TypedDict):
"""线程数据路径状态"""
workspace_path: NotRequired[str | None]
uploads_path: NotRequired[str | None]
outputs_path: NotRequired[str | None]
class ViewedImageData(TypedDict):
"""已查看图片的数据"""
base64: str
mime_type: str
def merge_artifacts(existing: list[str] | None, new: list[str] | None) -> list[str]:
"""Artifacts 合并函数 - 去重并保持顺序"""
if existing is None:
return new or []
if new is None:
return existing
return list(dict.fromkeys(existing + new))
def merge_viewed_images(
existing: dict[str, ViewedImageData] | None,
new: dict[str, ViewedImageData] | None
) -> dict[str, ViewedImageData]:
"""已查看图片合并函数"""
if existing is None:
return new or {}
if new is None:
return existing
if len(new) == 0: # 空字典表示清空
return {}
return {**existing, **new}
class ThreadState(AgentState):
"""
DeerFlow 线程状态
继承自 LangChain 的 AgentState,扩展了以下字段:
- sandbox: 沙箱环境信息
- thread_data: 线程相关路径
- title: 对话标题
- artifacts: 生成的文件路径列表
- todos: 任务列表 (plan mode)
- uploaded_files: 上传的文件信息
- viewed_images: 已查看的图片数据 (键为 image_path)
"""
sandbox: NotRequired[SandboxState | None]
thread_data: NotRequired[ThreadDataState | None]
title: NotRequired[str | None]
artifacts: Annotated[list[str], merge_artifacts] # 自定义合并函数
todos: NotRequired[list | None]
uploaded_files: NotRequired[list[dict] | None]
viewed_images: Annotated[dict[str, ViewedImageData], merge_viewed_images] # image_path -> {base64, mime_type}
关键设计点:
-
TypedDict 类型安全:所有字段都有明确的类型注解,便于 IDE 提示和静态检查。
-
Annotated 自定义合并:
artifacts和viewed_images使用自定义合并函数,确保状态更新时的正确行为。 -
NotRequired 可选字段:大部分字段是可选的,适应不同场景的需求。
4.4 中间件链构建
DeerFlow 的中间件系统是其架构的核心亮点之一。通过中间件链,可以将不同关注点的逻辑解耦:
def _build_middlewares(
config: RunnableConfig,
model_name: str | None,
agent_name: str | None = None,
custom_middlewares: list[AgentMiddleware] | None = None
):
"""
构建中间件链(动态组合,数量取决于配置)
基础中间件(build_lead_runtime_middlewares 提供):
1. ThreadDataMiddleware → 线程数据路径管理
2. UploadsMiddleware → 文件上传处理
3. SandboxMiddleware → 沙箱环境隔离
4. DanglingToolCallMiddleware → 修复缺失的 ToolMessage
5. LLMErrorHandlingMiddleware → LLM 错误处理
6. GuardrailMiddleware → 工具调用守卫(可选,需配置 guardrails.enabled)
7. SandboxAuditMiddleware → 沙箱审计
8. ToolErrorHandlingMiddleware → 工具异常处理
动态添加的中间件:
- SummarizationMiddleware(启用压缩时)
- TodoMiddleware(plan mode 时)
- TokenUsageMiddleware(启用 token 统计时)
- TitleMiddleware(始终)
- MemoryMiddleware(始终)
- ViewImageMiddleware(模型支持视觉时)
- DeferredToolFilterMiddleware(启用 tool_search 时)
- SubagentLimitMiddleware(启用子 Agent 时)
- LoopDetectionMiddleware(始终)
- ClarificationMiddleware(必须最后)
"""
middlewares = build_lead_runtime_middlewares(lazy_init=True)
# 添加 SummarizationMiddleware(如果启用)
summarization_middleware = _create_summarization_middleware()
if summarization_middleware is not None:
middlewares.append(summarization_middleware)
# 添加 TodoMiddleware(如果启用 plan mode)
is_plan_mode = config.get("configurable", {}).get("is_plan_mode", False)
todo_list_middleware = _create_todo_list_middleware(is_plan_mode)
if todo_list_middleware is not None:
middlewares.append(todo_list_middleware)
# 添加 TokenUsageMiddleware(如果启用)
if get_app_config().token_usage.enabled:
middlewares.append(TokenUsageMiddleware())
# 添加 TitleMiddleware
middlewares.append(TitleMiddleware())
# 添加 MemoryMiddleware(在 TitleMiddleware 之后)
middlewares.append(MemoryMiddleware(agent_name=agent_name))
# 添加 ViewImageMiddleware(如果模型支持视觉)
app_config = get_app_config()
model_config = app_config.get_model_config(model_name) if model_name else None
if model_config is not None and model_config.supports_vision:
middlewares.append(ViewImageMiddleware())
# 添加 DeferredToolFilterMiddleware(如果启用 tool_search)
if app_config.tool_search.enabled:
from deerflow.agents.middlewares.deferred_tool_filter_middleware import DeferredToolFilterMiddleware
middlewares.append(DeferredToolFilterMiddleware())
# 添加 SubagentLimitMiddleware(如果启用子 Agent)
subagent_enabled = config.get("configurable", {}).get("subagent_enabled", False)
if subagent_enabled:
max_concurrent_subagents = config.get("configurable", {}).get("max_concurrent_subagents", 3)
middlewares.append(SubagentLimitMiddleware(max_concurrent=max_concurrent_subagents))
# 添加 LoopDetectionMiddleware
middlewares.append(LoopDetectionMiddleware())
# 注入自定义中间件
if custom_middlewares:
middlewares.extend(custom_middlewares)
# ClarificationMiddleware 必须是最后一个
middlewares.append(ClarificationMiddleware())
return middlewares
4.5 前端触发流程
从前端视角看,触发 LangGraph 的入口是 useThreadStream Hook:
export function useThreadStream({
threadId,
context,
isMock,
onStart,
onFinish,
onToolEnd,
}: ThreadStreamOptions) {
// 使用 LangGraph SDK 的 useStream Hook
const thread = useStream<AgentThreadState>({
client: getAPIClient(isMock), // LangGraph Client
assistantId: "lead_agent", // Agent ID
threadId: onStreamThreadId, // 线程 ID
reconnectOnMount: runMetadataStorageRef.current // 断线重连支持
? () => runMetadataStorageRef.current!
: false,
fetchStateHistory: { limit: 1 }, // 获取历史状态
// 事件回调
onCreated(meta) {
handleStreamStart(meta.thread_id);
setOnStreamThreadId(meta.thread_id);
},
onLangChainEvent(event) {
if (event.event === "on_tool_end") {
onToolEnd?.({ name: event.name, data: event.data });
}
},
onUpdateEvent(data) {
// 处理标题更新等
},
onCustomEvent(event) {
// 处理子任务状态、LLM 重试等
},
onError(error) {
setOptimisticMessages([]); // 清除乐观消息
toast.error(getStreamErrorMessage(error));
},
onFinish(state) {
onFinish?.(state.values);
queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
},
});
// 乐观消息:立即显示用户输入,不等服务器响应
const [optimisticMessages, setOptimisticMessages] = useState<Message[]>([]);
const [isUploading, setIsUploading] = useState(false);
const sendInFlightRef = useRef(false); // 防止重复提交
// 发送消息函数
const sendMessage = async (threadId, message, extraContext, options) => {
if (sendInFlightRef.current) return; // 防止重复提交
sendInFlightRef.current = true;
try {
// 1. 上传文件(如有)
if (message.files?.length > 0) {
setIsUploading(true);
uploadedFileInfo = await uploadFiles(threadId, files);
}
// 2. 提交到 LangGraph
await thread.submit({
messages: [{
type: "human",
content: [{ type: "text", text }],
additional_kwargs: { files: filesForSubmit },
}],
context: {
thinking_enabled: context.mode !== "flash",
is_plan_mode: context.mode === "pro" || context.mode === "ultra",
subagent_enabled: context.mode === "ultra",
reasoning_effort: ...,
thread_id: threadId,
},
}, {
streamSubgraphs: true, // 流式子图
streamResumable: true, // 可恢复流
});
} finally {
sendInFlightRef.current = false;
}
};
// 合并乐观消息与实际消息
const mergedThread = optimisticMessages.length > 0
? { ...thread, messages: [...thread.messages, ...optimisticMessages] }
: thread;
return [mergedThread, sendMessage, isUploading] as const;
}
关键实现细节:
- 乐观消息:用户发送后立即显示,不等服务器响应,提升体验
- 防重复提交:
sendInFlightRef防止用户快速点击导致的重复请求 - 断线重连:
reconnectOnMount配合runMetadataStorage实现会话恢复 - 文件上传状态:独立
isUploading状态,上传期间显示进度
五、架构选型指南
5.1 决策树
你的应用是什么类型?
│
┌───────────────┴───────────────┐
▼ ▼
CLI / 桌面应用 Web / SaaS 应用
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 单用户? │ │ 多用户? │
│ 同步交互? │ │ 并发请求? │
│ 本地持久化? │ │ 服务化部署? │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Agent Loop │ │ LangGraph │
│ 极简架构 │ │ 企业级架构 │
└─────────────────┘ └─────────────────┘
5.2 详细对比
| 维度 | Agent Loop (Claude Code 风格) | LangGraph (DeerFlow 风格) |
|---|---|---|
| 适用场景 | CLI 工具、桌面应用、原型验证 | Web 应用、SaaS、企业级服务 |
| 用户模型 | 单用户 | 多用户并发 |
| 状态管理 | 内存 + 本地文件 | Checkpointer (数据库) |
| 持久化 | 手动 JSON 序列化 | 自动事务性持久化 |
| 中间件 | 硬编码在循环中 | 可插拔中间件链 |
| 工具编排 | 手动处理 tool_calls 循环 | 自动工具调用循环 |
| 断线恢复 | 需手动实现 | 原生支持 |
| 可观测性 | 自定义日志 | LangSmith/Langfuse 集成 |
| 学习曲线 | 低 | 中高 |
| 开发效率 | 快速启动 | 初期投入大,长期收益高 |
| 维护成本 | 功能简单时低,复杂时高 | 架构清晰,长期维护成本低 |
5.3 何时选择 LangGraph?
推荐使用 LangGraph 的场景:
- 多用户 Web 应用:需要并发处理、会话隔离
- 复杂状态管理:对话历史只是冰山一角,还有沙箱、artifacts、todos 等
- 需要断线恢复:用户可能中途离开,需要从断点继续
- 可扩展架构:需要通过中间件扩展功能
- 生产级可观测性:需要集成追踪系统
- 多模型支持:需要统一的模型抽象层
可以不用 LangGraph 的场景:
- CLI 工具:如 Claude Code
- 原型验证:快速迭代,架构简单
- 单用户场景:无需并发处理
- 功能简单:不需要复杂的状态管理
5.4 实战场景示例
场景一:内部开发助手 CLI 工具
- 需求:为开发团队提供一个命令行工具,辅助代码审查、调试脚本。
- 用户量:单用户,每次运行独立。
- 状态管理:只需保存当前对话历史,无多轮复杂状态。
- 架构选择:Agent Loop 足够。
- 实现要点:
- 使用 Claude Code 风格的简单循环。
- 历史记录保存为本地 JSON 文件。
- 工具集限于文件读写、Shell 命令。
场景二:多租户 AI 客服 SaaS 平台
- 需求:为多个企业客户提供 AI 客服,每个客户有独立的对话历史和知识库。
- 用户量:多用户并发,每秒处理数十个请求。
- 状态管理:每个会话需保存对话历史、用户信息、工单状态、上传文件等。
- 架构选择:LangGraph 必需。
- 实现要点:
- 使用 ThreadState 管理每个会话的完整状态。
- 配置 Redis Checkpointer 实现状态持久化和断线恢复。
- 中间件处理鉴权、审计、限流。
- 集成 LangSmith 监控每个会话的 Token 消耗和延迟。
六、总结
Claude Code 和 DeerFlow 代表了两种不同的架构选择,没有绝对的对错,关键在于场景匹配。
Claude Code 的极简主义告诉我们:不要过度设计。对于 CLI 工具,Agent Loop 足够优雅且高效。
DeerFlow 的企业级架构则展示了:复杂场景需要系统性解决方案。LangGraph 提供的状态管理、持久化、中间件系统、可观测性,是构建生产级 AI Agent 应用的坚实基石。
架构选型的核心原则:以终为始,量体裁衣。理解你的场景需求,选择合适的工具,而不是盲目追求技术潮流。
术语表
- Agent Loop: 一种简单的循环结构,依次处理用户输入、模型调用、工具执行和状态更新,适用于单用户 CLI 场景。
- LangGraph: LangChain 提供的框架,用于构建有状态的、多步骤的 AI Agent 应用,支持复杂的状态管理和可观测性。
- ThreadState: DeerFlow 中定义的状态类型,包含对话历史、沙箱环境、生成文件等所有运行时信息。
- Checkpointer: LangGraph 的持久化组件,支持将状态保存到数据库(如 SQLite、Redis),实现断点恢复。
- 中间件 (Middleware): 在 Agent 执行流程中插入的可插拔组件,用于处理横切关注点,如错误处理、审计、压缩等。
- 可观测性 (Observability): 通过日志、追踪、指标等手段监控 Agent 的运行情况,LangSmith/Langfuse 是常用工具。
参考资料
- LangGraph 官方文档
- LangGraph SDK (Python)
- LangGraph SDK (JavaScript)
- LangChain 官方文档
- DeerFlow GitHub
- Anthropic Messages API
- Claude Code 官方介绍
- LangSmith 可观测性平台
- Agent 设计模式