package backthink import ( "context" "fmt" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop" ) // LoopThinker 把 *loop.Loop 适配为 Thinker。它是生产接线, // 让一次自主后台轮驱动完整的工具循环(记忆 // 注入、模型步骤、IoT/日历/提醒等工具调用),同时看到 // 主会话的最近对话并在 owner 环境下运行。 type LoopThinker struct { loop *loop.Loop recent recentProvider } // NewLoopThinker 把 l 包装成基于给定最近消息提供者的 Thinker // (生产中是 *history.Store;nil 会让思考降级为仅用引导提示词, // 见 mainContext)。 func NewLoopThinker(l *loop.Loop, recent recentProvider) *LoopThinker { return &LoopThinker{loop: l, recent: recent} } // Think 经被包装的 loop 跑一轮无人值守思考并返回 // 结论文本。它继承主会话的最近对话作为输入 // 上下文,并注入 owner 会话环境(MemWritable=true、ActorOwner、 // 完整工具集),因此该次思考能读写记忆并调用工具。loop 的 // Run 会话 id 是本服务自己的(不是主会话 id),所以该次思考绝不会 // 与活跃主会话的持久化检查点存储冲突(design/20)。 // 未注入 loop 时返回 ErrNoLoop。 func (t *LoopThinker) Think(ctx context.Context, sessionID, trigger string) (string, error) { if t.loop == nil { return "", ErrNoLoop } recent := mainContext(ctx, t.recent) messages := thinkInput(recent, trigger) envCtx := loop.WithSessionEnv(ctx, loop.OwnerEnv()) // 静音 UI 广播:后台反思不得把其中间 // 推理泄漏进面向用户的 chat 流(它只需要结论)。 res, err := t.loop.Run(loop.WithMutedBroadcast(envCtx), sessionID, messages) if err != nil { return "", fmt.Errorf("backthink: think: %w", err) } return res.Text, nil }