用 Vibe Coding 从零搭一个 ReActAgent 智能体,边做边学 Agent 框架

用 Vibe Coding 从零搭一个 ReAct 智能体,边做边学 Agent 框架

项目地址:https://github.com/wangzpap/ZpAgent

如果你觉得有帮助,欢迎 Star 支持!感谢!


2025 年,“Vibe Coding” 成了开发者社区的热词。与此同时,AI Agent 开发也走向工程实践,ReAct、Function Calling、MCP 等范式层出不穷。我对这两件事都好奇,于是一个想法自然冒了出来:如果用 Vibe Coding 的方式,从零搭建一个 ReAct 智能体项目,能不能在"不手写代码"的同时,更深入地理解 Agent 的底层机制?

这个想法的产物就是 ZpAgent——一个基于 LangChain + LangGraph + FastAPI + Vue 3 的 ReAct 智能体应用。它支持 SSE 流式输出、动态工具选择、Human-in-the-Loop 审批和双层会话记忆,全部代码通过与 AI 对话完成。

但对我来说,代码本身不是目的。Vibe Coding 改变的是学习路径——过去学一个框架是读文档、写 demo、踩坑;现在是把想法描述给 AI,看它生成代码,然后反过来阅读和理解这些代码。遇到不懂的设计就追问"为什么用策略模式而不是直接 if-else",遇到 bug 就研究"连接池为什么会断连"。AI 写代码,我来理解代码,学起来似乎比自己啃文档更高效一些。

这篇文章会拆解这个项目的设计思路、技术选型和核心实现,也会分享 Vibe Coding 作为学习方法的真实体验。

为什么做这个项目?

我想做一个真正跑起来的全栈 Agent 应用,具备以下特征:

  • 完整的 ReAct 推理循环,而不只是单次工具调用
  • 实时的流式输出,让用户看到 Agent 的每一步思考
  • 可控的工具执行,敏感操作需要人类审批
  • 持久化的会话记忆,刷新页面不丢上下文
  • 可扩展的工具系统,支持 MCP 协议接入外部工具

同时,这也是一个面试向项目——它覆盖了异步编程、设计模式(策略模式、工厂方法、注册表模式)、流式通信、状态管理等高频考点,每一处设计决策都经得起追问。

界面一览

先看效果,再拆架构。

对话界面——左侧是会话列表,右侧是聊天主区域。Agent 的每一次思考、工具调用和观察结果都以结构化的方式呈现在消息气泡中:

在这里插入图片描述
工具选择——底部工具栏可以动态勾选启用哪些工具,支持内置工具和通过 MCP 协议加载的外部工具:
在这里插入图片描述

Human-in-the-Loop 审批——当 Agent 想要调用一个被标记为"需要审批"的工具时,前端会弹出审批面板。用户可以选择批准、编辑参数、拒绝,或者自行回复:

在这里插入图片描述

整体架构

ZpAgent 采用前后端分离的单体架构,通过 SSE(Server-Sent Events)实现实时通信:

外部服务

后端 · FastAPI :8000

前端 · Vue 3 + Vite :5173

存储层

核心引擎

POST /api/chat

模型节点

工具节点

审批中断

内置工具

MCP 工具

可选

可选

SSE 事件流

用户界面

SSE 流式解析器

全局状态管理

API 路由层

ReAct Agent 编排器

LangGraph 状态图

ChatOpenAI LLM

ToolNode 工具节点

HumanInTheLoop 中间件

会话元数据存储
策略模式

对话状态存储
策略模式

DeepSeek / 智谱 / Moonshot

MCP 工具服务

MySQL

前端是纯手写的 Vue 3 单页应用(没有用任何 UI 组件库),后端是 FastAPI + LangGraph 的异步服务。两层存储都使用策略模式,通过环境变量在 InMemory 和 MySQL 之间无缝切换。

核心亮点拆解

一、ReAct 循环 + SSE 流式输出

ReAct(Reasoning + Acting)是 Agent 领域的经典范式:模型先思考(Thought),再行动(Action)调用工具,然后观察(Observation)工具返回的结果,如此循环直到得出最终答案。

ZpAgent 通过 LangGraph 的状态图实现 ReAct 循环,并结合 SSE 将每一步实时推送到前端:

