- 缺口 d:状态在**思考前**读一次,同一份快照既喂思考轮又喂 Decide(读不到 = Known=false,绝不伪装成中性状态) - 缺口 c:新增只读工具 capability_list(owner-only、三重输出边界、读写隔离),她可自查当下真实可用面 - 缺口 b:内置允许集补 transcode / capability_list(此前 CAPACITY_ENABLED=true 时新工具会从模型可见面消失) - 缺口 e:guidancePrompt 旧文案承诺的「IoT/日历/提醒」在全仓根本没有对应工具(chat 侧也没有)⇒ 按诚实化改为「与主人对话同等工具面 + 指名自查入口」,不再有空头支票 - (a) 未做且**不自创**:那三把工具无任何设计规格(名字/schema/读写边界/输出上限)⇒ 已报回并登记进 design/20(裁决:走能力/插件/MCP 暴露路线) - 变异实测:允许集去掉新工具 ⇒ TestDecide_AllowedSets + 组合根跨层测试红;丢掉状态快照 ⇒ TestLoopThinker_Think_InjectsRoundStateBlock 红 - 已知弱项(如实登记):组合根那行 wireCapabilitySurface() 调用本身无测试覆盖(与既有 wireTranscode 同惯例)
53 lines
2.2 KiB
Go
53 lines
2.2 KiB
Go
package backthink
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||
)
|
||
|
||
// LoopThinker 把 *loop.Loop 适配为 Thinker。它是生产接线,
|
||
// 让一次自主后台轮驱动完整的工具循环(记忆
|
||
// 注入、模型步骤、工具调用),同时看到主会话的最近对话并在 owner 环境下运行。
|
||
//
|
||
// 它包的就是**主人对话所用的那一个 loop**(组合根传 a.loop),因此「与 chat 同一套
|
||
// 工具注册表」不是承诺而是结构事实:两侧共用同一个 dispatcher/registry。
|
||
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)。
|
||
//
|
||
// 本轮的用户状态快照由服务经 ctx 挂上(WithRoundState,X1 缺口 d):
|
||
// 读到时它被渲染成引导提示词末尾的状态块,与随后的打扰决策同一份;
|
||
// 未挂载时输入逐字与接线前一致。未注入 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, RoundStateFrom(ctx))
|
||
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
|
||
}
|