make autonomy proactive messaging strategy configurable

This commit is contained in:
DBT
2026-02-24 04:27:50 +00:00
parent 2db6b24de1
commit 791eb3b96c
5 changed files with 71 additions and 34 deletions

View File

@@ -770,18 +770,21 @@ func buildHeartbeatService(cfg *config.Config, msgBus *bus.MessageBus) *heartbea
func buildAutonomyEngine(cfg *config.Config, msgBus *bus.MessageBus) *autonomy.Engine {
a := cfg.Agents.Defaults.Autonomy
return autonomy.NewEngine(autonomy.Options{
Enabled: a.Enabled,
TickIntervalSec: a.TickIntervalSec,
MinRunIntervalSec: a.MinRunIntervalSec,
MaxPendingDurationSec: a.MaxPendingDurationSec,
MaxConsecutiveStalls: a.MaxConsecutiveStalls,
MaxDispatchPerTick: a.MaxDispatchPerTick,
NotifyCooldownSec: a.NotifyCooldownSec,
QuietHours: a.QuietHours,
UserIdleResumeSec: a.UserIdleResumeSec,
Enabled: a.Enabled,
TickIntervalSec: a.TickIntervalSec,
MinRunIntervalSec: a.MinRunIntervalSec,
MaxPendingDurationSec: a.MaxPendingDurationSec,
MaxConsecutiveStalls: a.MaxConsecutiveStalls,
MaxDispatchPerTick: a.MaxDispatchPerTick,
NotifyCooldownSec: a.NotifyCooldownSec,
QuietHours: a.QuietHours,
UserIdleResumeSec: a.UserIdleResumeSec,
WaitingResumeDebounceSec: a.WaitingResumeDebounceSec,
Workspace: cfg.WorkspacePath(),
DefaultNotifyChannel: a.NotifyChannel,
DefaultNotifyChatID: a.NotifyChatID,
ImportantKeywords: cfg.Agents.Defaults.Texts.AutonomyImportantKeywords,
CompletionTemplate: cfg.Agents.Defaults.Texts.AutonomyCompletionTemplate,
BlockedTemplate: cfg.Agents.Defaults.Texts.AutonomyBlockedTemplate,
Workspace: cfg.WorkspacePath(),
DefaultNotifyChannel: a.NotifyChannel,
DefaultNotifyChatID: a.NotifyChatID,
}, msgBus)
}

View File

