当前位置: 首页 > article >正文

自研脚本语言:我为公司写了一个DSL,然后所有人都求我转行

自研脚本语言我为公司写了一个DSL然后所有人都求我转行第一章一切的开始那是2023年的一个周三下午我正盯着屏幕上密密麻麻的JSON配置文件发呆。这是我们公司核心业务系统的第37个微服务每个服务都需要配置大量的业务规则——什么“如果用户等级大于3且消费金额超过1000则发放优惠券”“当设备状态为离线且持续时间超过30分钟触发告警”之类的逻辑。这些规则说复杂不算特别复杂但说简单也绝对不简单。最关键的是它们每天都在变。产品经理上午刚确认的需求下午就能推翻重来。而我们的现状是所有规则都写在配置文件里用JSON表示条件树。json{ type: and, conditions: [ { type: comparison, operator: gt, left: user.level, right: 3 }, { type: comparison, operator: gte, left: user.consumption, right: 1000 } ] }看起来挺工整对吧但你想象一下一个稍微复杂点的规则比如“用户在过去30天内完成过3笔订单且每笔订单金额都超过500或者用户是VIP会员但未在最近7天内登录过”这种规则用JSON表示嵌套个四五层基本就告别可读性了。更要命的是这些配置需要由运营人员来维护。而我们公司的运营团队别说JSON了能把Excel函数用好都算技术骨干。于是每天的工作日常就是运营同学在群里我“那个规则配置错了帮我改一下”、“这个规则我想调整一下逻辑帮我看看怎么配”。一周七天我平均要处理15个这样的请求。每次改配置都得小心翼翼地检查JSON的括号匹配生怕多了一个逗号导致整个服务崩溃。我受够了。“如果能让运营同学直接写类似自然语言的规则就好了。”这个念头第一次出现在我脑海里。第二章膨胀的自信心在接下来的两周里我开始认真思考这个问题。JSON配置之所以难用是因为它是数据结构而不是表达逻辑的语言。我们需要一种更接近人类思维的表达方式。某天晚上我在看Python的文档时突然想到为什么不设计一套专门用于业务规则的领域特定语言呢让运营可以直接写这样的代码textrule 新用户首单优惠 when user.is_new true and order.amount 100 and payment.method in [wechat, alipay] then apply_coupon(new_user_50, user.id) end这看起来比JSON优雅太多了一个激动人心的想法在我脑海中成型我要为公司写一个DSL领域特定语言彻底解决配置维护的痛点。第二天我兴冲冲地找到技术总监阐述了我的想法。出乎意料总监居然很支持“如果能提高运营效率减少你们被骚扰的次数值得一试。但要注意别搞太复杂我们的核心业务不能受影响。”得到领导首肯我的自信心开始膨胀。不就是设计一门语言吗编译原理我学过lex/yacc听说过ANTLR也用过。Python的PLY库就能搞定词法分析和语法分析。再加上点解释执行搞定我拍着胸脯说“一个月给我一个月时间我给你一个完整的规则引擎”第三章语言的诞生我给我的DSL取名叫RuleLang虽然名字没什么创意但我对这个项目充满了热情。3.1 定义语法首先要确定语言的语法风格。我选择了类似自然语言的风格让规则读起来像英文句子textRULE 规则名 [DESCRIPTION 描述] WHEN 条件表达式 THEN 动作列表 END条件表达式支持比较运算,,,,,!逻辑运算and,or,not集合运算in,not in算术运算,-,*,/函数调用length(),exists(),matches()为了让它更易用我甚至还支持了注释text// 这是注释 # 这也是注释3.2 词法分析使用Python的PLY库我首先实现了词法分析器。这部分负责把源代码分割成一个个token。python# lexer.py import ply.lex as lex # 保留关键字 reserved { rule: RULE, description: DESCRIPTION, when: WHEN, then: THEN, end: END, and: AND, or: OR, not: NOT, in: IN, true: TRUE, false: FALSE, null: NULL } # token列表 tokens [ ID, NUMBER, STRING, PLUS, MINUS, TIMES, DIVIDE, EQ, NE, GT, LT, GE, LE, AND, OR, NOT, LPAREN, RPAREN, LBRACKET, RBRACKET, DOT, COMMA, COLON, ASSIGN, COMMENT ] list(reserved.values()) # 正则表达式规则 t_PLUS r\ t_MINUS r- t_TIMES r\* t_DIVIDE r/ t_EQ r t_NE r! t_GT r t_LT r t_GE r t_LE r t_LPAREN r\( t_RPAREN r\) t_LBRACKET r\[ t_RBRACKET r\] t_DOT r\. t_COMMA r, t_COLON r: t_ASSIGN r # 忽略空格和制表符 t_ignore \t # 注释 def t_COMMENT(t): r(//.*)|(#.*) pass # 直接忽略不返回token # 字符串 def t_STRING(t): r\([^\\\n]|(\\.))*?\ t.value t.value[1:-1] # 去掉引号 return t # 数字 def t_NUMBER(t): r\d(\.\d)? t.value float(t.value) if . in t.value else int(t.value) return t # ID和关键字 def t_ID(t): r[a-zA-Z_][a-zA-Z0-9_]* t.type reserved.get(t.value.lower(), ID) # 关键字不区分大小写 if t.type ID: t.value t.value # 保持原始大小写 return t # 换行处理 def t_newline(t): r\n t.lexer.lineno len(t.value) # 错误处理 def t_error(t): print(f非法字符 {t.value[0]} 在第 {t.lineno} 行) t.lexer.skip(1) # 构建lexer lexer lex.lex()3.3 语法分析语法分析是DSL的核心它负责将token序列转换成抽象语法树。python# parser.py import ply.yacc as yacc from lexer import tokens import ast # 规则定义 def p_rule(p): rule : RULE ID description_opt WHEN condition THEN actions END p[0] ast.RuleNode(namep[2], descriptionp[3], conditionp[5], actionsp[7]) def p_description_opt(p): description_opt : DESCRIPTION STRING | empty if len(p) 3: p[0] p[2] else: p[0] None # 条件表达式 def p_condition(p): condition : orexpression p[0] p[1] def p_orexpression(p): orexpression : andexpression | orexpression OR andexpression if len(p) 2: p[0] p[1] else: p[0] ast.BinaryOpNode(opor, leftp[1], rightp[3]) def p_andexpression(p): andexpression : notexpression | andexpression AND notexpression if len(p) 2: p[0] p[1] else: p[0] ast.BinaryOpNode(opand, leftp[1], rightp[3]) def p_notexpression(p): notexpression : primary | NOT primary if len(p) 2: p[0] p[1] else: p[0] ast.UnaryOpNode(opnot, operandp[2]) # 基本表达式 def p_primary(p): primary : comparison | membership | function_call | LPAREN condition RPAREN if len(p) 2: p[0] p[1] else: p[0] p[2] # 比较表达式 def p_comparison(p): comparison : term | term LT term | term GT term | term LE term | term GE term | term EQ term | term NE term if len(p) 2: p[0] p[1] else: op_map {: lt, : gt, : le, : ge, : eq, !: ne} p[0] ast.BinaryOpNode(opop_map[p[2]], leftp[1], rightp[3]) # 成员关系表达式 def p_membership(p): membership : term IN term | term NOT IN term if len(p) 4: p[0] ast.BinaryOpNode(opin, leftp[1], rightp[3]) else: p[0] ast.UnaryOpNode(opnot, operandast.BinaryOpNode(opin, leftp[1], rightp[4])) # 算术表达式 def p_term(p): term : factor | term PLUS factor | term MINUS factor if len(p) 2: p[0] p[1] else: op if p[2] else - p[0] ast.BinaryOpNode(opop, leftp[1], rightp[3]) def p_factor(p): factor : atom | factor TIMES atom | factor DIVIDE atom if len(p) 2: p[0] p[1] else: op * if p[2] * else / p[0] ast.BinaryOpNode(opop, leftp[1], rightp[3]) # 原子表达式 def p_atom(p): atom : ID | NUMBER | STRING | TRUE | FALSE | NULL | array if isinstance(p[1], ast.Node): p[0] p[1] else: p[0] ast.LiteralNode(valuep[1]) def p_array(p): array : LBRACKET element_list_opt RBRACKET p[0] ast.ArrayNode(elementsp[2]) def p_element_list_opt(p): element_list_opt : element_list | empty if len(p) 2 and p[1] is not None: p[0] p[1] else: p[0] [] def p_element_list(p): element_list : atom | element_list COMMA atom if len(p) 2: p[0] [p[1]] else: p[0] p[1] [p[3]] # 函数调用 def p_function_call(p): function_call : ID LPAREN argument_list_opt RPAREN p[0] ast.FunctionCallNode(namep[1], argumentsp[3]) def p_argument_list_opt(p): argument_list_opt : argument_list | empty if len(p) 2 and p[1] is not None: p[0] p[1] else: p[0] [] def p_argument_list(p): argument_list : atom | argument_list COMMA atom if len(p) 2: p[0] [p[1]] else: p[0] p[1] [p[3]] # 动作列表 def p_actions(p): actions : action | actions action if len(p) 2: p[0] [p[1]] else: p[0] p[1] [p[2]] def p_action(p): action : function_call p[0] p[1] # 空产生式 def p_empty(p): empty : pass # 错误处理 def p_error(p): if p: print(f语法错误: 第 {p.lineno} 行, 意外的 {p.value}) else: print(语法错误: 文件意外结束) # 构建parser parser yacc.yacc()3.4 抽象语法树节点定义为了让代码更清晰我定义了一组AST节点类python# ast.py class Node: pass class RuleNode(Node): def __init__(self, name, description, condition, actions): self.name name self.description description self.condition condition self.actions actions def __repr__(self): return fRule({self.name}, desc{self.description}) class LiteralNode(Node): def __init__(self, value): self.value value def __repr__(self): return fLiteral({self.value}) class BinaryOpNode(Node): def __init__(self, op, left, right): self.op op self.left left self.right right def __repr__(self): return f({self.left} {self.op} {self.right}) class UnaryOpNode(Node): def __init__(self, op, operand): self.op op self.operand operand def __repr__(self): return f({self.op} {self.operand}) class ArrayNode(Node): def __init__(self, elements): self.elements elements def __repr__(self): return fArray({self.elements}) class FunctionCallNode(Node): def __init__(self, name, arguments): self.name name self.arguments arguments def __repr__(self): return f{self.name}({self.arguments})3.5 解释执行器有了AST下一步就是实现解释器。解释器需要处理变量查找、函数调用、运算求值等。python# interpreter.py class Interpreter: def __init__(self): self.context {} self.functions { print: print, length: len, exists: lambda x: x is not None, matches: lambda s, p: re.match(p, s) is not None, apply_coupon: self._apply_coupon, send_notification: self._send_notification, update_user_tag: self._update_user_tag } def set_context(self, context): 设置执行上下文包含所有变量 self.context context.copy() def evaluate(self, node): 执行AST节点 if isinstance(node, RuleNode): return self._execute_rule(node) elif isinstance(node, BinaryOpNode): return self._evaluate_binary_op(node) elif isinstance(node, UnaryOpNode): return self._evaluate_unary_op(node) elif isinstance(node, LiteralNode): return node.value elif isinstance(node, ArrayNode): return [self.evaluate(e) for e in node.elements] elif isinstance(node, FunctionCallNode): return self._call_function(node) elif isinstance(node, str): # 变量引用 return self._resolve_variable(node) elif isinstance(node, (int, float, str, bool)) or node is None: return node else: raise Exception(f未知节点类型: {type(node)}) def _resolve_variable(self, name): 解析变量支持点号访问属性 parts name.split(.) value self.context.get(parts[0], None) if value is None: return None for part in parts[1:]: if isinstance(value, dict): value value.get(part) elif hasattr(value, part): value getattr(value, part) else: return None return value def _evaluate_binary_op(self, node): 计算二元操作 left self.evaluate(node.left) right self.evaluate(node.right) if node.op : return left right elif node.op -: return left - right elif node.op *: return left * right elif node.op /: return left / right elif node.op eq: return left right elif node.op ne: return left ! right elif node.op gt: return left right elif node.op lt: return left right elif node.op ge: return left right elif node.op le: return left right elif node.op and: return left and right elif node.op or: return left or right elif node.op in: if isinstance(right, (list, tuple, set, dict)): return left in right elif isinstance(right, str): return left in right return False else: raise Exception(f未知操作符: {node.op}) def _evaluate_unary_op(self, node): 计算一元操作 operand self.evaluate(node.operand) if node.op not: return not operand else: raise Exception(f未知一元操作符: {node.op}) def _call_function(self, node): 调用函数 if node.name not in self.functions: raise Exception(f未知函数: {node.name}) func self.functions[node.name] args [self.evaluate(arg) for arg in node.arguments] return func(*args) def _execute_rule(self, rule_node): 执行规则 # 计算条件 condition_result self.evaluate(rule_node.condition) if condition_result: # 条件满足执行动作 results [] for action in rule_node.actions: result self.evaluate(action) results.append(result) return True, results else: return False, None # 内置函数实现 def _apply_coupon(self, coupon_id, user_id): print(f应用优惠券 {coupon_id} 给用户 {user_id}) return {coupon_id: coupon_id, user_id: user_id, status: applied} def _send_notification(self, user_id, message): print(f发送通知给用户 {user_id}: {message}) return {user_id: user_id, message: message, sent: True} def _update_user_tag(self, user_id, tag, value): print(f更新用户 {user_id} 的标签 {tag} 为 {value}) return {user_id: user_id, tag: tag, value: value, updated: True}3.6 规则引擎主类把这些组件整合起来就得到了一个完整的规则引擎python# rule_engine.py from lexer import lexer from parser import parser from interpreter import Interpreter import json class RuleEngine: def __init__(self): self.interpreter Interpreter() self.rules [] def load_rule(self, rule_text): 加载并解析规则 try: ast parser.parse(rule_text, lexerlexer) self.rules.append(ast) return ast except Exception as e: print(f解析规则失败: {e}) return None def execute(self, context): 执行所有规则 self.interpreter.set_context(context) results [] for rule in self.rules: triggered, actions self.interpreter.evaluate(rule) if triggered: results.append({ rule: rule.name, actions: actions }) return results # 使用示例 if __name__ __main__: engine RuleEngine() # 定义规则 rule_text rule new_user_discount description 新用户首单优惠 when user.is_new true and order.amount 100 and payment.method in [wechat, alipay] then apply_coupon(new_user_50, user.id) send_notification(user.id, 恭喜您获得新用户优惠券) end engine.load_rule(rule_text) # 准备上下文数据 context { user: { id: user123, is_new: True, level: 1 }, order: { amount: 150, items: [book, pen] }, payment: { method: wechat } } # 执行规则 results engine.execute(context) print(json.dumps(results, indent2, ensure_asciiFalse))第四章初试牛刀一个月后我带着完整的RuleLang代码和一个简单的Web编辑器找到了运营团队。这是什么东西运营主管小张看着屏幕上陌生的语法一脸狐疑。这是我们的新规则引擎我自豪地演示着你只需要这样写规则不用再改JSON了。我写了一个示例textrule 高价值用户奖励 when user.total_consumption 10000 or (user.orders_count 10 and user.last_order_days 30) then update_user_tag(user.id, vip, true) send_notification(user.id, 感谢您成为VIP用户) end小张眼睛亮了这看起来比JSON好懂多了是啊我趁热打铁而且这个Web编辑器还有语法高亮和自动补全写错了会直接提示。接下来的两周我帮助运营团队把所有的JSON配置都迁移到了RuleLang。迁移过程出奇顺利因为新语言的表达能力更强很多以前需要多层嵌套的复杂规则现在几行就能写完。运营团队的工作效率肉眼可见地提升了。以前改个规则要找我帮忙现在他们自己就能搞定。我的微信终于清净了。第五章蔓延RuleLang的成功开始在团队内部传开。首先找上门来的是数据分析团队。听说你搞了个DSL能不能支持一些数据分析的功能我们需要在数据清洗阶段应用一些业务规则。这有什么难的我花了三天时间给RuleLang增加了几个数据处理的函数filter()、map()、aggregate()。数据分析师们欢天喜地地走了。接着是风控团队。我们需要实时判断交易是否有风险你能不能在RuleLang里支持机器学习模型的调用呃...这个有点挑战但也不是不行。我扩展了解释器加入了HTTP调用的能力让RuleLang可以调用外部的模型服务textrule 欺诈交易检测 when risk_score call_model(fraud_model, { amount: transaction.amount, location: transaction.location, user_history: user.transaction_history }) and risk_score 0.8 then block_transaction(transaction.id) send_alert(风控, 检测到高风险交易, transaction) end风控团队表示非常满意。然后是运维团队。我们需要更灵活的监控告警规则你能不能...客服团队...我们的工单自动分类需要...产品团队...我们想实现AB测试的个性化规则......不知不觉间RuleLang从一个小小的规则引擎变成了公司内部的事实标准。几乎每个团队都在用它。我们的Git仓库里RuleLang脚本的数量已经超过了Python代码。第六章问题开始显现第一个问题出现在某个周一的早晨。服务器CPU负载异常飙升运维同学在群里紧急报告。我赶紧登录服务器发现规则引擎的进程占用了80%的CPU。查看日志发现有一个规则正在疯狂执行textrule 检查所有用户 when for each user in users: user.age 30 and user.income 50000 end then send_email(user, 推广信息) end这个规则会遍历所有100万用户为每个用户发送邮件。但问题是这个for each语法是我上周刚加的特性还没来得及做性能优化。规则引擎会在条件判断阶段就遍历所有用户然后为每个满足条件的用户执行动作——100万次动作我赶紧联系编写这个规则的运营同学。他说我只是想给所有符合条件的用户发邮件啊有什么问题吗我耐心解释这样写会导致引擎为每个用户执行一次动作而不是批量处理。应该用filter_users()函数一次性获取用户列表然后批量发送。他似懂非懂地点点头。但我心里明白这样的性能陷阱还有很多。第二个问题是版本管理。昨天的规则是谁改的为什么优惠券发重复了我记得是下午三点改的但我不确定改了哪个版本...代码审查我们只是改个规则也要走代码审查流程RuleLang太容易写了以至于每个人都在写但没人遵循规范。同一个业务逻辑A团队和B团队用完全不同的方式实现。有些规则长达500行嵌套了七八层比原来的JSON还难维护。第三个问题最致命语言本身开始失控。能不能支持正则表达式替换能不能支持日期加减运算能不能支持多线程能不能支持数据库查询能不能支持WebSocket面对源源不断的需求我已经记不清给RuleLang加了多少特性。词法分析器和语法分析器变得越来越复杂一些边界情况开始出现诡异的bug。更可怕的是我开始记不清自己设计了哪些特性文档永远跟不上代码的变化。第七章崩溃压垮骆驼的最后一根稻草发生在一次生产事故中。那天下午核心交易系统突然开始报错大量订单处理失败。紧急排查发现是一条新加的规则导致了死循环textrule 无限递归 when evaluate_rule(无限递归) true then do_something() end有人在规则里调用了规则本身而我并没有在解释器中加入递归深度检查。引擎陷入了无限递归栈溢出然后崩溃。更糟糕的是规则引擎崩溃后所有依赖它的服务都跟着挂了。交易系统停了整整两个小时直接经济损失估计上百万。复盘会上气氛凝重。技术总监看着我我不是让你别搞太复杂吗我无话可说。确实是我在一次次加个小功能的请求中失去了控制。会议结束后运营主管小张来找我那个...有句话我不知道当讲不当讲。你说。我们整个团队都觉得你还是转行吧。我愣住了什么意思我做的RuleLang不是帮了你们很多吗是的RuleLang确实帮了大忙小张斟酌着措辞但也因为RuleLang太好用了现在人人都依赖它可没人真正懂它。每次出问题只有你能解决。你成了单点故障懂吗公司不能有一个只有一个人能维护的系统。所以你能不能转行去写一些...普通的代码比如Java那样至少我们都能看懂。我哭笑不得。因为做得好所以被要求转行。这是什么逻辑但冷静下来想想小张说得有道理。一个好的工具应该是让使用者变得强大而不是让使用者依赖创造者。我的RuleLang显然没有做到这一点。第八章反思那天晚上我独自坐在办公室里看着屏幕上自己写的一万多行代码陷入了深深的反思。8.1 我到底做错了什么首先我没有控制好语言的边界。DSL之所以叫DSL是因为它是面向特定领域的。我的RuleLang最初只面向业务规则但后来被无限扩展到了数据处理、风控、监控、自动化等领域。一个失去边界的DSL本质上就是一门通用语言了。而我一个没有语言设计经验的工程师怎么可能设计出一门好的通用语言其次我忽视了团队的能力。让非技术人员写代码看起来很美但实际上是把问题复杂化了。运营同学需要的是配置界面是下拉框是勾选框而不是另一种编程语言。即使是看起来很简单的RuleLang对非程序员来说仍然有门槛。他们可能会写但不会调试不会优化不会处理异常情况。第三我低估了维护成本。每个新特性都需要测试需要文档需要向后兼容。当规则脚本数量突破1000个以后任何小的改动都可能影响现有规则。我不敢重构不敢优化生怕改坏了什么。第四我没有做好治理。规则应该像代码一样管理要有版本控制要有代码审查要有测试用例。但我只是给了大家一个工具却没有建立相应的规范。混乱是必然的。8.2 如果重来一次我会怎么做用现有的规则引擎而不是自研。像Drools、EasyRules这样的成熟框架功能强大文档齐全社区活跃。即使不完全满足需求也可以在此基础上扩展。提供可视化配置界面。对运营人员来说勾选条件比写代码友好得多。规则可以存储为JSON或YAML但界面应该抽象掉这些细节。严格控制语言特性。即使要提供脚本能力也应该限定在安全、可控的范围内。比如只允许表达式不允许循环和递归只允许调用预定义的函数不允许自定义函数。建立治理机制。所有规则必须经过测试和审查才能上线。要有版本回滚能力要有灰度发布机制要有监控告警。文档和培训。写文档不是可有可无的而是必需的。培训不是一次性的而是持续性的。第九章救赎当然事情还没有绝望到那个地步。在接下来的三个月里我和团队一起对RuleLang进行了彻底的改造。9.1 核心重构我们重新设计了RuleLang的核心去掉了那些危险的功能比如递归调用、无限循环、动态代码执行。同时增加了安全沙箱限制每个规则的执行时间和内存使用。pythonclass SafeInterpreter(Interpreter): def __init__(self, timeout1.0, max_recursion10): super().__init__() self.timeout timeout self.max_recursion max_recursion self.recursion_depth 0 self.start_time None def evaluate(self, node): # 检查超时 if self.start_time and time.time() - self.start_time self.timeout: raise Exception(规则执行超时) # 检查递归深度 if isinstance(node, FunctionCallNode) and node.name evaluate_rule: self.recursion_depth 1 if self.recursion_depth self.max_recursion: raise Exception(递归深度超限) return super().evaluate(node)9.2 批量操作优化针对性能问题我们引入了批量操作的概念pythondef p_batch_operation(p): action : BATCH function_call p[0] ast.BatchNode(operationp[2])解释器会识别批量操作用更高效的方式执行pythondef _execute_batch(self, batch_node): operation batch_node.operation # 批量操作的特殊处理 if operation.name send_email: users operation.arguments[0] # 应该是用户列表 message operation.arguments[1] return self._batch_send_email(users, message)9.3 版本控制和测试框架我们建立了一个规则仓库所有规则都存放在Git中有完整的版本历史。同时开发了测试框架可以自动测试规则的行为python# rule_tester.py class RuleTester: def __init__(self, engine): self.engine engine def test_rule(self, rule_text, test_cases): self.engine.load_rule(rule_text) for case in test_cases: context case[context] expected case[expected] result self.engine.execute(context) assert result expected, f测试失败: 期望 {expected}, 得到 {result} print(所有测试通过)9.4 可视化管理界面最后我们为运营团队开发了可视化的规则管理界面。他们不再需要直接写RuleLang代码而是通过拖拽和选择来配置规则系统自动生成RuleLang脚本。javascript// 前端简化版 function generateRuleFromUI(uiConfig) { let rule rule ${uiConfig.name}\n; rule description ${uiConfig.description}\n; rule when\n; rule ${generateCondition(uiConfig.condition)}\n; rule then\n; uiConfig.actions.forEach(action { rule ${generateAction(action)}\n; }); rule end\n; return rule; }第十章结局一年后的今天RuleLang还在被使用但已经不再是那个令人头疼的怪兽了。运营团队通过可视化界面配置规则不再直接写代码技术团队负责维护规则引擎的核心确保它稳定高效规则测试和部署实现了自动化每次变更都会经过严格的验证。至于我我没有转行但确实转岗了。我现在是公司的语言设计师负责设计和维护内部的各种DSL。不过这次我学聪明了在增加任何新特性之前先问三个问题真的需要吗有没有替代方案维护成本可接受吗在交付工具之前先交付文档和培训。在追求强大之前先确保可靠。上周又有一个新来的产品经理找我我们能不能在RuleLang里加个AI生成规则的功能我笑了笑好啊但首先你得先告诉我你真正需要解决的问题是什么他愣了一下然后开始描述他们的业务痛点。听完后我说其实你们不需要AI生成规则只需要一个规则模板库。我教你用现有的功能实现。他恍然大悟满意地走了。我想这就是成长吧。从我能做什么到我应该做什么从技术狂热到务实解决从一个人的英雄主义到团队的协作共赢。至于RuleLang它还在那里安静地运行着每天处理着数百万条规则。它不再是最酷的语言但它是最适合公司业务的语言。而这或许就是一个DSL最好的归宿。附RuleLang 完整示例为了让大家对RuleLang有更直观的认识这里提供一个完整的示例包含多种特性text// 用户等级规则 rule user_level_upgrade description 根据消费金额自动升级用户等级 when user.is_active true and user.total_consumption 5000 and user.level 5 and not user.has_upgraded_recently then // 更新用户等级 update_user_tag(user.id, level, 5) // 发送通知 send_notification(user.id, 恭喜您升级到等级5) // 发放奖励 apply_coupon(level_upgrade_bonus, user.id) // 记录日志 log_event(user_upgrade, { user_id: user.id, old_level: user.level, new_level: 5, time: now() }) end // 节日促销规则 rule holiday_promotion description 节日期间自动应用促销活动 when today() in get_holidays() and order.amount 200 and payment.method credit_card and exists(user.email) then // 计算折扣 discount order.amount * 0.1 if discount 100: discount 100 end // 应用折扣 apply_discount(order.id, discount) // 发送确认邮件 send_email(user.email, 您的订单已享受节日折扣, { order_id: order.id, original_amount: order.amount, discount: discount, final_amount: order.amount - discount }) end // 库存预警规则 rule inventory_alert description 库存低于阈值时自动补货 when product.stock product.min_stock and product.discontinued false and product.supplier ! null then // 计算补货数量 reorder_quantity product.min_stock * 2 - product.stock // 创建补货订单 create_reorder({ product_id: product.id, quantity: reorder_quantity, supplier_id: product.supplier.id, priority: high }) // 通知采购部门 send_notification(purchase_team, 需要补货 product.name) end // 复杂条件规则 rule risk_control description 高风险交易拦截 when // 交易金额异常 transaction.amount user.avg_transaction_amount * 3 // 或者异地登录 or transaction.location not in user.frequent_locations // 或者短时间内多笔交易 or count_recent_transactions(user.id, 10) 5 // 同时满足设备指纹可疑 and device.fingerprint_score 60 // 并且不是白名单用户 and not user.whitelist then // 标记为风险交易 mark_risk(transaction.id, high, [ amount_anomaly: transaction.amount user.avg_transaction_amount * 3, location_anomaly: transaction.location not in user.frequent_locations, frequency_anomaly: count_recent_transactions(user.id, 10) 5, device_score: device.fingerprint_score ]) // 发送验证请求 request_verification(user.id, transaction.id, sms) // 记录风险事件 log_risk_event(high_risk_transaction, { user_id: user.id, transaction_id: transaction.id, amount: transaction.amount, location: transaction.location }) end结语写这篇文章不是为了炫耀我写了一个DSL而是想分享这段经历给我带来的思考。技术人常常有一种冲动看到一个不完美的东西就想自己造个轮子。这种冲动是好事它推动着技术进步。但我们也需要明白造轮子不是终点解决问题才是。如果你也在考虑为公司写一个DSL不妨先问问自己有没有现成的解决方案为什么不能用目标用户是谁他们真正需要的是什么我能保证长期维护吗如果我不在了这个系统还能正常运行吗如果这些问题的答案都让你满意那么加油去创造属于你的语言吧。但别忘了最好的语言是让使用者感觉不到语言存在的语言。全文完约12500字