ToolNode LLM LangGraph Agent 前端 用户 ToolNode LLM LangGraph Agent 前端 用户 alt [有 tool_calls] [无 tool_calls] loop [ReAct 循环(最多 N 轮)] 发送消息 POST /api/chat (SSE) astream_events 启动 模型节点:推理 event: thinking(思考过程) event: token(逐 token 输出) 工具节点:执行 event: tool_result(观察结果) event: done(最终答案) 渲染完整回答

这里有一个技术细节值得一提:前端没有使用浏览器原生的 EventSource API,因为它只支持 GET 请求,而聊天接口需要 POST。所以我用 fetch + ReadableStream 手动实现了 SSE 解析,处理了分块传输中的 buffer 拼接、不完整事件缓冲等边界情况。

后端定义了 7 种事件类型:start(开始)、token(逐 token 文本)、thinking(思考过程)、tool_result(工具执行结果)、approval_required(需要审批)、done(完成)、error(错误),前端根据事件类型分别渲染到消息气泡的不同区域。

二、Human-in-the-Loop:让 Agent 不再"为所欲为"

这是整个项目中我最满意的功能。大多数 Agent demo 中,工具调用是无条件执行的——但在真实场景中,你不会希望 Agent 不经确认就删除数据库记录或发送邮件。

ZpAgent 通过 LangGraph 的 HumanInTheLoopMiddleware 实现了工具级别的审批控制:

批准

编辑参数

拒绝

自行回复

LLM 决定调用工具

该工具需要审批?

ToolNode 直接执行

触发 interrupt 中断

前端弹出审批面板

用户决策

修改参数后执行

返回拒绝原因给 LLM

用户输入作为工具返回值

观察结果返回 LLM 继续推理

审批策略通过 ToolRegistry(注册表模式)管理,支持三种粒度:False 自动执行、True 全量审批、dict 细粒度决策类型控制。更关键的是,系统处理了多轮审批的场景——当 Agent 在一次推理中连续请求多个需审批的工具时,每次都会独立中断等待,不会因为前一个批准了就直接放行后面的。

还有一个容易忽略的边界情况:如果用户在审批面板弹出后直接发送了新消息(没有处理审批),系统会自动拒绝(auto-reject)那些挂起的工具调用,避免状态不一致。

三、双层会话记忆架构

"记忆"是 Agent 应用的核心挑战之一。ZpAgent 将记忆拆分为两层,各司其职:

策略模式

策略模式

框架层 · checkpoint/

对话历史上下文

LangGraph 图状态

中断状态恢复

应用层 · conversation/

会话列表管理

标题 / 时间戳

创建 / 删除 / 重命名

Agent 编排器

InMemory / MySQL

InMemorySaver / AIOMySQLSaver

应用层的 ConversationStore 管理业务维度的会话元数据(标题、创建时间、最后更新时间),框架层的 CheckpointStore 则直接对接 LangGraph 的 checkpointer 机制,负责持久化对话历史和图执行状态(包括中断恢复)。两层都通过抽象基类定义接口,工厂函数根据 .env 配置创建具体实现,切换后端不需要改一行代码。

MySQL 实现层有一个值得注意的坑:LangGraph 官方的 AIOMySQLSaver 在连接池回收后复用时可能报 Not connected。解决方案是使用 aiomysql 的连接池并设置 pool_recycle,避免单连接被 MySQL 服务端的 wait_timeout 机制关闭。这也是 Vibe Coding 中常见的场景——AI 帮你写了代码,但运行时的坑需要你自己发现并反馈给它。

四、动态工具系统:内置 + MCP

工具系统分为内置工具和 MCP 外部工具两类,统一由 ToolRegistry 管理:

MCP 工具

内置工具

ToolRegistry

热加载

注册表
工具 + HITL 策略

get_datetime
日期时间查询

base64_tool
Base64 编解码

自定义工具...
@tool 装饰器

mcp_servers.json

stdio / SSE / HTTP / WebSocket

Agent 使用

内置工具通过 LangChain 的 @tool 装饰器定义,写一个函数就是一个工具,docstring 自动提取为工具描述,类型注解自动生成 JSON Schema。MCP 工具则通过 mcp_servers.json 配置,支持 stdio、SSE、streamable_http、websocket 四种传输协议。

一个很实用的功能是工具热加载——通过 POST /api/tools/reload 接口可以在不重启服务的情况下重新加载 MCP 工具配置。加载过程是容错的:单个 MCP 服务器连接失败不会影响其他工具的正常使用。

五、前端:Vue 3 应用

