# 规则引擎 DSL 设计分析 > **分析时间**: 2026-02-12 > **目的**: 验证当前数据库设计是否支持规则引擎DSL需求 --- ## 一、DSL代码示例分析 ### 1.1 示例代码 ```python @_call_verify_func_int(expected_output_id) def calculate_score(标准分_ids: list[id]) -> str: return sum(标准分_ids) @_call_verify_func_notnone() def build_one_five_section(one_two_ch: id, one_tree_ch: id, calculate_score_callback: ref, prompt: str) -> str: one_two_ch_str = _call_export_resource_str(one_two_ch) one_tree_ch_str = _call_export_resource_str(one_tree_ch) return _call_ai_assistant(system=prompt, user='%s\n%s\n标准分:%s' % (one_two_ch_str, one_tree_ch_str, calculate_score_callback())) @_call_verify_func_notnone() def build_one_full_chapter(one_two_ch: id, one_tree_ch: id, one_four_ch: id, one_five_ch_callback: ref): one_two_ch_str = _call_export_resource_str(one_two_ch) one_tree_ch_str = _call_export_resource_str(one_tree_ch) one_four_ch_str = _call_export_resource_str(one_four_ch) return '%s\n%s\n%s\n%s' % (one_two_ch_str, one_tree_ch_str, one_four_ch_str, one_five_ch_callback()) # 参数配置 one_two_ch_id=12578 one_tree_ch_id=12389 one_four_ch_id=13456 score_expected_output_id=120234989 standard_item1_score_id=234808 standard_item2_score_id=234874 standard_item3_score_id=2234 standard_item4_score_id=12398 section_prompt='从原文生成100以内字数的摘要,并说明标准分' # 执行链 score_ref = &calculate_score([standard_item1_score_id, standard_item2_score_id, standard_item3_score_id, standard_item4_score_id], expected_output_id=score_expected_output_id) section_ref = &build_one_five_section(one_two_ch_id, one_tree_ch_id, score_ref, prompt=section_prompt) one_full_chapter = build_one_full_chapter(one_two_ch_id, one_tree_ch_id, one_four_ch_id, section_ref) return one_full_chapter ``` ### 1.2 核心特性提取 | 特性 | 说明 | 示例 | |------|------|------| | **函数定义** | 支持自定义函数,带装饰器 | `@_call_verify_func_int` | | **参数类型** | id, list[id], ref, str | `标准分_ids: list[id]` | | **内置函数** | 导出资源、AI调用 | `_call_export_resource_str()` | | **函数引用** | 创建和调用函数引用 | `&calculate_score(...)` | | **依赖链** | 函数间依赖关系 | score_ref → section_ref → result | | **输出验证** | 验证输出类型和非空 | `expected_output_id` | --- ## 二、当前数据库设计分析 ### 2.1 RULE节点属性(当前设计) ```sql -- 规则节点属性 node_properties: - rule_type: VARCHAR # 规则类型(direct_entity/extraction/llm/aggregate) - action_type: VARCHAR # 动作类型(use_entity_value/regex_extract/llm_generate) - description: TEXT # 规则描述 - action_config: JSONB # 动作配置(JSON格式) - dsl_content: TEXT # DSL内容(预留字段) - last_output_text: TEXT # 最后输出文本 - last_output_json: JSONB # 最后输出JSON - last_run_status: VARCHAR # 最后运行状态 - last_run_time: TIMESTAMP # 最后运行时间 - last_run_error: TEXT # 最后运行错误 ``` ### 2.2 RULE关系(当前设计) ```sql -- 规则关系 PROJECT --HAS_RULE--> RULE # 项目包含规则 RULE --FOR_ELEMENT--> ELEMENT # 规则对应要素 RULE --INPUT_FROM--> ENTITY # 规则输入来源(实体) RULE --INPUT_FROM--> ATTACHMENT # 规则输入来源(附件) RULE --INPUT_FROM--> VALUE # 规则输入来源(其他值) ``` ### 2.3 INPUT_FROM边属性(当前设计) ```sql -- 输入边属性 edge_properties: - input_key: VARCHAR # 输入参数名(如 'entity') - input_type: VARCHAR # 输入类型(entity_ref/attachment_ref/value_ref) - input_name: VARCHAR # 输入名称(显示用) - fixed_value: TEXT # 固定值(可选) ``` --- ## 三、DSL需求 vs 当前设计对比 ### 3.1 ✅ 已支持的特性 | DSL特性 | 当前设计 | 说明 | |---------|----------|------| | **节点ID引用** | ✅ 支持 | `INPUT_FROM` 边 + `input_type='entity_ref'` | | **输入参数** | ✅ 支持 | `edge_properties.input_key` | | **输出存储** | ✅ 支持 | `last_output_text`, `last_output_json` | | **规则描述** | ✅ 支持 | `description` 字段 | | **执行状态** | ✅ 支持 | `last_run_status`, `last_run_time` | ### 3.2 ❌ 缺失的特性 | DSL特性 | 当前设计 | 问题 | |---------|----------|------| | **函数定义** | ❌ 不支持 | 没有存储函数定义的结构 | | **函数引用** | ❌ 不支持 | 没有 `RULE --DEPENDS_ON--> RULE` 关系 | | **参数类型** | ❌ 不完整 | 只有 `input_type`,没有详细类型定义 | | **装饰器/验证** | ❌ 不支持 | 没有存储验证规则的字段 | | **DSL代码** | ⚠️ 部分支持 | 有 `dsl_content` 字段,但未定义结构 | | **执行顺序** | ❌ 不支持 | 没有依赖关系的拓扑排序机制 | | **输出验证** | ❌ 不支持 | 没有 `expected_output_id` 机制 | --- ## 四、设计改进方案 ### 4.1 方案A:扩展当前设计(推荐) **核心思路**: 在当前图数据库基础上扩展,支持DSL特性 #### 4.1.1 新增节点类型 ```sql -- 不需要新增节点类型,RULE节点足够 ``` #### 4.1.2 新增关系类型 ```sql INSERT INTO edge_types (type_code, type_name, from_node_type, to_node_type, description) VALUES ('DEPENDS_ON', '依赖规则', 'RULE', 'RULE', '规则依赖关系(用于函数引用)'), ('VALIDATES_TO', '验证输出', 'RULE', 'VALUE', '规则验证输出到指定要素值'); ``` #### 4.1.3 扩展RULE节点属性 ```sql -- 新增属性 ALTER TABLE node_properties ADD COLUMN IF NOT EXISTS prop_int BIGINT; -- 规则节点新增属性 node_properties: - rule_type: VARCHAR # 规则类型 - 'function_def' # 函数定义 - 'direct_entity' # 直接引用实体 - 'extraction' # 正则提取 - 'llm' # LLM生成 - 'aggregate' # 聚合计算 - function_name: VARCHAR # 函数名(如 'calculate_score') - function_params: JSONB # 函数参数定义 { "标准分_ids": {"type": "list[id]", "required": true}, "expected_output_id": {"type": "id", "required": false} } - function_body: TEXT # 函数体(DSL代码) - return_type: VARCHAR # 返回类型(str/int/float/json) - validators: JSONB # 验证器配置 [ {"type": "verify_int", "target_id": 120234989}, {"type": "verify_notnone"} ] - execution_order: INT # 执行顺序(拓扑排序后的序号) ``` #### 4.1.4 扩展INPUT_FROM边属性 ```sql -- 输入边属性扩展 edge_properties: - input_key: VARCHAR # 参数名(如 'one_two_ch') - input_type: VARCHAR # 输入类型 - 'id' # 单个节点ID - 'list[id]' # 节点ID列表 - 'ref' # 函数引用 - 'str' # 字符串 - 'int' # 整数 - input_value_type: VARCHAR # 值类型(node_ref/rule_ref/literal) - input_value: TEXT # 字面值(如 prompt字符串) - input_value_json: JSONB # 复杂值(如ID列表) - sort_order: INT # 参数顺序 ``` #### 4.1.5 数据示例 ```sql -- 示例:calculate_score 函数 INSERT INTO nodes (id, node_type, node_key, name, status) VALUES (700, 'RULE', 'func:calculate_score', 'calculate_score函数', 'active'); INSERT INTO node_properties (node_id, prop_key, prop_value, prop_json) VALUES (700, 'rule_type', 'function_def', NULL), (700, 'function_name', 'calculate_score', NULL), (700, 'function_params', NULL, '{ "标准分_ids": {"type": "list[id]", "required": true} }'), (700, 'function_body', 'return sum(标准分_ids)', NULL), (700, 'return_type', 'int', NULL), (700, 'validators', NULL, '[{"type": "verify_int", "target_id": 120234989}]'); -- 输入:标准分ID列表 INSERT INTO edges (id, edge_type, from_node_id, to_node_id, sort_order) VALUES (2000, 'INPUT_FROM', 700, 234808, 1), (2001, 'INPUT_FROM', 700, 234874, 2), (2002, 'INPUT_FROM', 700, 2234, 3), (2003, 'INPUT_FROM', 700, 12398, 4); INSERT INTO edge_properties (edge_id, prop_key, prop_value) VALUES (2000, 'input_key', '标准分_ids'), (2000, 'input_type', 'list[id]'), (2000, 'input_value_type', 'node_ref'), (2001, 'input_key', '标准分_ids'), (2001, 'input_type', 'list[id]'), (2001, 'input_value_type', 'node_ref'), (2002, 'input_key', '标准分_ids'), (2002, 'input_type', 'list[id]'), (2002, 'input_value_type', 'node_ref'), (2003, 'input_key', '标准分_ids'), (2003, 'input_type', 'list[id]'), (2003, 'input_value_type', 'node_ref'); -- 示例:build_one_five_section 函数 INSERT INTO nodes (id, node_type, node_key, name, status) VALUES (701, 'RULE', 'func:build_one_five_section', 'build_one_five_section函数', 'active'); INSERT INTO node_properties (node_id, prop_key, prop_value, prop_json) VALUES (701, 'rule_type', 'function_def', NULL), (701, 'function_name', 'build_one_five_section', NULL), (701, 'function_params', NULL, '{ "one_two_ch": {"type": "id", "required": true}, "one_tree_ch": {"type": "id", "required": true}, "calculate_score_callback": {"type": "ref", "required": true}, "prompt": {"type": "str", "required": true} }'), (701, 'function_body', 'one_two_ch_str = _call_export_resource_str(one_two_ch)\none_tree_ch_str = _call_export_resource_str(one_tree_ch)\nreturn _call_ai_assistant(system=prompt, user=''%s\\n%s\\n标准分:%s'' % (one_two_ch_str, one_tree_ch_str, calculate_score_callback()))', NULL), (701, 'return_type', 'str', NULL), (701, 'validators', NULL, '[{"type": "verify_notnone"}]'); -- 输入1: one_two_ch (节点ID) INSERT INTO edges (id, edge_type, from_node_id, to_node_id, sort_order) VALUES (2010, 'INPUT_FROM', 701, 12578, 1); INSERT INTO edge_properties (edge_id, prop_key, prop_value) VALUES (2010, 'input_key', 'one_two_ch'), (2010, 'input_type', 'id'), (2010, 'input_value_type', 'node_ref'); -- 输入2: one_tree_ch (节点ID) INSERT INTO edges (id, edge_type, from_node_id, to_node_id, sort_order) VALUES (2011, 'INPUT_FROM', 701, 12389, 2); INSERT INTO edge_properties (edge_id, prop_key, prop_value) VALUES (2011, 'input_key', 'one_tree_ch'), (2011, 'input_type', 'id'), (2011, 'input_value_type', 'node_ref'); -- 输入3: calculate_score_callback (函数引用) INSERT INTO edges (id, edge_type, from_node_id, to_node_id, sort_order) VALUES (2012, 'DEPENDS_ON', 701, 700, 3); INSERT INTO edge_properties (edge_id, prop_key, prop_value) VALUES (2012, 'input_key', 'calculate_score_callback'), (2012, 'input_type', 'ref'), (2012, 'input_value_type', 'rule_ref'); -- 输入4: prompt (字符串字面值) INSERT INTO edge_properties (edge_id, prop_key, prop_value) VALUES (2013, 'input_key', 'prompt'), (2013, 'input_type', 'str'), (2013, 'input_value_type', 'literal'), (2013, 'input_value', '从原文生成100以内字数的摘要,并说明标准分'); ``` ### 4.2 方案B:独立DSL存储(备选) **核心思路**: 将DSL代码作为整体存储,运行时解析 #### 优点 - 实现简单,直接存储DSL代码 - 灵活性高,不受数据库结构限制 #### 缺点 - 无法利用图数据库的关系查询能力 - 难以可视化规则依赖关系 - 难以做规则影响分析 #### 实现方式 ```sql -- 只需使用 dsl_content 字段 INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (700, 'dsl_content', '完整的DSL代码...'); ``` --- ## 五、推荐方案:方案A(扩展设计) ### 5.1 优势 1. **保留图数据库优势**: 可以查询规则依赖关系、影响分析 2. **可视化友好**: 前端可以展示规则DAG图 3. **增量执行**: 只执行受影响的规则 4. **调试友好**: 可以追踪每个规则的输入输出 5. **兼容性好**: 与现有设计无缝集成 ### 5.2 实现步骤 #### Step 1: 更新数据库Schema ```sql -- 1. 新增关系类型 INSERT INTO edge_types (type_code, type_name, from_node_type, to_node_type, description) VALUES ('DEPENDS_ON', '依赖规则', 'RULE', 'RULE', '规则依赖关系'), ('VALIDATES_TO', '验证输出', 'RULE', 'VALUE', '规则验证输出'); -- 2. 新增属性定义 INSERT INTO property_definitions (owner_type, target_type, prop_key, prop_name, data_type, required) VALUES ('node', 'RULE', 'function_name', '函数名', 'string', false), ('node', 'RULE', 'function_params', '函数参数', 'json', false), ('node', 'RULE', 'function_body', '函数体', 'string', false), ('node', 'RULE', 'return_type', '返回类型', 'string', false), ('node', 'RULE', 'validators', '验证器', 'json', false), ('node', 'RULE', 'execution_order', '执行顺序', 'number', false), ('edge', 'INPUT_FROM', 'input_value_type', '值类型', 'string', false), ('edge', 'INPUT_FROM', 'input_value', '字面值', 'string', false), ('edge', 'INPUT_FROM', 'input_value_json', '复杂值', 'json', false); ``` #### Step 2: 后端实现规则解析器 ```java /** * DSL规则解析器 */ @Service public class RuleDSLParser { /** * 解析DSL代码,创建规则节点和关系 */ public Long parseDSL(String dslCode, Long projectId) { // 1. 解析函数定义 List functions = parseFunctions(dslCode); // 2. 为每个函数创建RULE节点 Map functionNodeMap = new HashMap<>(); for (FunctionDef func : functions) { Long ruleId = createRuleNode(func, projectId); functionNodeMap.put(func.getName(), ruleId); } // 3. 创建依赖关系(DEPENDS_ON边) for (FunctionDef func : functions) { createDependencies(func, functionNodeMap); } // 4. 拓扑排序,计算执行顺序 calculateExecutionOrder(functionNodeMap.values()); return functionNodeMap.get("main"); // 返回主函数ID } } ``` #### Step 3: 后端实现规则执行器 ```java /** * DSL规则执行器 */ @Service public class RuleDSLExecutor { /** * 执行规则(按依赖顺序) */ public Object executeRule(Long ruleId) { // 1. 获取规则节点 Rule rule = ruleService.getById(ruleId); // 2. 获取依赖规则(DEPENDS_ON边) List dependencies = getDependencies(ruleId); // 3. 按execution_order排序 dependencies.sort(Comparator.comparing(Rule::getExecutionOrder)); // 4. 递归执行依赖规则 Map context = new HashMap<>(); for (Rule dep : dependencies) { Object result = executeRule(dep.getId()); context.put(dep.getFunctionName(), result); } // 5. 执行当前规则 Object result = executeFunctionBody(rule, context); // 6. 验证输出 validateOutput(rule, result); // 7. 保存输出 saveOutput(ruleId, result); return result; } /** * 执行函数体 */ private Object executeFunctionBody(Rule rule, Map context) { // 根据 rule_type 选择执行器 switch (rule.getRuleType()) { case "function_def": return executePythonFunction(rule, context); case "direct_entity": return executeDirectEntity(rule); case "llm": return executeLLM(rule, context); default: throw new UnsupportedOperationException("Unknown rule type: " + rule.getRuleType()); } } } ``` #### Step 4: 前端规则配置界面 ```vue ``` --- ## 六、接口设计更新 ### 6.1 新增接口 ```java // 1. DSL解析接口 POST /api/v1/projects/{projectId}/rules/parse-dsl { "dslCode": "完整的DSL代码..." } Response: { "code": 200, "data": { "mainRuleId": 700, "functions": [ {"id": 700, "name": "calculate_score", "order": 1}, {"id": 701, "name": "build_one_five_section", "order": 2}, {"id": 702, "name": "build_one_full_chapter", "order": 3} ] } } // 2. 规则依赖查询接口 GET /api/v1/rules/{ruleId}/dependencies Response: { "code": 200, "data": { "ruleId": 701, "dependencies": [ {"id": 700, "name": "calculate_score", "type": "ref"} ], "dependents": [ {"id": 702, "name": "build_one_full_chapter", "type": "ref"} ] } } // 3. 规则执行接口(支持依赖链) POST /api/v1/rules/{ruleId}/execute { "context": { "one_two_ch_id": 12578, "one_tree_ch_id": 12389, "section_prompt": "从原文生成100以内字数的摘要" } } Response: { "code": 200, "data": { "ruleId": 702, "output": "完整章节内容...", "executionTrace": [ {"ruleId": 700, "name": "calculate_score", "output": 93.33, "duration": 10}, {"ruleId": 701, "name": "build_one_five_section", "output": "摘要内容...", "duration": 2500}, {"ruleId": 702, "name": "build_one_full_chapter", "output": "完整内容...", "duration": 100} ] } } ``` --- ## 七、总结 ### 7.1 当前设计评估 | 维度 | 评分 | 说明 | |------|------|------| | **基础支持** | ⭐⭐⭐⭐ | 节点、关系、属性基础完善 | | **DSL支持** | ⭐⭐ | 缺少函数引用、依赖关系 | | **扩展性** | ⭐⭐⭐⭐⭐ | 图数据库架构易于扩展 | | **实现难度** | ⭐⭐⭐ | 需要扩展但不复杂 | ### 7.2 改进建议 ✅ **推荐采用方案A**(扩展当前设计) **需要新增**: 1. 关系类型:`DEPENDS_ON`(规则依赖) 2. 节点属性:`function_name`, `function_params`, `function_body`, `return_type`, `validators`, `execution_order` 3. 边属性:`input_value_type`, `input_value`, `input_value_json` **实现优先级**: - P0: 基础函数定义和执行(Week 4) - P1: 函数引用和依赖链(Week 4) - P2: DSL解析器和可视化(Week 5) ### 7.3 兼容性 ✅ **完全兼容当前设计** - 不需要修改现有表结构 - 只需新增关系类型和属性定义 - 现有的 `direct_entity`、`extraction` 等规则类型继续有效 ### 7.4 下一步行动 1. [ ] 更新 `init_mock_new.sql`,添加新的关系类型和属性定义 2. [ ] 实现 `RuleDSLParser` 解析器 3. [ ] 实现 `RuleDSLExecutor` 执行器 4. [ ] 前端实现DSL编辑器和依赖图可视化 5. [ ] 编写测试用例验证DSL功能