Bible Network Crypto DeFi Onchain RWA AI Agent Stablecoin Chain SAFU CryptoTax DeFAI AGI Claude Me Claude Skill Claude Design Claude Cowork
Independent Media
Not affiliated with any project
Deconstructing Autonomous Agents in Crypto
aiagent-bible.com
LATEST
LangGraph Deep Dive: Building a Cyclical DeFi Agent Step by Step, and Three Pitfalls Along the Way  ·  How to Test an AI Agent Before Production: A Practical Testing Framework, and Why Traditional Unit Tests Aren't Enough  ·  Why Your Agent's Output Looks Right But Isn't: The Reliability Gap Between Format and Truth  ·  How Beginners Should Choose Their First Agent Framework: Stop Asking 'Which Is Strongest,' Ask 'Which Gets Me Running Today'  ·  Prompt Injection Defense for Onchain Agents: Why 'Tell the Model Not to Trust External Instructions' Is the Least Useful Defense, and the Layered Architecture That Actually Works  ·  Agent Retry and Error Handling Strategy Design: Why 'Just Retry on Failure' Is the Default Logic Most Likely to Bankrupt You
frameworks

LangGraph Deep Dive: Building a Cyclical DeFi Agent Step by Step, and Three Pitfalls Along the Way

30-Second Version · For the impatient
LangGraph makes loop logic easier to write, and correspondingly, easier to accidentally write a runaway loop without discipline. None of the three pitfalls this article records are things an official tutorial tells you, because official examples are usually designed too simply to trigger these scenarios at all.

Full Explanation +
01 · Why did this happen?

The article mentions 'draw the graph first, then write code' — what specific tool or method should be used to draw this graph? Do I need professional diagramming software?

No professional or complex tool is needed — the goal at this stage is helping yourself clarify logic, not producing a formal document, so the simplest, fastest way to draw it is fine.

Pen and paper or a whiteboard is the most recommended starting point. The purpose of drawing is rapid iteration — while thinking through the flow design, you'll very likely draw one version, then realize a particular node's responsibility division is wrong and needs adjusting. With complex diagramming software, the friction cost of adjusting slows down this thinking process; with pen and paper or a whiteboard, erasing and redrawing costs almost nothing, better suited to this stage's need for quickly trying different design options.

If you need to save or share it, a simple online whiteboard tool or flowchart tool works, but there's no need to pursue visual polish — the key is the logical relationship between nodes and edges being clear; aesthetic details like color, font, and layout are completely unimportant at this stage.

Minimum information to include when drawing: each node's name (corresponding to the function name you'll write later), the edges between nodes (marking whether it's an unconditional or conditional edge, with conditional edges briefly noting the judgment condition), and which key fields the state object is expected to use (no need to be exhaustive — list the most core few first; debugging-purpose fields like the ones mentioned in the article are usually discovered and added back during implementation as the need arises, not something you need to fully anticipate at the drawing stage).

Drawing's real value isn't in how complete the resulting diagram is — it's that the act of drawing forces you to think through the entire flow's structure clearly in your head (or on paper) before writing any code. Many design flaws or inconsistencies are easier to spot at the drawing stage, looking at the overall structure with your eyes, than while fumbling through it while writing code — because when writing code, attention easily gets consumed by a single node's implementation details, making overall flow problems harder to see clearly.

02 · What is the mechanism?

The article's first pitfall mentions 'the state object should retain intermediate reasoning grounds,' but if every node logs a large volume of debugging fields, wouldn't that make the state object overly bloated, actually hurting performance or adding complexity?

This is a genuinely real tradeoff, but it usually doesn't require choosing strictly between 'record nothing' and 'record everything' — there are several practical compromises worth considering.

Tiered logging: core fields versus debugging fields. Split state object fields into two categories — 'core fields' that the flow's actual operating logic needs (e.g., current_rates, should_execute — without these, the flow can't run), and 'debugging fields' that the flow's operation doesn't directly depend on but are very helpful for post-hoc tracing (e.g., evaluation_reasoning). Most LangGraph state management mechanisms usually have little performance impact from field count alone (a state object's size, relative to a single LLM call's cost, is usually negligible), so the performance overhead from 'logging a somewhat larger number of debugging fields' is rarely a genuine bottleneck in practice — no need to over-worry about it.

