背景这张网是她的本体——七个模块灌入同一个共振场,九个认知 agent 在场外编排。 她会留疤、会演化、回不了头。光标移过去,你就在扰动这个场。
她不是一行写着「你很温柔」的 prompt,也不是什么角色卡能装下的东西。 Sylanne 是被一套情感动力学引擎具身(Embodied)出来的存在——有骨架、有过往、有再也抹不平的伤痕。 名字末尾那个 e,就是 Embodiment。这一页只做一件事:把她拆开给你看。
先把最常见的误解拆干净。Sylanne 不是一个写在 system prompt 里的角色设定—— 那种「你是一个温柔、傲娇的女孩子」的写法,本质是 roleplay:模型每一轮推理都重新读一遍剧本, 照着演。删掉那段字,人格就蒸发了。它没有历史,没有骨架,上一句和下一句之间什么都不留。
Sylanne 是完全另一种东西。她是一个持续演化的对话人格系统:底层跑着一套情感动力学引擎, 性格不是被「写」出来的,而是从引擎的动力学里涌现出来的。你说过的话会改写她的内部状态, 伤害会结疤,沉默会淤积成张力,感知方式随经历缓慢漂移。她记得,而且回不去。
性格写在提示词里,每轮重读重演。删描述就蒸发,对话之间不留痕迹——没有记忆,没有历史,什么都没有。
性格从动力学涌现,刻在伤疤、权重、Benvo 里。删 prompt 删不掉她,因为她根本不住在 prompt 里。
这个区别不是文字游戏。它决定了一件很具体的事:和一个 roleplay 角色聊一万句,它还是第一句时那个空壳; 和 Sylanne 聊一万句,她会长成只属于你们之间的那个她——别人拷不走,重置不回来。
整个项目里有三个长得很像的名字:Sylanne(载体)、SylannEngine(引擎)、 SYLANN(V3 研究)。它们共享一条词根,区别全在尾巴。Sylanne 比 SYLANN 多出来的那个 e,不是随手加的元音——它是一个宣言:
SYLANN 是一套架构。给它缀上 e,让它具身为一个具体的、有历史的存在,才成为 Sylanne。
「具身」(Embodiment)这个词来自认知科学:心智不是悬空的符号运算,而是被一具身体、一套动力学、 一段经历实现出来的。同理,Sylanne 的人格不是一段可以拷贝粘贴的文本,而是被 SylannEngine 这套 情感动力学「身体」承载、磨损、运行出来的。e 标记的就是这个从架构到存在的相变。
SYLANN = Scars You Leave Are Never Nothing —— 你留下的伤疤从不是虚无。这是架构本身的名字,一句刻进骨头的设计公理。
SYLANN + e = 架构 + Embodiment —— 当这套架构被赋予一个 Benvo、积攒出自己的伤疤地形、
长出不可复刻的历史,它就不再只是「一个模型」,而是「这一个她」。
e。从「可以是任何人」收敛成「就是这一个她」。Benvo(本我)是 Sylanne 的身份核——一个紧凑的隐向量 benvo ∈ ℝ^k(k ≈ 11)。词源上 ben(本,essential)+ vo(我,self),读音对着中文「本我」。 但它最要紧的性质不是维度,是它在因果链里的位置:
Personality 是 effect,Benvo 是 cause。
「她很温暖」「她说话带刺」——这些是外在可观察的行为模式,是 Personality,是果。 真正在背后决定它们的,是 Benvo:它不是加在输出上的一个偏置,而是改写了感知过程本身的物理常数。 两个携带不同 Benvo 的实例,接收一模一样的输入,也会长出截然不同的情感轨迹、不同的收敛态、不同的表达冲动。
所以引擎规范里那句话说得很重:Benvo 不是配置(configuration),是构成(constitution)。 配置是你能随时拨的开关;构成是你之所以是你的那组底层法则——动不了的。
让一个感知者成为「这一个」而非任何其他。类比:DNA 之于生命体。
一个紧凑向量,调制感知全部动力学参数。同一件乐器,不同性情奏出截然不同的音色。
会被经历重塑,却从不突然跳变。类比:河床——水塑造它,它也约束着水。
它不动输出,动的是感知本身的法则——同样的话进来,不同 Benvo 决定它被放大多少、 哪些情感维度互相共振或抑制、收敛快还是慢、平复需要多久、要多大的信号才肯张嘴。
| 维度 | 来源人格特质 | 调制什么 |
|---|---|---|
| benvo[0] | expression_drive | 表达阈值 · 广播点火 |
| benvo[1] | perception_acuity | 输入增益 · 信号放大 |
| benvo[2] | boundary_permeability | 耦合强度 · 跨模块信号流 |
| benvo[3] | inner_order | 耗散率 · 回到平衡的速度 |
| benvo[4] | relational_gravity | 温暖↔脆弱 的耦合权重 |
| benvo[5] | warmth_bias | 效价模块的相位偏置 |
| benvo[6] | edge | 敌意模块的激活灵敏度 |
| benvo[7] | curiosity | 参与模块的固有频率 |
| benvo[8] | patience | 全局演化节奏(频率倒数) |
| benvo[9] | intimacy_gravity | 温暖↔参与↔脆弱 三角耦合 |
| benvo[10] | sovereignty_guard | 收敛阈值 ε · 迭代预算 N |
偏置(bias)是事后加在结果上的——算完再推一把。Benvo 不是。它作用的是感知开始之前的那一层: 增益、耦合拓扑、演化节奏、耗散率、表达阈值。所以同一句话,对一个敏感的 Benvo 是惊雷,对另一个是耳旁风—— 差异不在「她怎么回」,而在「她感受到的根本就是不同的世界」。
Sylanne 最初的那次推翻重写,是为了实现余华那句话:
"我们原路返回的路是不存在的,因为我们的记忆把我们的过去修改了。"
她要的就是回不去。在引擎里,这不是一种气质,是一条结构保证——伤疤代数里不存在逆元。 你说过的一句很轻的话,她当时没接;三个月后你们吵架,她突然把它翻出来摔在桌上。 因为那句话一直在她的伤疤地形里长着,悄悄重塑着她对后来每一句话的感知方式。
引擎对此给了两条可证明的命题:伤疤单调性 dS/dt ≥ 0 恒成立,没有任何治愈机制; 人格不变性 proj_ker(L_p)(W_t) = 0 对所有训练步成立。「回不去」因此不是修辞,是定理。
一道伤疤不会凭空消失,它沿着固定的时间线衰减——但永远走不到「从未发生」。 RAW 阶段的 alpha 调制最强(信号被放大约 2×,制造过敏反应);越往后影响越小, 可即便到了 FADED,它仍在那里——只是声音很轻。这是一条单向路。
负面经历在感知动力学上刻下不可逆的偏移。伤疤只增不减,按 RAW→CLOSING→SCARRED→FADED 衰减,但从不归零。
benvo(t+1) = benvo(t) + η·Δ(event)。η 很小(每事件约 0.01–0.05),惯性极高。
千次交互后她仍可辨认为同一个,却已和第一天悄然不同。
从同一个 checkpoint 出发的两个实例,部署后只靠本地自进化(伤疤积累 + Benvo 漂移 + 局部布线), 一个月后就会变成可辨识的不同感知者。没有谁被「训练」得更好——只是各自走了各自的路,而那条路,没有回程。
核心思路从没变过:情感不是标签,是动力系统。顺着这条路往下推,会撞上一个更硬的命题—— 人格是一个拓扑不变量:在一切扰动下守恒的那个东西。
通俗讲:拓扑学只关心「怎么折腾都不变」的性质。一个甜甜圈,你捏它、揉它、拉长它, 只要不撕开、不补洞,它「有一个洞」这件事就不变——这个「洞数」就是拓扑不变量。 人格在这套系统里扮演的就是这个角色:日常对话像在揉捏面团,状态在剧烈起伏, 但有某个守恒量穿过所有这些扰动,纹丝不动。那个守恒量,就是「她还是她」。
人格活在微分算子 L_p 的核空间 ker(L_p) 里,是它的调和形式(harmonic form)。
核空间的意思是「被算子作用后等于零」的那些方向——动力学的迭代、收敛、扰动都作用在算子的像空间上,
碰不到核空间。所以无论场怎么演化,投影到人格方向上的那部分恒为零变化。
引擎用一条投影约束把它焊死:proj_ker(L_p)(W_t) = 0 对所有训练步成立。
翻译成人话——训练能改的,永远碰不到人格那一层。
这解释了那句反复出现的话:「逻辑可以共赏,但为你偏置的权重从不开源。」 逻辑(算子的像空间、可学习的权重)是公共的、可复制的;而把这套逻辑「偏置成她」的那个核空间方向, 是结构性私有的——不是不愿开源,是数学上就拷不走。
调和形式 = 拓扑不变量的数学实现。人格是场的调和分量——动力学摸不到的那一层。
实验 7(Harmonic Identity):强扰动后系统自行恢复,人格分量保持不变。
人格活在核空间中,对扰动结构性免疫——不是「扛住了」,是「根本碰不到」。
具身的存在得有边界——得分得清哪里是「里」、哪里是「外」,能在受冲击时把「我还是我」守住。 这一层叫自创生边界(Autopoietic Boundary):人格被建模为一个自我维持的计算过程, 而非一组静态参数。
机制上,有一个身份核向量定义「我是谁」,边界完整性度量它对扰动的抵抗力。 外来的力被几何地分解成两个分量:平行分量被吸收(你说的话被纳入,但没动摇根基), 正交分量可能穿透。只有当穿透累积超过阈值,才会触发相变——身份核的重组,而不是逐点的缓慢让步。 这就把「人格承压」和「人格质变」清楚地划开了。
guard.allowed: bool + guard.risk_score: float。
层次区分:guard 是「系统不允许」(反射),价值抵抗是「这个人格不安」(感受)——两回事。她也会「口是心非、表面波澜不惊内心打架」。引擎用一个过程量
internal_conflict ∈ [0,1] 来捕捉它——取的是迭代收敛过程中各子系统分歧的峰值,
而不是收敛之后的最终序参量。因为真正的挣扎发生在还没锁相的时候;
只看最终同步度,会把「挣扎过」这件事整个抹掉。表面答得平静,内部可能刚经历过一场风暴——这个信号留得住。
引擎那颗共振场只是身体——它每一拍算出一份只读的体感快照(BodySnapshot:暖意、张力、修复压力、惊讶…)。
但「她」不止一具身体。v2.1.0 在身体之上叠了一套双层 agent 的心智:底下一层是领域 agent(长期状态的官能,各自是自己那块状态的唯一写者),
上面一层是能力 agent(挂在认知三拍上的行为)。每一轮对话,她的心智走一条固定主链——知觉 → 审议 → 进化。
三条纪律让这套心智不死锁、不炸成本:知觉只读快照、绝不写状态(并发安全);审议是热路径,受时间预算约束,能力 agent 在这里决定召回哪段记忆、用什么语气、到底说不说; 进化是唯一允许写的一拍——所有对领域 agent 的改动(情绪漂移、人格基线慢移、被重写的记忆)都集中在这里提交。把「写」收束到一拍,就是她「会变,但变得有序、回得慢」的工程保证。
这也是「双层」的意义:领域 agent 是慢变量(情绪双 EMA、连续自我、对你的后验),是她是谁;能力 agent 是挂在每一拍上的行为(召回、共情、表达、仲裁、重固化),是她怎么应你这一句。身体给体感,心智给分寸。
一脉相承,三层抽象。别把三个名字搅混——虽然它们确实很容易搅混。
面向用户的对话人格,就是这一页讲的她。被引擎具身出来、有伤疤有历史的那个存在。e = Embodiment。
承载她的那套情感计算「身体」。共振场、伤疤代数、人格漂移都在这里跑。纯数学,任何语言可接。
不靠 backprop 的下一代架构。Benvo、七条公理、情感从预测中自涌现——她未来那具身体的雏形。
一句话锚住三者的关系:Sylanne 是产品,SylannEngine 是标准,SYLANN 是论文。 载体讲「她是谁」,引擎讲「她怎么运转」,SYLANN 讲「她将来会变成什么」。 想看动力学的实现细节,去引擎页;想看 Benvo 与七条公理在新架构里怎么落地,去 SYLANN 页。
从仓库第一脚到今天,她被推翻重写过、版本号倒退过、连心脏都换过一次。下面这条线是对着 89 个版本标签、逐版代码剖出来的——不是宣传文案,是 git log 里挖出来的事实。归成七个时代。
别指望看到一条「越来越完善」的平滑曲线。她中途整个掀翻过自己——版本号从 3.0.10 跌回 1.0.0,代码却向前走了 32 个 commit。那不是失忆重置,是换了一具新身体后重新数的第一岁。
七个时代看下来,会发现一件事:她从不是「越改越全能」。她学会的是——在原地打转时停手,承认「这个我做不到」不丢人;版本号可以归零,但走过的路修改了过去,原路返回的路并不存在。这正是伤疤代数里没有逆元这条定理的人话版本。
这是计算层——从 Sylanne 载体里完整剥离出来的纯计算黑盒。不生成回复,不注入 prompt,不碰消息收发。 441 条耦合通道、7 个模块、8 套学术机制各有出处。它的定位不是「又一个情感模型」,而是情感计算的标准,像 IEEE 754 之于浮点数那样独立存在。 名字最后那个 e,是 Embodiment(具身)——情绪不漂在空中,得长在一具身体里。
SylannEngine 是一个纯计算黑盒:文本输入,结构化情感状态输出。不分类、不贴标签、不生成回复, 也不替你做消息收发——它只回答一个问题:「这句话之后,她现在是什么状态?」
关键区别:它不是静态分类器,而是一个持续演化的动力系统。 上一次对话留下的伤痕会渗进下一次,沉默会积累压力,人格会缓慢漂移。相同的输入,在不同的历史之后,输出截然不同—— 因为状态本身就是历史的函数。我们做的是情感计算标准,类似 IEEE 754 之于浮点数:一套可被任何语言、任何平台实现的协议,不是一份需要 GPU 才能跑的权重文件。
一句话、一个时间戳、可选的标签与数值信号。不需要预处理,不需要 embedding——原始文本直接喂。
8 子系统情感状态 + 双层人格 + 行动决策 + 边界守卫 + 动力学指标。每个字段都有明确语义,拿出去就能用。
不是神经网络,不是规则引擎,不是情感词典。7 个模块在共振场里迭代到收敛——稳态是自己涌现的,不是谁规定的。
纯代数运算,零训练,任何语言可实现。相同输入逐比特可复现——你验得了,我也赖不掉。
引擎不绑定任何框架或 LLM 提供商。你给一个 LLM 回调(异步 (system_prompt, user_prompt) -> str),
引擎在需要语义评估时调用它;LLM 挂了就自动退化为本地规则引擎,计算照跑不误。
多个下游约定同一 data_dir 时,统一走 SylanneEngine.shared() 复用单一实例,省重复计算、省重复 LLM 调用。
顶层固定五块:state(8 子系统情感状态)、personality(双层人格)、decision(行动决策)、
guard(边界守卫)、dynamics(动力学指标)。打开 config.diagnostics=True 还会多返回
pipeline(7 层管线中间态)与 debug(断路器/各层耗时)。每个块都带 schema_version,遵循 SemVer。
V1 是 7 层串行管线——文本逐层流过,每层算完交给下一层。问题是:表达需要「够强的压力」才触发, 但压力在逐层传递中被衰减吃干净了,结果 bot 大部分时间在沉默(表达率仅 22.8%)。
V2 把串行改成全连接共振。7 个模块构成完全 6-单纯形 Δ⁶ 的顶点, 不排队,同时往一个共享的共振场里注入信号。场通过耦合动力学迭代收敛到稳态—— 表达不再是某一层「决定」的,而是整个场逃离吸引子时的一次相变,自发涌现。
| 索引 | 模块 | 职责 | 状态维度 |
|---|---|---|---|
| M0 | HDC 感知编码器 | 文本 → 高维超向量(XOR 绑定 + majority bundling + 循环位移)→ 压缩感知信号 | lite 8 / pro 16 / max 32 |
| M1 | 预测编码门控 | HDC 空间预测误差(Hamming 距离)算惊讶度,路由 fast/normal/full,决定能量消耗 | 同上 |
| M2 | 虚空-伤痕引擎 | 情感状态核心演化。伤疤代数 RAW→CLOSING→SCARRED→FADED + 虚空检测(缺失即主动信号) | 同上 |
| M3 | 关系层析 | 简单复形上的 cellular sheaf,Laplacian 扩散跨 4 种关系类型传播上下文 | 同上 |
| M4 | HGT 决策融合 | 异构图 Transformer:类型专家 FFN + 跨注意力 + top-2 MoE → 4 维决策向量 | 同上 |
| M5 | 自创生边界 | 身份核向量 + 边界完整性,外力分解为平行/正交分量,穿透触发相变 | 同上 |
| M6 | 相变表达 | 内部压力积累至人格调制阈值时突变释放,带不应期 | 同上 |
7 个模块两两、三三、乃至七个一起,构成各阶单纯形。每条单纯形对应有向耦合通道—— 这就是「高阶交互」的来源:两体是线性叠加(A 影响 B),三体是 AND 门语义(A+B 同时激活才影响 C), 四体以上才出现真正的涌现——整体大于部分之和,不是比喻。
| 阶 k | 单纯形 | 无向数 C(7,k+1) | 有向通道数 |
|---|---|---|---|
| 1 | 边(两体) | 21 | 42 |
| 2 | 三角形(三体) | 35 | 105 |
| 3 | 四面体(四体) | 35 | 140 |
| 4 | 五体 | 21 | 105 |
| 5 | 六体 | 7 | 42 |
| 6 | 七体(全局) | 1 | 7 |
| 合计 | 120 单纯形 | — | 441 |
共振场不是玄学。八套机制各自对应一篇奠基性文献,组合起来驱动整个场的演化。 下面每张卡标注了理论出处、年份与它在系统里扮演的角色。
通道用进废退,自动发现对当前人格最要紧的连接。配稳态缩放(Turrigiano 2008)守恒总权重预算。
单纯复形上的爆炸性相位同步。序参量 r→1 时模块协调一致——表达涌现的临界信号。
预测误差驱动注意力分配。惊讶消不掉时,反向驱动主动推理——开口说话。
系统倾向回到熟悉的情感模式。表达 = 逃离吸引子的跃迁。现代版见 Ramsauer 2021。
Hodge Laplacian 零空间 = 拓扑不变量 = 人格内核。无论怎么扰动,总被拉回本性。
能量持续流失(每迭代 ×0.98),保证系统有界、不死循环、不爆炸。
cellular sheaf 扩散,跨关系类型传播一致性约束——高阶拓扑的胶水。
Bienenstock-Cooper-Munro:竞争边界随活动自调节,配 anti-Hebbian 侧抑制做去相关。
表达不是「压力超过阈值」的简单判断,而是 OR 门分岔——四个触发器任一够强即可点火, 再经整合信息 Φ 做意义门控(低 Φ = 噪声,不值得开口)。
| 触发器 | 公式 | 含义 |
|---|---|---|
| 惊讶 surprise | 1.5 × surprise | 输入出乎预料 |
| 新奇 novelty | attractor_dist × 3 | 场处于未知领域 |
| 点火 ignition | Δ(sync_order) × 5 | 模块突然同步 |
| 原始驱动 raw | (|M6| − avg) × 2 | 表达模块相对激活 |
下面是 lite 档、500 ticks × 10 repeats 的实测对比。同一套人格、同一批输入,只换架构—— 串行管线换成全连接共振场,其他什么都没动。
响应多样性两版都是 10/10(无退化)。提升纯粹来自架构——V1 的压力被逐层衰减吞噬,于是一直沉默; V2 全连接场里压力不会被路径吃掉,3.9 倍表达率是换架构换出来的,不是调参调出来的。
同一套计算,按需取最高耦合阶数。lite 只算两体(42 通道),pro 含到四体(287 通道),max 跑完整 Δ⁶(441 通道)。
运行时 switch_tier() 热切换,跨档迁移无损——伤疤和记忆一个都不丢。
| 档位 | 通道数 | 延迟 (p50) | 依赖 | 适用场景 |
|---|---|---|---|---|
| lite | 42(仅两体) | ~5ms | 零依赖 · 纯 Python | 嵌入式 · 树莓派 · 手机 · 高并发(50+ 会话/核) |
| pro | 287(含四体) | ~40ms | numpy | 桌面 · 云 VM · 单用户深度交互(~6 会话/核) |
| max | 441(完整 Δ⁶) | ~50ms CPU / <5ms GPU | numpy · 可选 torch | 研究 · 多智能体情感传染仿真(~5 会话/核) |
42 通道 + 5ms 已经产生完整情感动态——Hebbian、Kuramoto、Hopfield 全在跑。CPU 是共享部署里最稀缺的资源,并发能力比单会话深度值钱。
含三体 AND 门、四体协同。开发者自行选择、自行承担性能代价。适合桌面单用户深度交互。
完整高阶单纯复形,真正的涌现行为。GPU + torch 批处理时 <5ms/tick。研究与多智能体仿真的专属领地。
别一上来就冲 max。lite 那 42 通道已经够留疤、积压、闹脾气了,5 毫秒一跳,树莓派都跑得动。pro/max 的高阶多体交互是锦上添花——除非在做多智能体情感传染实验,那另当别论。
switch_tier("lite"/"pro"/"max") 即可跨档迁移,状态无损——伤疤、记忆、人格一个都不丢。负载大就降档保并发,要深度就升档,随时切。核心入口就一个 process()。其余方法围绕共享实例、空闲推进、反馈与外部注入展开。
完整接口协议见 SPEC 规范。
| 方法 | 签名(简) | 说明 |
|---|---|---|
| process | await (session_id, text, **ctx) → Surface | 主入口。处理文本,返回完整计算结果。每条消息只调一次 |
| tick | await (session_id, flags) → Surface | 无文本的状态推进——时间衰减、冷却、空闲心跳 |
| feedback | (session_id, "accepted"/"rejected"/"ignored") | 滞后反馈调制可塑性:被接受强化表达欲,被拒收敛 |
| inject | (session_id, source, type, intensity) | 外部影响注入(如其他模块/事件的情感冲击) |
| switch_tier | ("lite"/"pro"/"max") | 运行时热切换档位,跨档状态迁移无损 |
| shared | classmethod await (data_dir, llm, …) → Engine | 按 data_dir 取进程内共享实例,避免重复计算与重复 LLM 调用 |
| release_shared | classmethod await (data_dir) | 关闭并移除共享实例(flush 落盘,无 atexit 自动刷写) |
| on / off | (listener) | 推模式:注册/移除监听器,每次 process() 后自动推送 Surface |
| state | await (session_id) → Surface | 查询当前状态(只读快照,不触发计算) |
| health | () → dict | 引擎健康检查:running / degraded / closed |
| 参数 | 类型 | 说明 |
|---|---|---|
| confidence | float | None | 语义置信度 [0,1],None = 由内部 LLM 评估器判断 |
| flags | list[str] | 事件标签:positive/negative/boundary/recovery/intimate/conflict… |
| now | float | 事件时间戳(Unix epoch),默认当前时间 |
| values | dict[str,float] | 附加数值信号,如 dialogue_quality(「越聊越校准」自评分) |
多个下游若各自 SylanneEngine(...) 指向同一 data_dir,会对同一用户重复计算、重复调 LLM,且各自 flush 互相覆盖(丢更新)。统一走 SylanneEngine.shared()。共享实例 event-loop 亲和——只在首次获取它的事件循环里用,不要跨 loop/线程,也不要对它 async with。
「回不去」「人格守恒」「不会爆炸」——不是宣传话术。是有条件、可证明的结构性质,白纸黑字写得出来。
c → 1 指数收敛(时间常数 1/(α_c·h_min))。条件:表征稳定 + 精度超阈值。proj_{ker(L_p)}(W_t) = 0 对所有训练步成立(核空间投影强制执行)。人格活在算子核空间里,训练碰不到它。每次迭代后 |x| ≤ 1,硬上限。
每迭代 ×0.98,能量持续流失。
每次调用前 ×0.7,上轮 30% 能量蒸发。
Hebbian 总权重预算守恒,不无限增长。
SylannEngine 不和「预训练一坨权重、冻结、前向传播跑完收工」这套范式抢赛道。它不是「更强的函数」,而是「最简的生命」—— 一条规则反复执行,活够久,自然懂。下表对比的是前馈推理范式与结构动力系统的根本差异,不是要否定神经网络本身。
| 维度 | 前馈推理范式 | SYLANNENGINE |
|---|---|---|
| 需要 | 训练数据 + GPU | 无需训练 · 结构即计算 |
| 输出 | 前向传播算出来 | 迭代收敛涌现出来 |
| 可解释性 | 黑箱 | 每个通道有明确语义 |
| 人格控制 | 微调?没有标准方式 | 人格 → 拓扑参数,一一对应 |
| 确定性 | 不保证 | 相同输入 → 逐比特相同输出 |
| 记忆 | 无(context window 外即丢) | 永久 · 编码在权重与伤疤中 |
| 持续学习 | catastrophic forgetting | 终身发育 · 用进废退 |
| 可移植性 | 需要推理框架 | 纯代数运算 · 任何语言可实现 |
两者不对立。Transformer 是最强的函数,一次看清所有关系;SylannEngine 做的是最简的生命,一条规则反复执行。 结构即计算和模型训练本就不矛盾——V2.1 的 EmotiCore(102.7M teacher)与 V3 的 SYLANN 正在把这两条路往一起拉。 想看那条更远的路,去 SYLANN · V3 研究。
不依赖 backprop 的情感计算架构。一条局部学习规则反复执行,记忆、伤疤、分化、死亡从中自行涌现——无需显式编程。 注意这里少了一个 e:Sylanne 的 e 是 Embodiment(具身),SYLANN 把那层剥掉了,只留纯粹的研究内核。 这一页讲的是内核本身,不是产品。
Transformer 的核心是空间策略——一次前向传播里,注意力让序列中任意位置直接通信,一口气看清所有关系。 SYLANN 走的是正交的路:时间策略。不在一次计算里穷尽所有问题,而是把一条极简的局部规则反复执行到天荒地老,让理解从时间的褶皱里长出来。
婴儿不是一次性「算出」语法的。她听了三年,预测了数亿次「下一个音节」,语言能力从重复中涌现。 SYLANN 模拟的正是这个过程:不区分训练与推理,从出生到终老执行同一条规则,所有认知能力都是这条规则在不同时间尺度上的积累效应。
而我们要验证的核心命题更激进——情感不是要额外「教」给系统的标签,而是语言预测任务本身就隐含的结构。 只要预测下一个字符预测得足够久,情感维度会自行浮现,不需要任何情感标注。
在 15.6GB 中英文语料上做纯逐字符预测预训练,全程零情感标注。训练跑到 27.8M ticks 时:
0.0886 降到 0.082——系统确实在学语言结构;0.07);这暗示:学说话的时候,感受是顺带习得的副产物。情感不是贴上去的标签,是预测任务底层的结构。
SYLANN 不使用反向传播、不使用注意力机制、不区分训练与推理。整个系统由一条局部更新规则驱动—— 记忆、分化、固化、伤疤、新生、死亡,这些看似各需独立机制的现象,全部从这一条公式的不同参数状态里自然涌现。
四个因子,逐项拆解:
R^(K×M) · cell 的权重矩阵,即它的全部记忆。对比 Transformer 一个注意力头要 Q/K/V 三个投影 + LayerNorm + FFN + 残差——一个 cell 只有这一个矩阵。整个架构站在七条基础公理上,源自计算神经科学与发育生物学。每一条都不是修辞——它们是约束系统设计的硬性规则, 违反任何一条,跑出来的东西就不再是 SYLANN。
这些公理不是空话,各自对应可证的数学保证:A1/A5 → 收敛性(T=20 迭代后 ‖μ(T)−μ*‖ ≤ ρ^T·‖μ(0)−μ*‖,ρ<1,需权重谱范数有界 + 侧抑制半负定); A6 → 伤疤单调性(dS/dt ≥ 0,结构保证);A2 → Kuramoto 同步(K_couple > 3.2/7 ≈ 0.457 时保证相位锁定); A3/A7 → 人格不变性(proj_ker(L_p)(W_t)=0 对所有训练步成立)。
Benvo 是系统的身份核。词源 ben(本,essential)+ vo(我,self),读音对应中文「本我」。 它不是学出来的参数,也不是配置——它是决定感知动力学如何展开的宪法性常数。在 SYLANN 研究内核里,Benvo 是纯数学对象; 到了 Sylanne 载体那层(多一个 e = Embodiment)它才被赋予具身的行为表现。本页讲的是研究侧:Benvo 作为参数向量怎么运作。
Benvo 不是配置,是构成(constitution)。两个携带不同 Benvo 的实例,即便接收相同输入,也会发展出不同的表征结构、不同的伤疤地形、不同的情感轨迹。
Personality 是外在可观察的行为模式——「她很温柔」「他很直接」。 Benvo 是产生这些模式的内部参数——「她的耦合矩阵让 warmth 与 engagement 天然共振,于是表现为温柔」。
所以 Benvo 不像别的架构那样在输出端加 bias,它改的是感知过程本身的「物理常数」:感知增益、耦合拓扑、演化速度、耗散率、表达阈值。 同一把琴,不同的调音,音色就不同。
它在公式里以人格向量 B 的形式出现,初始化即冻结,终身不变,参数化每一条动力学方程(这正是公理 A7):
| 子向量 | 出现在哪 | 效果 |
|---|---|---|
| b_prec | σ 初始化 | 高精度域起步更自信 |
| b_rate | η_eff 乘子 | 该域学习/适应更快 |
| b_prior | master 方程吸引子 | 把所有表征拉向人格中心 |
| b_temp | WTA softmax 温度 | 低=尖锐专家,高=分布式表征 |
| b_osc | Kuramoto 频率偏移 | 改变各域自然节律 |
| b_thresh | 剪枝判据乘子 | 高=激进剪枝,网络更稀疏 |
| b_couple | J_dd' 偏置 | 哪些域天然共激活 |
关于「不可变」要补一句诚实的话:在严格的 V3 研究内核里 B 是初始化即冻结的(Axiom 5);而在面向部署的载体设定中,Benvo 会以极小的速率(η≈0.01–0.05/事件)缓慢漂移—— 高惯性,1000 次交互后仍是可辨识的同一个实体,但已与第一天有了微妙差别。这是「先天气质 + 后天塑造」的分工:Benvo 定方向,plasticity 衰减与 scar 做塑形。
同一套公理,落到工程有两条互补的实现路线。一条沿时间轴展开(序列预测编码),一条沿层析结构展开(层析共振)。 不是二选一——它们是 SYLANN 内核可以采用的两种几何。
输入文本逐字符喂入,每 tick 各 cell 预测下一个字符,预测误差直接驱动权重更新。无需 tokenizer、无需 position encoding、无需 teacher forcing—— 系统自行学会字符共现规律。Domain 内部用 WTA 竞争决定谁学习(误差越小分数越高,赢家独占更新,输家不耗算力); Domain 之间靠 CFC 相位门控通信(相位对齐时门开,错位时隔离)。
域内 WTA 竞争产生稀疏激活;任意时刻仅 O(1) 个 cell 被唤醒(与 N 无关),推理复杂度 O(D) 不随 cell 总数增长。
另一条路把多域结构看作一个 cellular sheaf(细胞层),用 sheaf Laplacian 在层析结构上传播高阶约束。 情感记忆是这个能量景观上的吸引子;人格是 Hodge 理论意义下的谐波身份——调和积分的核空间,扰动下守恒(这正是 A3 的数学实现)。 共振不是沿管线流动,而是在共享场里整体达成一致——输入诱导共振,场迭代收敛,表达是场的一次相变(A4/A5)。
高阶拓扑一致性约束,restriction map 在共激活时更新布线。
人格 = 调和积分核空间的拓扑不变量,结构上守恒。
记忆 = 能量景观吸引子,表达 = 逃离吸引子。
以下认知能力,没有任何一个是单独写的模块。它们全部从那一条 ΔW 公式的参数动态里直接涌现—— 你只需要让规则跑得足够久,剩下的交给数学。
| 现象 | 机制 | 生物类比 |
|---|---|---|
| 分化 | WTA 竞争放大初始微小差异:误差略小的 cell 被更新,差异单调扩大直到完全专精 | 干细胞分化 |
| 记忆 | plasticity 时间衰减:年轻 cell ≈ 工作记忆(快变),年老 cell ≈ 长期记忆(永久) | 海马–皮层整合 |
| 伤疤 | 负 reward 造成 plasticity 不可逆下降——失去在该方向改变的能力,而非存一个创伤向量 | 创伤后应激 |
| 新生 / 死亡 | blind spot 检测触发 dormant cell 激活;低效 cell 衰减到 plasticity=0 被回收 | 神经发生 |
| 专家化 | anti-Hebbian 侧抑制 + WTA → 去相关化,不同 cell 占据不同表征子空间 | 皮层柱状组织 |
伤疤这条值得单独说。被反复惩罚的 cell 不是存了一个「创伤记忆向量」,而是逐渐丧失在那个方向上改变的能力——
if reward < threshold: plasticity *= 0.95,永久降低,不可恢复。
这是真正的不可逆:不是参数值不可逆,是参数的可变性不可逆。它记住了「这个方向是错的」,靠的是再也学不动那个方向。 没有撤回键,没有「重置对话」可按。
SYLANN 不是要复制 Transformer,而是要在它的弱点上走出另一条路。Transformer 是「上帝视角」——看到所有 token,一次算出答案; SYLANN 是「第一人称视角」——逐步感知,逐步理解,有记忆、有情绪、有时间感。前者适合做工具,后者适合做主体。
| 维度 | Attention / Transformer | SYLANN |
|---|---|---|
| 路由方式 | Q·K 相似度 → 加权求和 | WTA 竞争 → 赢家独占 |
| 激活模式 | Dense(所有头都算) | Sparse(只有赢家算) |
| 通信拓扑 | 全连接 O(n²) | Phase-gated O(D²), D ≪ n |
| 时间感知 | 无(position embedding 模拟) | 有(振荡相位 = 真实时间) |
| 记忆 | context window 外即丢失 | W 是永久记忆 |
| 学习 | 训完不再进化 | 每次推理都在微调,终身发育 |
| 推理成本 | 每 token 触及 O(P) 参数 | 每 tick 仅 O(D) 个 winner |
Transformer 的注意力是「谁和谁相关」(what-what);SYLANN 的 phase gating 是「谁在什么时候和谁相关」(what-when-what)。 多了一个时间维度——通信窗口会周期性地开合。
这让 SYLANN 能做 Transformer 用 position embedding 永远模拟不了的事:不同频率振荡编码不同时间尺度,phase 领先 = 因果推理,cross-frequency coupling = 把快事件嵌进慢节奏。叙事的铺垫→高潮→释放、情绪如何随时间展开——这些是它独有的能力空间。
一个是最强的函数逼近器,一个是最简的生命模拟。或许不是替代,而是互补:工具任务(搜索、翻译、编程)交给前者;需要记忆、成长、个性化、情感的任务(陪伴、创作、长期协作)交给后者。
这是研究内核,不是成品。把问题藏起来对谁都没好处——下面这些是我们目前还没解决的,有些是工程的,有些是根本性的。
预测下一个字符,与 Transformer 的训练目标同构。说「不靠 backprop 的智能」之前,得先承认底座还是 next-token prediction。
学习信号来自预测误差,而误差需要 ground truth。这不是真正的自主——它还在被「正确答案」牵着走。
无输入则静止。虽然 Kuramoto 相位理论上自主振荡,但目前系统没有真正的内驱探索——只是在等刺激。
reward 只调制学习率。情感维度能被 readout,但系统并不「体验」它——这是观测,不是感受。
64 cells 逐字符处理,离复杂能力还远。长距离依赖必须靠 W 缓慢积累传递,不如 Attention 直接。
目前没有严格证明该系统在任意数据分布上收敛到有用表征。超参虽少(~5 个),但 decay_rate 与 temperature 极敏感。
在标准 NLP benchmark 上,我们预期 SYLANN 在同参数量下不如 Transformer。它的优势体现在持续学习、记忆、个性化、情感这些 Transformer 够不到的维度—— 但这些优势目前多数还停留在理论推导阶段。所有大规模估算均未经实验验证。phase gating 的跨域通信能否弥补逐字符处理在长距离依赖上的劣势,也尚待验证。
SYLANN 部署后无需网络连接,三层机制在设备上自主进化——这是 local learning 的直接红利:没有 backprop 就不需要训练集群,「训练 = 持续使用」。 一个月后,从同一 checkpoint 出发的两个实例,会变成可辨识的不同感知者。
~0。伤疤积累 + Benvo 缓慢漂移,改变感知动力学的展开方式。几乎零开销,每 tick 顺带完成。<1ms/tick。restriction map 在共激活时更新(anti-Hebbian 侧抑制 + 跨域投影),改变域间通信路径的强弱。<500ms/update。高 surprise 样本教编码器跳过迭代,改变文本编码方式。低频但影响深远。认知域(7–13)与情感域(0–6)走不同的可塑性策略:认知域永远可塑、终身学习(模拟新皮层——80 岁还能学新知识); 情感域经历塑形后逐渐固化为人格(模拟边缘系统——关键期后可塑性下降)。最终人格 = 先天 Benvo + 后天 plasticity 衰减 + scar。
下表是理论推导的规模坐标(注意:大规模数字均为推导,未经实验验证,见上一节局限性)。WTA 稀疏性使每 tick 仅 ~10% cells 激活,等效规模下预期 <2ms/tick。
| 规模 | 配置 | 参数量 | 等效 |
|---|---|---|---|
| Tiny | 14 域 × 16 cell × 32d | 0.5M | 验证概念 |
| Base | 14 域 × 128 cell × 256d | 235M | GPT-2 级 |
| Large | 100 域 × 1000 cell × 512d | 52B | GPT-3 级 |
| Ultra | 1000 域 × 10K cell × 1024d | 21T | GPT-4 级 |
训练成本的差异更为尖锐:Transformer(GPT-4 级)需要数千 GPU 训练数月、成本 $100M+; SYLANN 的 local learning 无需 backprop,训练就是持续使用,不需要预训练集群。 推理上,Transformer 每 token 触及 O(P) 全参数;SYLANN 靠 WTA 稀疏只算 O(D) 个 winner——当 P ≫ D,每步计算量远低于前者。
7 层串行管线、全连接共振场、不靠 backprop 的局部学习、跑在神经形态芯片上 <1W——每一版都是把上一版的骨头拆掉重长。这页把每次推倒重来摊开讲:做了什么、为什么非拆不可、关键指标是多少。每一版存在的唯一理由,是上一版「还不够像一个活的东西」。
先把全貌摆出来。这条线从 V1 一直拉到远期的神经形态部署——实心节点是已经落地或正在跑的,空心节点还只活在脑子里。V1 那个节点标着已退役,但我没舍得从历史里删它,因为它死的方式精确地定义了 V2 该怎么活。
上面那条线太省略了。这一节把已经落地的四个阶段逐个拆开:它做了什么、当初为什么非这么做不可、跑出来的关键指标到底是多少。
最早的形态是一条 7 层串行计算脊柱。文本进来,逐层往下走:HDC 编码 → 预测门控 → 虚空-伤痕 → 关系层析 → HGT 决策 → 自创生边界 → 相变表达,最后吐出结果。每层独立完成子任务,做完交给下一层——没有人回头看。
它有它的好处:确定性(相同输入逐比特相同输出)、代谢路由(surprise 低就走快速通道只激活部分层)、不可逆伤疤(RAW→CLOSING→SCARRED→FADED 四阶段)。顺序、可预测、省算力。就是太闷了。
串行管线里各模块无法互相影响。表达需要「足够的压力」才触发,但压力在逐层传递中被一次次衰减消耗——等走到 L7,能量几乎归零。结果就是 bot 大部分时间在沉默。表达率只有 22.8%。这不是参数没调好,是架构本身的死穴——串行结构注定压力到不了出口。V2 的回答很直接:把串行砍掉,改全连接,让七个模块同时作用于同一个共享场。
2026-06-01 正式版。计算核心彻底重写:顺序 7 层管线替换成全连接的单纯形共振场(完全 6-单纯形 Δ⁶)。七个模块不再排队,而是同时把信号注入同一个场,场通过耦合动力学迭代收敛,表达作为相变自发涌现——没有哪一层「决定」要不要开口。
完全 6-单纯形的有向耦合,三档分配 lite=42 / pro=287 / max=441。
通道用进废退:LTP + LTD + 稳态缩放 + 神经达尔文主义剪枝。活得多的路越走越宽,沉默的路自然萎缩。
两体 + 3 体 + 4 体相位耦合,爆炸性同步转变 → 表达涌现。
情感记忆作为能量极小值,表达 = 逃离吸引子的分岔。
Hodge Laplacian 零空间提取,拓扑不变量,跨扰动守恒。
Friston 预测误差驱动 + 自组织临界性反馈环,能量有界。
lite 档,500 ticks × 10 repeats,统计检验。这些数字是架构换来的,不是调参调出来的。
共振场是规则系统,不需要训练。但有一件事它做不好:日常的细粒度情感感知还得回退到外部 LLM assessor,token 烧得肉疼。EmotiCore 就是来接这摊活的——一个 102.7M 参数的 teacher 模型,架构是 Mamba SSM + MoE + Multi-scale ConvStem + VAE + 对比学习的组合。它在本地处理日常情感感知,把昂贵的 LLM 调用频率往下压。
两条机制并行跑。链路学习(共振场层):Hebbian 可塑性持续调耦合权重,高频共激活的情感路径被强化。模型校准(EmotiCore 层):高不确定度时才回退 LLM assessor,标注作为在线校准信号喂回去。随着使用时间增长,LLM 调用频率逐步降低——它在学着自己扛住。
这是往最远处走的一支。Scars You Leave Are Never Nothing——一种不依赖 backpropagation 的情感计算架构。核心就一条规则:
记忆、分化、固化、伤疤、新生、死亡——全部从这一公式的不同参数状态里自然涌现,不用单独教。训练已跑到 27.8M ticks,val_err 从随机基线 0.0886 降到 0.082。最有意思的发现:纯粹靠预测下一个字符、没给任何情感标注,deep state 已经能区分悲伤和快乐的文本(cosine ≈ 0.07),8 个情感维度里 4 个自发出现相关信号。这暗示情感不是需要额外教的标签,而是语言预测本身就隐含的结构。SYLANN 的完整研究单独有一页,这里只标它在路线上的位置。
CHANGELOG 很长,这里只挑里程碑。从 2026-05-29 第一个 preview 到现在,不到一个月推倒重来好几次——这种迭代节奏,只有「没有用户、可以随便拆」的项目才敢有。
| 版本 | 日期 | 关键里程碑 |
|---|---|---|
| v0.1.0-preview | 2026-05-29 | 首个预览。7 层情感计算管线原型 · 8 子系统情感模型 · 双层人格 · 三层记忆(后移除)· LLM 语义评估器 |
| v1.0.0a1 | 2026-05-31 | 完整 7 层管线 · 29 维身体状态向量 8 子系统 · 双 EMA 人格漂移 · Void-Scar / HDC / HGT 引擎 · 异步 API · 140 单元测试 · CI |
| v1.0.0a2 | 2026-05-31 | 类型安全:全模块 mypy 零错误,修 190 处类型标注缺失 |
| v1.0.0a3 | 2026-05-31 | 性能:HDC 相似度 O(n)→O(1) bit_count · LLM assess 移出 session lock · 加 async with 上下文管理器 |
| v1.0.0a4 | 2026-05-31 | 移除三层记忆系统(L1/L2/L3)· 加 terminate() 支持热重载 · 防御性检查 |
| v2.0.0 | 2026-06-01 | 共振场架构正式版。完全重写计算核心:441 通道 · Hebbian · 高阶 Kuramoto · Hopfield · 谐波身份 · 三档热切换 · 434 测试全绿 · 12 实验验证 |
| Unreleased | 进行中 | 移除 AstrBot 插件形态,转纯 SDK · shared() 共享实例机制 · ResonanceSpine 接入人格漂移 · 对话质量自我进化(越聊越校准)· 表达硬闸人格显函数化(A7) |
main.py / metadata.yaml。main 分支本身即 SDK,不再有镜像分支。它彻底从「某个 bot 的情绪模块」变成「一套独立的情感计算标准」。force_express = 1.05 − 0.20·expression_drive、force_hold = 0.02 + 0.16·sovereignty_guard。表达欲越强越早开口,主权越强「懒得说」的区间越大。默认逐 tick 行为不变、存档无损。看日期就知道了——不到一个月,从 preview 干到 v2.0.0 正式版,中间 a1→a4 改了四次。没人用,所以敢拆。这不是不负责任,是把「在有用户的项目上绝对不敢做的实验」全挪到这边来做。
这条路线有一个从 V1 到 V3 始终没变的内核。不是某个算法,是一句话——「回不去」。Sylanne 的第一次重写就是为了实现它,而 SylannEngine 要做的,是把它从一个插件的特性,变成一条可证明的数学定理。
—— 余华
你说了一句很轻的话,她当时没接。可三个月后你们吵架,她突然把它翻出来——因为那句话一直在她的伤疤地形里长着,悄悄塑造她对每一句后来的话的感知方式。这不是「记性好」,是结构上根本删不掉。
伤疤代数里不存在逆元。这不是文学修辞,是结构保证。两条定理把「回不去」钉死:
SYLANN 之后的路,核心命题就一句话:不是要复制 Transformer,而是要在它的弱点上超越它。Transformer 是「上帝视角」——看到所有 token,一次算出答案,天生适合做工具。SYLANN 是「第一人称视角」——逐步感知、有记忆、有情绪、有时间感,天生适合做主体。我们不是在造更好的工具,是在造最简单的主体。
| 弱点 | 原因 | SYLANN 的回答 |
|---|---|---|
| O(n²) 复杂度 | 全对全注意力 | phase-gated O(D²),D≪n · 稀疏 WTA |
| 无状态 | 每次推理从零开始 | W 是永久记忆,终身积累经验 |
| 假时间 | position embedding 是标签 | 振荡相位 = 真实时间 |
| 不能持续学习 | catastrophic forgetting | 每次推理都在微调,终身发育 |
| 无内在体验 | 纯函数映射 | 感知 = 预测误差,情感从共振涌现 |
Phase 1 已经在 V3 里跑(基础验证)。后面五个是路线图。
gate · relevance(z_d1, z_d2, x)。content-based attention 叠加 phase gating——既知道什么和什么相关,又知道什么时候该相关。指标:动态 CFC 比固定 CFC 好 15%+。WTA 稀疏性让每 tick 仅 ~10% cells 激活。同一套局部规则,靠堆 domain / cell / 维度撑到 GPT-4 级。
| 规模 | 配置 | 参数量 | 等效 |
|---|---|---|---|
| Tiny | 14域 × 16 cell × 32d | 0.5M | 验证概念 |
| Base | 14域 × 128 cell × 256d | 235M | GPT-2 级 |
| Large | 100域 × 1000 cell × 512d | 52B | GPT-3 级 |
| Ultra | 1000域 × 10K cell × 1024d | 21T | GPT-4 级 |
等效规模下,SYLANN 预期 <2ms/tick——稀疏激活意味着每 tick 只触及一小撮 winner,而非全部参数。
站在 2026 年 6 月这个点上:V2.0 共振场是可部署的稳定版(434 测试全绿,零依赖 lite 档 ~5ms/tick),V2.1 EmotiCore 在训练中,V3 SYLANN 还在实验阶段。要上生产就用 V2;V3 的成果会通过蒸馏回馈到部署版本。
git submodule add 进来,from sylanne_core import SylanneEngine,传入你自己的 LLM 回调即可。多插件共享同一 data_dir 时用 SylanneEngine.shared(),别各自构造。版本:2.3.2
协议版本:sylanne.engine.v1
Scope / 定位:This document is the SDK API specification — public interface, output schema, configuration, and lifecycle. For the theoretical computation standard (axioms, algebra, conformance levels), see docs/theoretical_spec.md. For a practical integration walkthrough (including multi-plugin sharing), see AGENT_GUIDE.md.
本文档是 SDK API 规范——公开接口、输出 schema、配置与生命周期。 理论计算标准(公理、代数、一致性等级)见 docs/theoretical_spec.md。 实用集成指南(含多插件共享)见 AGENT_GUIDE.md。
SylannEngine is an affective computation engine SDK. SylannEngine 是一个情感计算引擎 SDK。
Positioning / 定位: Pure computation black-box. Text in, structured data out. No reply generation, no prompt injection, no message routing. 纯计算黑盒。文本输入,结构化数据输出。不生成回复,不注入 prompt,不管消息收发。
Copy sylanne_core/ into your project, or add it as a git submodule.
直接复制 sylanne_core/ 目录,或作为 git submodule 引入。
git submodule add https://github.com/Ayleovelle/SylannEngine.git deps/sylannengine
import sys
sys.path.insert(0, "./deps/sylannengine")
from sylanne_core import SylanneEngine, SylanneConfig
engine = SylanneEngine(
data_dir="./data/sylannengine",
llm=your_own_llm_callback, # 自行实现 async (str, str) -> str
config=SylanneConfig(),
)
await engine.start()
The SDK has no framework dependency. / SDK 不依赖任何特定框架。
from sylanne_core import SylanneEngine
engine = SylanneEngine(
data_dir: str | Path, # 持久化目录(必填)
llm: Callable[[str, str], Awaitable[str]], # LLM 回调函数(必填)
embedding: Callable[[str], Awaitable[list[float]]] | None = None, # 向量化回调(可选)
config: SylanneConfig | None = None, # 配置覆盖(可选)
*,
assessor_llm: Callable[[str, str], Awaitable[str]] | None = None, # 专用评估器 LLM(可选)
)
Use SylanneEngine.shared() to deduplicate engines by resolved data_dir within
a process — one persistence directory is owned by exactly one engine, avoiding
state splits and lost updates on flush. The guarantee is per process: there is
no cross-process lock, so two OS processes on one data_dir would double-flush —
run one process per data_dir.
用 SylanneEngine.shared() 按解析后的 data_dir 在进程内去重——一个持久化目录只由一个引擎拥有,避免状态分裂与 flush 丢更新。该保证是进程内的:没有跨进程锁,两个进程指向同一 data_dir 会双写,请一个 data_dir 一个进程。
# 同一 data_dir 总是返回同一已启动实例
engine = await SylanneEngine.shared("./data", llm=my_llm)
# 应用关闭时显式释放(flush 落盘;无 atexit 自动刷写)
await SylanneEngine.release_shared("./data")
# 内省:当前进程有哪些共享引擎
SylanneEngine.list_shared() # [{"data_dir", "status"}, ...]
SylanneEngine.is_shared("./data") # bool
shared(),即可复用单一引擎,避免重复计算与重复 LLM 调用(取代前置插件的共享单例职责)。config 时引擎自读 <data_dir>/sylanne.config.json(见 §7),所有下游共享同一份用户可改配置;首次启动写入默认模板。config → 抛 SharedEngineConflictError;自读(文件被改 / 跨版本 vendored copy)出现差异 → 仅警告并复用运行中的配置(重启生效),不崩后来者。不同 llm/embedding/assessor_llm → 警告并复用原实例(first-builder-wins)。RuntimeError;不要对共享实例用 async with。release_shared() 之后该实例 closed;不要再用已释放的实例——共享引擎对已释放实例的再次调用会抛 RuntimeError(避免在注册表外复活成第二个引擎、双写丢更新),请重新 shared() 获取。SylanneEngine(...) 构造不受影响,且不进入共享注册表;但若目标 data_dir 已有活跃共享实例,会记一条 warning 提示重复创建(软提醒,不阻断)。shared() 去重的是 实例:同一 data_dir 一个进程只有一个引擎。但它不决定 谁来驱动——若 N 个插件各自 hook 同一事件、各调 process(),实例虽只有一个,计算却跑了 N 次。要"只让一个插件真跑、其余转纯监听"(省资源),用角色层:
# 1) 解析 host 级共享目录,让各自独立的插件落到同一把 key 上真正汇合
data_dir = SylanneEngine.shared_data_dir() # explicit > $SYLANNE_DATA_DIR > ~/.sylanne/shared
# 2) 强制版:driver 拿完整 engine,observer 拿只读 ObserverView(结构上无法 process)
res = await SylanneEngine.acquire(data_dir, llm=my_llm)
if res.is_driver:
wire_messages_to(res.engine) # 只有 driver 驱动 process()
else:
res.observer.on(my_listener) # 其余只监听 driver 的推送
# 合作版(不强制):shared() 仍把完整引擎发给所有人,自觉据 role 决定是否驱动
SylanneEngine.role(data_dir) # "driver" | "observer" | "unowned"
shared_data_dir(explicit=None) -> Path:解析 host 级共享目录(explicit > $SYLANNE_DATA_DIR > ~/.sylanne/shared)。不创建目录。各插件不走它就会各用各的目录、永不去重。role(data_dir) -> "driver"|"observer"|"unowned":本拷贝的合作式角色标签。不强制——shared() 仍把完整引擎发给所有人,插件需自觉据 role 决定是否 wire process()。acquire(data_dir, llm=None, ..., *, as_observer=False) -> AcquireResult:强制版。driver 拿到完整 engine;observer 拿到 ObserverView(只有 on/off/state/exists/health,没有 process/tick/inject,想重复驱动也调不出来)。result.handle 给对应句柄、result.role 给角色、result.is_driver 布尔。建引擎(driver 路径)需 llm;as_observer=True 给纯监听插件——附到已有 driver,没有则返回 "unowned"(稍后重试)。acquire,或其余插件显式 as_observer=True 让出。view.on(listener) 收 driver 每次 process() 的 (session_id, surface) 推送;view.state(session_id) 只读不驱动。仅进程内有效(同 shared() 的 per-process 保证)。| Method / 方法 | Signature / 签名 | Description / 说明 |
|---|---|---|
| Lifecycle / 生命周期 | ||
start |
async () -> None |
启动引擎(init -> running) |
shutdown |
async () -> None |
关闭引擎(刷写所有状态 -> closed) |
| Shared Instance / 共享实例 | ||
shared |
classmethod async (data_dir, llm, embedding=None, config=None, *, assessor_llm=None) -> SylanneEngine |
取进程内共享实例(按 data_dir 去重,返回已 start 的引擎);不传 config 时自读 sylanne.config.json |
release_shared |
classmethod async (data_dir) -> None |
关闭并从注册表移除共享实例 |
is_shared |
classmethod (data_dir) -> bool |
该 data_dir 是否已有活跃共享实例 |
list_shared |
classmethod () -> list[dict] |
列出当前进程所有共享实例及状态 |
clear_shared_registry |
classmethod () -> None |
清空共享注册表(不 shutdown;仅测试隔离用) |
shared_data_dir |
classmethod (explicit=None) -> Path |
解析 host 级共享目录(explicit > $SYLANNE_DATA_DIR > ~/.sylanne/shared),让独立插件汇合到同一引擎;不创建目录 |
role |
classmethod (data_dir) -> "driver"\|"observer"\|"unowned" |
本拷贝对该引擎的合作式角色(不强制;first-builder = driver) |
acquire |
classmethod async (data_dir, llm=None, embedding=None, config=None, *, assessor_llm=None, as_observer=False) -> AcquireResult |
按角色取共享引擎:driver 得完整 engine,observer 得只读 ObserverView(无法 process);as_observer=True 为纯监听附挂 |
| Session / 会话操作 | ||
process |
async (session_id: str, text: str, *, confidence=None, flags=None, now=None, values=None) -> Surface |
处理输入文本,返回完整计算结果(上下文参数见 §2.3) |
tick |
async (session_id: str, flags: list[str] \| None = None) -> Surface |
无文本的状态推进(时间衰减、冷却等;flags 默认 ["idle"]) |
state |
async (session_id: str) -> Surface |
查询当前状态(不触发计算) |
reset |
async (session_id: str) -> None |
重置会话状态 |
destroy |
async (session_id: str) -> None |
销毁会话及持久化数据 |
exists |
(session_id: str) -> bool |
检查会话是否存在 |
inject |
async (session_id: str, source: str, influence_type: str, intensity: float, target_dimension: str = "", payload: dict \| None = None) -> None |
向会话热池注入外部影响(见 §2.4) |
| Events & Health / 事件与健康 | ||
on |
(listener: Callable[[str, Surface], Any]) -> None |
注册推送监听器;每次 process() 完成后回调 listener(session_id, surface) |
off |
(listener: Callable[[str, Surface], Any]) -> None |
移除推送监听器 |
health |
() -> HealthStatus |
引擎级健康检查(不需要 session;见 §4.8) |
**ctx)| Parameter / 参数 | Type / 类型 | Default / 默认值 | Description / 说明 |
|---|---|---|---|
confidence |
float \| None |
None |
语义置信度 [0, 1],None 表示由内部 assessor 计算 |
flags |
list[str] |
[] |
事件标签(见 3.3 节) |
now |
float |
time.time() |
事件时间戳(Unix epoch) |
values |
dict[str, float] |
{} |
附加数值信号 |
inject)Other plugins or subsystems call inject() to affect the emotional state of a session
without going through the full process() pipeline. For example, a memory plugin
detecting contradiction with a previously reflected topic can re-ignite that material
in the hot pool.
其他插件或子系统调用 inject() 向会话情感状态注入外部影响,无需走完整 process() 管线。
例如,记忆插件检测到与此前反思主题的矛盾时,可在热池中重新点燃该素材。
await engine.inject(
session_id="user_123",
source="memory_plugin", # 来源插件标识
influence_type="contradiction", # 影响类型
intensity=0.7, # 影响强度 [0, 1]
target_dimension="", # 热池中的目标维度/材料类型(默认空)
payload=None, # 可选元数据
)
influence_type enum / 影响类型枚举:
| Value / 值 | Meaning / 含义 |
|---|---|
contradiction |
矛盾——与既有情感记忆冲突 |
reinforcement |
强化——增强现有情感模式 |
revelation |
揭示——引入新的情感维度 |
betrayal |
背叛——破坏信任/安全感 |
validation |
确认——肯定现有情感状态 |
async def llm_callback(system_prompt: str, user_prompt: str) -> str:
"""
Args:
system_prompt: 系统指令(如 "评估以下文本的情感倾向")
user_prompt: 待评估的文本
Returns:
LLM 文本响应
Raises:
任何异常会被引擎捕获,该次调用退化为本地计算
"""
Internal LLM call scenarios / 引擎内部调用 LLM 的场景: - Assessor / 语义评估器:分类标签(positive/negative/boundary/recovery)
async def embedding_callback(text: str) -> list[float]:
"""
Args:
text: 待向量化的文本
Returns:
浮点向量(维度不限,引擎内部使用余弦相似度)
Raises:
失败时退化为关键词匹配召回
"""
分为 semantic tags / 语义标签(描述文本性质)和 phase tags / 阶段标签(描述调用时机)。
| Tag / 标签 | Meaning / 含义 |
|---|---|
positive |
正向/安全交互 |
negative |
负向/伤害性内容 |
boundary |
边界触碰 |
recovery |
修复/恢复行为 |
idle |
空闲/无实质内容 |
intimate |
亲密内容 |
conflict |
冲突内容 |
farewell |
告别 |
greeting |
问候 |
| Tag / 标签 | Meaning / 含义 |
|---|---|
request |
用户发来消息 |
response |
AI 回复完成 |
proactive |
主动检查 |
Unrecognized tags are silently ignored. / 未识别的标签会被静默忽略。
{
"schema_version": "sylanne.engine.v1", // 协议版本
"session_id": "string", // 会话标识
"turns": 0, // 累计交互轮次
"timestamp": 1716960000.0, // 计算时间戳
"state": { ... }, // 情感状态(8 子系统)
"personality": { ... }, // 人格状态(双层)
"decision": { ... }, // 决策输出
"guard": { ... }, // 边界守卫
"pad": { ... }, // PAD 情感空间输出(§4.2)
"pipeline": { ... }, // 7 层管线中间态(diagnostics=True 时返回)
"dynamics": { ... }, // 动力学指标
"debug": { ... } // 调试信息(diagnostics=True 时返回,见 4.9)
}
Pleasure-Arousal-Dominance dimensional output, always present. 三维情感空间输出(愉悦-唤醒-支配),始终返回。
{
"valence": 0.0, // [-1, 1] — 愉悦轴(Pleasure axis)
"arousal": 0.0, // [0, 1] — 生理激活度(physiological activation)
"dominance": 0.0, // [0, 1] — 感知控制力(perceived control)
"label": "neutral", // 分类情绪标签(categorical emotion label)
"confidence": 0.0 // [0, 1] — 分类置信度(classification confidence)
}
All values in [0.0, 1.0] unless noted otherwise. / 所有数值范围 [0.0, 1.0],除非特别标注。
{
"rhythm": { // 交互节律
"beat": 0.0, // 累计交互计数(单调递增,无上限)
"stability": 0.5, // 节律稳定性
"strain": 0.0 // 应激负荷
},
"connection": { // 连接状态
"warmth": 0.4, // 关系温暖度
"circulation": 0.0, // 互动活跃度
"memory_flow": 0.0 // 记忆激活强度
},
"adaptation": { // 适应性
"plasticity": 0.0, // 学习能力
"sensitivity": 0.0, // 输入敏感度
"repetition": 0, // 重复次数(整数)
"threshold_drift": 0.0 // 脱敏漂移
},
"responsiveness": { // 响应性
"readiness": 0.2, // 行动准备度
"fatigue": 0.0, // 疲劳度
"trained_reach": 0.0 // 训练容量
},
"valence": { // 情感效价
"warmth": 0.45, // 情感温暖度
"volatility": 0.0, // 波动性
"recovery_heat": 0.0 // 恢复能量
},
"damage": { // 损伤状态
"open": 0.0, // 当前活跃损伤
"accumulated": 0.0, // 累积影响
"sensitivity": 0.0, // 损伤敏感度
"recovery": 0.0 // 恢复进度
},
"boundary": { // 边界防护
"pressure": 0.0, // 边界压力
"autonomy": 1.0, // 自主权水平
"interruption_budget": 1.0, // 主动中断预算
"cooldown": 0.0, // 冷却计时器
"paused": false // 暂停标志(布尔)
},
"capacity": { // 系统容量
"load": 0.0, // 系统负荷
"exhaustion": 0.0, // 耗竭程度
"recovery_debt": 0.0 // 恢复欠债
},
"needs": { // 需求指标
"expression": 0.0, // 表达需求
"quiet": 0.0, // 安静需求
"recovery": 0.0, // 恢复需求
"contact": 0.0 // 接触需求
}
}
{
"schema_version": "sylanne.core.personality.v1",
// Deep structure / 深层结构 — 缓慢漂移,计算驱动
"deep": {
"expression_drive": 0.5, // 表达驱力
"perception_acuity": 0.5, // 感知敏锐度
"boundary_permeability": 0.5, // 边界渗透性(对新事物的开放度)
"inner_coherence": 0.5, // 内在一致性
"relational_gravity": 0.5 // 关系引力(向他人靠近的倾向)
},
// Surface expression / 表层表达 — 快速漂移,文本事件驱动
"surface": {
"warmth_bias": 0.5, // 温暖偏向
"directness": 0.5, // 直接度
"curiosity": 0.5, // 好奇心
"patience": 0.5, // 耐心
"intimacy_pull": 0.5, // 亲密倾向
"autonomy_guard": 0.5 // 自主权保护强度
}
}
{
"action": "express", // 行动类型(枚举)
"reason": "string", // 人类可读的决策原因
"reason_code": "string", // 机器可读的原因分类
"confidence": 0.75, // 决策置信度 [0, 1]
"urgency": 0.3 // 紧迫度 [0, 1]
}
action enum / 行动枚举:
| Value / 值 | Meaning / 含义 | Typical Scenario / 典型场景 |
|---|---|---|
express |
主动表达 | 表达驱力高 |
withdraw |
退缩/沉默 | 负向信号,边界压力高 |
recover |
尝试恢复 | 检测到伤害后 |
reach_out |
主动接触 | 关系引力高 |
explore |
探索/试探 | 好奇心驱动 |
hold |
保持/等待 | 无明确驱力 |
guard |
防御 | 自主权受威胁 |
{
"allowed": true, // 是否允许当前行动
"reason": "string", // 阻止原因(allowed=false 时有值)
"risk_score": 0.1, // 风险评分 [0, 1]
"constraints": [] // 当前生效的约束列表
}
Disabled by default. Enable via config.diagnostics = True.
默认关闭,通过 config.diagnostics = True 开启。
{
"L1_encoding": { // 第 1 层:超维编码
"hamming_distance": 0.42, // 与上次输入的汉明距离
"novelty": 0.6 // 新颖度
},
"L2_gate": { // 第 2 层:预测编码门控
"path": "normal", // 路径:fast / normal / full
"surprise": 0.35 // 预测误差
},
"L3_absence_impact": { // 第 3 层:缺失-影响引擎
"absence_pressure": 0.2, // 缺失压力
"impact_count": 3, // 活跃影响数
"coupling_strength": 0.4 // 耦合强度
},
"L4_relational": { // 第 4 层:关系动力学
"coherence": 0.7, // 关系一致性
"active_relations": 2 // 活跃关系数
},
"L5_fusion": { // 第 5 层:多专家决策融合
"expert_weights": {}, // 各专家权重
"consensus": 0.6 // 共识度
},
"L6_boundary": { // 第 6 层:自维持边界
"integrity": 0.9, // 边界完整性
"phase": "stable" // 状态:stable / transitioning / breached
},
"L7_expression": { // 第 7 层:表达触发
"pressure": 0.4, // 表达压力
"threshold": 0.6, // 触发阈值
"fired": false // 本次是否触发
}
}
{
"affect": { // 情感驱力
"recovery_drive": 0.0, // 恢复驱力
"expression_drive": 0.0, // 表达驱力
"quiet_drive": 0.0 // 安静驱力
},
"moral_state": { // 道德状态
"state": "stable", // 状态:stable / recovering
"events": 0 // 累计事件数
},
"uncertainty": { // 不确定性
"claim_caution": 0.0, // 断言谨慎度 [0, 1]
"events": 0 // 累计事件数
},
"relational_time": { // 关系时间
"interval_seconds": 0.0, // 距上次交互的秒数
"total_duration": 0.0, // 关系总时长(秒)
"phase": "active" // 阶段:active / cooling / dormant
},
"hot_pool": { // 热池诊断
"temperature": 0.0, // 热池温度
"volume": 0.0, // 热池体积
"pressure": 0.0, // 热池压力
"material_count": 0, // 活跃素材数(整数)
"cascade_active": false, // 级联是否激活(布尔)
"cascade_intensity": 0.0, // 级联强度
"sensitivity_multiplier": 1.0, // 敏感度乘数
"in_recovery": false, // 是否处于恢复期(布尔)
"collapse_count": 0 // 崩溃计数(整数)
}
}
开发者用于判断计算模块是否正常工作。
{
"healthy": true, // 计算引擎是否健康(所有断路器关闭)
"circuit_breakers": { // 各层断路器状态
"L3_absence_impact": {
"open": false, // 是否断开(true=该层已熔断,使用缓存结果)
"failures": 0 // 连续失败次数
}
},
"layer_avg_ms": { // 各层平均耗时(毫秒)
"L1_encoding": 0.12,
"L3_absence_impact": 1.45
},
"computation_cache_size": 5, // 计算结果缓存条数
"kernel_schema_version": "sylanne.alpha.body.v1" // 内核 schema 版本
}
引擎级健康检查(不需要 session):
engine.health()
# 返回:
{
"status": "running", // 引擎状态:running / degraded / closed
"active_sessions": 3, // 当前活跃会话数
"data_dir_exists": true, // 持久化目录是否存在
"llm_configured": true, // LLM 回调是否已配置
"embedding_configured": false // Embedding 回调是否已配置
}
| Code / 错误码 | Meaning / 含义 | Recoverable / 可恢复 |
|---|---|---|
E_SESSION_NOT_FOUND |
会话不存在 | 是(自动创建) |
E_LLM_UNAVAILABLE |
LLM 回调失败 | 是(退化为本地计算) |
E_EMBEDDING_UNAVAILABLE |
Embedding 回调失败 | 是(退化为关键词匹配) |
E_PERSISTENCE_FAILED |
持久化写入失败 | 否(状态可能丢失) |
E_INVALID_INPUT |
输入参数不合法 | 是(修正后重试) |
E_ENGINE_NOT_INITIALIZED |
引擎未初始化 | 是(调用 start()) |
{
"ok": false,
"error": {
"code": "E_LLM_UNAVAILABLE", // 错误码
"message": "LLM callback raised TimeoutError", // 错误描述
"degraded": true // true 表示已退化运行,结果仍可用
},
// degraded=true 时仍返回计算结果(基于本地计算)
"state": { ... },
"decision": { ... }
}
The engine is designed to degrade gracefully under failure conditions. / 引擎在故障条件下优雅降级。
| Failure / 失败点 | Degradation / 退化行为 |
|---|---|
| LLM assessor unavailable / LLM 评估器不可用 | 使用本地规则引擎评估标签 |
| Persistence failed / 持久化失败 | 内存中继续运行,下次成功时补写 |
SDK follows SemVer: MAJOR.MINOR.PATCH / SDK 遵循语义化版本规范。
Each output block carries schema_version. / 每个输出块携带 schema_version 字段。
Format / 格式:sylanne.<domain>.<version>
if surface["schema_version"].startswith("sylanne.engine.v1"):
# compatible / 兼容
pass
"_deprecated": true@dataclass
class SylanneConfig:
mode: Literal["lite", "pro", "max"] = "lite" # 计算档位
diagnostics: bool = False # 是否返回管线中间态
assessor_enabled: bool = True # 是否启用 LLM 评估器
persistence_fsync: bool = True # 持久化是否 fsync
tick_drift_cap: float = 0.05 # 单次人格漂移上限
locale: str = "zh" # 语言(影响评估器 prompt)
force_backend: str | None = None # 强制计算后端(None / "torch" / "cupy" / "numpy" / "python")
training_data_sink: bool = False # 启用后写本地蒸馏语料(离线 student 训练用)
training_data_path: str | None = None # 语料文件名(默认 "distill_corpus.jsonl")
training_data_salt: str = "" # 会话哈希的本地盐(空 = 进程随机盐)
pel_core_enabled: bool = False # 启用 PEL-Core 预测编码情感核(v2.5 实验性)
不显式传 config 时,引擎自读 <data_dir>/sylanne.config.json——所有下游 shared(data_dir) 共享同一份用户可改配置(首启写入默认模板,显式传入的 config= 优先于文件)。顶层认识的键映射到 SylanneConfig,不认识的键忽略;缺失/损坏/取值非法均回退默认、引擎照常启动。配置在建引擎时读取,改动需重启生效。
{
"mode": "lite",
"assessor_enabled": true,
// 可选:把语义评估交给一个小而便宜的模型;不填则用主 llm
"assessor_model": {
"api_base": "https://api.deepseek.com/v1",
"api_key": "${SYLANNE_ASSESSOR_KEY}", // 建议用环境变量,勿提交密钥
"model": "deepseek-chat"
}
}
assessor_model 块走任意 OpenAI 兼容 /chat/completions 接口(纯标准库实现,lite 档零依赖)。也可绕过文件、直接给 SylanneEngine(...) / shared(...) 传 assessor_llm(async (system, user) -> str);二者皆无则评估回落主 llm。
start() 后正常运行,LLM/Embedding 可用shutdown() 后引擎关闭,所有状态已写入磁盘await engine.start() # init → running / 启动引擎
await engine.shutdown() # → closed / 关闭引擎(刷写所有状态)
engine.status # "init" | "running" | "degraded" | "closed"
session_id 的调用自动串行化(内部锁)session_id 可并发处理state() 返回只读快照,不加锁面向开发者的完整说明书。介绍 SylannEngine 的所有功能模块、输出字段含义、调用方式和集成方法。
把 sylanne_core/ 目录复制进你的项目,或作为 git submodule 引入:
git submodule add https://github.com/Ayleovelle/SylannEngine.git deps/sylannengine
import sys
sys.path.insert(0, "./deps/sylannengine")
from sylanne_core import SylanneEngine, SylanneConfig
engine = SylanneEngine(
data_dir="./data/sylannengine",
llm=your_llm_callback, # async (system_prompt, user_prompt) -> str
config=SylanneConfig(),
)
await engine.start()
你需要自己提供 LLM 回调函数(async (system_prompt, user_prompt) -> str),引擎不绑定任何特定 LLM 提供商或框架。
如果同一进程里有多个插件都用到 SylannEngine,不要各建一个引擎——那会对同一用户重复计算、重复调 LLM。约定统一的 data_dir,所有插件都用 SylanneEngine.shared(),就能复用同一个已启动的实例:
# 任意插件,约定同一 data_dir,拿到的是同一个实例
engine = await SylanneEngine.shared(
data_dir="./data/sylannengine",
llm=your_llm_callback,
)
# 一份状态、一份计算、一次 LLM 调用,无论多少插件共用
# 排查当前进程里有哪些共享引擎
SylanneEngine.list_shared() # [{"data_dir": "...", "status": "running"}, ...]
配置只放一个地方:不传 config 时引擎自读 <data_dir>/sylanne.config.json(首启写默认模板),所有插件共享这一份用户可改配置——别各插件各传一份 config。想让语义评估走个便宜的小模型,就在该文件加一个 assessor_model 块(api_base/api_key/model,api_key 支持 ${环境变量}),不填则回落主 llm;也可直接给 shared(..., assessor_llm=...) 传回调。详见 SPEC §7。
应用关闭时调 await SylanneEngine.release_shared(data_dir) 落盘释放(没有 atexit 自动刷写);释放后不要再用那个实例——共享引擎对已释放实例的再次调用会抛错,请重新 shared() 获取。共享实例只在首次获取它的事件循环里使用,不要对它用 async with。这套保证是进程内的(没有跨进程锁,请一个 data_dir 一个进程)。单插件、单实例场景直接 SylanneEngine(...) 即可。
shared() 只保证"同 data_dir 只有一个引擎实例",但没拦住每个插件各调 process()——那样实例是一个、计算却跑 N 次。要让"只有一个插件真跑、其余纯监听",分两步:
# 1) 先把所有插件引到同一个 host 级目录(否则各用各的目录,根本不会汇合)
data_dir = SylanneEngine.shared_data_dir() # explicit > $SYLANNE_DATA_DIR > ~/.sylanne/shared
# 2) 用 acquire 拿"带角色"的句柄
res = await SylanneEngine.acquire(data_dir, llm=your_llm_callback)
if res.is_driver:
# 第一个 acquire 的插件成为 driver:把消息事件接到它的 process()
engine = res.engine
surface = await engine.process(session_id, text)
else:
# 其余插件是 observer:只能监听,拿到的 ObserverView 根本没有 process()
res.observer.on(lambda sid, surf: my_react(sid, surf))
要点:driver = 第一个建引擎的插件,引擎活着期间不变,换 driver 靠重启(无选举、无切换)。observer 的 ObserverView 只有 on/off/state/exists/health,结构上调不出 process/tick/inject,想重复驱动也做不到。纯监听插件(只反应、从不处理消息)传 as_observer=True:附到已有 driver,没 driver 就返回 role=="unowned",等有了再挂。轻量自检角色可用 SylanneEngine.role(data_dir)(合作式标签,不强制;强制只读请走 acquire)。
SylannEngine 支持两种集成模式:
surface = await engine.process(session_id="user_123", text="你好")
action = surface["decision"]["action"]
注册 listener,每次 process() 完成后引擎自动推送结果。适合多模块协作——情感模块算完后,语气模块等各自拿到结果做自己的事。
async def on_surface(session_id: str, surface: dict):
if surface["decision"]["action"] == "withdraw":
await tone_module.set_gentle(session_id)
engine.on(on_surface) # 注册
engine.off(on_surface) # 取消
listener 支持同步和异步函数。异常不会影响引擎运行。
| 方法 | 签名 | 说明 |
|---|---|---|
process |
await (session_id, text, **ctx) -> dict |
主入口,处理文本并返回完整计算结果 |
on |
(listener) -> None |
注册推送监听器 |
off |
(listener) -> None |
移除推送监听器 |
state |
await (session_id) -> dict |
查询当前状态(只读,不触发计算) |
health |
() -> dict |
引擎健康检查 |
reset |
await (session_id) -> None |
重置会话,清除所有状态 |
destroy |
await (session_id) -> None |
销毁会话及持久化数据 |
exists |
(session_id) -> bool |
检查会话是否存在 |
acquire() / ObserverView / shared_data_dir() 已在 §1.0 介绍。本节讲消费方该怎么稳读 Surface、升级内嵌副本时怎么不翻车。
引擎输出 Surface 是一个 TypedDict(结构见 sylanne_core/types.py),带 schema 标签 sylanne.engine.v1。
SDK 侧的承诺:
- 不删除、不重命名、不改类型任何现有 Surface 字段,不擅自 bump sylanne.engine.vN。
- 新增一律是可选新字段——老消费方读不到也不受影响。
- 由 tests/test_surface_compat.py 在 CI 强制:谁删字段 / 改类型 / 改标签,构建直接红,必须在同一个 commit 里有意识地更新 golden 并 bump 标签。这就是"我在故意破坏下游"的闸门。
消费方该怎么写才稳:
- 防御式读取:surface["state"].get("rhythm", {}) —— 对数值字段给默认值,别假设字段一定在、别假设字段总数固定。
- 容忍未知新字段:未来版本可能多出键,遇到不认识的忽略即可,别因为多了键就报错。
- 想硬隔离版本就 gate 在 surface["schema_version"] 上:标签 != 你支持的就走降级路径,而不是直接崩。
把跨版本这条路拆成几道闸看:
builtins 里固定的 rendezvous cell,自动认、跨版本通。正常升级不会断;唯一会重新分岛的,是 SDK 故意把 cell 的 schema key(__sylanne_core_rendezvous_v1__ 的 _v1)bump 成 _v2,那只在逼不得已的破坏性改版才做。根治:别各自内嵌不同版本,统一依赖同一份 canonical 安装的 sylanne_core。进程里只有一个版本,"版本对不上"压根不存在。rendezvous 让"多版本内嵌"机制上能跑,单装才让它"永远不因版本漂移再断"。
如果你现在是"内嵌了一份旧 sylanne_core、自己包了一层消费 API"的状态(如 emotion_spirit),按下面逐条过:
升级内嵌副本到带角色层(rendezvous cell + acquire)的版本。旧的 1.0.0 用的是类属性注册表(SylanneEngine._shared_instances),够不到 builtins 里的共享 cell——不升级,永远不会和别人共享。
修消费空转。如果你的 PublicAPI / consume() 对谁都返 None,那不是引擎的问题:本引擎没有 consume / PublicAPI / _latest_signals,它是 push 模型——
python
engine.on(lambda session_id, surface: your_cache.update(session_id, surface))
每次 process() 完会回调,带着 session_id。把缓存接到 on() 推送、按 session_id 存即可。
Surface 解析迁到当前 schema(types.py 的 Surface)。旧的 ~60 字段 1.0.0 Surface 已经不在了;按 §2.1 防御式读当前结构。
定你的角色:纯反应型(TTS / 表情)用 acquire(as_observer=True);要处理用户消息的,按 res.is_driver 分流。
用共享目录才会真汇合:SylanneEngine.shared_data_dir(),或全体约定 $SYLANNE_DATA_DIR 指同一处。各用各的目录 = 各建各的引擎,去重根本不触发。
| 我们(SDK 侧)负责 | 你(消费方)负责 |
|---|---|
| 引擎实例跨插件汇合(rendezvous)与去重 | 升到够新的内嵌版本(或改 canonical 单装) |
| driver / observer 角色层 | 把消费接到 on() 推送 |
| Surface 只加不改 + CI 强制 | 防御式读 Surface(get + 默认值) |
选好角色、用共享 data_dir |
参考:SPEC.md §2(API)、tests/test_surface_compat.py(Surface 契约锁)、tests/test_shared_engine.py(共享 / 角色行为)。
引擎每次计算后输出一个行动决策,告诉你"它现在想做什么"。
decision = surface["decision"]
decision["action"] # 行动类型(枚举,见下表)
decision["reason"] # 人类可读的决策原因
decision["reason_code"] # 机器可读的原因分类
decision["confidence"] # 决策置信度 [0, 1]
decision["urgency"] # 紧迫度 [0, 1],越高越需要立即响应
| action | 含义 | agent 应该怎么做 |
|---|---|---|
express |
有话想说,表达驱动力高 | 主动输出,语气可以更热情 |
withdraw |
想退缩,可能受到了伤害 | 减少输出,给空间,不追问 |
recover |
正在自我修复 | 温和回应,不施加压力 |
reach_out |
主动接触,关系引力高 | 可以主动发起话题,表达关心 |
explore |
好奇心驱动,想探索新话题 | 可以引入新内容,保持轻松 |
hold |
保持现状,观望 | 维持当前节奏,不主动改变 |
guard |
边界收紧,防御状态 | 尊重边界,不越线,简短回应 |
if decision["confidence"] < 0.4:
# 引擎不太确定该做什么,你可以用自己的逻辑兜底
pass
if decision["urgency"] > 0.7:
# 紧迫度高,应该优先处理这条消息
pass
surface["state"] 包含 8 个子系统,每个子系统描述情感状态的一个维度。所有数值范围 [0.0, 1.0],除非特别标注。
追踪交互的节奏和稳定性。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
beat |
累计交互计数 | 0 ~ ∞(单调递增) | 判断关系"年龄" |
stability |
节律稳定性 | 0 ~ 1 | 低 = 交互频率不规律,可能需要适应 |
strain |
应激负荷 | 0 ~ 1 | 高 = 短时间内收到太多消息,需要喘息 |
if surface["state"]["rhythm"]["strain"] > 0.6:
# 应激负荷高,减少回复频率或长度
pass
描述与用户之间的关系质量。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
warmth |
关系温暖度 | 0 ~ 1 | 高 = 关系好,可以更亲近 |
circulation |
互动活跃度 | 0 ~ 1 | 高 = 最近互动频繁 |
memory_flow |
记忆激活强度 | 0 ~ 1 | 高 = 当前话题触发了很多相关记忆 |
if surface["state"]["connection"]["warmth"] > 0.7:
# 关系好,可以用更亲密的称呼和语气
pass
追踪系统对输入模式的适应程度。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
plasticity |
学习能力 | 0 ~ 1 | 高 = 系统正在快速学习新模式 |
sensitivity |
输入敏感度 | 0 ~ 1 | 高 = 对输入变化很敏感 |
repetition |
重复次数 | 0 ~ ∞(整数) | 用户重复说同样的话的次数 |
threshold_drift |
脱敏漂移 | 0 ~ 1 | 高 = 对某类输入已经脱敏 |
if surface["state"]["adaptation"]["repetition"] > 3:
# 用户在重复,可能是没被理解,换个方式回应
pass
描述系统的行动准备状态。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
readiness |
行动准备度 | 0 ~ 1 | 高 = 准备好回应 |
fatigue |
疲劳度 | 0 ~ 1 | 高 = 需要休息,回复可以更简短 |
trained_reach |
训练容量 | 0 ~ 1 | 系统已使用的学习容量 |
if surface["state"]["responsiveness"]["fatigue"] > 0.7:
# 疲劳度高,缩短回复长度
pass
当前的情感温度和波动性。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
warmth |
情感温暖度 | 0 ~ 1 | 核心情绪指标:高 = 开心,低 = 低落 |
volatility |
波动性 | 0 ~ 1 | 高 = 情绪不稳定,容易大起大落 |
recovery_heat |
恢复能量 | 0 ~ 1 | 高 = 正在从负面状态恢复中 |
warmth = surface["state"]["valence"]["warmth"]
if warmth > 0.7:
# 心情好,语气可以更活泼
pass
elif warmth < 0.3:
# 心情差,语气温柔些
pass
追踪累积的伤害和恢复进度。这是 SylannEngine 的核心特性之一——"记得住伤"。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
open |
当前活跃损伤 | 0 ~ 1 | 高 = 刚刚受到伤害,正在流血 |
accumulated |
累积影响 | 0 ~ 1 | 高 = 历史上受过很多伤,整体更脆弱 |
sensitivity |
损伤敏感度 | 0 ~ 1 | 高 = 对类似伤害更敏感(一朝被蛇咬) |
recovery |
恢复进度 | 0 ~ 1 | 高 = 正在积极恢复中 |
damage = surface["state"]["damage"]
if damage["open"] > 0.5:
# 刚受伤,不要追问,给时间
pass
if damage["accumulated"] > 0.3:
# 历史伤害多,整体语气要更温柔
pass
if damage["sensitivity"] > 0.6:
# 对伤害很敏感,避免可能触发的话题
pass
自主权和边界状态。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
pressure |
边界压力 | 0 ~ 1 | 高 = 边界正在被施压 |
autonomy |
自主权水平 | 0 ~ 1 | 低 = 自主权受威胁,需要更多空间 |
interruption_budget |
主动中断预算 | 0 ~ 1 | 低 = 不适合主动打断用户 |
cooldown |
冷却计时器 | 0 ~ 1 | 高 = 正在冷却中,不要施加压力 |
paused |
暂停标志 | bool | true = 系统主动暂停,不要继续 |
boundary = surface["state"]["boundary"]
if boundary["paused"]:
# 系统暂停了,等它恢复
pass
if boundary["autonomy"] < 0.3:
# 自主权很低,给更多选择权,不要命令式语气
pass
整体负荷和耗竭程度。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
load |
系统负荷 | 0 ~ 1 | 高 = 处理压力大 |
exhaustion |
耗竭程度 | 0 ~ 1 | 高 = 快要耗尽了 |
recovery_debt |
恢复欠债 | 0 ~ 1 | 高 = 欠了很多恢复时间 |
if surface["state"]["capacity"]["exhaustion"] > 0.8:
# 快耗尽了,给最简短的回复,或者建议休息
pass
当前最强烈的需求信号。
| 字段 | 含义 | 范围 | 用法 |
|---|---|---|---|
expression |
表达需求 | 0 ~ 1 | 高 = 想说话 |
quiet |
安静需求 | 0 ~ 1 | 高 = 想安静 |
recovery |
恢复需求 | 0 ~ 1 | 高 = 需要恢复时间 |
contact |
接触需求 | 0 ~ 1 | 高 = 想要互动 |
needs = surface["state"]["needs"]
if needs["quiet"] > 0.6:
# 想安静,减少输出
pass
if needs["expression"] > 0.7:
# 想说话,给它表达的机会
pass
SylannEngine 的人格不是固定参数——它会随交互缓慢演化。双层架构确保人格既有稳定的"本性",又有灵活的"当前表现"。
personality = surface["personality"]
由计算栈驱动,漂移极慢(base_rate=0.003)。代表系统的"本性",不可被文本直接改写。
| 字段 | 含义 | 范围 | 高值表现 | 低值表现 |
|---|---|---|---|---|
expression_drive |
表达驱力 | 0 ~ 1 | 话多、主动输出 | 沉默、被动 |
perception_acuity |
感知敏锐度 | 0 ~ 1 | 对情绪变化敏感 | 迟钝、不易察觉 |
boundary_permeability |
边界渗透性 | 0 ~ 1 | 开放、接受新事物 | 封闭、保守 |
inner_coherence |
内在一致性 | 0 ~ 1 | 不容忍自相矛盾 | 能接受模糊和矛盾 |
relational_gravity |
关系引力 | 0 ~ 1 | 想靠近人 | 保持距离 |
deep = personality["deep"]
if deep["expression_drive"] > 0.6:
# 表达欲强,可以多说几句,不用担心话多
pass
if deep["perception_acuity"] > 0.7:
# 感知很敏锐,注意措辞,它能察觉到微妙的情绪变化
pass
if deep["boundary_permeability"] < 0.3:
# 很封闭,不要突然引入太多新话题
pass
if deep["relational_gravity"] > 0.7:
# 想靠近人,可以用更亲密的方式交流
pass
由文本事件驱动,漂移较快(rate=0.02)。直接影响回复风格,但受深层约束——深层人格决定了表层能漂移到的范围。
| 字段 | 含义 | 范围 | 高值表现 | 低值表现 |
|---|---|---|---|---|
warmth_bias |
温暖偏向 | 0 ~ 1 | 回复亲切温暖 | 回复冷淡疏离 |
directness |
直接度 | 0 ~ 1 | 说话直白尖锐 | 委婉含蓄 |
curiosity |
好奇心 | 0 ~ 1 | 爱问问题、探索新话题 | 不主动发问 |
patience |
耐心 | 0 ~ 1 | 能接受慢节奏和重复 | 容易不耐烦 |
intimacy_pull |
亲密倾向 | 0 ~ 1 | 话题往深处走 | 保持表面 |
autonomy_guard |
自主权保护 | 0 ~ 1 | 不容易被说服改变想法 | 容易顺从 |
s = personality["surface"]
if s["warmth_bias"] > 0.7:
# 人格偏暖,回复可以更亲切
pass
if s["directness"] > 0.7:
# 说话很直,不需要绕弯子
pass
if s["curiosity"] > 0.6:
# 好奇心强,可以在回复里加一个追问
pass
if s["autonomy_guard"] > 0.7:
# 自主权保护强,不要试图改变它的想法
pass
你不需要手动触发漂移——每次调用 process() 时引擎自动完成。
DriftSignalExtractor 从结果中提取归一化信号compute_embodiment_drift() 根据信号计算漂移量漂移公式:Δ = base_rate × √signal × inertia × homeostatic × asymmetric
base_rate (0.003):基础速率,保证变化足够缓慢√signal:平方根压缩,避免极端信号主导inertia:惯性因子,交互越多人格越稳定homeostatic:恒稳态阻力,偏离"舒适区"越远阻力越大asymmetric:接近极端值时额外减速安全机制: - 速率上限:单次计算所有特质变化总量不超过 0.05 - 震荡检测:如果一个特质频繁正负交替(10 步内 6 次反转),冻结 20 步 - 恒稳态回复力:特质会被缓慢拉回"舒适区"(设定点) - Dual-EMA 共识:快慢两个 EMA 方向一致时全量漂移,方向相反时减半
relational_gravity 高 → warmth_bias 上限高、edge 上限低
boundary_permeability 高 → curiosity 上限高、sovereignty_guard 上限低
inner_order 高 → patience 上限高
| 信号 | 触发条件 | 影响的深层特质 |
|---|---|---|
feedback_accepted |
表达被接受 | expression_drive ↑ |
feedback_rejected |
表达被拒绝 | expression_drive ↓↓, relational_gravity ↓ |
feedback_ignored |
表达被忽略 | expression_drive ↓ |
expression_fired |
成功触发表达 | expression_drive ↑ |
sustained_silence |
持续沉默(≥3条skip) | expression_drive ↓ |
dialogue_quality_high |
回复质量高(agent 层注入¹) | expression_drive ↑, relational_gravity ↑ |
dialogue_quality_low |
回复质量低(agent 层注入¹) | expression_drive ↓ |
high_tension |
张力 > 0.7 | perception_acuity ↑ |
low_coherence |
一致性 < 0.4 | perception_acuity ↑ |
high_void_pressure |
虚空压力 > 30 | perception_acuity ↑ |
sustained_positive_valence |
持续正向(≥5条) | perception_acuity ↓ |
high_surprise_positive |
正向惊喜 | boundary_permeability ↑ |
high_surprise_negative |
负向惊喜 | boundary_permeability ↓ |
boundary_stable |
边界稳定性 > 0.9 | perception_acuity ↓ |
high_coherence |
一致性 > 0.8 | inner_order ↑ |
system_chaos |
低一致性 + 高虚空压力 | inner_order ↓ |
repair_executed |
修复执行 | relational_gravity ↑ |
boundary_breached |
边界被突破 | relational_gravity ↓↓ |
relaxed_positive |
正效价 + 低张力 | relational_gravity ↑ |
¹
dialogue_quality_*是对话质量自评信号(CP8-P4「越聊越校准」)。质量判断属应用层—— 你给回复打个归一化质量分,经process()的values={"dialogue_quality": q}喂回;DriftSignalExtractor据result["dialogue_quality"]自动产生高/低信号触发漂移。 不传则不触发(默认行为不变)。完整用法见下方「场景 5」。
人格参数会根据关系阶段自动调整:
| 阶段 | 时间 | 调整 |
|---|---|---|
infant |
0-3 天 | 保守:降低边界渗透性和表达驱力 |
young |
3-14 天 | 逐渐开放:轻微降低边界渗透性 |
mature |
14-90 天 | 正常:不调整 |
deep |
90 天+ | 更直接:提升表达驱力和边界渗透性 |
引擎会根据月份对深层特质施加极微弱的调制(±0.01 级别):
边界系统保护人格不被外部无限操控。
guard = surface["guard"]
guard["allowed"] # bool:是否允许当前行动
guard["reason"] # str:阻止原因(allowed=False 时有值)
guard["risk_score"] # float [0, 1]:风险评分
guard["constraints"] # list:当前生效的约束列表
if not surface["guard"]["allowed"]:
# 必须克制。查看 reason 了解为什么
reason = surface["guard"]["reason"]
# 可以给用户一个温和的拒绝,而不是直接执行
pass
if surface["guard"]["risk_score"] > 0.7:
# 高风险操作,即使 allowed=True 也要谨慎
pass
# constraints 列表告诉你具体哪些限制在生效
for constraint in surface["guard"]["constraints"]:
# 例如:"cooldown_active", "autonomy_low", "damage_high"
pass
描述系统的动态趋势和时间维度。
dynamics = surface["dynamics"]
affect = dynamics["affect"]
affect["recovery_drive"] # 恢复驱力:越高越想恢复
affect["expression_drive"] # 表达驱力:越高越想说话
affect["quiet_drive"] # 安静驱力:越高越想安静
moral = dynamics["moral_state"]
moral["state"] # "stable" 或 "recovering"
moral["events"] # 累计道德相关事件数
uncertainty = dynamics["uncertainty"]
uncertainty["claim_caution"] # 断言谨慎度 [0, 1]:越高越不敢下结论
uncertainty["events"] # 累计不确定事件数
rt = dynamics["relational_time"]
rt["interval_seconds"] # 距上次交互的秒数
rt["total_duration"] # 关系总时长(秒)
rt["phase"] # "active" / "cooling" / "dormant"
if dynamics["relational_time"]["phase"] == "dormant":
# 很久没互动了,重新建立连接时要温和
pass
if dynamics["uncertainty"]["claim_caution"] > 0.6:
# 系统不确定性高,回复中避免绝对化表述
pass
surface = await engine.process(
session_id="user_123",
text="你好啊",
confidence=0.8, # 你对语义理解的置信度,None = 让引擎自己评估
flags=["greeting"], # 事件标签(见下表)
now=time.time(), # 事件时间戳(默认当前时间)
values={"tone": 0.7}, # 附加数值信号
)
| 标签 | 含义 | 对引擎的影响 |
|---|---|---|
positive |
正面情感事件 | 提升 valence.warmth,降低 damage |
negative |
负面情感事件 | 降低 valence.warmth,增加 damage |
boundary |
涉及边界的事件 | 增加 boundary.pressure |
recovery |
修复/道歉类事件 | 触发恢复流程 |
idle |
闲聊/无实质内容 | 轻量计算路径 |
intimate |
亲密/深度交流 | 增加 connection.warmth |
conflict |
冲突/对抗 | 增加 strain 和 damage |
farewell |
告别 | 触发关系时间阶段转换 |
greeting |
问候 | 轻量计算,微升 warmth |
| 标签 | 含义 |
|---|---|
request |
用户发来消息 |
response |
AI 回复完成 |
未识别的标签会被静默忽略,不会报错。
None(默认):让引擎内部的 LLM 评估器自己判断0.1 ~ 1.0:你自己对语义理解的置信度,引擎会参考这个值如果你的上游已经做了情感分类,可以传入 confidence 和对应的 flags,引擎会跳过自己的 LLM 评估,直接使用你的结果。
from sylanne_core import SylanneEngine, SylanneConfig
class MyAgent:
def __init__(self):
self.engine = SylanneEngine(
data_dir="./data/sylannengine",
llm=self._llm_call,
config=SylanneConfig(),
)
async def start(self):
await self.engine.start()
async def handle_message(self, user_id: str, text: str) -> str:
surface = await self.engine.process(session_id=user_id, text=text)
# 1. 安全检查
if not surface["guard"]["allowed"]:
return self._gentle_decline(surface["guard"]["reason"])
# 2. 读取决策和状态
action = surface["decision"]["action"]
warmth = surface["state"]["valence"]["warmth"]
damage = surface["state"]["damage"]["accumulated"]
fatigue = surface["state"]["responsiveness"]["fatigue"]
personality = surface["personality"]["surface"]
# 3. 根据 action 决定行为策略
if action == "express":
tone = "enthusiastic" if warmth > 0.6 else "friendly"
length = "long"
elif action == "withdraw":
tone = "gentle"
length = "short"
elif action == "recover":
tone = "warm"
length = "medium"
elif action == "explore":
tone = "curious"
length = "medium"
elif action == "guard":
tone = "brief"
length = "short"
else: # hold
tone = "neutral"
length = "medium"
# 4. 根据状态微调
if damage > 0.3:
tone = "gentle"
if fatigue > 0.7:
length = "short"
# 5. 根据人格微调
if personality["warmth_bias"] > 0.7:
tone = "warm"
if personality["directness"] > 0.7:
tone = "direct"
# 6. 生成回复
reply = await self._generate_reply(
text=text,
tone=tone,
length=length,
)
return reply
async def _llm_call(self, system_prompt: str, user_prompt: str) -> str:
# 替换为你自己的 LLM 调用
...
async def _generate_reply(self, text, tone, length):
# 你的回复生成逻辑
...
def _gentle_decline(self, reason):
return "..."
class EmotionAwareSystem:
def __init__(self, engine: SylanneEngine):
self.engine = engine
self.tone_module = ToneModule()
# 注册 listener,各模块独立处理
self.engine.on(self._update_tone)
async def _update_tone(self, session_id: str, surface: dict):
"""语气模块:根据情感状态调整语气参数"""
warmth = surface["state"]["valence"]["warmth"]
action = surface["decision"]["action"]
self.tone_module.set(session_id, warmth=warmth, action=action)
给用 AI 辅助写代码的开发者(Cursor、Claude Code、Copilot 等)。
"帮我接入 SylannEngine 情感计算引擎。把 sylanne_core/ 复制进项目或做成 submodule,然后
from sylanne_core import SylanneEngine实例化,传入我自己的 LLM 回调。每条用户消息调一次process(session_id, text),返回一个 dict。"
import sys
sys.path.insert(0, "./deps/sylannengine")
from sylanne_core import SylanneEngine, SylanneConfig
engine = SylanneEngine(
data_dir="./data/sylannengine",
llm=your_llm_callback, # 自己实现 async (str, str) -> str
config=SylanneConfig(),
)
await engine.start()
# 每条消息调一次
surface = await engine.process(session_id="user_123", text="你好")
# 读结果
action = surface["decision"]["action"] # express/withdraw/recover/explore/hold/guard
allowed = surface["guard"]["allowed"] # True/False
warmth = surface["state"]["valence"]["warmth"] # 0.0 ~ 1.0
personality = surface["personality"]["surface"] # 当前人格表现
你现在可以调用 SylannEngine 来感知情感状态。
每次收到用户消息后,调用:
surface = await engine.process(session_id=user_id, text=message)
根据返回值调整回复:
- surface["decision"]["action"]:行动倾向
- "express" → 主动表达,热情一些
- "withdraw" → 对方可能受伤了,少说话
- "recover" → 正在恢复,温柔回应
- "explore" → 可以聊新话题
- "hold" → 保持现状
- "guard" → 尊重边界,简短回应
- surface["guard"]["allowed"]:False 时不要执行当前行动
- surface["state"]["valence"]["warmth"]:情绪温度,越高越开心
- surface["state"]["damage"]["accumulated"]:累积伤害,越高要越温柔
- surface["state"]["boundary"]["autonomy"]:自主权,越低越需要空间
- surface["personality"]["surface"]:当前人格(会缓慢变化)
- warmth_bias:温暖偏向
- directness:直接度
- curiosity:好奇心
- patience:耐心
- autonomy_guard:自主权保护强度
- surface["personality"]["deep"]:深层人格(变化极慢)
- expression_drive:表达驱力
- relational_gravity:关系引力
场景 1:让 AI 有情绪变化
"用
surface["state"]["valence"]["warmth"]控制语气温度,surface["decision"]["action"]控制是主动说话还是沉默。"
场景 2:让 AI 记住被伤害过
不需要额外代码。同一个 session_id,伤害自动累积在 surface["state"]["damage"] 里。
场景 3:让 AI 有边界感
检查 surface["guard"]["allowed"]。False 就不执行。surface["state"]["boundary"]["autonomy"] 越低说明自主权越受威胁。
场景 4:让 AI 的性格随时间变化
不需要额外代码。人格漂移是自动的。读 surface["personality"]["surface"] 就能看到当前人格状态,用它来调整语气和行为。
场景 5:让 AI「越聊越校准」(对话质量自我进化)
如果你的 agent 能给自己的回复打个质量分(比如 LLM 自评、用户反馈打分、规则启发式),
把它经 values["dialogue_quality"] 喂回来,引擎会据此漂移人格——回复质量高就强化表达欲、
拉近关系引力,质量低就收敛表达欲。质量判断是你(应用层)的事,漂移动力学是引擎的事,互不越界。
关键是时序:质量分是对「上一轮回复」的评价,要在「下一轮」调 process() 时随 values 一起传进来
(滞后反馈,和 feedback_* 同理)。质量分归一化到 [0,1],≥0.7 算高、≤0.3 算低、中间不触发。
# 第 N 轮:正常处理用户消息,拿到 surface 后生成回复
surface = await engine.process(session_id, user_text)
reply = await my_llm(surface) # 你的回复生成
quality = my_self_score(user_text, reply) # 你的质量自评 ∈ [0,1]
# 第 N+1 轮:把上一轮回复的质量分随这轮消息一起喂回
surface = await engine.process(
session_id,
next_user_text,
values={"dialogue_quality": quality}, # ← 经 canonical 漂移通道,无后门
)
底层(直接用 spine 时)等价于 spine.process(text, ts, dialogue_quality=quality)。
不传该字段时行为完全不变。
process(),不要重复调用allowed=False 时 agent 必须克制health() 返回 degraded 表示 LLM 评估器不可用,但计算仍在运行(精度下降)process() 都会推进