feat(engage): 群聊自主参与度引擎核心(design/18 P2a+P2b)
- persona 加 triggerWords(昔涟/Cyrene/有瑕的追忆/追忆);Loader 解析 - light.go: shouldRespond 拆分 legacy 与 engage 路径;LightInput/LightDecision 扩展 - engage.go(新): EngageConfig + IDLE/PROBING/ACTIVE/COOLDOWN 状态机(按 platform:groupID 内存) + 概率触发(可注入rand) + 角色名提词触发 + 活动期跟随(话题转移/连续无感/窗口) + 负面态度冷避 - engagejudge.go(新): NewLLMLightJudge,一次成本受控轻LLM判 值不值得回(不判身份), 解析失败/超时 fail-closed 静默 - channelbridge.go: Options(TriggerWords/PersonaPrompt/Engage/EngageFor/EngageRand) + 字段初始化 - wire.go: 接线 Light+TriggerWords+PersonaPrompt+Engage(默认概率0.10) - 红线: 未触发/未命中概率绝不调LLM; 身份靠actor元数据; 敏感不落日志; 零新依赖 - 测试: engage_test.go(触发/状态机/冷避/成本受控/JUDGE输入) + persona triggerWords 用例 - 验收: go build/vet/test ./... 全绿
This commit is contained in:
1 parent
2d7a377d74
commit
8eaf81bd0d
9 files changed
+1156
-13
No files matched your search
@@ -538,6 +538,14 @@ func (a *app) wireChannels() error {
|
||||
// Optional review model prunes low-value queued messages near the per-adapter
|
||||
// cap (design/12 §6 策略四). Nil adapter → the gate falls back to FIFO evict.
|
||||
Review: reviewFor(a.modelAdapter, logger.WithSource(a.log, "services/channelbridge/review")),
|
||||
// Light is the design/18 §3 participation judge: a cost-controlled light LLM
|
||||
// decision on a triggered group/stranger message (role-name mention / probability
|
||||
// / active window). Only an actual trigger feeds the group context.
|
||||
Light: engageLightFor(a.modelAdapter, logger.WithSource(a.log, "services/channelbridge/engage")),
|
||||
TriggerWords: a.defaultTriggerWords(),
|
||||
PersonaPrompt: a.defaultInstructions(),
|
||||
Engage: channelbridge.DefaultEngageConfig(),
|
||||
// EngageFor is left nil (P2a); per-group overrides arrive with P2c.
|
||||
// Factory lets the panel save hot-replace a platform's adapters without a
|
||||
// restart (design/04 §3.1).
|
||||
Factory: factory,
|
||||
@@ -742,3 +750,24 @@ func reviewFor(llm model.Adapter, log *slog.Logger) adapter.Reviewer {
|
||||
}
|
||||
return channelbridge.NewLLMReviewer(llm, log)
|
||||
}
|
||||
|
||||
// engageLightFor builds the design/18 §3 participation judge, or nil when no model
|
||||
// adapter is available so the bridge's participation engine fails closed toward
|
||||
// silence (a triggered message without a judge is never answered).
|
||||
func engageLightFor(llm model.Adapter, log *slog.Logger) channelbridge.LightJudge {
|
||||
if llm == nil {
|
||||
return nil
|
||||
}
|
||||
return channelbridge.NewLLMLightJudge(llm, log)
|
||||
}
|
||||
|
||||
// defaultTriggerWords returns the default persona's role-name reminder words
|
||||
// (design/18 §2) so the channel bridge knows which group messages cause a PROBING.
|
||||
func (a *app) defaultTriggerWords() []string {
|
||||
if c, ok := a.personas.DefaultCharacter(); ok {
|
||||
if p, ok := a.personas.Persona(c.PersonaID); ok {
|
||||
return append([]string(nil), p.TriggerWords...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user