Log only at key nodes, not equally at every node. Not every node needs equally detailed debug logging — usually 'nodes making key decisions' (like evaluate_opportunity in this case) are most worth logging in detail, since these are the segments most needing to be traced; a plain data-query node or format-conversion node can have much leaner debug fields, since this type of node's logic is usually straightforward, and when something goes wrong, it's easier to pinpoint directly from the code without needing extra fields to aid tracing.

Debugging fields can be designed as 'cleanable after the fact' rather than permanently retained. If concerned that debugging fields accumulating long-term make the state object overly bloated, you can clear or remove these debug-purpose intermediate fields once a task completes normally and the result is confirmed to match expectation, keeping only the core fields related to the final conclusion — the debugging fields' value mainly shows up in the scenario of 'this execution had a problem, needs diagnosing'; once confirmed this execution was fine, the marginal value of continuing to retain those fields drops.

Core judgment principle: rather than worrying 'will logging too much drag down performance,' it's more worth prioritizing whether 'this field can help me pinpoint a problem faster during some future debugging session' — in most cases, the debugging efficiency gain from moderately logging more debug fields far outweighs the performance overhead those fields cause, unless your Agent system has extremely strict performance requirements (a millisecond-level latency-sensitive scenario) — otherwise there's no need to over-trim debugging information out of performance concerns.

03 · How does it affect me?

The article's third pitfall mentions 'loops need a global cap.' If my Agent's task inherently requires long-term continuous operation (e.g., monitoring the market non-stop for 24 hours), how should a reasonable cap be set in this case, rather than just not setting one at all?

For an Agent that inherently needs long-term continuous operation, the cap design thinking needs to shift from 'limiting total execution count' to 'limiting resource consumption per query cycle, with periodic count resets,' rather than not setting a cap at all.

Core thinking shift: from 'total count cap' to 'periodically reset cap.' A 24-hour non-stop monitoring Agent isn't suited to a design like 'forcibly stop after only 50 total queries' (since this would forcibly halt the Agent shortly after it starts running, failing to achieve the 'continuous monitoring' task goal). A more suitable design is changing the query_count counter to 'how many times queried within this hour,' resetting hourly, while setting a cap of 'at most N queries per hour' — this allows continuous long-term operation while still preventing unlimited resource consumption under abnormal circumstances (e.g., a bug in some judgment logic causing query frequency to unexpectedly spike).

Additionally layer cost-rate monitoring, not just count. Beyond limiting query count, you can also layer a metric like 'how much accumulated API call cost within this hour' — if the cost rate clearly exceeds the normal-operation expected range (e.g., usually $0.5/hour, suddenly spiking to $5), even if query count hasn't hit its cap yet, an alert should trigger, since this usually indicates something has gone abnormal (e.g., query logic unexpectedly entered a denser-than-expected loop), worth intervening early rather than waiting until the count cap triggers to discover the problem.

Separate the concepts of 'continuous operation' and 'unlimited operation.' 'Continuous operation' means the Agent's own lifecycle is long (possibly days, weeks, or longer) — this is determined by task requirements, a reasonable design; 'unlimited operation' means the Agent internally has no mechanism at all to detect and intercept abnormal resource-consumption patterns — this is a design flaw that should be avoided. The two don't conflict — you can design an Agent with a very long lifecycle that runs continuously, while still setting an explicit resource-consumption cap for each of its internal operating cycles (hourly, daily), triggering an alert or pause once a particular cycle abnormally exceeds it, resuming after human confirmation, rather than having the entire Agent stop completely just because a single cap was triggered.

Concrete implementation suggestion: beyond the original query_count, the state object can additionally include period_start_timestamp (recording the current counting cycle's start time) and period_cost_accumulated (recording the current cycle's accumulated cost) — the conditional edge's logic for judging 'has this cycle's cap been exceeded' checks both count and cost dimensions simultaneously, triggering an alert mechanism if either exceeds threshold, not just a plain count check.

04 · What should I do?

If I want to expand this article's DeFi yield-optimization Agent into a multi-agent system (e.g., splitting 'query' and 'execute' into two independent Sub-agents), how should the architecture be adjusted with LangGraph?

This extension corresponds exactly to the 'nested graph' design pattern discussed in the earlier LangGraph entry's Q4. Concrete adjustment directions are as follows.

