我们选择LangChain构建编码代理的原因
We chose LangGraph to build our coding agent

原始链接: https://www.qodo.ai/blog/why-we-chose-langgraph-to-build-our-coding-agent/

Qodo 使用 LangGraph 构建了一个动态的 AI 编码助手,此前他们发现 Claude Sonnet 3.5 比他们之前结构化的工作流程提供了更灵活的方法。LangGraph 的基于图的结构使他们能够在适应性和他们坚持的编码最佳实践之间取得平衡,创建了一个状态机,其中节点代表上下文收集、规划、执行和验证等步骤。其 API 有助于创建一个自文档化的工作流程,可以轻松地根据新的 LLM 功能进行重新校准。 LangGraph 的基于节点的架构促进了可重用性,上下文收集和验证节点等组件可以插入各种工作流程中。内置的状态管理,包括通过 Postgres、SQLite 或内存检查点进行持久化,是一个重要的优势。 挑战包括文档不完整以及测试和模拟方面的困难,尤其是在 IDE 交互方面。尽管这些方面还有改进空间,但 LangGraph 证明了其灵活性和成熟性,使 Qodo 能够从概念验证阶段过渡到生产阶段。

Hacker News评论员CharlieDigital批评了qodo.ai一篇关于他们选择使用LangGraph构建编码代理的博文。文章解释了为什么LangGraph比自定义解决方案更受青睐,但未能证明LangGraph优于其他类似框架,例如Mastra。评论员认为文章中提到的优势(大概是灵活性等)也适用于其他替代方案。 他还提到了早期采用的常见问题:文档挑战,强调了由于LangGraph快速发展而导致的文档不完整或过时。他与早期使用微软Semantic Kernel的经验进行了类比,指出大量示例和集成测试的重要性。最后,他提出了一个发人深省的问题:哪家AI初创公司正在利用LLM自动生成高质量的文档、代码示例和库指南?这突出了需要更好的工具来支持开发者应对快速发展的AI框架。
Hacker News 最新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 我们为什么选择 LangGraph 来构建我们的编码代理 (qodo.ai) 8 分 jimminyx 2 小时前 | 隐藏 | 过去 | 收藏 | 讨论 加入我们,参加 6 月 16-17 日在旧金山举办的 AI 初创公司学校! 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系我们 搜索:

原文

We’ve been building AI coding assistants at Qodo since the GPT-3 days. Our initial approach was highly structured with predefined flows for different coding tasks like test generation, code reviews, and improvements. This approach worked well with earlier generations of LLMs and with structured flows we were able to get real-world value from older models, despite all of their limitations.

Since Claude Sonnet 3.5 was released 9 months ago, LLMs have become significantly more capable at general-purpose coding tasks. The new models opened up the possibility to build something more dynamic and flexible while still maintaining our standards for code quality. We wanted to move away from rigid workflows to an agent that could adapt to any kind of user request, while still reflecting our opinionated views on how AI can be best used for coding.

Initially, we needed a framework that would let us quickly validate our ideas and from the few options that were available about 4 months ago, we settled on LangGraph for our initial proof of concept. We were pleasantly surprised to see that the framework has proven flexible and mature enough to carry us all the way to production.

In this post, I’ll explain why LangGraph was the right choice for us, and how it enabled us to build a coding assistant that balances flexibility with our opinionated approach to coding best practices.

Flexibility to be opinionated

Our key consideration was the ability to create opinionated workflows while maintaining adaptability. LangGraph takes a graph-based approach that gives you flexibility to build agents that land anywhere on the spectrum from completely open-ended — where you just give an LLM all available tools and let it run in a loop — to fully structured deterministic flows (like the ones we started with).

At its core, LangGraph lets you define a state machine for your agent. You create nodes that represent discrete steps in your workflow and edges that define the possible transitions between them. Each node can perform specific functions—gathering context, planning, generating code, or validating—while the graph structure determines how these functions connect.

The density of connections in the graph corresponds to how structured or flexible your agent is. A sparse graph with few connections corresponds with a more rigid, predictable flow where each step leads to exactly one next step. A dense graph with many interconnections gives the agent more freedom to choose its path.

Future, more capable models might work best with fully open-ended approaches. But even with the best current LLMs, you still get better results when you guide them through the problem. If you use LLMs directly for coding, you’ve probably already developed your own workflow — like breaking problems down, providing context strategically, guiding the model through complex reasoning, and backtracking or iterating when needed.

