bilibili / gengine Goto Github PK
View Code? Open in Web Editor NEWLicense: Other
License: Other
rule "rule_decision_matrix_user_level" "" salience 1
begin
rule_decision_matrix_user_level_matrix = map[string]map[string]string{"1": {"1":
"D", "2": "D", "3": "D", "4": "D"}, "2": {"1": "D", "2": "D", "3": "D", "4":
"D"}, "3": {"1": "D", "2": "D", "3": "D", "4": "D"}, "4": {"1": "C", "2": "C",
"3": "C", "4": "C"}, "5": {"1": "C", "2": "C", "3": "C", "4": "B"}, "6": {"1":
"B", "2": "B", "3": "B", "4": "B"}, "7": {"1": "A", "2": "B", "3": "B", "4":
"B"}, "8": {"1": "A", "2": "A", "3": "A", "4": "A"}}
tmp_rule_decision_matrix_user_level_matrix = rule_decision_matrix_user_level_matrix[score_level]
resp["user_level"] = tmp_rule_decision_matrix_user_level_matrix[approve_periods]
return resp
end
有时候需要判断是否有error 再执行相关逻辑逻辑
希望还是
value,err = function()
if ! isNil(err){
//do something
}
https://github.com/bilibili/gengine/wiki 里面的文档链接需要重新梳理下
ExecuteDAGModel方法中returnResult属性assignment to entry in nil map
在方法中没有进行 g.returnResult = make(map[string]interface{})
context/data_context.go
line 281: return core.GetStructAttributeValue(value, b) 应该是
return core.GetStructAttributeValue(value, c)
dataContext.Add 参数实际传入类型为int64,引擎调用本地函数签名类型也为int64,但引擎应该是作为float进行处理,导致出现精度问题,这个问题能解决下么?
后台的配置页面。规则怎么配置。
使用golang版本1.12.17,提示找不到reflect.IsZero
github.com/bilibili/gengine/internal/core/is_nil.go:32:22: value.Field(i).IsZero undefined (type reflect.Value has no field or method IsZero)
翻了下version note发现这个IsZero是1.13才引入的?为什么gengine文档上写支持1.12.x呢?
写了一个规则引擎的插件,使用的时候发现gengine的DSL语法只支持一个接收一个参数,对于go这种语言多返回值的时候,很难处理
rule "func_chain"
begin
data2=data.Filter("d1", "v1").HasGT(3)
end
类似这样的连续函数调用不支持,会直接报错 mismatched input '.' expecting END
const ( data_rule = \
rule "data rule" "a test" salience 10
begin
println(up["status"])
if up["status"] == 1{
return true
}else{
return false
}
end `
)
func Test_check(t *testing.T) {
upDataMap := make(map[string]interface{})
busiz1 := []byte({"reference_id":2073666894,"status":1}
)
decoder1 := json.NewDecoder(bytes.NewReader(busiz1))
err := decoder1.Decode(&upDataMap)
if err != nil {
fmt.Printf("Unmarshal error, err: %+v", err)
return
}
dataContext := context.NewDataContext()
dataContext.Add("up", &upDataMap)
ruleBuilder := builder.NewRuleBuilder(dataContext)
err1 := ruleBuilder.BuildRuleFromString(data_rule)
if err1 != nil {
panic(err1)
}
eng := engine.NewGengine()
err2 := eng.Execute(ruleBuilder, true)
if err2 != nil {
panic(err2)
}
fmt.Printf("Finish")
}`
请问gengine的规则里面,不支持for循环吗?只能通过下标一个个单独去访问吗?目前来看,我们只能遍历调用了,效率可能是个问题,多谢了哈
比如读取规则文件或是通过api获取
阅读源码时看到一些文件命名,我理解应该是手误敲错了吧,强迫症患者看着有点难受,所以提一下
internal/gengine_paser_vistior.go => gengine_parser_visitor.go
test/mutli_rules_test.go => multi_rules_test.go
WARNING: DATA RACE
Read at 0x00c038a220b8 by goroutine 61:
github.com/bilibili/gengine/engine.(*GenginePool).getGengine()
/Users/liupengtao/go/pkg/mod/github.com/bilibili/[email protected]/engine/gengine_pool.go:160 +0x77
github.com/bilibili/gengine/engine.(*GenginePool).prepareWithMultiInput()
/Users/liupengtao/go/pkg/mod/github.com/bilibili/[email protected]/engine/gengine_pool.go:522 +0x50
github.com/bilibili/gengine/engine.(*GenginePool).Execute()
/Users/liupengtao/go/pkg/mod/github.com/bilibili/[email protected]/engine/gengine_pool.go:706 +0xa3
Previous write at 0x00c038a220b8 by goroutine 67:
github.com/bilibili/gengine/engine.(*GenginePool).putGengineLocked.func1()
/Users/liupengtao/go/pkg/mod/github.com/bilibili/[email protected]/engine/gengine_pool.go:195 +0x269
`package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
"github.com/sirupsen/logrus"
"strings"
"time"
)
const rule1 = rule "name test" "i can" salience 0 begin if FreeData == ""{ println("Error:data is null") }else{ rows = split(FreeData,"\n") println(rows[1]) } end
func main() {
dataContext := context.NewDataContext()
freeData := total used free shared buff/cache available Mem: 131910844 41687584 24113760 4251248 66109500 84970700 Swap: 0 0 0
//这里可以成功切割出来
fmt.Println(strings.Split(freeData,"\n")[1])
//注入初始化的结构体
dataContext.Add("FreeData", freeData)
dataContext.Add("split", strings.Split)
dataContext.Add("println", fmt.Println)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
start1 := time.Now().UnixNano()
//构建规则
err := ruleBuilder.BuildRuleFromString(rule1) //string(bs)
end1 := time.Now().UnixNano()
logrus.Infof("rules num:%d, load rules cost time:%d", len(ruleBuilder.Kc.RuleEntities), end1-start1 )
if err != nil{
logrus.Errorf("err:%s ", err)
}else{
eng := engine.NewGengine()
start := time.Now().UnixNano()
//执行规则
err := eng.Execute(ruleBuilder,true)
end := time.Now().UnixNano()
if err != nil{
logrus.Errorf("execute rule error: %v", err)
}
logrus.Infof("execute rule cost %d ns",end-start)
}
}
`
第19行会报错:
ERRO[0000] execute rule error: [rule: "name test" executed, error:
line 8, column 3, code: println(rows[1]), reflect: slice index out of range
输出:
Mem: 131910844 41687584 24113760 4251248 66109500 84970700
INFO[0000] rules num:1, load rules cost time:16206000
ERRO[0000] execute rule error: [rule: "name test" executed, error:
line 8, column 3, code: println(rows[1]), reflect: slice index out of range
goroutine 1 [running]:
var serviceRules string = rule "single" "test" begin resp.At = room.GetAttention() if room.Element == "%s" && room.Value %s{ resp.Judge = true }else{ resp.Judge = false } println("rule 1...") end
实现一个简单的巡检,每个巡检项和巡检值都不一样,是否可以这样使用
func(m *Model) Update(tx *gorm.DB, updateSql string) error
规则:e = Model.Update(tx, sql)
返回结果:e 永远是nil,我方法实现直接返回 errors.New("Err"),还是返回nil
.\increment_update_test.go:212:13: undefined: Reqest
.\increment_update_test.go:280:13: undefined: Reqest
是否有计划集成图形拖拽界面,例如像 drools 集成的 JBPM ? 虽然引擎语言比golang稍微好理解些,但是还不具备让业务方直接拖拽配置
switch case for while 之类的支持么??
context/data_context.go:371 ==> Vars[variable] = newValue.
限制同时10个50个goroutinue并发请求,均出现过concurrent map writes,map读写问题。
请求场景:
func API(ctx context.Context, req *Request) error {
data := make(map[string]interface{})
data["req"] = req
data["stg_name"] = &stgName
err, _ = engine.EngPool.ExecuteSelectedRules(data, stgRule)
// do sth ...
}
release并未发现。
我有一个规则,规则的字符串如下:
code := rule "weight_rule" "weight_rule" salience 0 begin return ((1 * (1 + data.UpvoteNum) / (5.19 + data.CardShow)) + 10*(1+data.UpvoteNum)/(317.65+data.CardShow) + 5*(1+data.CollectNum)/(797.57+data.CardShow) + 0*(1+data.ShareNum)/(4790.89+data.CardShow) + 10*(1+data.ThankNum)/(1585.75+data.CardShow) + 25*(1+data.CommentNum)/(3465.75+data.CardShow) + 60*(1+data.AnswerNum)/(11097.10+data.CardShow) + -40*(1+data.VoteDownNum)/(9400.99+data.CardShow) + 35*(1+data.FollowNum)/(2986.13+data.CardShow)) * Sigmoid(Log10N(data.CardShow/50)) end
然后我通过引擎池去执行这个规则
enginePool, err := engine.NewGenginePool(1, 10, 2, code, map[string]interface{}{
"Sigmoid": Sigmoid,
"Log10N": Log10N,
})
if err != nil {
panic(err)
}
err, resultMap := enginePool.Execute(map[string]interface{}{
"data": contentAggregateData,
}, false)
if err != nil {
panic(err)
}
我发现,在第一步解析规则字符串的时候耗时非常高,大约要3min以上才能完成
gengine/internal/base/rule_entity.go
Line 47 in f123ff9
这里在并发情况下,容易出现,2个Execute的调用里,实际上传递的是同一个Vars,这会导致一个concurrent map writes的问题
重现方法:poolMin:100,poolMax:100,压测并发:10000,基本上1分钟内必定出现
必复现
--- FAIL: TestShakeEngine_DoShake (0.00s)
panic: permission denied [recovered]
panic: permission denied
goroutine 11 [running]:
testing.tRunner.func1.1(0x494b2c0, 0xc00003f448)
/usr/local/go/src/testing/testing.go:940 +0x2f5
testing.tRunner.func1(0xc0001fd680)
/usr/local/go/src/testing/testing.go:943 +0x3f9
panic(0x494b2c0, 0xc00003f448)
/usr/local/go/src/runtime/panic.go:969 +0x166
git.code.oa.com/NGTest/gomonkey.modifyBinary(0x46f5b90, 0xc0001185c4, 0xc, 0xc)
/Users/jingqiyu/dev/go/pkg/mod/git.code.oa.com/!n!g!test/[email protected]/modify_binary_darwin.go:11 +0x190
git.code.oa.com/NGTest/gomonkey.replace(0x46f5b90, 0x4a64ab0, 0x0, 0x0, 0x1)
/Users/jingqiyu/dev/go/pkg/mod/git.code.oa.com/!n!g!test/[email protected]/patch.go:197 +0xef
git.code.oa.com/NGTest/gomonkey.(*Patches).ApplyCore(0xc0000e1f38, 0x4914360, 0x4a64ad8, 0x13, 0x4914360, 0x4a64ab0, 0x13, 0x404da2c)
/Users/jingqiyu/dev/go/pkg/mod/git.code.oa.com/!n!g!test/[email protected]/patch.go:154 +0x14a
git.code.oa.com/NGTest/gomonkey.(*Patches).ApplyFunc(0xc000118738, 0x4914360, 0x4a64ad8, 0x4914360, 0x4a64ab0, 0xc000118748)
/Users/jingqiyu/dev/go/pkg/mod/git.code.oa.com/!n!g!test/[email protected]/patch.go:65 +0x130
git.code.oa.com/NGTest/gomonkey.ApplyFunc(...)
/Users/jingqiyu/dev/go/pkg/mod/git.code.oa.com/!n!g!test/[email protected]/patch.go:23
git.code.oa.com/submarine/activity/logic/monopoly_logic.TestShakeEngine_DoShake(0xc0001fd680)
/Users/jingqiyu/dev/trpc/submarine/activity/logic/monopoly_logic/shake_test.go:23 +0xdc
testing.tRunner(0xc0001fd680, 0x4a64ab8)
/usr/local/go/src/testing/testing.go:991 +0xdc
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:1042 +0x357
../../../../../../go/pkg/mod/github.com/bilibili/[email protected]/internal/iantlr/alr/gengine_parser.go:5415:221: constant 4227858432 overflows int
when I register len func 2 engine pool, build error
目前:逻辑运算没有逻辑短路特性,不符合对逻辑运算的一般理解
我这里实现了逻辑短路的特性,是否允许提pr
背景:我们的项目中用流程画图的方式在写业务逻辑,把逻辑落地成gengine的 rule运行
问题:某些业务场景较为复杂,生成的规则代码超过几万行、里面大量的重复代码(例如一个流程图很多节点都会汇聚到一个最终处理结果的节点,做一些计算工作然后返回给上层业务,造成了很多重复代码)
需求:希望gengine可以支持在rule里面可以自定义函数。
const rule2 = `
rule "bot_ask_name" "" salience 12
begin
println("conversation_list")
if ( isNil( BaseData.UserProfile) || BaseData.UserProfile.Name == "") && lastConversationTime(Base, "", "", "10", "70", "") {
return true
}
return false
end
`
base := BaseStartConversation{
ConversationBaseData: ConversationBaseData{
ConversationLogic: *logicObj,
UserId: 9819,
NodeId: 9818,
Node: &httpparams.GetOneRobotNodeResp{
NodeId: 9818,
RobotNodeGeneralProperties: &robotnodedao.RobotNodeGeneralProperties{NickName: "node nick name"},
RobotNodeCharacterProperties: nil,
},
},
}
apis := make(map[string]interface{})
apis["println"] = fmt.Println
apis["index"] = strings.Index
pool, err := engine.NewGenginePool(10, 20, 1, rule1, apis)
data := make(map[string]interface{})
data["BaseData"] = base.ConversationBaseData
data["Base"] = &base
data["println"] = fmt.Println
data["lastConversationTime"] = lastConversationTime
err = pool.UpdatePooledRulesIncremental(rule2)
//lastConversationTime()
t.Log(err)
assert.Assert(t, err == nil)
err, result := pool.ExecuteSelectedRules(data, []string{"bot_ask_name"})
t.Log(err)
assert.Assert(t, err == nil)
t.Log(result)
其中UserProfile是BaseData的一个结构体指针成员, 报错信息如下
conversation_list
--- FAIL: Test_gengine_pool_bot_inform_city_bot_ask_weather (0.08s)
panic: reflect: call of reflect.Value.FieldByName on zero Value [recovered]
panic: reflect: call of reflect.Value.FieldByName on zero Value
这个 我测试了,就是isNil执行完后,虽然命中了条件(BaseData.UserProfile==nil),但是还会继续 || 后面的那个BaseData.UserProfile.Name,访问了空指针,想问下这个执行逻辑是什么?isNil 不能搭配 || && 使用吗
例如:核心DSL代码为:AND(age > 25, lengthOfService > 5, LEN(name) > 1)
,AND是内置函数。
执行 ruleBuilder.BuildRuleFromString(rule)
后,怎么获取规则所需变量?期望可以拿到变量列表,如果内提供变量类型更好。比如:{age: int, lengthOfService: int, name: string}
规则对用户暴露,如果用户创建了长时运行的规则,是否有超时机制直接停止该条规则执行,从而防止引擎被阻塞。
../../../../pkg/mod/github.com/bilibili/[email protected]/builder/rule_builder.go:6:2: ambiguous import: found package github.com/antlr/antlr4/runtime/Go/antlr in multiple modules:
hi team,
我想请问下, 我对 wiki里的最佳实践一节有个疑问,这里的MyService是一个业务请求新建一次,还是所有业务请求共用一个MyService?
比如go的一个函数myFunc,在入rule映射为MyFunc:
func myFunc()(int, string){
}
在rule中应用:
rule "ruleId"
begin
a, str := MyFunc()
Print(str)
end
实际在执行rule的过程中,只能拿到a的值,str的值是拿不到的吗,后续能支持吗
D:\gopath\pkg\mod\github.com\bilibili\[email protected]\internal\iantlr\alr\gengine_parser.go:5415:221: constant 4227858432 overflows int
@rencalo770 @Lynnworld @Cluas @Terry-Mao 大佬们这个还在维护么😂
rule "name test" "i can" salience 0
begin
name = "abc\"def"
end
外部获取 name 的值时 print 出来 为 abc\"def
而不是 abc"def
目前测试发现如下的规则 BuildRuleFromString
无法验证规则的有效性,日志有错误信息输出,但是返回 err
为 nil
规则脚本
rule "test" salience 1
begin
规则脚本
end
验证逻辑
dataContext := context.NewDataContext()
rb := builder.NewRuleBuilder(dataContext)
if err := rb.BuildRuleFromString(tpl.String()); err != nil {
return err
}
日志输出
line 3:5 token recognition error at: '规'
line 3:6 token recognition error at: '则'
line 3:7 token recognition error at: '脚'
line 3:8 token recognition error at: '本'
true || true && false 返回false 正常不是应该&& 优先级高于||吗
rule1 = rule "rule_1" "rule_1" begin return isNil(MyMap["123456789"]["123"]) end
myMap := make(map[string]map[string][]string)
if myMap["123456789"] == nil {
myMap["123456789"] = make(map[string][]string)
}
myMap["123456789"]["123"] = []string{"123"}
dtx := context.NewDataContext()
dtx.Add("MyMap", myMap)
rb := builder.NewRuleBuilder(dtx)
err := rb.BuildRuleFromString(rule1)
if err != nil {
panic(err)
}
eg := engine.NewGengine()
err = eg.Execute(rb, false)
if err != nil {
panic(err)
}
执行返回:
panic: [line 2:33 no viable alternative at input 'isNil(MyMap["123456789"][']
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.