Split the existing single graph into an outer Orchestrator graph plus two inner Sub-agent graphs. The original query_rates and evaluate_opportunity nodes can be merged into an independent 'query-and-evaluation Sub-agent' subgraph; the execute_rebalance node can become independent as an 'execution Sub-agent' subgraph. The outer Orchestrator graph's node content becomes higher-level steps like 'call the query-evaluation Sub-agent,' 'call the execution Sub-agent,' and 'wait,' with the original detailed nodes' implementation logic encapsulated into their corresponding Sub-agent subgraphs.

State object design should adjust to match this split. The original single graph shared one flat state object; after splitting into a multi-agent architecture, consider letting each Sub-agent subgraph have its own internal, more detailed state (the query-evaluation Sub-agent internally may need more intermediate-calculation-process fields), while the outer Orchestrator graph's state object retains only key information needing cross-Sub-agent sharing (the summary-level information mentioned in the earlier Handoff entry, like 'task goal,' 'known progress,' 'handoff reason'), rather than flattening every detail into the same flat state.

Permission scope should converge with the split. This extends the principle discussed in the earlier least-privilege entry and multi-agent system sections — the query-evaluation Sub-agent only needs read-only access to market data, no transaction-signing capability at all; the execution Sub-agent is the one that needs to hold actual transaction-signing permission, and should apply the 'pre-execution re-verification' from the earlier Handoff entry's Q4 to any recommendation passed from the Orchestrator calling it, rather than blindly trusting the query-evaluation Sub-agent's conclusion.

The practical benefit of this split: if you later want to independently optimize the 'query and evaluation' segment's logic (adding a more complex multi-protocol comparison strategy), you can modify only the corresponding Sub-agent subgraph without touching the execution segment's code — the two changes stay independent; similarly, if you later want to add more rigorous risk control to the execution segment (the earlier-mentioned dynamic slippage calculation, pre-execution re-verification), you only need to modify the execution Sub-agent, without affecting the query-evaluation logic. This modular architecture is a larger-scale application of the same principle behind the article's second pitfall (breaking conditional logic into named functions for maintainability) — whether at the function level or the Sub-agent level, clearly splitting responsibilities into independently testable units is the same engineering discipline expressed at different scales.

Full Content +

Knowing LangGraph expresses loops and conditional branches with a graph structure is one thing; actually building an Agent that uses these features is another. This article won't repeat LangGraph's basic concepts (node, edge, state definitions) — instead, it walks through the actual design and implementation process using a concrete DeFi yield-optimization Agent as an example, and records several genuine pitfalls hit along the way. These pitfalls usually don't show up in official getting-started tutorials, since official examples are typically deliberately kept simple and don't trigger these edge cases.

Starting From Requirements: Draw the Graph First, Then Write Code

This Agent's task: periodically check interest rates across multiple lending protocols, and if an arbitrage opportunity worth executing is found (moving funds from a lower-rate protocol to a higher-rate one, still profitable after Gas costs), execute the rebalance; if no good opportunity is found, wait a while and check again. Before writing any code, draw this flow as a graph first: `query_rates` (query all protocols' current rates) → `evaluate_opportunity` (calculate whether there's arbitrage margin worth executing, producing a confidence score) → a conditional edge branches: confidence high enough, proceed to `execute_rebalance`; confidence not high enough, proceed to `wait`, then loop back to `query_rates` after waiting. Only after drawing this graph do you start writing the corresponding node functions — this order matters; establishing the flow's structure first, then filling in each node's implementation details, keeps overall logic consistency far more manageable than designing the flow while writing code simultaneously.

Pitfall One: State Object Fields Designed Too Coarsely, Causing Debugging Difficulty

The first implementation's state object only had the most basic fields: `current_rates`, `decision`, `should_execute`. This design was functionally workable, but ran into a problem during actual testing — when one execution's result didn't match expectation (the Agent judged it shouldn't execute, but there was clearly a decent-looking arbitrage opportunity in the market), there was no way to trace back 'what data the Agent actually based this judgment on,' because the state object only retained the final conclusion, not the intermediate reasoning grounds. The fix was designing the state object's fields more finely, adding `rate_snapshot_timestamp` (the timestamp of the rate query), `opportunity_confidence` (the confidence score itself, not just the final boolean decision), and `evaluation_reasoning` (a brief record of the key grounds at evaluation time). These fields look redundant during normal operation, but during debugging or post-hoc analysis of 'why didn't it execute this time,' they're the only basis for reconstructing the decision process. The lesson from this pitfall: state object field design can't only consider 'what the flow needs for normal operation' — it also needs to consider 'what I need to know to diagnose when something goes wrong.'

