feat(backthink): 思考轮工具面补齐(裁决 15)—— 能力面接入 + 只读 capability_list + 本轮状态快照
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 缺口 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 同惯例)
This commit is contained in:
AskaEth committed 2026-10-05 13:54:57 +08:00
1 parent 34cef95e17
commit 321cc3b763
14 files changed
+1664 -73

No files matched your search

+11 -4
View File
@@ -7,12 +7,14 @@ import (
)
// allTools is a representative registration-order tool list for tests; it covers
// the basic/io/terminal plugins actually shipped by the repo.
// the basic/io/terminal plugins actually shipped by the repo, plus the two tools
// added to the built-in state sets by X1 (transcode / capability_list).
func allTools() []string {
return []string{
"terminal", "file", "knowledge", "text",
"web-search", "web-fetch", "http",
"calculator", "datetime", "random", "json", "markdown",
transcodeToolName, capabilityListToolName,
}
}
@@ -175,13 +177,18 @@ func TestDecide_PersonaMinConfidence_ForcesQuiet(t *testing.T) {
func TestDecide_AllowedSets(t *testing.T) {
d := New(activeConfig())
// 内置档位集里的「后加能力」也必须在允许集内(X1 / 裁决 15):登记了能力
// 却不在集合里 = CAPACITY_ENABLED=true 时它调不动。
extra := []string{transcodeToolName, capabilityListToolName}
quiet := d.Decide(context.Background(), in("你好")).AllowedTools
if !reflect.DeepEqual(quiet, []string{"terminal", "file", "knowledge", "text"}) {
t.Fatalf("quiet allowed = %v", quiet)
wantQuiet := append([]string{"terminal", "file", "knowledge", "text"}, extra...)
if !reflect.DeepEqual(quiet, wantQuiet) {
t.Fatalf("quiet allowed = %v, want %v", quiet, wantQuiet)
}
awake := d.Decide(context.Background(), in("帮我查一下资料")).AllowedTools
wantAwake := []string{"terminal", "file", "knowledge", "text", "web-search", "web-fetch", "http"}
wantAwake := append([]string{"terminal", "file", "knowledge", "text", "web-search", "web-fetch", "http"}, extra...)
if !reflect.DeepEqual(awake, wantAwake) {
t.Fatalf("awake allowed = %v, want %v", awake, wantAwake)
}
+23 -2
View File
@@ -7,17 +7,38 @@ import (
"strings"
)
// 内置档位集里「后加的能力」的工具名(字面量,与既有条目同一口径:
// 这两份集合是**策略清单**,不反向依赖具体能力包)。两处都登记是 X1 的
// 硬要求 —— 能力注册了却不在允许集里,等于 CAPACITY_ENABLED=true 时它调不动:
//
// - "transcode"(internal/capability/transcode,design/25):纯本地、有输入上限
// 与并发闸,属陪伴侧真正会用的能力(语音转写);
// - "capability_list"(internal/capability/registry 的 ToolName,X1 / 裁决 15):
// 她自查「我有哪些能力/插件、外围子系统什么状态」的**只读**抓手,必须在她
// 思考时所处的静息态就可见,否则这条裁决在安静态失效。
//
// 重命名任一个 ⇒ internal/runtime/capacity/capacity_test.go 的
// TestDecide_AllowedSets 与 cmd/cyrene 的组合根断言同时变红(见 X1 报告变异表)。
const (
transcodeToolName = "transcode"
capabilityListToolName = "capability_list"
)
// defaultQuietTools is the companion-safe face (design §1): terminal + file edit
// + memory/emotion companion. It is a function building the slice fresh so no
// package-level global slice is introduced (project rule §10).
func defaultQuietTools() []string {
return []string{"terminal", "file", "knowledge", "text"}
return []string{"terminal", "file", "knowledge", "text", transcodeToolName, capabilityListToolName}
}
// defaultAwakeTools extends the quiet face with web/network retrieval (design
// §1 唤醒态): a real task wakes the retrieval tools, file and terminal.
func defaultAwakeTools() []string {
return []string{"terminal", "file", "knowledge", "text", "web-search", "web-fetch", "http"}
return []string{
"terminal", "file", "knowledge", "text",
"web-search", "web-fetch", "http",
transcodeToolName, capabilityListToolName,
}
}
// intent classifies the round. It runs L1 on the current turn, falls to L2 on