告别 OpenClaw:从 AI 网关依赖到自研 Agent 编排
背景
EduMind 是一个 AI 驱动的智能教学平台,技术栈是 Spring Boot 4 + Vue 3,核心功能包括 RAG 知识库检索、作业批改、课堂实时互动和 QQ 机器人答疑。
从项目初期开始,所有 LLM 调用都经过 OpenClaw——一个开源的 AI Agent 框架(38 万 star,2026 年仍然极度活跃)。它负责:接收 QQ 消息、调用 MCP 工具、管理会话、路由 LLM 请求。
用了三个月后,我们决定把它从项目里彻底移除。这篇文章记录了为什么、怎么做、以及做完之后的架构变成了什么样。
为什么要移除 OpenClaw?
1. 品类错配
OpenClaw 是一个聊天机器人框架——它的核心价值是多频道消息(QQ/微信/Discord/Telegram)、Skill 插件系统、多 Agent 工作流。但 EduMind 只用了它最小的一块功能:作为一个 HTTP 代理把请求转发给 DeepSeek。
这就像租了一整层办公楼,只用了两间房,其中一间还漏水。
2. 问题积累
三个月的使用中,OpenClaw 带来的问题逐渐超过了它提供的便利:
- 视觉模型无法路由:
VisionPdfParser早就被迫绕过 OpenClaw 直连 Kimi API——代码注释里直接写着”OpenClaw 无法在文本模型和视觉模型间自动路由” - 中间层延迟:每个 LLM 调用多一次 HTTP 往返(Java → OpenClaw → LLM API),积少成多
- 排查困难:出问题时要同时排查 Java、OpenClaw、LLM API 三层,”是 OpenClaw 的问题还是我的问题”成了日常
- 多一个运维依赖:每次开发环境启动都要额外启动一个 Node.js 服务
3. 核心能力用不上
OpenClaw 最强的多 Agent 编排、Skill 热插拔、跨频道消息路由——EduMind 一个都没用。只用了 /chat/completions 代理这一个边角料功能。
替代方案分析
评估了三种方案:
| 方案 | 代表产品 | 结论 |
|---|---|---|
| AI 网关 | One API / New API / LiteLLM | 多一个 Go 服务,运维负担从 OpenClaw 换成另一个网关,不值得 |
| Agent 框架 | LangChain / CrewAI / AutoGen | 抽象层太多,EduMind 已有的 WorkflowEngine + StructuredOutputInvoker 已经够用 |
| 自研 | Agent Loop + 直连 API | 300 行核心代码,零外部依赖,完全掌控 |
选了自研。原因很简单:EduMind 已经自研了 RAG 全链路、DAG 工作流引擎、MCP JSON-RPC 端点、Redis Stream 消费框架——多 Agent 编排不过是这些轮子的自然延伸。
实现方案
核心思路:保留 OpenClawService 接口,换一个实现。
改动范围
1 | 新建:2 个文件 |
架构对比:
1 | 之前: 之后: |
核心:Agent Loop(50 行)
1 | while (step < maxSteps) { |
关键设计决策:
- 工具调用零 HTTP 开销:OpenClaw 的 MCP 回调走 HTTP JSON-RPC,自己做直接调 Spring Bean
- 内部循环非流式 + 最终答案流式:工具调用阶段不需要流式,最终回答才用 SSE 推给用户
@Lazy打破循环依赖:AgentLoopServiceImpl → KnowledgeSearchTool → RagService → QueryRewriter → AgentLoopServiceImpl,在QueryRewriter注入点加@Lazy断开
OneBot:从 HTTP 双通道到 WebSocket 单向
OpenClaw 时代,QQ 消息的收发走两条不同的路径:
1 | 收消息: QQ → NapCat → WebSocket → OpenClaw → HTTP → Java (/mcp + /api/onebot/rag) |
自研后统一为一条 WebSocket 连接:
1 | 收发: QQ → NapCat → WebSocket → Java(收事件 + 发 action 都在同一条连接) |
OneBot v11 协议本身就支持双向通信——WebSocket 连接上,NapCat 推送事件下来,客户端发 action 上去。之前分两条路纯粹是因为 OpenClaw 挡在中间。
作业批改 Prompt 升级
从 OpenClaw 的 homework-grader Skill 中提取了 C 语言批改的详细规则,合并到三个 grading prompt 模板中:
grading-grade.txt:新增 C 语言评分维度(contentScore 70 + formatScore 30)、分级扣分表(minor 1-3 / major 5-10 / critical 10-20)、典型场景扣分参考grading-errors.txt:新增 7 种 C 语言错误类型、severity 与扣分对应关系、知识点分类规则grading-suggestions.txt:新增优先级分级、类别词模板、可执行建议规范
DevTools 踩坑
Spring Boot DevTools 的 RestartClassLoader 导致 Course 实体被两个不同 classloader 加载,出现 Course cannot be cast to Course 的诡异错误。spring.devtools.restart.enabled=false 和 META-INF/spring-devtools.properties 都不生效——最终从 pom.xml 直接删除 spring-boot-devtools 依赖解决。
架构收益
1. 零外部 AI 依赖
现在 EduMind 只需要这 4 个服务:
1 | PostgreSQL + Redis + MinIO + 应用 |
OpenClaw 从部署清单里消失。开发环境少启动一个 Node 进程,生产环境少维护一个服务。
2. 性能提升
| 指标 | 之前 | 之后 |
|---|---|---|
| LLM 调用跳数 | Java→OpenClaw→API(2 跳) | Java→API(1 跳) |
| 工具调用 | HTTP JSON-RPC 往返 | 直接方法调用 |
| QQ 消息路径 | 4 跳(含两次 HTTP 回调) | 1 跳 WebSocket |
保守估计单次 QQ 问答响应时间减少 200-500ms。
3. 可观测性
之前 LLM 调用的 trace 埋在 OpenClaw 的日志里。现在所有调用链在 Java 侧可见——Micrometer @Timed 打点、@CircuitBreaker 熔断、日志里完整的 Agent 步数追踪。
4. 模型自由
1 | # .env 里一行切换 |
不再受 OpenClaw agent 路由的限制,多模型供应商随意切换。
5. 课程级个性化
之前 OpenClaw 只有一个全局的 Jarvis system prompt。现在 resolveSystemPrompt() 根据 QQ 群号 → 班级 → 课程 → course.system_prompt,不同班级自动加载不同的 prompt。群聊优先用群绑定的课程,私聊不绑定课程让学生自由提问。
可扩展性
做完这次改造后,项目处于一个很舒服的位置来应对未来的需求:
多 Agent 编排(零成本起步)
EduMind 已有的组件距离多 Agent 只差一层抽象:
1 | 已有: WorkflowEngine(DAG 拓扑) + StructuredOutputInvoker(JSON 重试) + ToolDefinition(工具接口) |
GradingWorkflow 的 GRADE → ERROR_ANALYSIS → SUGGESTION 三个节点本质上已经是三个 Agent——各有专属 prompt、各自调用 LLM、产出结构化结果交给下一个节点。
模型供应商扩展
1 | // LlmProperties 加一个 providers map |
新工具接入
MCP 工具体系保持不变,新增工具只需实现 ToolDefinition 接口:
1 |
|
Spring 自动扫描 ToolDefinition 的所有实现,AgentLoopServiceImpl 自动注册。
总结
移除 OpenClaw 的本质不是”换一个网关”,而是认清自己真正需要的抽象层次。
OpenClaw 提供的 Tool-Calling Loop 用 50 行 Java 就能实现。它提供的多 Agent 编排,EduMind 的 WorkflowEngine + GradingWorkflow 已经做到了,且更贴合教学业务。它提供的多频道消息路由,一条 WebSocket 连接 + OneBot v11 协议就够了。
真正难的东西——RAG 检索、Embedding 推理、Reranker 精排、DAG 工作流引擎、结构化输出重试——EduMind 早已自己搞定。
当你的基础设施比框架更懂你的业务时,框架就是多余的中间层。
本次改动总计:新建 4 个文件,修改 14 个文件,核心代码约 500 行。12 个消费者类零改动。移除 spring-boot-devtools 依赖解决 classloader 冲突。







