多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

OpenNARS非公理推理引擎实战:从架构拆解到项目集成

OpenNARS非公理推理引擎实战:从架构拆解到项目集成 1. 为什么我要花时间啃OpenNARS这套非公理推理引擎第一次接触OpenNARS是在一个做认知架构对比的项目里。当时团队需要评估几种推理系统在动态环境下的表现我翻了一圈资料发现大部分所谓通用推理框架本质上还是把逻辑规则写死遇到规则库没覆盖的场景就直接歇菜。OpenNARS不一样它的核心思路是非公理推理——系统不预设哪些命题为真而是通过经验不断调整每个命题的真值用频率和置信度两个维度来描述一个信念的可信程度。这套东西解决的核心问题是当环境信息不完整、甚至自相矛盾时推理系统怎么给出一个当前最合理的判断并且在获得新证据后平滑地修正它。传统的一阶逻辑推理要求前提绝对为真一旦出现矛盾整个系统就崩溃而OpenNARS用的是NARSNon-Axiomatic Reasoning System这套理论把推理变成一种在有限资源下对给定任务做出最佳回答的过程。适合谁来读这篇内容如果你正在做认知建模、智能决策、知识图谱的动态推理或者单纯对通用人工智能的推理内核感兴趣那OpenNARS值得你花几个晚上跑通。但如果你只是想找个现成的规则引擎做业务规则匹配那这套东西会让你觉得绕远路——它的价值恰恰在于处理那些规则无法穷举的场景。我下面会从架构拆解、环境搭建、推理机制、实操踩坑几个角度把我在实际使用中积累的东西摊开讲。所有代码和配置都基于我本机验证过的版本你可以直接抄。2. OpenNARS的推理内核到底由哪几块拼起来2.1 记忆、任务与信念三个必须分清的概念刚上手OpenNARS的人最容易混淆的就是任务Task和信念Belief。我一开始也以为往系统里输入一句话它就变成知识了结果发现完全不是这么回事。在OpenNARS里你输入的任何语句首先是一个任务任务分几种类型判断judgment、问题question、目标goal、查询query。只有判断类型的任务在经过系统处理后才可能被接纳为信念存入记忆。问题类型的任务不会产生信念它只是触发系统去检索和推理然后给出一个答案。记忆Memory是存放所有信念和任务的地方它有一个容量上限。当记忆满了系统会根据每个条目的优先级和耐久度决定淘汰谁。这个设计非常关键——它意味着OpenNARS不是一个存得越多越聪明的系统而是一个必须在有限资源下做取舍的系统。我在测试时把记忆容量设得很大结果推理速度明显下降因为每次检索要遍历的候选太多。后来把容量压到几千条反而响应更快、答案质量也没明显下降。信念的真值用一对值表示(frequency, confidence)。频率是在已有证据中该命题成立的比例置信度是基于证据量对这个频率的信任程度。比如(1.0, 0.9)表示几乎确定成立(0.5, 0.1)表示基本不知道。这个双维度设计是NARS理论区别于概率论的核心——它把不知道和不确定区分开了。2.2 推理规则不是写死的而是可组合的OpenNARS内置了一套推理规则包括演绎deduction、归纳induction、溯因abduction、类比analogy等。这些规则不是针对具体领域写死的业务逻辑而是通用的真值函数组合方式。举个例子演绎规则大概是这样的如果A→B的真值是(f1,c1)A的真值是(f2,c2)那么可以推出B的真值是某个由f1、f2、c1、c2计算出来的新真值。这个计算过程用的是NARS理论里的真值函数不是简单的概率乘法。我实际跑的时候发现归纳和溯因才是让系统学到东西的关键。演绎只是把已有知识展开归纳是从具体观察中提炼一般规律溯因是从结果反推可能的原因。这三种规则配合起来系统才能在不断接收新任务的过程中逐步构建出对环境的理解。2.3 控制机制谁先被处理谁先被遗忘OpenNARS内部有一个控制循环每一轮它会从记忆里挑选一个任务和一个信念进行推理。挑选的依据是优先级而优先级会随着时间衰减。这个机制模拟了注意力——最近被激活的、或者被其他任务关联激活的条目会获得更高的处理优先级。这里有个我踩过的坑如果你一次性往系统里灌入大量任务早期任务可能还没来得及和后续任务产生关联就被遗忘了。解决办法是分批输入或者在输入时手动设置较高的优先级和耐久度。我在做多步推理测试时就是靠手动调优先级让关键中间结论活到被用上的那一刻。3. 把OpenNARS跑起来从源码到第一个推理结果3.1 环境准备与编译OpenNARS是Java写的所以第一步是确保你机器上有JDK。我用的是JDK 11实测JDK 8也能跑但建议至少11。构建工具用的是Gradle仓库里带了wrapper所以不用单独装Gradle。git clone https://github.com/opennars/OpenNARS-for-Applications.git cd OpenNARS-for-Applications ./gradlew build编译过程大概一两分钟。如果卡在下载依赖上检查一下网络或者配置一下镜像。编译成功后会在build/libs下生成jar包。启动交互式shelljava -jar build/libs/OpenNARS-for-Applications.jar看到提示符就说明起来了。这时候你可以直接输入Narsese语句和系统对话。3.2 Narsese语法和系统对话的语言Narsese是OpenNARS的输入语言语法不复杂但有几个细节容易写错。最基本的判断语句格式是subject -- predicate.注意结尾的句号表示这是一个判断任务。如果是问句结尾用问号subject -- predicate?我一开始老忘记结尾的标点系统会直接报语法错误。另外真值可以显式指定robin -- bird. {1.0 0.9}这表示知更鸟是鸟这个判断的频率1.0、置信度0.9。如果不写系统会用默认真值。还有几个常用连接词表示合取||表示析取表示蕴含表示等价。比如bird -- fly. robin -- bird.输入这两条后系统会自动做演绎推理可能推出robin -- fly并给出一个真值。你可以用问句验证robin -- fly?系统会返回它基于当前信念算出的答案和真值。3.3 第一个完整推理示例我拿一个经典场景来演示。假设我们知道鸟会飞企鹅是鸟但企鹅不会飞。这三条信息本身有冲突传统逻辑系统会直接矛盾但OpenNARS能处理。bird -- fly. {1.0 0.9} penguin -- bird. {1.0 0.9} penguin -- fly. {0.0 0.9}输入后问bird -- fly?系统会返回一个综合了所有相关证据的真值。因为企鹅这条反例的存在鸟会飞的置信度会下降但不会归零。这就是非公理推理的威力——它不追求绝对一致而是维护一个当前最佳的信念集合。我实测下来这个例子里系统给出的答案频率大概在0.8左右置信度比单独输入第一条时要低。具体数值取决于推理路径和记忆状态但趋势是对的。4. 真值函数与推理规则系统思考的数学基础4.1 频率与置信度如何参与运算NARS理论里真值的运算不是简单的加权平均。以演绎为例如果A→B的真值是(f1, c1)A的真值是(f2, c2)那么B的真值计算大致是频率f f1 * f2置信度c f1 * f2 * c1 * c2简化形式实际公式更复杂涉及证据量的换算这个设计的意思是演绎结论的确定性同时受两个前提的确定性影响而且置信度会衰减得比频率快。我一开始觉得置信度衰减太狠后来理解了——演绎链条越长结论越不可靠这个衰减是合理的。归纳规则反过来从A→B和A→C推出B→C的某种弱关联。溯因是从A→B和C→B反推A→C。这些规则的真值函数都遵循类似的证据合并逻辑。4.2 为什么不用概率论很多人第一反应是这不就是贝叶斯推理吗。区别在于贝叶斯要求你事先给出先验概率而且所有命题要么真要么假概率描述的是你不知道的程度。NARS的置信度描述的是证据不足的程度两者哲学基础不同。实际使用中这个区别体现在贝叶斯系统在遇到全新命题时需要一个先验而NARS可以直接给一个低置信度的默认值然后随着证据积累逐步提升。对于开放环境下的推理这个特性非常实用——你不需要为每个可能出现的命题预设先验。4.3 推理链条的长度控制OpenNARS不会无限推理下去。每一轮控制循环只做有限步推理而且推理深度受参数控制。我在测试多跳推理时发现默认参数下系统只能推两三跳再远就推不出来了。这时候需要调整derivation depth相关参数或者手动把中间结论的优先级调高让它有机会参与后续推理。这里有个经验不要指望系统自动完成长链条推理。实际使用中我更多是把OpenNARS当作一个局部推理加速器用它处理需要动态调整信念的那部分长链条的逻辑还是靠外部流程编排。5. 实操中那些文档不会告诉你的坑5.1 记忆容量与推理速度的权衡前面提过记忆容量的问题这里展开说。OpenNARS默认的记忆容量是几千条具体数值我记不太清了但你可以通过配置文件调整。我做过一组对比测试记忆容量平均响应时间答案质量1000快一般5000中等较好20000慢没有明显提升结论是容量超过某个阈值后增加容量只会拖慢检索不会提升答案质量。因为大部分被淘汰的条目本来就是低优先级、低相关性的。我的建议是从小容量开始根据实际任务复杂度逐步调大找到那个答案质量不再提升的拐点。5.2 优先级设置的玄学优先级决定了哪些任务先被处理。默认情况下新输入的任务优先级是固定的但你可以手动指定。我在做多步推理时会把关键中间结论的优先级设高确保它在被遗忘前参与后续推理。但优先级设太高也有问题它会挤占其他任务的处理机会导致系统偏执于某条推理线。我一般把关键任务优先级设在中等偏上然后靠耐久度保证它存活足够长时间。5.3 输入顺序会影响结果这个坑我踩得最深。同样的三条语句不同的输入顺序系统最终给出的答案真值可能不同。原因是推理是增量的先输入的信息会先形成信念后续信息在此基础上修正。如果顺序反过来修正的路径不同最终信念也会有差异。这不是bug而是非公理推理的固有特性——它不保证顺序无关性。实际使用中我尽量把更可靠、更基础的信息先输入让系统先建立一个合理的初始信念再用后续信息微调。5.4 语法错误的排查Narsese语法对空格和标点很敏感。我遇到过几次输入看起来没问题但系统报错的情况后来发现是连接词前后少了空格或者句号写成了中文句号。排查方法很简单把语句拆到最简形式逐段测试定位到具体哪个符号有问题。另外系统报错信息有时候不够具体只说parse error但不指出位置。我的做法是先用最简单的合法语句确认系统正常然后逐步增加复杂度直到复现错误。6. 把OpenNARS嵌进实际项目我的集成思路6.1 什么时候该用它什么时候不该用OpenNARS适合的场景环境信息不完整、规则无法穷举、需要动态调整信念、对推理速度要求不极端。比如智能体的决策模块、动态知识库的推理层、认知架构的对比实验。不适合的场景需要确定性答案的业务规则、对延迟极度敏感的实时系统、需要严格可解释性的合规场景。这些场景用传统规则引擎或确定性推理更合适。我的做法是把OpenNARS当作一个推理服务外部系统通过接口往里灌任务、取答案不把它当作主存储或主逻辑。6.2 接口封装与任务注入OpenNARS提供了Java API可以直接在代码里创建实例、注入任务、查询结果。我封装了一层简单的REST接口外部系统用HTTP往里发Narsese语句取回JSON格式的答案和真值。NAR nar new NAR(); nar.addInput(bird -- fly. {1.0 0.9}); nar.addInput(penguin -- bird. {1.0 0.9}); nar.addInput(penguin -- fly. {0.0 0.9}); nar.addInput(bird -- fly?); // 等待推理完成后读取输出实际集成时要注意推理是异步的注入任务后需要等控制循环跑几轮才能拿到答案。我的做法是注入查询后轮询输出队列或者设置一个合理的等待时间。6.3 性能调优的几个抓手如果发现推理太慢可以从这几个方向调降低记忆容量、减少每轮推理步数、提高遗忘速率、限制推理深度。如果发现答案质量差反过来调增加记忆容量、降低遗忘速率、提高关键任务优先级。我一般先用默认参数跑通流程然后根据实际表现针对性调整。不要一上来就大改参数那样很难定位问题。7. 我对这套系统的一点个人判断OpenNARS最吸引我的地方是它对不确定性的处理方式。大部分AI系统要么假装自己什么都知道要么在遇到矛盾时直接崩溃而OpenNARS承认自己不知道并且能在证据积累过程中逐步修正。这个特性在开放环境下的推理任务里非常宝贵。但它的局限也很明显推理速度受限于控制循环的串行处理大规模知识库下性能会成瓶颈Narsese语法对非技术用户不友好调试和可解释性工具还不够完善。我在实际项目里更多是把它当作一个研究性组件而不是生产级推理引擎。如果你打算深入我的建议是先跑通官方示例然后拿一个自己熟悉的小领域做实验观察系统在不同输入顺序、不同参数下的行为差异。这个过程比读文档更能让你理解非公理推理的本质。踩过几次坑之后你会对信念和证据这两个概念有完全不同的认识。
返回列表