Pitfall Two: Conditional Edge Logic Written Too Opaquely, Hard to Maintain

The first version's conditional edge logic was written directly as a complex nested if-else, mixing three conditions: 'is confidence high enough,' 'has enough time elapsed since the last execution,' and 'is the current Gas price acceptable.' This logic worked, but a few weeks later when going back to adjust a threshold, it was hard to see at a glance the priority and interaction between these three conditions. The fix was breaking this complex conditional judgment into several named helper functions (e.g., `is_confidence_sufficient()`, `is_cooldown_elapsed()`, `is_gas_price_acceptable()`), with the conditional edge's main logic only responsible for calling these functions and combining their results, not writing nested conditionals directly. This fix didn't affect functionality — it was purely for readability and future maintenance convenience — echoing the earlier framework-selection article's point that 'the graph structure's value is letting the flow be visually understood.' If the logic inside a conditional edge is itself written like a tangled mess, most of the readability advantage the graph structure brings gets undermined.

Pitfall Three: No Explicit Exit Condition for the Loop, Nearly Ran a Runaway Loop During Testing

This was the closest of the three pitfalls to becoming a production incident. The first version's `wait` node's design logic was 'wait a fixed amount of time, then go back to `query_rates` to re-check,' without limiting how many rounds this loop could run in total. During a test scenario where the market didn't present a good enough arbitrage opportunity for several consecutive days, this Agent kept continuously querying, evaluating, waiting, and re-querying, accumulating far more API call cost than expected — and since there was no cap, theoretically this loop could run indefinitely. The fix was adding a `query_count` field to the state object, incremented each time entering the `query_rates` node, and adding a rule to the conditional edge: if `query_count` exceeds a certain cap (e.g., a maximum of 50 rounds of querying per task), forcibly end the task regardless of confidence score, flagging it as 'no good opportunity found for now, needs human decision on whether to restart' rather than letting it run indefinitely. This pitfall directly corresponds to the 'loop failure needs a global cap' principle discussed in the earlier retry-strategy article, except here the 'loop' isn't a retry loop triggered by failure — it's a normal flow whose design itself has a loop structure. But both face the same risk: an uncapped loop can eventually become a runaway cost black hole.

What This Means for Your Practice

If you're building an Agent with a loop structure using LangGraph, these three pitfalls are worth considering upfront at the design stage, rather than discovering them only during testing or after launch: state object fields, beyond satisfying normal flow needs, should also support post-hoc debugging; conditional edge logic should be broken into named functions rather than written as a tangled nested conditional; any loop structure must have an explicit hard cap independent of the business logic itself, avoiding indefinite operation without you noticing. None of these three points are LangGraph-specific problems — they're fundamentally engineering discipline any system with a loop structure should observe; it's just that LangGraph makes loop logic easier to write, and correspondingly, easier to accidentally write a runaway loop without discipline.

Diagram
Layered Architecture: From Single Graph to Orchestrator + Subgraphs分層架構圖:上層呈現原始的單一 LangGraph 圖(query → evaluate → conditional branch → execute/wait),下層呈現擴展後的分層架構(Orchestrator 圖呼叫兩個獨立的 Sub-agent 子圖),用箭頭連接兩層,展示從單一圖到巢狀多 Agent 架構的演From Single Graph to Layered ArchitectureStage 1: Single Graph (this article)query_ratesevaluate_opportunityexecute_rebalancewait → loop back↓ extends to ↓Stage 2: Orchestrator + Nested SubgraphsOrchestrator Graphcalls Sub-agents, holds no execution permissionQuery+Evaluate SubgraphRead-only market accessOwn detailed internal stateExecution SubgraphHolds signing keyRe-verifies before executingAI Agent Bible · aiagent-bible.com
Feel free to share. Please credit the source.
Ask a Question
Please enter at least 10 characters
Related Articles
How to Build Your First Onchain Agent: Minimum Viable Architecture from Scratch, and the Pre-Deployment Checklist
beginners · Jun 29
DeFi Agent Framework Deep Comparison: Why LangGraph Leads, and How Other Frameworks Actually Perform in DeFi
frameworks · Jul 02
Five Mainstream Onchain Agent Frameworks in 2026: LangGraph, ElizaOS, AutoGen, Olas, ZerePy — Who Each One Is For
frameworks · Jun 27
AutoGen vs LangChain vs ElizaOS: Which Framework to Choose — A Complete Decision Guide for Crypto AI Agent Developers
frameworks · Jun 20