自定义 Workflow:Eino 之上的 Agent 编排
系列「企业级 AI Agent 实现拆解」E30 篇也是 Part 8DeepFlux 适配实践的收尾。上一篇讲了五个适配器DeepFlux 如何把 Eino 接进 DDD 架构。这篇把视角提高一层看完整的 ReAct 循环是怎么把这些适配器组装起来的以及 Eino 没有覆盖哪些部分——DeepFlux 自己填补了什么。读完这篇你会知道BuildReActRunnable为什么要自定义StreamToolCallCheckerHITL 中断的接线方式WithInterruptBeforeNodesWithGraphCompileOptions的陷阱ForceNewRun是什么新 turn 为什么要丢弃旧检查点RunTurnHandler.HandleEino 负责的和 DeepFlux 自己做的各是什么系统提示组装memory few-shot skill 三层注入Token 预算和墙钟预算怎么联动HITL 恢复路径的审计留痕一、BuildReActRunnable把 react.Agent 包进一个外层 GraphrAgent,err:react.NewAgent(ctx,react.AgentConfig{ToolCallingModel:chatModel,ToolsConfig:compose.ToolsNodeConfig{Tools:baseTools},MaxStep:maxStep,StreamToolCallChecker:allChunksStreamToolCallChecker,})anyG,nodeOpts:rAgent.ExportGraph()g:compose.NewGraph[[]*schema.Message,*schema.Message]()g.AddGraphNode(react_agent,anyG,nodeOpts...)g.AddEdge(compose.START,react_agent)g.AddEdge(react_agent,compose.END)returng.Compile(ctx,compose.WithCheckPointStore(cfg.CPStore))react.NewAgent构建内部 ReAct 图ExportGraph()把它导出为anyGcompose.AnyGraph 编译选项nodeOpts。然后 DeepFlux 把这个图当一个节点嵌进外层 Graph再整体 Compile。为什么要套一层compose.WithCheckPointStore只在最外层Compile时生效内层react.Agent的编译不支持直接传检查点存储。套一层才能让 checkpoint 正确绑定。二、自定义 StreamToolCallCheckerEino 默认的 checker 叫firstChunkStreamToolCallChecker——看到第一个非空文字 chunk 就返回false判定为这次不调工具。这对 GPT-4o 没问题但 DeepSeek / Claude 有时会先输出一段文字再输出 tool_calls。用默认 checker这些 tool_calls 会被漏掉ReAct 循环提前退出。DeepFlux 替换成allChunksStreamToolCallCheckerfuncallChunksStreamToolCallChecker(_context.Context,sr*schema.StreamReader[*schema.Message])(bool,error){defersr.Close()for{msg,err:sr.Recv()iferrio.EOF{returnfalse,nil}iferr!nil{returnfalse,err}ifmsg!nillen(msg.ToolCalls)0{returntrue,nil// 只要看到工具调用就返回 true}}// 读完所有 chunk 都没有 ToolCalls → false}读完所有 chunk 再判断代价是多一点延迟但保证跨模型行为一致。三、HITL 接线WithGraphCompileOptions 的陷阱react.ExportGraph()返回的nodeOpts里已经打包了 Eino ReAct 内部所需的编译选项WithMaxRunSteps(maxStep) WithNodeTriggerMode(AnyPredecessor) WithGraphName(react.GraphName)加 HITL 时需要追加WithInterruptBeforeNodes([tools])。直觉是append(nodeOpts, newOpt)——但compose.WithGraphCompileOptions是赋值语义第二次调用会静默覆盖第一次之前三个编译选项全部丢失。修复方式把全部选项合并成一次调用ifcfg.HITLEnabled{nodeOpts[]compose.GraphAddNodeOpt{compose.WithGraphCompileOptions(compose.WithMaxRunSteps(maxStep),// 从 react 原始选项里复制compose.WithNodeTriggerMode(compose.AnyPredecessor),compose.WithGraphName(react.GraphName),compose.WithInterruptBeforeNodes([]string{tools}),// 新增),}}tools是 react 内部未导出的节点 keynodeKeyTools与react.ToolsNodeName不同需要对着源码确认Eino 升级时要重新验证。四、ForceNewRun新 turn 为什么要丢弃旧检查点同一个checkPointID session UUID上Eino 默认行为是如果检查点存在就从那个点恢复执行。这对 HITL 恢复是正确的但对用户发送新消息开启新 turn就不对——新消息要从头开始不应该继续上一次的图状态。opts:[]compose.Option{compose.WithCallbacks(cb),compose.WithCheckPointID(string(sid)),}if!in.IsResume{optsappend(opts,compose.WithForceNewRun())// 新 turn 丢弃旧 checkpoint}compose.WithForceNewRun()告诉 Eino即使这个 checkPointID 有历史状态也忽略它重新开始。这样同一个 session 可以经历多轮对话每次新消息开启新图执行HITL 中断时再从中间恢复。五、resume 路径的 checkpoint 预检ifin.IsResume{if_,found,err:f.cpStore.Get(ctx,string(sid));err!nil{returnnil,nil,err}elseif!found{returnnil,nil,port.ErrCheckpointMissing}}Eino 图在 resume 时如果 checkpoint 不存在不会报错——它会静默开启一个全新执行丢失历史对话状态。DeepFlux 在调runnable.Stream之前先检查checkpoint 不存在就 fail fast 返回ErrCheckpointMissing上层能正确提示会话状态已过期而不是让用户看到一个什么都不记得的 Agent。六、Eino 负责什么Handle 自己做什么RunTurnHandler.Handle是调用 Eino 的地方但它做的事比 Eino 多得多Eino 负责的部分ReAct 循环LLM → tool_call 判断 → 工具执行 → 下一轮 LLM流式输出检查点持久化通过CheckPointStore中断WithInterruptBeforeNodes最大步数限制MaxStepHandle自己做的部分Eino 没有覆盖会话状态机idle → running新 turn completed → running多轮续聊清除上轮 stop_reason paused → runningHITL 恢复 running → completed / failed / cancelled状态迁移都在 domain 层model.SessionHandle只是驱动它。Eino 不知道 session 存在。系统提示三层组装sysContent:h.resolveSystemPrompt(ctx,cfg.SystemPrompt,sess)// 版本化 prompt 模板memCtx// 长期记忆召回memory BCfewCtx// few-shot 样例少样本示例库skillCtx// skill 推荐SKILL.md trigger evaluator再加上可选的第四层ifhybridCtxEnabled{summaryText:h.loadHybridContext(...)// 长会话摘要压缩滑动窗口摘要混合sysContent\n\n[会话摘要]\nsummaryText}四层内容全部拼进 system prompt交给 Eino 的 ReAct 循环使用。两种预算护栏墙钟预算整个 ReAct 循环的时间上限。ifcfg.MaxWallSeconds0{ctx,cancelcontext.WithTimeout(ctx,time.Duration(cfg.MaxWallSeconds)*time.Second)defercancel()}超时时ctx.Done()触发Stream返回错误进入completeWithReason(StopBudgetWallclock)——不是 failSession是带部分结果的安全完成。Token 预算累计 Token 超限后中止循环。ifcfg.MaxTokensBudget0{budgetCtx,budgetCancel:context.WithCancel(ctx)deferbudgetCancel()budgetTrackerNewLoopBudgetTracker(cfg.MaxTokensBudget,budgetCancel)ctxcontext.WithValue(budgetCtx,port.ContextKeyLLMBudgetProbe{},budgetTracker.Probe())}LoopBudgetTracker挂在 context 里LLM client 每次收到 usage 数据时调Probe()累加。超限时budgetCancel()取消 contextReAct 循环退出。HITL 审计留痕ifh.audit!nilinterrupt!nil{h.audit.Record(ctx,...,hitl.decision_applied,...,map[string]any{interrupt:interrupt.ID,decision:action,// approve / rejecttool:interrupt.ToolCall.Name,decided_by:dec.DecidedBy,})}HITL 恢复时在真正执行之前先写一条不可变审计记录。如果进程在写审计后、执行工具前崩溃至少留下了谁批准了什么的痕迹稽核时能还原决策链。异步后处理完成后触发两件事都是 fire-and-forget不阻塞响应记忆提炼把这轮对话异步写入 memory BC下一轮召回长会话摘要超过 token 阈值时把历史压缩成摘要存入 context_summaries 表七、整体数据流用户 HTTP → RunTurnHandler.Handle ├── 加载/更新 session 状态机domain 层 ├── 加载 agent_configcfg.MaxTurns, HITLEnabled... ├── 组装 system promptmemory few-shot skill 摘要 ├── 装配 budgetTracker context deadline │ ├── ReActFactory.StreamTurnEino 细节全在这里 │ ├── buildRunnable命中/构建 Runnable 缓存 │ ├── NewAgentCallbackSSE HookRunner │ └── runnable.Stream(inputMsgs, opts...) │ ├── Eino ReAct 循环 │ │ ├── ChatModel.Stream → tokenStreamReader │ │ ├── allChunksStreamToolCallChecker │ │ └── ToolsNode → toolBrokerAdapter.InvokableRun │ └── 返回 (TokenReader, interrupt, err) │ ├── 如果 interrupt → handleEinoInterrupt → session.Pause → SSE 推 interrupt 帧 ├── 如果 TokenReader → consumeEinoStream → 推 turn.delta SSE │ ├── session.Complete() ├── 落库commitFinalAnswer D6 outbox 事务 ├── 审计记录 └── 异步记忆提炼 会话摘要小结BuildReActRunnable解决了三个 Eino 的适配问题自定义 StreamToolCallChecker 兼容先文字后工具的模型、外包一层 Graph 接 CheckPointStore、HITL 时把编译选项合并成一次WithGraphCompileOptions调用避免覆盖。ForceNewRun让同一个 session ID 能区分新 turn和HITL 恢复两种执行路径。checkpoint 预检在 Eino 的静默恢复前 fail fast避免丢失对话历史。RunTurnHandler.Handle是 Eino 之上的业务外壳会话状态机驱动、四层 system prompt 组装、墙钟 Token 双预算、审计留痕、异步后处理。这些都是 Eino 不覆盖的部分也是企业级 Agent 和 demo Agent 之间的真正差距。Part 8 到此收尾。代码来源DeepFlux platform ·server/internal/agent/

相关新闻

3步解锁百度网盘高速下载:告别限速的终极指南

3步解锁百度网盘高速下载:告别限速的终极指南

3步解锁百度网盘高速下载:告别限速的终极指南 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘几十KB的下载速度而抓狂吗?每次下载重要文…

2026/7/7 19:30:00阅读更多 →
5分钟快速掌握百度网盘解析工具:告别限速实现高速下载

5分钟快速掌握百度网盘解析工具:告别限速实现高速下载

5分钟快速掌握百度网盘解析工具:告别限速实现高速下载 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘几十KB的下载速度而烦恼吗?每次下…

2026/7/7 19:30:00阅读更多 →
当游戏数据不再沉默:Seraphine如何让英雄联盟的每一场对局都变得透明

当游戏数据不再沉默:Seraphine如何让英雄联盟的每一场对局都变得透明

当游戏数据不再沉默:Seraphine如何让英雄联盟的每一场对局都变得透明 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 你有没有经历过这样的时刻?BP阶段,你完全不了解队友和…

2026/7/7 19:30:00阅读更多 →
文档下载终极指南:kill-doc如何让你轻松获取30+平台的在线文档

文档下载终极指南:kill-doc如何让你轻松获取30+平台的在线文档

文档下载终极指南:kill-doc如何让你轻松获取30平台的在线文档 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就…

2026/7/7 20:40:08阅读更多 →
从零构建渗透测试实战能力:系统性学习路径与核心工具精讲

从零构建渗透测试实战能力:系统性学习路径与核心工具精讲

1. 项目概述:为什么“实打实”的渗透测试学习如此稀缺?在信息安全领域,“渗透测试”这四个字自带光环,也伴随着巨大的信息迷雾。市面上充斥着大量号称“从入门到精通”的教程、速成班和书籍,但很多学习者投入大量时间后…

2026/7/7 20:40:08阅读更多 →
Pong游戏AI实战包:含REINFORCE策略梯度与A3C异步训练双实现,附动图演示和预训练模型

Pong游戏AI实战包:含REINFORCE策略梯度与A3C异步训练双实现,附动图演示和预训练模型

本文还有配套的精品资源,点击获取 简介:直接跑通Pong游戏的两个经典深度强化学习方案——REINFORCE纯策略梯度方法(pong_reinforce.py 已训练好的pong_reinforce.h5)和A3C多线程异步Actor-Critic架构(pong_a3c.py&…

2026/7/7 20:40:08阅读更多 →
Transformer 多头注意力 8 头并行计算:PyTorch 实现与 CUDA 核函数优化

Transformer 多头注意力 8 头并行计算:PyTorch 实现与 CUDA 核函数优化

Transformer多头注意力8头并行计算:PyTorch实现与CUDA核函数优化1. 多头注意力机制的核心原理Transformer模型的核心创新在于其完全基于注意力机制的设计,摒弃了传统的循环和卷积结构。多头注意力机制(Multi-Head Attention)通过并…

2026/7/7 20:40:08阅读更多 →
加密性能优化实战:从算法选型到硬件加速的8个核心策略

加密性能优化实战:从算法选型到硬件加速的8个核心策略

1. 项目概述:当加密成为性能瓶颈 最近在排查一个线上服务的性能问题时,发现了一个让我有点意外的现象:一个核心的API接口,在启用了高强度加密后,其吞吐量(TPS)相较于明文处理时,直接…

2026/7/7 20:40:08阅读更多 →
碧蓝航线Alas脚本:3分钟掌握全自动游戏管理的终极指南

碧蓝航线Alas脚本:3分钟掌握全自动游戏管理的终极指南

碧蓝航线Alas脚本:3分钟掌握全自动游戏管理的终极指南 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 碧蓝航线A…

2026/7/7 20:35:07阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/7 4:43:43阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/7 2:56:31阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/7 1:03:28阅读更多 →
Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

1. 项目概述:Acunetix v24.8 高级版漏洞扫描器深度解析作为一名在网络安全领域摸爬滚打多年的老兵,我深知一款趁手的“兵器”对于安全测试工作意味着什么。今天要聊的,就是Web应用安全测试领域里一个响当当的名字——Acunetix。特别是其v24.8…

2026/7/7 0:02:41阅读更多 →
国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot) 🌐 演示地址:http://ruoyioffice.com | 📦 源码1GitHub:ruoyi-office | 📦 源码2GitCode:ruoyi-o…

2026/7/7 0:02:41阅读更多 →
CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS SSH连接故障排查:从基础检查到深度修复的完整指南引言当你尝试通过Xshell或其他SSH客户端连接CentOS服务器时,突然遭遇"Connection refused"或"Connection timed out"的错误提示,这种经历对任何运维人员或开发者来…

2026/7/7 0:02:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/7 5:11:21阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/7 5:11:21阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/7 5:11:21阅读更多 →