@@ -39,7 +39,10 @@
"unsupported_action": "unsupported action",
"system_rewrite_template": "Rewrite the following internal system update in concise user-facing language:\n\n%s",
"runtime_compaction_note": "[runtime-compaction] removed %d old messages, kept %d recent messages",
"startup_compaction_note": "[startup-compaction] removed %d old messages, kept %d recent messages"
"startup_compaction_note": "[startup-compaction] removed %d old messages, kept %d recent messages",
"autonomy_important_keywords": ["urgent", "重要", "付款", "payment", "上线", "release", "deadline", "截止"],
"autonomy_completion_template": "✅ 已完成:%s\n下一步如需我继续处理后续直接回复“继续 %s”",
"autonomy_blocked_template": "⚠️ 任务受阻:%s\n原因%s\n建议回复“继续 %s”我会按当前状态重试。"
},
"context_compaction": {
"enabled": true,

View File

@@ -33,6 +33,9 @@ type Options struct {
QuietHours string
UserIdleResumeSec int
WaitingResumeDebounceSec int
ImportantKeywords []string
CompletionTemplate string
BlockedTemplate string
}
type taskState struct {
@@ -385,16 +388,20 @@ func (e *Engine) dispatchTask(st *taskState) {
func (e *Engine) sendCompletionNotification(st *taskState) {
e.writeReflectLog("complete", st, "task marked completed")
e.writeTriggerAudit("complete", st, "")
if !isHighValueCompletion(st) {
if !e.isHighValueCompletion(st) {
return
}
if !e.shouldNotify("done:" + st.ID) {
return
}
tpl := strings.TrimSpace(e.opts.CompletionTemplate)
if tpl == "" {
tpl = "✅ 已完成:%s\n下一步如需我继续处理后续直接回复“继续 %s”"
}
e.bus.PublishOutbound(bus.OutboundMessage{
Channel: e.opts.DefaultNotifyChannel,
ChatID: e.opts.DefaultNotifyChatID,
Content: fmt.Sprintf("✅ 已完成:%s\n下一步如需我继续处理后续直接回复“继续 %s”", shortTask(st.Content), shortTask(st.Content)),
Content: fmt.Sprintf(tpl, shortTask(st.Content), shortTask(st.Content)),
})
}
@@ -404,10 +411,14 @@ func (e *Engine) sendFailureNotification(st *taskState, reason string) {
if !e.shouldNotify("blocked:" + st.ID) {
return
}
tpl := strings.TrimSpace(e.opts.BlockedTemplate)
if tpl == "" {
tpl = "⚠️ 任务受阻:%s\n原因%s\n建议回复“继续 %s”我会按当前状态重试。"
}
e.bus.PublishOutbound(bus.OutboundMessage{
Channel: e.opts.DefaultNotifyChannel,
ChatID: e.opts.DefaultNotifyChatID,
Content: fmt.Sprintf("⚠️ 任务受阻:%s\n原因%s\n建议回复“继续 %s”我会按当前状态重试。", shortTask(st.Content), strings.TrimSpace(reason), shortTask(st.Content)),
Content: fmt.Sprintf(tpl, shortTask(st.Content), strings.TrimSpace(reason), shortTask(st.Content)),
})
}
@@ -754,7 +765,7 @@ func min(a, b int) int {
return b
}
func isHighValueCompletion(st *taskState) bool {
func (e *Engine) isHighValueCompletion(st *taskState) bool {
if st == nil {
return false
}
@@ -765,8 +776,13 @@ func isHighValueCompletion(st *taskState) bool {
return true
}
s := strings.ToLower(strings.TrimSpace(st.Content))
for _, k := range []string{"urgent", "重要", "付款", "payment", "上线", "release", "deadline", "截止"} {
if strings.Contains(s, k) {
keywords := e.opts.ImportantKeywords
if len(keywords) == 0 {
keywords = []string{"urgent", "重要", "付款", "payment", "上线", "release", "deadline", "截止"}
}
for _, k := range keywords {
kk := strings.ToLower(strings.TrimSpace(k))
if kk != "" && strings.Contains(s, kk) {
return true
}
}

View File

@@ -60,18 +60,21 @@ type AutonomyConfig struct {
}
type AgentTextConfig struct {
NoResponseFallback string `json:"no_response_fallback"`
ThinkOnlyFallback string `json:"think_only_fallback"`
MemoryRecallKeywords []string `json:"memory_recall_keywords"`
LangUsage string `json:"lang_usage"`
LangInvalid string `json:"lang_invalid"`
LangUpdatedTemplate string `json:"lang_updated_template"`
SubagentsNone string `json:"subagents_none"`
SessionsNone string `json:"sessions_none"`
UnsupportedAction string `json:"unsupported_action"`
SystemRewriteTemplate string `json:"system_rewrite_template"`
RuntimeCompactionNote string `json:"runtime_compaction_note"`
StartupCompactionNote string `json:"startup_compaction_note"`
NoResponseFallback string `json:"no_response_fallback"`
ThinkOnlyFallback string `json:"think_only_fallback"`
MemoryRecallKeywords []string `json:"memory_recall_keywords"`
LangUsage string `json:"lang_usage"`
LangInvalid string `json:"lang_invalid"`
LangUpdatedTemplate string `json:"lang_updated_template"`
SubagentsNone string `json:"subagents_none"`
SessionsNone string `json:"sessions_none"`
UnsupportedAction string `json:"unsupported_action"`
SystemRewriteTemplate string `json:"system_rewrite_template"`
RuntimeCompactionNote string `json:"runtime_compaction_note"`
StartupCompactionNote string `json:"startup_compaction_note"`
AutonomyImportantKeywords []string `json:"autonomy_important_keywords"`
AutonomyCompletionTemplate string `json:"autonomy_completion_template"`
AutonomyBlockedTemplate string `json:"autonomy_blocked_template"`
}
type HeartbeatConfig struct {
@@ -324,9 +327,12 @@ func DefaultConfig() *Config {
SubagentsNone: "No subagents.",
SessionsNone: "No sessions.",
UnsupportedAction: "unsupported action",
SystemRewriteTemplate: "Rewrite the following internal system update in concise user-facing language:\n\n%s",
RuntimeCompactionNote: "[runtime-compaction] removed %d old messages, kept %d recent messages",
StartupCompactionNote: "[startup-compaction] removed %d old messages, kept %d recent messages",
SystemRewriteTemplate: "Rewrite the following internal system update in concise user-facing language:\n\n%s",
RuntimeCompactionNote: "[runtime-compaction] removed %d old messages, kept %d recent messages",
StartupCompactionNote: "[startup-compaction] removed %d old messages, kept %d recent messages",
AutonomyImportantKeywords: []string{"urgent", "重要", "付款", "payment", "上线", "release", "deadline", "截止"},
AutonomyCompletionTemplate: "✅ 已完成:%s\n下一步如需我继续处理后续直接回复“继续 %s”",
AutonomyBlockedTemplate: "⚠️ 任务受阻:%s\n原因%s\n建议回复“继续 %s”我会按当前状态重试。",
},
ContextCompaction: ContextCompactionConfig{
Enabled: true,

View File

@@ -138,6 +138,15 @@ func Validate(cfg *Config) []error {
errs = append(errs, fmt.Errorf("agents.defaults.texts.startup_compaction_note must contain two %%d placeholders"))
}
}
if len(texts.AutonomyImportantKeywords) == 0 {
errs = append(errs, fmt.Errorf("agents.defaults.texts.autonomy_important_keywords must contain at least one keyword"))
}
if strings.Count(strings.TrimSpace(texts.AutonomyCompletionTemplate), "%s") < 2 {
errs = append(errs, fmt.Errorf("agents.defaults.texts.autonomy_completion_template must contain two %%s placeholders"))
}
if strings.Count(strings.TrimSpace(texts.AutonomyBlockedTemplate), "%s") < 3 {
errs = append(errs, fmt.Errorf("agents.defaults.texts.autonomy_blocked_template must contain three %%s placeholders"))
}
if cfg.Agents.Defaults.ContextCompaction.Enabled {
cc := cfg.Agents.Defaults.ContextCompaction