前端最核心的设计决策是消息数据结构。一条 Agent 回复不是简单的字符串,而是一个 segments 数组,交替存储文本片段和工具调用片段:

{
  role: 'assistant',
  segments: [
    { type: 'text', content: '让我查一下当前时间...' },
    { type: 'tool_call', tool: 'get_datetime', args: { timezone: 'Asia/Shanghai' }, observation: '2026-07-04 15:30:00' },
    { type: 'text', content: '当前北京时间是 2026年7月4日 15:30。' }
  ]
}

这种结构让 ChatMessage.vue 组件可以按照 segments 顺序渲染出"思考 → 工具调用 → 观察 → 最终回答"的完整 ReAct 时间线,而不是把所有内容混在一起。

设计模式速览

项目中用到的设计模式,也是面试中常被问到的:

«abstract»

ConversationStore

+create()

+list()

+delete()

+update_title()

InMemoryConversationStore

MySQLConversationStore

«abstract»

CheckpointStore

+get_saver()

InMemoryCheckpointStore

MySQLCheckpointStore

ToolRegistry

-tools: dict

-hitl_policies: dict

+register()

+get_tools()

+get_policy()

create_conversation_store

+create_conversation_store()

create_checkpoint_store

+create_checkpoint_store()

策略模式 · 应用层

策略模式 · 框架层

注册表模式

工厂方法

工厂方法

模式应用位置解决的问题
策略模式conversation/checkpoint/存储后端可替换,InMemory / MySQL 透明切换
工厂方法create_conversation_store()create_llm()build_agent_graph()集中创建逻辑,隐藏构造细节
注册表模式ToolRegistry统一管理工具和审批策略,支持动态注册
中间件HumanInTheLoopMiddleware在 LangGraph 执行流中横切审批逻辑
单例Settings()模块级配置实例,全局共享

Vibe Coding 作为学习方法

回过头看,Vibe Coding 最大的收获不是代码本身,而是它逼着我用一种新方式学习。

传统路径是:读文档 → 写 demo → 踩坑 → 理解原理。Vibe Coding 把这个流程反转了——先看结果,再倒推原理。当 AI 生成一段用策略模式实现的存储层时,我不会直接接受,而是追问"为什么不用 if-else 直接切换?",于是对开闭原则有了比教科书更具体的体会。当 AI 用 HumanInTheLoopMiddleware 实现审批时,我去研究 LangGraph 的中断恢复机制,算是弄明白了 interrupt()Command(resume=...) 之间的协作原理——这些东西如果只看文档,很可能一扫而过。

当然,Vibe Coding 也有它不擅长的地方。运行时问题排查、环境配置、依赖兼容性这些,AI 很难替你兜底。比如 MySQL 连接池的 pool_recycle 问题,AI 生成的代码逻辑上完全正确,但跑了一小时后连接被服务端 wait_timeout 断开才暴露出来。这类坑得自己踩,然后把现象反馈给 AI 让它修。

我的感受是: Vibe Coding 不是"不用动脑",而是把动手的时间省下来动脑。你要知道自己想学什么、架构该怎么选、哪些边界情况值得关注。AI 是高效的执行者,但提问者和审查者的角色,始终是你自己。

快速体验

# 克隆项目
git clone https://github.com/wangzpap/ZpAgent.git
cd ZpAgent

# 配置 API Key
cd back && cp .env.example .env
# 编辑 .env,填入你的 LLM API Key

# 创建虚拟环境(仅首次)
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt

# 一键启动
cd .. && chmod +x zpagent.sh
./zpagent.sh start

打开 http://localhost:5173 即可体验。详细的环境变量说明和手动启动方式请参阅 README

写在最后

ZpAgent 不是什么颠覆性的产品,它更像一个"用 Vibe Coding 驱动的学习实验"。通过从零搭建一个完整的全栈 Agent 应用,我把 ReAct 推理循环、SSE 流式传输、Human-in-the-Loop、双层记忆这些概念从论文和博客里拽进了真实可运行的代码中。过程中的每一次追问、每一个 bug、每一次"为什么这么设计"的思考,才是这个项目最大的产出。

如果你也在学 AI Agent,或者对 Vibe Coding 的学习方式感兴趣,欢迎到 GitHub 看看代码,点个 Star,一起交流!


项目地址: https://github.com/wangzpap/ZpAgent

技术栈: Python · FastAPI · LangChain · LangGraph · Vue 3 · Vite · SSE

协议: MIT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值