资讯详情

REA架构实战:从空白项目名到记录-评估-行动的闭环设计

📅 2026/10/21 22:05:35 | 华诺云谱 👁 阅读
REA架构实战:从空白项目名到记录-评估-行动的闭环设计
我在某协作平台上收到一个交接单项目代号写着rea正文空白关键词空白一句话摘要也没有。点开附件只有三个以rea_开头的文件一叠日志样本、一份规则草稿、一个几乎没写的 README。这种“只有一个代号”的项目现实中不算罕见常见做法是马上找人来问或者立刻去搜系统里有没有同名老代码。但这次我选了另一种方式先把rea当成一个动词组合来拆拆完再决定做什么。后面整套方案的骨架——记录、评估、行动——就是从这个连注释都没写满的空白代号里长出来的。1. 一个只有“rea”的交接单我是怎么把它拆成三个动词面对只有三个字母的项目名最容易出的问题是一上来就查它的“标准含义”。rea可以对应 read-eval-apply也可以对应 resource-event-agent甚至可以被理解成某个内部产品的缩写。可如果指导意见、用户背景、使用场景都没有任何一个标准含义都只是猜测。我的习惯是反过来先问一个问题这个系统最终要“动”什么是动界面、动数据、动流程还是动消息通知1.1 先别猜技术名先确定这东西最终会“动”什么把这个动词定下来再回看项目名就清晰很多。我当时列了一张表把可能的解释按交付形态排开候选含义常见领域适合的交付形态对组织的要求read-eval-apply运行时/语言工具命令行工具、在线求值服务使用者得懂代码request-evaluate-act风控/爬虫/审批API服务、规则平台需要明确的请求来源resource-event-agent会计/进销存业务领域模型需要熟悉财务的同事record-evaluate-act监控/数据/闭环任务事件管道、自动化通知能接受“事件驱动”思维对比下来只有 record-evaluate-act 和“动数据、动通知、回写业务”最吻合。这不是因为它更高端而是因为它照顾了现状我手上正好有一批日志样本和规则草稿这些东西天然就是这个框架的“输入”。1.2 三条业务路线收敛在我选的方向上选定方向之前我个人喜欢再追问三个定位问题用来排除不靠谱的路线。第一谁会看这个系统的输出如果输出是给值班工程师的那动作大概率是通知如果是给管理层的那动作就是报表。第二系统的输出会不会反向影响输入比如补货单产生后会生成新的入库单这就是一个闭环。第三动作要不要经过人审批如果要那“一触发就执行”的设计就必须被改成“一触发就建议”。顺着这三个问题逼自己想我从“做通用后台”跑偏到“做一条闭环事件管道”。最后我得到的具体故事大概是日志样本表明有一次外部系统上报了货品入库却未登记数量规则草稿里有一条“某仓库物料低于安全水位就提示补货”README 里只写了rea三个字母似乎暗示这个系统本身就是动作链。当这些材料建立联系后rea在我这边就不再是一个词而是一个最小闭环记录每一笔业务事件评估它该触发什么规则最终交给能执行的模块去行动。于是整个项目骨架变成记录、评估、行动。这也是后续所有设计和评审的统一语言。2. 记录、评估、行动为什么“rea”最终落在这一组语义上一个项目只有三个字母的名字好处是约束范围坏处是容易把范围定得过大。把rea读成 record-evaluate-act 之后我做的第一件事不是建仓库而是把每个词都圈成一块能独立复用的能力。2.1 三个模块各自的边界记录模块负责一切“进来”的数据。它不判断数据有没有业务价值只做四件事接收原始事件、做基本校验、保留一份原始副本、给事件分配唯一编号。很多人会在这里忍不住做业务清洗比如顺手把错误的订单状态改正或者把字段格式统一。我不建议这么做因为一旦记录模块承担了业务改写后面评估阶段看到的就不是真实事件出了问题会很难定位。评估模块是整个rea的核心负责把“记录”阶段拿到的原始事件转化为决策。它有几种典型形态阈值规则、统计模型、由人操作的复核队列。在我的实现里评估模块被设计成纯函数式同一个事件进来无论评估多少次结果都应该一样。如果做不到这一点说明评估的输入不够干净需要往前回到记录阶段找问题。行动模块最直接也最容易被人忽略。它的职责是把评估产生的决策变成具体业务动作比如发送通知、创建工单、调用下游接口。这个模块不能自己决定做什么只能执行已经形成的决策同时必须给每次执行留痕。下面这张表就体现了当时我要求团队关注的边界模块输入输出最容易踩的坑record原始事件规范化事件 event_id过度清洗导致原始信息丢失evaluate规范化事件decision 记录依赖外部实时数据导致结果抖动actdecision 记录动作日志 回执重复执行下游操作2.2 从单次执行到可持续闭环三个模块单独拎出来都很简单真正让它值钱的是闭环关系。行动并不是终点行动发生后系统会产生新的状态变化比如工单被点击、补货被确认、通知被阅读这些新事件又会回到 record 模块变成下一次评估的输入。换句话说rea运转起来之后就像一条环形的传送带而不是三段直连的管道。我在测试环境里搭过一个“库存到位提醒”的验证仓库收到一次入库但数量低于某个水位record 模块记录了该事件评估模块判断“数量低于安全水位”给出一个补货建议行动模块生成一条补货通知。随后运营人员点了“确定补货”这个点击动作本身又是一条新的事件会再次进入记录列表。于是在日志里你能看到一条完整的链入库事件 → 低库存决策 → 补货动作 → 确认事件 → 新一轮决策。这套循环跑通后它就不再是一个局部工具而是一个可持续扩张的平台。我当时的判断是如果后面的需求只是想“看数据”rea的三个模块都能单独接出报表如果想做自动化三个模块必须同时上。正是这种“既能拆开用也能合起来跑”的特性让它变成很稳的项目底盘。3. 最小闭环落地从事件申报到自动动作的三步实现理论拆得再好最后也得落在可运行代码上。这里分享我当时做的最小闭环实现它不依赖重量级框架任何细节都只是为了说明 REA 的执行路径。3.1 先约定事件结构再谈代码必须先确定协议再写代码。我用了一种很朴素的事件结构每个事件必须有四样东西event_id用于全局唯一标识event_type用于描述业务类型occurred_at用于保留发生时间payload用于携带业务字段。可选字段包括原始大小写raw、来源系统source和调用方标识trace_id。校验规则也定得很克制必填字段缺一不可类型不对就打回死信但业务规则尽量不看。比如“库存数量不能为负”肯定要校验这是常识但“低于安全水位就补货”这类业务判断留给评估阶段。这样做的原因是评估规则会频繁修改把规则绑死在入口校验层会让每次改规则都牵连输入协议。事件结构界定如下字段是否必填说明event_type是区分 stock.in、order.finish、link.click 等occurred_at是统一为 UTC ISO8601source否哪个上游系统上报payload是业务字段建议最外层固定raw否原始内容排查时使用3.2 用 Python 实现一个不依赖框架的 30 行 REA 样例我尽量让三个环节保持各自独立于是把逻辑压缩成一个很简单的 Python Runtimefrom datetime import datetime, timezone from typing import Any, Dict class ReaRuntime: def __init__(self): self.events: Dict[str, dict] {} self.decisions: Dict[str, dict] {} def record(self, event: Dict[str, Any]) - str: event_id evt_ str(len(self.events) 1) normalized { event_type: event[event_type], occurred_at: event.get( occurred_at, datetime.now(timezone.utc).isoformat() ), source: event.get(source, unknown), payload: event.get(payload, {}), raw: event, status: recorded } self.events[event_id] normalized return event_id def evaluate(self, event_id: str): ev self.events[event_id] if ev[event_type] ! stock.in: ev[status] passed return None quantity int(ev[payload].get(quantity, 0)) if quantity 10: decision_id dec_ str(len(self.decisions) 1) self.decisions[decision_id] { event_id: event_id, level: low_stock, suggestion: 检查补货渠道, status: suggested } ev[status] evaluated return decision_id ev[status] passed return None def act(self, decision_id: str): decision self.decisions[decision_id] if decision[status] ! suggested: raise ValueError(decision already acted) decision[status] acted decision[acted_at] datetime.now(timezone.utc).isoformat() # 这里连接通知、工单或下游接口 return faction_{decision_id}这一段直接表达了 REA 三元组。record负责入参evaluate根据数量和事件类型产出一个决策act给决策状态做过渡并留下动作号。在这个最小例子里没有连接真实下游但只要把act里的注释改成 HTTP 调用或队列写入闭环就能跑起来。调用方式也简单直接runtime ReaRuntime() eid runtime.record({ event_type: stock.in, occurred_at: 2024-06-01T08:00:0000:00, payload: {item: A001, quantity: 8} }) did runtime.evaluate(eid) if did: action_no runtime.act(did) print(action_no)真实项目里不建议把这些模块揉在一个对象里至少应该拆成 record 服务、evaluate 服务、act 服务三个独立部署单元。但第一版可以揉目的是让参与者看清楚 REA 的骨架而不是一上来就面对微服务复杂度。我习惯用“先跑通闭环、再拆服务”的推进顺序收益远大于直接设计一个分布式系统。4. 只给模块命名还不够这些边界条件决定了REA能不能用模块名字确认了、最小闭环也跑通了剩下的都是真正上线时会暴露的硬问题。这里把我遇到的和事后复盘的内容集中写出来基本都是常规文档里少见但实际价值很高的经验。4.1 事件进得来但干净的日志才是真正的入口REA 系统的第一道关不是代码而是日志。有一回测试数据里混入了一个occurred_at缺失的旧事件record 阶段按当时时间补了时间戳结果评估阶段把它当成“刚刚发生”的低库存事件误发了一轮补货建议。查了一下午才发现是记录阶段的“好心补全”惹的祸。从那之后我规定任何缺失字段都打成missing标记不在入口偷偷补值。时间字段是重灾区。不同上游系统上报的时间可能有时区差异有的给 08:00有的给 UTC还有的直接传本地时间。我的处理方式是occurred_at必须包含时区如果上游做不到就在 source 系统名里标注“默认 UTC”再由记录模块统一转换。转换规则写死在模块里而不是写在评估代码里。4.2 评估结果不一致带来最隐蔽的系统抖动REA 闭环上线后最隐蔽的问题是“同一个事件被重新评估时结论变了”。比如一条 low_stock 建议第一次评估是“建议补货”几小时后重跑却变成“无需补货”。导致这种结果的常见原因有几种评估函数里直接查了外部数据库、接口响应会变化、用了当前时间而不是事件时间。我在评估阶段调整了一个原则评估只读“记录阶段已经沉淀下来的数据”外部需要的信息必须在事件进入时同步成快照。这个改动成本不高收益很直接。以后再看决策记录就可以明确区分“当时的事实”和“后来的修正”。做规则引擎和自动化任务最忌讳让规则结果随时间漂移宁可先做大事件间的快照也不要让业务人员开始怀疑系统为什么“一票两判”。4.3 动作要做成幂等这是REA闭环最后的尊严行动模块接到决策后调用下游可能超时、失败、重复投递。如果没做幂等一个补货建议可能变成两个补货工单。我在 act 阶段给决策记录增加了一个执行状态suggested → acted并在调用外部系统之前先以决策 ID 为幂等键写入“执行中”标记。下游允许重复请求时还会带上decision_id作为业务参数让下游自行去重。执行状态含义重试策略suggested决策生成未执行正常进入 actacting已下发未确认超时后最多重试一次acted下游确认成功不重试只记录failed执行失败进人工列表cancelled人工取消不自动重试另一个容易忽略的点是“动作成功不等于下游成功”。我曾遇到通知模块返回 200但下游工单系统实际创建失败因为没有检查业务回执。后来 act 模块统一要求只有收到下游业务状态码才把 acted 状态写回。简单说动作是 REA 面向外界的手指手指按下去是否真的生效必须自己确认否则闭环就不成立。5. 关于“rea”的复盘命名如何帮一个空项目落地最后聊点偏软的、但对实际推进很有帮助的东西。很多项目有非常完整的说明文档却因为团队语言不统一做出来的东西总是各说各话。rea这个项目反过来信息少得可怜反而是因为名字太短逼出了共识。5.1 用名字倒逼细节每次讨论都从“它记录什么、评估什么、行动什么”开始当项目只有三个字母时每次评审我都被迫使用同一套问法这个东西记录了什么事件评估用了什么数据行动会触达哪里只要问题能完整回答细节基本就齐了。而 REA 的名字恰好是一个天然的提问框架它不会让你跑偏到“要不要做一个可视化大屏”因为大屏既不是记录、也不是评估、也不是行动。内部做模块划分时我也建议用 REA 把需求归档。某次需求方提出“希望把历史异常也纳入信号”按模块对应关系一目了然这是评估模块的特征来源问题不是记录模块的问题。这样处理问题时就不会出现“一个需求改遍三端”的情况。5.2 从“建议模式”切成“执行模式”的灰度经验最后补一条控制策略的实践。REA 闭环真正上线时不建议第一天就让 act 直接执行所有下游操作。更稳妥的做法是先切“建议模式”评估结果只写成建议消息放到预览区域等人点确认。确认动作触发 act未确认则定期汇总。等运维或业务人员对规则有信任度后再把某些低风险动作切成“执行模式”。我实际用到的状态链是suggested → previewed → approved → executed。人工确认期间如果发现规则误判可以直接把 decision 改成 cancelled并拦住后续重试如果正常approved 之后的动作才会继续。这套状态机在后面几乎没变过因为它用最小代价守住了信任底线。行动自动化的时间点取决于错误容忍度。低风险通知可以一周后自动化涉及资金或库存的动作我会保持人工确认更久宁慢勿快。这个判断没有统一标准但原则很简单出错代价低放权给系统出错代价高放权给人直到人对规则不再挑刺。我对rea的最大心得不是它最终做成一个多聪明的系统而是看到了“一个空白代号能被三字架构撑起来”的完整路径。只要每个闭环还回答得了“记录了什么、评估了什么、行动了什么”这个项目就不算没方向。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