Graph Engineering 101: When a Loop Isn’t Enough ✍ Alex Prompter🕐 2026-07-22📦 23.6 KB 🟢 已读 𝕏 文章列表 文章探讨了AI Agent从简单的ReAct循环向图工程架构的演进。循环模式在处理复杂、多步骤及需人工介入的任务时存在状态持久化、错误处理和分支逻辑的局限性。图工程通过显式的节点、边和状态管理,解决了并发、暂停恢复及复杂流程控制问题,为构建更健壮的Agent系统提供了架构基础。 AgentGraph EngineeringReActLangGraph架构设计状态管理LLM # Graph Engineering 101: When a Loop Isn’t Enough **作者**: Alex Prompter **日期**: 2026-07-21T11:39:14.000Z **来源**: [https://x.com/alex_prompter/status/2079531644810138082](https://x.com/alex_prompter/status/2079531644810138082) ---  Your agent calls an LLM in a simple agent loop. It reasons, picks an action, observes the result, and goes around again. That pattern works until the task needs a human approval halfway through, a retry policy for a flaky API, and the ability to survive a server restart without losing 40 minutes of work.  The ReAct loop (Yao et al., 2022, arXiv:2210.03629) is the atom of every AI agent. Reason, act, observe, repeat. Every major framework starts here: LangChain's AgentExecutor, the OpenAI Agents SDK's runner, Anthropic's Claude Agent SDK. The loop is correct. It's also incomplete. As agents take on longer, messier, multi-step work, the loop doesn't break in one dramatic failure. It quietly stops being enough. Graph engineering is the practice of making an agent's control flow, state, and failure handling explicit in the system architecture instead of leaving them implicit inside a single model call. It doesn't replace loops. It structures them. This is what the shift actually looks like, what graph-based agent architectures contain, and when keeping a simple loop is the smarter call. ## What a loop is and what a graph is  A loop is a single cycle: prompt the model, let it pick an action, execute the action, feed the result back, repeat until the model says it's done or you hit a step limit. ``` messages = [{"role": "system", "content": SYSTEM_PROMPT}] messages.append({"role": "user", "content": user_request}) for step in range(MAX_STEPS): response = llm.call(messages) if response.stop_reason == "end_turn": return response.text for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({"role": "tool", "content": result}) ``` That's the entire agent. The LLM decides everything: what to do next, when to stop, how to handle errors. The orchestration is invisible because there's only one path. A graph is a set of named steps (nodes) connected by explicit transitions (edges), with shared state that persists across the entire run. Each node is a function. Edges can be conditional: "if the classifier returns 'toxic,' route to the moderation node instead of the response node." State is a typed dictionary that every node can read and write. The runtime handles sequencing, checkpointing, and recovery. ``` graph = StateGraph(AgentState) graph.add_node("research", research_node) graph.add_node("draft", draft_node) graph.add_node("review", review_node) graph.add_node("human_approval", human_gate) graph.add_edge("research", "draft") graph.add_edge("draft", "review") graph.add_conditional_edges("review", route_review) graph.add_edge("human_approval", END) ``` The loop is a special case of the graph: a graph with one node and one edge that points back to itself. Both are useful. The question is when one stops being enough. ## Where the loop starts failing A concrete task, three versions, escalating requirements. Version 1: the loop works fine You build an agent that answers customer questions by searching a knowledge base. The cycle is: receive question, search docs, generate answer, return. Three steps, no branching, no state to persist. A simple loop with tool calls is perfect here. Don't complicate it. Version 2: the cracks appear Now the agent handles refund requests. After searching the knowledge base, it checks order status via an API, calculates the refund amount, and needs a manager's approval before processing anything. Three problems show up at once. The approval step means the agent has to pause, wait for a human (minutes, hours, maybe overnight), and resume exactly where it stopped. A simple in-memory loop can’t pause durably. If the server restarts while the manager is at lunch, the entire run is gone. The order API is flaky. It times out roughly 10% of the time. The loop retries, but the retry logic lives inside the LLM's reasoning. The model decides whether to try again. Sometimes it retries 15 times. Sometimes it gives up after one failure and hallucinates an order status instead. A documented case from 2026 showed an agent calling a broken tool 400 times in five minutes because nothing external enforced a limit (source: Data Science Dojo, "Agentic Loops" guide, June 2026). The refund calculation depends on three prior results (order lookup, return policy, discount history). All of that context sits in the message list, which grows with every iteration. By step 12, the model is processing 8,000+ tokens of its own prior reasoning to make a simple arithmetic decision. Version 3: the loop can't hold it  The agent now handles complex support cases: multi-item returns across different orders, partial refunds, store credit versus original payment method, escalation to specialized teams for billing, shipping, or fraud. Some of these can run in parallel (check each order simultaneously). Some have dependencies (can't calculate the total refund until all individual orders are assessed). Some need different models (a cheap fast model for order lookup, an expensive reasoning model for policy interpretation). No amount of clever prompting can make one linear loop handle this cleanly. You haven't hit a model intelligence limit. You've hit an architecture limit. ## The anatomy of an agent graph Each component exists because a single loop can't handle what it does. Nodes are functions. Each one does exactly one thing: call an LLM, execute a tool, validate data, transform state. A node takes the current state as input and returns updated fields. For the implementation examples below, I’m using LangGraph because its node-and-edge model makes the architecture easy to see. The same principles apply regardless of framework. In LangGraph, every node is a plain Python function that accepts and returns a typed state dictionary. Edges define transitions. A static edge always routes to the same next node. A conditional edge runs a routing function that inspects state and returns the name of the next node. This is where branching lives. State is a typed dictionary that flows through the entire graph. Unlike a loop where everything lives in the message history, graph state is explicit. You define the schema upfront: order_id: str, refund_amount: float, approval_status: Literal["pending", "approved", "denied"], retry_count: int. Every node reads what it needs and writes what it produces. State replaces the hope that the LLM will remember what happened six tool calls ago. Checkpoints are state snapshots saved after every node execution. If the process crashes, the graph resumes from the last checkpoint instead of starting over. LangGraph supports PostgreSQL, Redis, SQLite, and MongoDB as checkpoint backends. Redis adds approximately 1-2ms of latency per checkpoint operation (source: LangGraph + Redis integration guide, Markaicode, May 2026). Postgres is slower but queryable, which matters for audit trails. Human gates are interrupt points where the graph pauses and waits for external input. The state persists, the process can shut down entirely, and when the human responds (minutes, hours, days later), the graph loads the checkpoint and continues from exactly where it stopped. This is the single biggest architectural advantage of graphs over loops. A loop can't pause because it lives in memory. A graph can pause because its state lives in a database. Routers are the conditional edge functions that inspect state and decide what happens next. A router might check retry_count and route to an error handler if it exceeds 3, check approval_status and continue if approved, or inspect a classifier output and send the run to the appropriate specialist node. Termination conditions are explicit. Unlike a loop where the LLM decides when it's done (and sometimes doesn't), a graph has named end states. A graph ends when it reaches a node with an edge to END, when a router determines the task is complete based on state values, or when a hard ceiling fires (max steps, max tokens, budget cap, timeout). Peter Steinberger's $1.3 million monthly token bill, 603 billion tokens across 100 Codex instances running on the OpenClaw project, is the extreme version of what happens when long-running agents operate without hard budget limits (source: Tom's Hardware, May 17, 2026; The Decoder, May 16, 2026). Observability is structural. Because every node is named and every transition is logged, you can trace exactly what happened in any run: which nodes executed, in what order, how long each took, how many tokens each consumed. LangGraph integrates with LangSmith for this. The OpenAI Agents SDK ships with built-in tracing enabled by default. These aren't add-ons. They're consequences of making the control flow explicit. ## The same workflow, two architectures Here's a refund processing agent in both styles. The difference isn't code length. It's what happens when something breaks. The loop version: ``` messages = [{"role": "system", "content": REFUND_PROMPT}] messages.append({"role": "user", "content": customer_request}) for step in range(MAX_STEPS): response = llm.call(messages) if response.stop_reason == "end_turn": return response.text for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({"role": "tool", "content": result}) ``` If the order API times out, the LLM decides whether to retry. If the server restarts, the run is lost. If the manager takes two hours to approve, the process sits in memory, or dies. If the refund calculation is wrong, you can't replay the run to find out why. The graph version: ``` from langgraph.graph import StateGraph, END from langgraph.checkpoint.postgres import PostgresSaver class RefundState(TypedDict): order_id: str order_data: dict | None refund_amount: float approval: str # "pending" | "approved" | "denied" retry_count: int messages: list def lookup_order(state: RefundState) -> dict: try: data = order_api.get(state["order_id"]) return {"order_data": data, "retry_count": 0} except TimeoutError: count = state["retry_count"] + 1 return {"order_data": None, "retry_count": count} def route_after_lookup(state: RefundState) -> str: if state["order_data"] is None and state["retry_count"] < 3: return "lookup_order" # retry if state["order_data"] is None: return "escalate" # max retries hit return "calculate_refund" # success graph = StateGraph(RefundState) graph.add_node("lookup_order", lookup_order) graph.add_node("calculate_refund", calculate_refund) graph.add_node("request_approval", request_approval) graph.add_node("process_refund", process_refund) graph.add_node("escalate", escalate_to_human) graph.set_entry_point("lookup_order") graph.add_conditional_edges("lookup_order", route_after_lookup) graph.add_edge("calculate_refund", "request_approval") graph.add_conditional_edges("request_approval", route_approval) graph.add_edge("process_refund", END) graph.add_edge("escalate", END) checkpointer = PostgresSaver.from_conn_string(DB_URI) app = graph.compile( checkpointer=checkpointer, interrupt_before=["process_refund"] # human approval gate ) ``` The retry logic is deterministic: 3 attempts, then escalate. The human gate pauses the graph and persists state to Postgres. If the server restarts, the graph resumes from the last checkpoint using the same thread_id. Every node execution is logged and replayable. The graph version answers the question the loop version can't: "What exactly happened in this run, and can we reproduce it?" ## The builder's path You don't refactor a working loop into a graph because graphs sound better. You do it because the task broke the loop. Each step in this progression should be driven by a specific failure you've actually hit. Step 1: Start with a loop. If your agent does one thing (answers questions, summarizes documents, classifies tickets), a loop is correct and a graph is overhead. The OpenAI Agents SDK is built around the premise that most agent work is model-driven loops with tool calls and handoffs. That's not a limitation. It's an accurate assessment of most agent use cases as of 2026. Step 2: Externalize state. The moment your agent needs to remember anything beyond the current message history, pull it into a typed dictionary. Define the schema: what fields exist, what types they are, what their defaults are. This costs almost nothing and it means the LLM no longer has to dig through its own conversation history to find the order ID it looked up four steps ago. Highest return, lowest cost. Step 3: Add checkpoints. When you need crash recovery or pause-and-resume, add a persistence layer. This is the actual transition from loop to graph. In LangGraph, the switch is a one-line change in the compile() call: swap MemorySaver() for PostgresSaver.from_conn_string(DB_URI) and your graph survives restarts. Step 4: Separate responsibilities. When different steps need different models, different tools, or different error-handling strategies, split them into named nodes. The research step uses a model with web search access. The drafting step uses a model with a large context window. The review step uses a cheap fast model for quick validation. Each node gets its own config. This is functionally impossible in a single loop body without increasingly brittle conditional logic. Step 5: Branch and parallelize only when the task demands it. Conditional edges and parallel execution are powerful, but they multiply possible execution paths and your testing surface grows with them. Add them when you have genuinely independent subtasks (checking three orders simultaneously) or genuinely different routing paths (billing vs. shipping vs. fraud). Not because the architecture diagram looks impressive.  ## The six signals  When a loop should become a graph. Bookmark this. → 1. State exceeds one context window. If your agent tracks more information than fits in the model's working memory, state needs to live outside the message list. A graph with typed state handles this structurally. A loop handles it by hoping the model remembers. → 2. The run must survive crashes. If losing a run mid-execution is unacceptable (because it took 20 minutes, because it cost $5 in API calls, because a customer is waiting), you need checkpointing. Checkpointing requires named steps. Named steps are a graph. → 3. Human approval is required mid-run. If any step requires a person to review and approve before the agent continues, you need interrupt-and-resume. A loop can't do this without keeping the entire process alive in memory. A graph persists state, shuts down, and restarts cleanly. → 4. The workflow has parallel branches. If the agent needs to do multiple independent things simultaneously (research three topics, check multiple APIs, run generation and validation in parallel), you need parallel node execution. A loop is sequential by definition. → 5. Multiple models or agents must coordinate. If different steps need different models (fast and cheap for classification, slow and expensive for reasoning) or different agents (a researcher and a writer), the routing logic belongs in the graph's edges, not inside one LLM's reasoning. → 6. Execution must be replayable and auditable. If you need to answer "what exactly happened in this run?" after the fact, you need named steps with logged inputs and outputs. A loop gives you a flat message history. A graph gives you a structured execution trace. If none of these six apply, keep the loop. A graph is not an upgrade. It's a response to specific architectural requirements. ## The engineering that makes graphs actually work Switching from a loop to a graph gives you the structure to handle complexity, but the details matter enormously. Idempotency. Every node must be safe to re-execute. If a crash happens after a node sends an email but before the checkpoint is saved, the graph replays that node on recovery. The email node needs to check "did I already send this?" before sending again. Pattern: write a sent_email_id to state before sending, check it at the top of the node on every execution. Side effect isolation. Nodes with side effects (sending messages, writing to databases, calling external APIs) need special care. The safest pattern: separate the decision ("we should send this email with this content") from the execution ("actually send it"). The decision node is safe to replay. The execution node checks for prior execution before acting. Retry policies. Never let the LLM decide whether to retry. Put retry_count in the state, increment it in the node, and use a conditional edge that routes to a fallback after N attempts. Three lines of code in LangGraph. The alternative, letting the model retry inside the loop, is how you get an agent calling a broken tool hundreds of times with no stopping condition. State persistence. Use MemorySaver only in tests. For anything in production, use PostgresSaver or RedisSaver. Set a TTL on checkpoint keys to prevent unbounded storage growth. Purge completed threads older than your audit window. The ActiveWizards production guide for LangGraph recommends capping state at 3,000 tokens per field and using external store IDs for large payloads instead of embedding them in state directly. Termination. Every graph needs at least three exit conditions: a success state (the task completed), a failure state (retries exhausted), and a hard ceiling (max total steps, max total tokens, or a dollar-amount budget cap). Without these, you're trusting the model to know when to stop. It doesn't. Context growth. In any long-running graph, message history grows with every LLM call. The recommended pattern is a summarization node that fires when the message list exceeds a token threshold. The node compresses the history and replaces it. This keeps state lean and prevents the slow reasoning degradation that comes with context window bloat. Concurrency. Parallel nodes need careful scoping. Two nodes writing to the same state field will race. Two nodes calling the same external API might trigger rate limits. Anthropic's Claude Code subagents enforce this with per-subagent tool permissions: a research subagent gets read-only file access and web search, a writer subagent gets edit and shell access but no network (source: Totalum Claude Code Subagents Playbook, June 2026). Scope tools per node the same way. Recovery testing. Test the resume path explicitly. Kill the process mid-graph, restart it, and verify that the graph resumes from the correct node with the correct state using the same thread_id. LangGraph's own documentation recommends this as a mandatory integration test, not an optional one. ## What graphs cost you  Graphs aren't free. They add specific overhead that loops don't have, and pretending otherwise leads to over-engineering. Latency. Every checkpoint is a database write. Every conditional edge is a function call. Every node boundary is a serialization/deserialization cycle. For a five-node graph with Postgres checkpointing, expect 10-50ms of added overhead per run compared to a raw loop. For most applications, invisible. For real-time chat with sub-second response requirements, it might matter. Coordination overhead. Parallel nodes need synchronization points. Error handling multiplies: each parallel branch can fail independently, and the failure of one branch might or might not invalidate the others. You'll write more error-handling code for a graph than you will for a loop. Debugging complexity. A loop has one execution path. A graph with three conditional edges has up to 8 possible paths. Each path needs testing. LangGraph Studio helps by letting you step through execution visually and inspect state at each checkpoint, but the testing surface is inherently larger. State design. A typed state dictionary is more explicit than a message history, but it takes more upfront work. Every field needs a type, a default, and a reducer (how does this field update when multiple nodes write to it?). Get the schema wrong and you'll fight type errors across every node in the graph. More failure paths, period. The thing that makes graphs more reliable (explicit failure handling per node) also means more code to write and maintain. Retry logic, fallback nodes, error handlers, timeout conditions: these are all new code that the loop version didn't need because the loop version trusted the LLM to handle everything. The graph is more reliable precisely because it doesn't trust the LLM with orchestration, but that reliability costs engineering hours. ## The rule that outlasts any framework LangGraph has 34,000 GitHub stars today. CrewAI passed 52,000. OpenAI shipped an Agents SDK. Google launched ADK. Anthropic's Claude Agent SDK supports hierarchical subagent spawning. Microsoft merged Semantic Kernel and AutoGen into the Microsoft Agent Framework 1.0 in April 2026. Every major lab has an opinion about agent orchestration, and those opinions ship on a quarterly cycle. The principle that survives all of them: architecture follows the shape and duration of the task. A three-call agent with no external state doesn't need a graph. A 200-call agent running across four hours with human checkpoints and flaky APIs won't survive as a loop. The answer is never "always use graphs" and never "loops are dead." It's: look at what the task actually requires (persistence, recovery, coordination, auditability, parallelism, human gates, budget constraints) and pick the simplest architecture that handles those requirements without failing. The ReAct loop got agents off the ground. Graph engineering is what keeps them running when the job gets real. LLMs don't think, you do. P.S. I package everything I learn about AI into practical tools. Get my Claude skills bundle: linktr.ee/alex_prompter ## 相关链接 - [Alex Prompter](https://x.com/alex_prompter) - [@alex_prompter](https://x.com/alex_prompter) - [35K](https://x.com/alex_prompter/status/2079531644810138082/analytics) - [arXiv:2210.03629](https://arxiv.org/abs/2210.03629) - ["Agentic Loops" guide](https://datasciencedojo.com/blog/agentic-loops-explained-from-react-to-loop-engineering-2026-guide/) - [LangGraph + Redis integration guide](https://markaicode.com/integrate/langgraph-with-redis/) - [Tom's Hardware](https://www.tomshardware.com/tech-industry/artificial-intelligence/openclaw-creator-burns-through-1-3-million-in-openai-api-tokens-in-a-single-month) - [The Decoder](https://the-decoder.com/for-1-3-million-a-month-openclaw-founder-peter-steinberger-runs-100-ai-agents-that-code-review-prs-and-find-bugs/) - [Totalum Claude Code Subagents Playbook](https://www.totalum.app/blog/claude-code-subagents-totalum) - [linktr.ee/alex_prompter](https://linktr.ee/alex_prompter) - [Upgrade to Premium](https://x.com/i/premium_sign_up) - [7:39 PM · Jul 21, 2026](https://x.com/alex_prompter/status/2079531644810138082) - [35.9K Views](https://x.com/alex_prompter/status/2079531644810138082/analytics) - [View quotes](https://x.com/alex_prompter/status/2079531644810138082/quotes) --- *导出时间: 2026/7/22 09:41:12* --- ## 中文翻译 # 图工程 101:当循环不再足够 **作者**: Alex Prompter **日期**: 2026-07-21T11:39:14.000Z **来源**: [https://x.com/alex_prompter/status/2079531644810138082](https://x.com/alex_prompter/status/2079531644810138082) ---  你的 Agent 在一个简单的 Agent 循环中调用 LLM。它进行推理,选择一个动作,观察结果,然后再次循环。 这种模式一直有效,直到任务需要在中途获得人工批准、针对不稳定 API 的重试策略,以及在服务器重启后不丢失 40 分钟工作进度的能力。  ReAct 循环(Yao et al., 2022, arXiv:2210.03629)是每个 AI Agent 的原子。 推理,行动,观察,重复。 每个主要框架都始于此:LangChain 的 AgentExecutor、OpenAI Agents SDK 的 runner、Anthropic 的 Claude Agent SDK。 循环是正确的。但它也是不完整的。 随着 Agent 接手更漫长、更混乱、多步骤的工作,循环不会以一种戏剧性的方式崩溃。它只是悄无声息地变得不再足够。 图工程是一种实践,它将 Agent 的控制流、状态和故障处理在系统架构中显式化,而不是将它们隐含在单个模型调用内部。 它不取代循环。它构建循环。 这就是这种转变的实际样貌,基于图的 Agent 架构包含什么,以及何时保持一个简单的循环是更明智的选择。 ## 什么是循环,什么是图  循环是一个单次循环:提示模型,让它选择一个动作,执行该动作,将结果反馈回去,重复直到模型说它完成了或者你达到了步骤限制。 ``` messages = [{"role": "system", "content": SYSTEM_PROMPT}] messages.append({"role": "user", "content": user_request}) for step in range(MAX_STEPS): response = llm.call(messages) if response.stop_reason == "end_turn": return response.text for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({"role": "tool", "content": result}) ``` 这就是整个 Agent。LLM 决定一切:下一步做什么,何时停止,如何处理错误。编排是不可见的,因为只有一条路径。 图是一组通过显式转换(边)连接的命名步骤(节点),以及在整个运行期间持续存在的共享状态。每个节点都是一个函数。 边可以是条件的:“如果分类器返回‘toxic’(有害),则路由到审核节点而不是响应节点。” 状态是一个类型化的字典,每个节点都可以读写。运行时处理排序、检查点和恢复。 ``` graph = StateGraph(AgentState) graph.add_node("research", research_node) graph.add_node("draft", draft_node) graph.add_node("review", review_node) graph.add_node("human_approval", human_gate) graph.add_edge("research", "draft") graph.add_edge("draft", "review") graph.add_conditional_edges("review", route_review) graph.add_edge("human_approval", END) ``` 循环是图的一个特例:一个只有一个节点且有一条边指向自己的图。 两者都有用。问题在于其中一种何时变得不再足够。 ## 循环开始失效的地方 一个具体的任务,三个版本,逐步升级的需求。 版本 1:循环运行良好 你构建了一个通过搜索知识库来回答客户问题的 Agent。循环是:接收问题,搜索文档,生成答案,返回。 三个步骤,没有分支,没有需要持久化的状态。一个带有工具调用的简单循环在这里是完美的。不要把它搞复杂。 版本 2:裂缝出现 现在 Agent 处理退款请求。在搜索知识库后,它通过 API 检查订单状态,计算退款金额,并且在处理任何事情之前需要经理的批准。 三个问题同时出现。 批准步骤意味着 Agent 必须暂停,等待一个人(几分钟,几小时,也许过夜),然后准确地从它停止的地方恢复。 一个简单的内存循环无法持久地暂停。如果服务器在经理吃午饭时重启,整个运行就丢失了。 订单 API 不稳定。它大约有 10% 的时间超时。循环会重试,但重试逻辑存在于 LLM 的推理内部。 模型决定是否再试一次。 有时它会重试 15 次。有时它在一次失败后就放弃,并产生一个订单状态的幻觉。 2026 年的一个 documented 案例显示,一个 Agent 在五分钟内调用了一个损坏的工具 400 次,因为没有任何外部强制执行限制(来源:Data Science Dojo,“Agentic Loops”指南,2026 年 6 月)。 退款计算依赖于三个先前的结果(订单查找、退货政策、折扣历史)。所有这些上下文都位于消息列表中,该列表随着每次迭代而增长。 到第 12 步时,模型正在处理 8000 多个 token 的自身先前推理,仅仅是为了做一个简单的算术决定。 版本 3:循环无法支撑  Agent 现在处理复杂的支持案例:跨不同订单的多项退货、部分退款、商店积分与原始支付方式的对比、升级到专门团队处理计费、运输或欺诈问题。 其中一些可以并行运行(同时检查每个订单)。一些有依赖关系(在评估所有单个订单之前无法计算总退款)。一些需要不同的模型(用于订单查找的廉价快速模型,用于政策解释的昂贵推理模型)。 无论多么巧妙的提示词,都无法让一个线性循环干净利落地处理这个问题。你不是遇到了模型智能的极限。你是遇到了架构的极限。 ## Agent 图的解剖结构 每个组件的存在都是因为单个循环无法处理它能做的事情。 节点是函数。每个节点只做一件事:调用 LLM,执行工具,验证数据,转换状态。 节点接收当前状态作为输入并返回更新的字段。 对于下面的实现示例,我使用 LangGraph,因为它的节点和边模型使架构易于查看。无论使用什么框架,相同的原则都适用。 在 LangGraph 中,每个节点都是一个普通的 Python 函数,它接收并返回一个类型化的状态字典。 边定义转换。静态边总是路由到同一个下一个节点。条件边运行一个路由函数,该函数检查状态并返回下一个节点的名称。 这就是分支所在之处。 状态是一个流经整个图的类型化字典。与所有内容都存在于消息历史记录中的循环不同,图状态是显式的。 你预先定义模式: order_id: str, refund_amount: float, approval_status: Literal["pending", "approved", "denied"], retry_count: int. 每个节点读取它需要的内容并写入它产生的内容。状态取代了对 LLM 会记得六个工具调用前发生了什么的期望。 检查点是在每个节点执行后保存的状态快照。 如果进程崩溃,图会从最后一个检查点恢复,而不是重新开始。LangGraph 支持 PostgreSQL、Redis、SQLite 和 MongoDB 作为检查点后端。 Redis 每个检查点操作增加大约 1-2ms 的延迟(来源:LangGraph + Redis 集成指南,Markaicode,2026 年 5 月)。 Postgres 较慢但可查询,这对于审计跟踪很重要。 人工门是图暂停并等待外部输入的中断点。 状态持续存在,进程可以完全关闭,当人类响应时(几分钟后,几小时,几天后),图加载检查点并准确地从它停止的地方继续。 这是图相对于循环的最大的架构优势。循环无法暂停,因为它存在于内存中。图可以暂停,因为它的状态存在于数据库中。 路由器是检查状态并决定接下来发生什么的条件边函数。 路由器可能会检查 retry_count,如果超过 3 则路由到错误处理程序;检查 approval_status,如果已批准则继续;或者检查分类器输出并将运行发送到相应的专用节点。 终止条件是显式的。与循环中 LLM 决定何时完成(有时并不决定)不同,图具有命名的结束状态。 图在以下情况下结束:到达一个边指向 END 的节点;路由器根据状态值确定任务完成;或者触发硬上限(最大步数,最大 token,预算上限,超时)。 Peter Steinberger 每月 130 万美元的 token 账单,在 OpenClaw 项目上运行的 100 个 Codex 实例中消耗了 6030 亿 token,这是一个极端的例子,说明在没有硬预算限制的情况下运行长时间运行的 Agent 会发生什么(来源:Tom's Hardware,2026 年 5 月 17 日;The Decoder,2026 年 5 月 16 日)。 可观测性是结构性的。 因为每个节点都被命名并且每次转换都被记录,你可以准确追踪任何运行中发生了什么:哪些节点执行了,按什么顺序,每个花了多长时间,每个消耗了多少 token。 LangGraph 为此与 LangSmith 集成。 OpenAI Agents SDK 默认启用了内置跟踪。这些不是附加组件。它们是将控制流显式化的结果。 ## 相同的工作流,两种架构 以下是两种风格的退款处理 Agent。区别不在于代码长度。而在于当出现问题时会发生什么。 循环版本: ``` messages = [{"role": "system", "content": REFUND_PROMPT}] messages.append({"role": "user", "content": customer_request}) for step in range(MAX_STEPS): response = llm.call(messages) if response.stop_reason == "end_turn": return response.text for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({"role": "tool", "content": result}) ``` 如果订单 API 超时,LLM 决定是否重试。如果服务器重启,运行将丢失。如果经理花费两小时批准,进程会停留在内存中,或者死亡。 如果退款计算错误,你无法重放运行以找出原因。 图版本: ``` from langgraph.graph import StateGraph, END from langgraph.checkpoint.postgres import PostgresSaver class RefundState(TypedDict): order_id: str order_data: dict | None refund_amount: float approval: str # "pending" | "approved" | "denied" retry_count: int messages: list def lookup_order(state: RefundState) -> dict: try: data = order_api.get(state["order_id"]) return {"order_data": data, "retry_count": 0} except TimeoutError: count = state["retry_count"] + 1 return {"order_data": None, "retry_count": count} def route_after_lookup(state: RefundState) -> str: if state["order_data"] is None and state["retry_count"] < 3: return "lookup_order" # retry if state["order_data"] is None: return "escalate" # max retries hit return "calculate_refund" # success graph = StateGraph(RefundState) graph.add_node("lookup_order", lookup_order) graph.add_node("calculate_refund", calculate_refund) graph.add_node("request_approval", request_approval) graph.add_node("process_refund", process_refund) graph.add_node("escalate", escalate_to_human) graph.set_entry_point("lookup_order") graph.add_conditional_edges("lookup_order", route_after_lookup) graph.add_edge("calculate_refund", "request_approval") graph.add_conditional_edges("request_approval", route_approval) graph.add_edge("process_refund", END) graph.add_edge("escalate", END) checkpointer = PostgresSaver.from_conn_string(DB_URI) app = graph.compile( checkpointer=checkpointer, interrupt_before=["process_refund"] # human approval gate ) ``` 重试逻辑是确定性的:3 次尝试,然后升级。 人工门暂停图并将状态持久化到 Postgres。如果服务器重启,图使用相同的 thread_id 从最后一个检查点恢复。 每个节点执行都被记录且可重放。图版本回答了循环版本无法回答的问题:“这次运行到底发生了什么,我们可以重现它吗?” ## 构建者的路径 你不会因为图听起来更好就将一个工作的循环重构为图。 你这样做是因为任务打破了循环。这个进展中的每一步都应该由你实际遇到的具体失败来驱动。 步骤 1:从循环开始。 如果你的 Agent 只做一件事(回答问题,总结文档,分类工单),循环是正确的,图是开销。 OpenAI Agents SDK 是围绕这样一个前提构建的:大多数 Agent 工作都是带有工具调用和交接的模型驱动循环。 这不是一个限制。这是对截至 2026 年大多数 Agent 用例的准确评估。 步骤 2:外部化状态。 当你的 Agent 需要记住当前消息历史记录以外的任何内容时,将其拉入一个类型化的字典中。 定义模式:存在哪些字段,它们是什么类型,它们的默认值是什么。 这几乎没有任何成本,这意味着 LLM 不再需要翻阅它自己的对话历史来找到它在四步前查找的订单 ID。 最高回报,最低成本。 步骤 3:添加检查点。 当你需要崩溃恢复或暂停和恢复时,添加一个持久层。 这是从循环到图的实际转变。在 LangGraph 中,这种转变是 compile() 调用中的一行更改:用 PostgresSaver.from_conn_string(DB_URI) 交换 MemorySaver(),你的图就可以在重启中幸存下来。 步骤 4:分离职责。 当不同的步骤需要不同的模型、不同的工具或不同的错误处理策略时,将它们拆分为命名的节点。 研究步骤使用具有网络搜索访问权限的模型。起草步骤使用具有大上下文窗口的模型。审查步骤使用廉价快速模型进行快速验证。 每个节点都有自己的配置。在单个循环体中,如果没有越来越脆弱的条件逻辑,这在功能上是不可能的。 步骤 5:仅在任务需要时进行分支和并行化。 条件边和并行执行很强大,但它们成倍地增加了可能的执行路径,你的测试表面也随之增长。 当你有真正独立的子任务(同时检查三个订单)或真正不同的路由路径(计费与运输与欺诈)时,添加它们。不是因为架构图看起来令人印象深刻。  ## 六个信号  循环何时应该变为图。收藏这个。 → 1. 状态超过一个上下文窗口。如果你的 Agent 跟踪的信息超过了模型工作记忆的容量,状态需要存在于消息列表之外。具有类型化状态的图可以在结构上处理这个问题。循环通过寄希望于模型记得来处理这个问题。 → 2. 运行必须在崩溃中幸存。如果在执行中途丢失运行是不可接受的(因为它花了 20 分钟,因为它在 API 调用上花费了 5 美元,因为有一个客户在等待),你需要检查点。检查点需要命名步骤。命名步骤就是一个图。 → 3. 运行中需要人工批准。如果任何步骤需要人员在 Agent 继续之前审查和批准,你需要中断和恢复。循环如果不将整个过程保持在内存中就无法做到这一点。图持久化状态,完全关闭,然后干净地重新启动。 → 4. 工作流有并行分支。如果 Agent 需要同时做多个独立的事情(研究三个主题,检查多个 API,并行运行生成和验证),你需要并行节点执行。根据定义,循环是顺序的。 → 5. 多个模型或 Agent 必须协调。如果不同的步骤需要不同的模型(用于分类的快速廉价模型,用于推理的缓慢昂贵模型)或不同的 Agent(一个研究员和一个作家),路由逻辑属于...
3 3 Years of Graph Engineering with LangGraph 文章回顾了 LangGraph 三年来的发展,探讨了将智能体系统建模为图(Graph)的实践与价值。作者分析了何时使用图结构以平衡确定性与自主性,并指出生产级智能体通常需要循环和动态转换。最后,文章强调图工程并非全新概念,但随着节点的进化,现在的图更多是在编排智能体而非单一的 LLM 调用。 技术 › Agent ✍ Sydney Runkle🕐 2026-07-22 LangGraphGraph EngineeringAgentLLM架构设计循环确定性工作流
R Run Your Harness Outside of the Sandbox (Why and How) 本文探讨了2026年以来关于Agent运行位置的争论,指出行业趋势是将Agent运行在沙箱之外。作者详细解释了沙箱内运行的三个主要问题:爆炸半径、信任边界和沙箱的间歇性运行,并提出了将沙箱作为工具暴露的正确架构,最后提供了基于Vercel AI SDK的实现示例和生产环境中的挑战。 技术 › Agent ✍ Nathan Flurry🕐 2026-07-28 Agent沙箱架构设计DevOps后端LLM安全性生产环境Vercel AI SDK状态管理
G Graph Engineering:从 0 到 1 小白完整教程 文章介绍了 Graph Engineering,一种通过流程图协调多个 AI 协作完成复杂任务的方法。它将复杂任务拆解为节点、边和状态,解决了单 Loop 应对复杂任务时的局限性。文章详细解析了 Graph 的核心概念、与 Loop 的关系、四个核心模块及具体实践模板,并提供了新手学习路径。 技术 › Agent ✍ Adrian Punk🕐 2026-07-27 Graph EngineeringAgentLLMAI 协作工作流Loop Engineering教程节点设计状态管理AI 架构
彻 彻底告别Loop Engineering:一文读懂 Graph Engineering 本文介绍了AI Agent工程从Prompt到Loop再到Graph的演进。Graph Engineering通过图结构重新规划任务关系,实现并行处理、明确依赖、隔离失败,从而解决线性流程在复杂任务中效率低、易失控的问题。 技术 › Agent ✍ AI超元域🕐 2026-07-21 Graph EngineeringAgentLLM架构设计Claude Code工作流并行处理Dynamic Workflows
什 什么是图工程及其走红原因解析 文章解释了从“循环工程”到“图工程”的技术演进。循环是简单的单一代理执行模式,而图(由节点、边和状态组成)通过可视化的流程图处理复杂逻辑和多代理协作。文章介绍了如何使用 LangGraph 构建第一个图,并指出在逻辑变得复杂时应从循环升级到图。 技术 › Agent ✍ Alex Martin🕐 2026-07-21 Graph EngineeringLangGraphAgentLoopsLLMClaudeOpenAI教程
H Hermes Agent 架构详细拆解:工业级 Agent 框架底层运行时揭秘 本文详细拆解了 Hermes Agent 的底层架构,将其定义为工业级运行时而非简单的 LLM 循环。文章类比前端工程模式,阐述了 Agent = Harness + Model 的核心公式。重点解析了从平台入口、适配器层、事件总线、GatewayRunner 核心调度层到 AI Agent 执行层的五层架构,揭示了 Hermes 如何通过共享线程池、会话复用和生命周期管理,实现高并发、有状态且安全可靠的 Agent 运行环境。 技术 › Hermes ✍ MateMatt🕐 2026-07-07 AgentHermes架构设计LLM事件总线多线程源码解析运行时HarnessReAct
递 递归智能体推理性能测试:DSPy 与 Daytona 的 100 任务实证研究 文章介绍了 Fleet-RLM(基于 DSPy ReAct 智能体与 Daytona 沙箱)在 100 个长程推理任务中的表现。实验对比了递归执行与直接 LLM 推理,结果显示递归执行将准确率从 13% 提升至 33%,并完全消除了任务失败。文章详细分析了逻辑、CS、棋类、数学和化学等不同领域的性能差异,揭示了递归架构在工具调用和错误恢复上的优势。 技术 › Agent ✍ Zachary BENSALEM🕐 2026-05-04 LLMAgentDSPy推理测试DeepSeek沙箱执行ReAct架构设计
H How to master graph engineering 本课程教授如何构建 AI 智能体图,涵盖图的基本概念、关键模式(如菱形模式)、停止规则及人工审批环节。包含三个实战案例:深度研究台、SEO 内容生成器和市场推广套件,旨在提升业务效率并控制成本。 技术 › Agent ✍ Machina🕐 2026-07-23 AgentGraphLLMClaudeWorkflow工程化自动化架构设计效率实战
G Graph Engineering: 在一个窗口构建 1000+ Agent 循环的完整指南 本文介绍了 Graph Engineering 的概念,即从线性的 Agent 循环转向图结构的工作流。文章详细讲解了如何利用 Claude Code 的动态工作流功能,通过五个步骤构建并行处理任务的 Agent 图,以解决上下文限制和执行效率问题,并探讨了在扩展到 1000+ Agent 时可能遇到的挑战及解决方案。 技术 › Agent ✍ codila🕐 2026-07-22 Graph EngineeringClaude CodeWorkflowAgent并行处理动态工作流LLMDevOps
图 图工程究竟是什么? 文章探讨了AI工程领域的新术语“图工程”。它并非新发明,而是对多Agent协作系统的宏观视角,通过节点和边映射工作流、状态和依赖关系,解决复杂系统的架构与调试问题。 技术 › Agent ✍ Towards AI🕐 2026-07-20 Graph EngineeringAgentWorkflowAI架构Claude动态工作流状态管理
从 从 CoT 到 ReAct:用 Python 搭建 LLM Agent 实战指南 本文通过 Python 演示了如何搭建一个具备规划、工具调用、验证和复盘能力的完整 LLM Agent。教程详细拆解了 Plan-and-Solve、ReAct、Verification、Self-Refine 和 Reflexion 五个核心模块,强调了结构化数据控制流程的重要性,并提供了工程落地的具体代码实现与原则。 技术 › Agent ✍ Mr Panda🕐 2026-07-19 LLMAgentPythonReActCoT工程实践ReflexionPrompt Engineering教程
L Let's build Claude Code's harness (step-by-step) 本文深入解析了Claude Code的“harness”架构,解释了为何简单的模型调用不足以构建可靠的代码代理。作者通过CrewAI框架逐步重建了包括核心循环、工具管理、规划机制在内的关键组件,揭示了通过工程化手段弥补模型差距的方法。 技术 › Claude Code ✍ Akshay🕐 2026-07-16 Claude CodeAgentCrewAIHarness Engineering代码代理工具调用LLM工程实践架构设计Context Engineering