mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-27 21:03:16 +00:00
上一提交(2232d54)把并发安全改成了声明式,但内置工具那一路仍是将就:
声明靠往 required 变参里塞字符串 "toolParallel" 传递。
## 为什么那不算声明式
对照 SDK 的 NoMemory 逐条看:
| | SDK NoMemory | 当时的内置工具 |
|---|---|---|
| 载体 | `ToolDef.NoMemory` 字段 | required 里的字符串 |
| 拼错后果 | 编译器报错 | **静默失效** |
| 内核读取 | 查结构体字段 | 遍历工具表 + 解析字符串 |
"少一个工具能并发"恰恰是最难察觉的一类问题 —— 没有任何报错,
只是并行的批悄悄退化成串行。
## 改法
### 1. sdk.BuiltinToolDef 补声明项(与 NoMemory 同构)
```go
type BuiltinToolDef struct {
Name, Description string
Parameters map[string]interface{}
ParallelSafe bool // 零值 false = 默认串行(保守)
Serial bool // 优先于 ParallelSafe
}
func (d BuiltinToolDef) ConcurrencySafe() bool { return d.ParallelSafe && !d.Serial }
func (d BuiltinToolDef) ToSchema() map[string]interface{}
```
### 2. 工具定义处声明
```go
toolDef("memory_merge", ...) // 默认串行
toolDefWith("knowledge_search", ..., []string{"query"}, parallelOpts()) // 已核实只读
```
### 3. 内核一次聚合并缓存(照 StageHost.NoMemoryToolNames)
```go
graphOf() // 快照
declareParallelTool(name) // init 里登记
concurrencySafeOf(name) // 查表
```
不再每次 toolParallelSafe 都重跑 buildToolDefs()(O(工具数) 重复劳动,
而声明是静态的)。
## ★ 一个更隐蔽的问题:声明表曾经是空的
`declareParallelTool` 最初挂在 `toolDefWith` 的**运行时调用**上。而那 9 个
工具全在 `if a.knowledge != nil` / `if a.social != nil` / `if a.parentID != ""`
之类的条件分支里 —— 测试环境根本不走进这些分支 ⇒ 聚合表始终为空。
而判据查的是同一张表,于是**自证通过**:全绿,并发能力为零。
这就是判据设计的教训 —— 判据和数据源同源时,它证明的只是"我和我一致"。
现在判据双向核对:名单里的必须真声明了,声明了不在名单里的也会报出来;
并额外验证内核**真的读得到**(concurrencySafeOf 而非读同一份 map)。
## 顺带修掉的迁移事故
用正则批量改造 30+ 个 toolDef 调用点时,把 `person_set_trait("name", "content")`
这类**变参**调用误改成 toolDefWith(... "name", "content") —— 那是**写工具**,
差点被标成可并发。已全部回退并逐一核对:9 个声明并发,0 误伤。