宿主环境感知(INV-ENV)

wesgine 在启动时自动感知宿主机的环境信息(安装的工具、语言运行时版本、操作系统特征等),并将这些信息注入到 Agent 的系统提示词中作为 L1 环境层记忆。


核心问题

不同的 Cell 运行在同一台机器上——Python 版本、Go 版本、git 路径、已安装的 npm 包对所有 Cell 都是一样的。如果每个 Cell 各感知一遍,N 个 Cell 的进程要付 N 倍字面相同的代价,而这些 fork 全落在 Cool→Warm 的 100-500ms 预算上。


共享感知门

memenv.SharedSnapshot(ctx)    → 记忆环境快照(进程级 single-flight + 5min TTL)
capenv.SharedSurface(ctx)     → 能力环境表面(进程级 single-flight + 5min TTL)

INV-ENV-01:每进程感知一次

每个包只有 shared.go 一个构造点,包外任何 alias 都不得构造(含测试)。闸门 38-host-env-shared.sh。

INV-ENV-02:感知器不认识租户

internal/memory/env 与 internal/govern/capability/env 不得出现 CellID / Actor / Tenant。

如果快照能认租户,进程级共享就从优化变成串租。per-Cell 事实层叠在消费者上:Loader.WithRuntimeOverrides。

INV-ENV-03:不 fork 回答"在不在 PATH 上"

// ❌ 禁止
out, _ := exec.Command("command", "-v", "git").Output()

// ✅ 正确
path, err := exec.LookPath("git")

探测预算

层预算
环境快照 envSeedBudget15s
能力表面 capabilitySurfaceBudget25s
Provider 探测 providerProbeBudget10s

超时与缓存


共享门不收 per-caller 参数

// ❌ 禁止:竞态赢家的参数生效,其余静默丢弃
SharedSnapshot(ctx, mySensor)

// ✅ 正确:缓存自己拥有那趟感知
sharedSense.sense(ctx)

两个调用方传不同 sensor 得到同一答案且无从知道是哪个——这不是正确性问题,而是可观察的错误:第二个调用方拿到第一个的答案但认为是自己的。


EnvSnapshot 深拷贝

EnvSnapshot.clone() 必须逐字段深拷每个 map / slice——共享缓存的唯一防线是调用方拿到副本。

// 测试用反射校验
TestEnvSnapshot_CloneDeepCopiesEveryReferenceField
// 带 vacuity guard:反射一个字段都没查到时同样失败

层叠:per-Cell 事实

共享快照只是基线。per-Cell 的事实(比如 bundled Python 版本)通过 Loader.WithRuntimeOverrides 覆盖:

loader.WithRuntimeOverrides(map[string]string{
    "python_version": cell.BundledPythonVersion,
})

这些覆盖在消费者上,不在感知器里。


反模式速查

#反模式正确做法
290N 个 Cell 各感知一遍共享门 single-flight + TTL
291感知器下沉 CellID/Actor层叠在消费者上
292command -v / which fork 40 次exec.LookPath
293共享门收 per-caller sensor缓存自己拥有感知
294固定超时不看父截止min(自身预算, 父 ctx 剩余)
295clone() 漏拷 map/slice逐字段深拷 + 反射校验

各产品用法

产品场景
wescode感知 Go/Node/Python 版本、git 路径 → 注入编程 Agent 上下文
wesclaw感知系统信息 → 助手知道用户环境
wescraft感知文件工具 → 文件智能引擎选择解析器
企业版所有部门 Cell 共享同一台服务器的环境信息