The nice thing about LangGraph’s flexibility is that we can easily recalibrate how structured our flows are when new, more powerful models are released.

Our main flow follows a pattern that you might recognize: first, a context collection node gathers relevant information from the codebase (and external resources via MCP integration); next, a planning node breaks down the task into manageable steps; then an execution node generates the actual code; finally, a validation node checks the output against best practices and requirements. When validation fails, the agent loops back to execution with specific feedback rather than starting from scratch.

Coherent interface

When you’re building a complex system, a framework should simplify rather than complicate your work. LangGraph’s API does exactly that.

Here’s how a simplified version of our main workflow looks when implemented with LangGraph:

from langgraph.graph import StateGraph, END

workflow = StateGraph(name="coding_assistant")
workflow.add_node("context_collector", collect_relevant_context)
workflow.add_node("task_planner", create_execution_plan)
workflow.add_node("task_executor", execute_plan)
workflow.add_node("validator", validate_output)

# Define flow between nodes
workflow.add_edge("context_collector", "task_planner")
workflow.add_edge("task_planner", "task_executor")
workflow.add_edge("task_executor", "validator")

# Conditional routing based on validation results
workflow.add_conditional_edges(
    "validator",
    should_revise,
    {
        True: "task_executor",  # Loop back if revision needed
        False: END               # Complete if validation passes
    }
)

graph = workflow.compile()
graph.invoke({"user_input": "build me a game like levelsio"})

This declarative approach makes the code almost self-documenting. The workflow definition directly mirrors our conceptual diagram, which makes it easy to reason about and modify.

Each node function receives the current state and returns updates to that state. There’s no magic happening behind the scenes, just straightforward state transitions.

LangChain gets a lot of flack for its overly complicated abstraction, but the team really cooked with the LangGraph interface. It adds just enough structure without getting in your way or forcing you to adopt a complicated mental model and puts your agent logic on full display rather than obscuring it behind abstractions.

Reusable components across workflows

Reusability is what separates valuable frameworks from disposable ones. The node-based architecture that LangGraph uses is great here.

Our context collection node is a good example. It handles gathering relevant information from the codebase and it’s used in pretty much every flow. The same goes for our validation node, which checks code quality and runs tests. These components can slot into different graphs with minimal configuration.

As we build out more flows, the velocity payout is huge. We’re building specialized flows like TDD that have different structures but reuse many of the same nodes, just connected in a different configuration with a few specialized components added in.

State management

The most satisfying part of adopting the right framework is when you get useful functionality out of the box. LangGraph’s built-in state management is a perfect example.

Adding persistence to our agent took just a few lines of code:

from langgraph.graph import StateGraph, END
from langgraph.checkpoint.postgres import PostgresSaver

workflow = StateGraph(name="coding_assistant")
...
...

checkpointer = PostgresSaver.from_conn_string(
    "postgresql://user:password@localhost:5432/db"
)
checkpointer.setup()

graph = workflow.compile(checkpointer=checkpointer)

That’s it. With this simple addition, our entire workflow state—including context collected, plans made, and code generated—persists to our postgres database without us building any custom infrastructure. There are also SQLite and in memory checkpointers that can be added just as easily.

What’s really neat is that this doesn’t just enable basic persistence across sessions. It supports checkpoints and branch points so you can undo and replay changes.

Areas for growth

While LangGraph has been a great foundation for our agentic flows, it’s not without challenges. One pain point has been documentation. The framework is developing very quickly and the docs are sometimes incomplete or out of date. The maintainers are great and were super responsive on Slack (thanks Harrison and Nuno for all the help :)). Be prepared to potentially need to communicate directly with the project maintainers if you’re using the newer and more niche capabilities.

Testing and mocking is a huge challenge when developing LLM driven systems that aren’t deterministic. Even relatively simple flows are extremely hard to reproduce. Our agent interacts extensively with the IDE, which is difficult to simulate in automated tests. We built a mock repository that simulates basic IDE operations, but it doesn’t perfectly replicate the real environment. This creates a gap between what we can test automatically and what happens in production.

For example, operations like “find all usages of this function” that depend on the IDE’s language server are particularly hard to mock. This forced us to rely more on manual testing than we’d prefer, which slowed down the iteration cycle.

Mature frameworks tend to provide robust infrastructure for mocking and testing. I’m hopeful that LangGraph will develop in these areas over time.

联系我们 contact @ memedata.com