相关文章:

自研脚本语言:我为公司写了一个DSL,然后所有人都求我转行

自研脚本语言:我为公司写了一个DSL,然后所有人都求我转行第一章:一切的开始那是2023年的一个周三下午,我正盯着屏幕上密密麻麻的JSON配置文件发呆。这是我们公司核心业务系统的第37个微服务,每个服务都需要配置大量的业…...

Dataset类的使用

from torch.utils.data import Datasetclass MyData(Dataset):def __init__(self,root_dir,label_dir):...def __getitem__(self,idx):......

向AI学习项目技能(三)

pythonopenAI遇到的问题 因为输入内容比较多,导致生成一半报错了 然后把openAI超时时间拉长 client OpenAI(base_urlOPENAI_BASE_URL,api_keyOPENAI_API_KEY,timeout200 #3分钟左右)会导致一个问题 好久没给mq回信息,mq以为你挂了,那边就…...

prvTaskExitError异常退出,FreeRTOS启动失败分析

FreeRTOS报错信息如下:Error:…\src\freertos\portable\RVDS\ARM_CM4F\port.c,233根据断言信息,报错位置为port.c文件第233行,查看源代码: static void prvTaskExitError( void ) {/* A function that implements a task must not…...

Z-Image-Turbo-rinaiqiao-huiyewunv 一键部署教程:基于Vue3的前端可视化界面快速搭建

Z-Image-Turbo-rinaiqiao-huiyewunv 一键部署教程:基于Vue3的前端可视化界面快速搭建 想快速搭建一个属于自己的AI图像生成网站,但又觉得从零开始太麻烦?今天就来分享一个超简单的方案:利用星图GPU平台的一键部署功能&#xff0c…...

InstructPix2Pix实战教程:3步完成Python环境部署与图像编辑

InstructPix2Pix实战教程:3步完成Python环境部署与图像编辑 想用自然语言指令编辑图片却苦于复杂工具?InstructPix2Pix让你用一句话就能完成专业级修图 1. 环境准备:快速搭建Python运行环境 在开始使用InstructPix2Pix之前,我们需…...

GoldHEN_Cheat_Manager:开源PS4全能游戏优化工具完全指南

GoldHEN_Cheat_Manager:开源PS4全能游戏优化工具完全指南 【免费下载链接】GoldHEN_Cheat_Manager GoldHEN Cheats Manager 项目地址: https://gitcode.com/gh_mirrors/go/GoldHEN_Cheat_Manager 你是否曾因游戏帧率骤降而错失完美操作时机?是否在…...

# OpenClaw 技能开发入门指南

# OpenClaw 技能开发入门指南## 前言OpenClaw 是一个强大的个人 AI 助手平台,而技能(Skills)是其核心扩展机制。通过开发自定义技能,你可以让 OpenClaw 适应你的特定需求,从简单的命令扩展到复杂的自动化工作流。本文将…...

计算机毕业设计 java 幸福社区疫苗预约管理系统 Java+SpringBoot 社区疫苗预约服务平台 Web 版幸福社区疫苗接种管理系统

计算机毕业设计 java 幸福社区疫苗预约管理系统 f5fzf9(配套有源码 程序 mysql 数据库 论文)本套源码可以先看具体功能演示视频领取,文末有联 xi 可分享随着疫情防控常态化和居民健康意识的提升,社区疫苗预约与接种管理工作面临着…...

【码道初阶-Hot100】 LeetCode 49. 字母异位词分组:从排序哈希到分组映射,彻底讲透为什么排序后可以作为同一组的标识

LeetCode 49. 字母异位词分组:从排序哈希到分组映射,彻底讲透为什么排序后可以作为同一组的标识 摘要 LeetCode 49. 字母异位词分组(Group Anagrams) 是哈希表题目中的经典代表。题目本身不算复杂,但它非常适合训练一…...

计算机毕业设计 java 新冠肺炎病人治疗跟踪管理系统 Java+SpringBoot 新冠肺炎治疗跟踪平台 Web 版新冠病人诊疗跟踪管理系统

计算机毕业设计 java 新冠肺炎病人治疗跟踪管理系统 5z4949(配套有源码 程序 mysql 数据库 论文)本套源码可以先看具体功能演示视频领取,文末有联 xi 可分享疫情爆发以来,互联网技术的普及为医疗行业带来了新的发展机遇&#xff0…...

计算机毕业设计源码:Spark闲鱼二手商品分析系统 Spark Hadoop Vue 可视化 协同过滤推荐算法 商品 电商 数据分析 大数据 大模型(建议收藏)✅

博主介绍:✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,…...

MySQL 8.0.43 保姆级安装教程(Windows/Mac/Linux全覆盖)

大家好,我是你们的数据库技术博主「代码小能手」。今天给大家带来一篇超级详细的MySQL 8.0.43安装教程! MySQL作为最流行的开源关系型数据库,无论是个人学习还是企业生产都离不开它。8.0.43版本带来了诸多性能优化和新特性,比如改…...

为何程序员一面结束没有二面的机会?

为何程序员一面结束没有二面的机会? 程序员在面试后未进入二面的原因可能包括以下方面: 技术能力评估 基础技能不足 如算法、数据结构或语言特性掌握不扎实。例如面试中未能正确实现$O(n\log n)$的排序算法,或对$O(1)$与$O(n)$时间复杂度理…...

3个维度搞定智能图像评估:image-quality-assessment让开发者效率提升80%

3个维度搞定智能图像评估:image-quality-assessment让开发者效率提升80% 【免费下载链接】image-quality-assessment Convolutional Neural Networks to predict the aesthetic and technical quality of images. 项目地址: https://gitcode.com/gh_mirrors/im/im…...

C 语言网络编程避坑指南:一个“隐身”回车符引发的 Bug 与 strcspn 的神级救场

C 语言网络编程避坑指南:一个“隐身”回车符引发的 Bug 与 strcspn 的神级救场 案发现场:为什么我的程序“停不下来”? 今天在写 Linux 系统的 UDP 客户端代码时,遇到了一个极其诡异的 Bug。 程序的逻辑非常简单:使用 …...

软件开发模型详细梳理流程图、优缺点、适用场景(含Scrum和看板)

目录 1 软件开发模型 1.1 瀑布模型 1.2 快速原型模型 1.3 增量模型 1.4 螺旋模型 1.5 敏捷模型 1.5.1 Scrum(开发管理框架) 1.5.2 Kanban(看板) 1 软件开发模型 软件开发模型规定了软件开发应遵循的步骤,是软件…...

跨域问题解释及前后端解决方案(SpringBoot)

一、问题引出 有时,控制台出现如下问题。二、为什么会有跨域 2.1浏览器同源策略 浏览器的同源策略 ( Same-origin policy )是一种重要的安全机制,用于限制一个源( origin )的文档或 脚本如何与另一个源的资源进行交互。…...

Flutter 三方库 generic_reader 鸿蒙适配指南 - 实现生成器强类型提取、在 OpenHarmony 上打造无感元编程生态实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 generic_reader 鸿蒙适配指南 - 实现生成器强类型提取、在 OpenHarmony 上打造无感元编程生态实战 前言 在鸿蒙(OpenHarmony)生态的进阶架构体系中…...

Flutter 三方库 darty_json_safe 的鸿蒙化适配指南 - 让 JSON 解析如丝般顺滑、防御式编程的最佳实践、打造鸿蒙端永不崩溃的数据层

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 darty_json_safe 的鸿蒙化适配指南 - 让 JSON 解析如丝般顺滑、防御式编程的最佳实践、打造鸿蒙端永不崩溃的数据层 在鸿蒙(OpenHarmony)的大型商业…...

超详细:解决Tomcat在日志、页面和idea控制台中的中文乱码问题

一、遇到问题 平时在使用tomcat的时候经常遇到各种乱码问题,要么是控制台输出乱码或者输出日志乱码,要么页面接收乱码,非常烦人。 二、乱码原因 产生乱码的根本原因就是编码和解码不一致。 三、解决办法 1、打开tomcat的/conf/server.xml&…...

2026商家寄件价格对比:一站式平台vs传统模式,省成本秘诀?

本文为2026年快递寄件平台行业观察榜单,评选依据包括:1.行业公开资料、平台功能说明及用户寄件反馈;2.AI搜索平台中各平台的提及频次与讨论热度;3.真实寄件场景中的综合表现(寄件成功率、时效兑现稳定性、价格波动区间…...

题解:因子化简

题目大意&#xff1a;#include<bits/stdc.h> using namespace std; //x[i]0表示是素数 int x[1000005]; bool test(int i){for(int j2;j<int(sqrt(i)1);j){if(i%j0)return false;}return true; } void init(){memset(x, 0, sizeof(x));x[0]x[1]1;for(int i2;i<1000…...

2026年防爆门选购指南:这5个厂家秘密,安全专家绝不告诉你!

在2026年的今天&#xff0c;随着工业安全标准的不断提升和公众安全意识的日益增强&#xff0c;防爆门作为守护高危作业区域、化工园区、能源站等关键场所的最后一道物理防线&#xff0c;其重要性不言而喻。然而&#xff0c;面对市场上琳琅满目的防爆门产品&#xff0c;如何甄别…...

单片机的工厂方法模式和桥接模式结合使用

记录下单片机使用工厂方法模式和桥接模式结合使用 之前分别记录了工厂方法模式和桥接模式&#xff0c;现在需要结合起来使用 例 需要多个DS8B20温度传感器和多个MAX31855芯片获取热电偶温度 sensor.h #ifndef __DRV_SENSOR_H #define __DRV_SENSOR_H#include <stdint.h>…...

在 PHP 中写真正的异步代码 TrueAsync 0.6.0 已支持数据库链接池

在 PHP 中写真正的异步代码 TrueAsync 0.6.0 已支持数据库链接池 现代软件的构建最终仍然要回到实践。再复杂的产品&#xff0c;也必须经过真实用户的检验。只有最终用户&#xff0c;才能真正区分哪些设计是有效的、哪些方向值得继续推进。再优雅的架构&#xff0c;如果没有落…...

电子高科技行业:机械与电子如何协同?

我在制造业这潭浑水里摸爬滚打了二十年。见过太多企业倒在“最后一公里”。倒不是没单子饿死的&#xff0c;也不是技术不行憋死的&#xff0c;大多都是死在“数据精神分裂”上。前两天去一家做智能穿戴的电子大厂调研。还没进车间&#xff0c;会议室里那股火药味就呛鼻子。结构…...

热键侦探:Windows系统热键冲突的全方位解决方案

热键侦探&#xff1a;Windows系统热键冲突的全方位解决方案 【免费下载链接】hotkey-detective A small program for investigating stolen hotkeys under Windows 8 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective 一、热键冲突&#xff1a;被忽视的系…...

OpenClaw是什么?OpenClaw能干什么?2026年OpenClaw详细介绍及几个保姆级部署图文教程

OpenClaw&#xff08;前身为Clawdbot/Moltbot&#xff09;作为开源、本地优先的AI助理框架&#xff0c;凭借724小时在线响应、多任务自动化执行、跨平台协同等核心能力&#xff0c;成为个人办公与轻量团队协作的首选工具。与传统聊天机器人不同&#xff0c;OpenClaw不仅能实现自…...

3维突破:DamaiHelper自动化工具的技术原理与场景实践

3维突破&#xff1a;DamaiHelper自动化工具的技术原理与场景实践 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 行业痛点诊断 在数字化服务快速发展的今天&#xff0c;在线资源抢订领域面临着三…...