Files
HomeAgent/internal/agent/core/multimodal_departed_test.go
JianFeeeee 4e70b8d5d8 refactor(module): 核心仓 Go module 路径改为 github.com/JianFeeeee/HologramAgent
批 1,不含 SDK 仓与目录改名。

- go.mod 第 1 行 module 路径
- 366 个 .go 文件的完整前缀 github.com/JianFeeeee/HomeAgent 机械替换
  (857 处,仅完整前缀;展示名/注释里的 HomeAgent 字样留给批 5)
- 未改:third_party 目录名、SDK 自身 go.mod module、go.mod replace 目标、
  homed/waiter 可执行名、.hmap 后缀
2026-10-21 12:17:19 +08:00

139 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 结构性判据:多模态工具**已从内核退场**(2026-10-06)
//
// ## 为什么需要结构性判据
//
// 前面 plugin_test.go 验的是「插件侧行为对不对」。但这次改动的**要害**
// 不是插件能不能用,而是**内核不再持有这三个工具** ——
// 因为内核承诺零 IO,而它们要读本地文件。
//
// 行为判据抓不住这件事:哪怕把内核实现改回去,只要插件侧仍然工作,
// 功能测试照样全绿。而「内核能不能读文件」正是本仓第一原则的红线。
//
// ⇒ 这条判据直接扫源码:三个工具名不得出现在 internal/agent/core/ 的
//
// 实现文件里,也不得有 os.ReadFile 之类为它们服务。
package core
import (
"os"
"regexp"
"strings"
"testing"
)
// mediaToolNames 是已移出内核的三个工具。
var mediaToolNames = []string{"describe_image", "ocr_image", "transcribe_audio"}
// kernelImplFiles 内核的实现文件(不含 _test.go —— 判据自身要断言的东西不该自己触发)。
func kernelImplFiles(t *testing.T) []string {
t.Helper()
ents, err := os.ReadDir(".")
if err != nil {
t.Fatalf("读包目录: %v", err)
}
var out []string
for _, e := range ents {
n := e.Name()
if strings.HasSuffix(n, "_test.go") || !strings.HasSuffix(n, ".go") {
continue
}
out = append(out, n)
}
if len(out) < 5 {
t.Fatalf("只找到 %d 个实现文件,判据覆盖不足", len(out))
}
return out
}
// TestMediaToolsGoneFromKernel 钉住:内核实现里不再出现这三个工具名。
//
// ★ 这是本次改动的**核心不变量**。它对应 README 的第一原则:
//
// 内核零 IO,而这三个工具要读本地文件。
func TestMediaToolsGoneFromKernel(t *testing.T) {
for _, f := range kernelImplFiles(t) {
b, err := os.ReadFile(f)
if err != nil {
t.Fatalf("读 %s: %v", f, err)
}
src := string(b)
// 只看代码行:注释里可以说明「已移出」(那是给人看的)
for _, line := range strings.Split(src, "\n") {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "//") {
continue
}
for _, name := range mediaToolNames {
if !strings.Contains(line, name) {
continue
}
// 允许出现在「已移出」的说明性注释里(带 ★ 或 已移出 标记)
if strings.Contains(line, "已移出") || strings.Contains(line, "★") {
continue
}
t.Errorf("internal/agent/core/%s 仍出现 %q —— 它已移出为内置插件 multimodal:\n\t%s",
f, name, trimmed)
}
}
}
}
// TestKernelHasNoMediaFileReading 钉住:内核不得为多模态读本地文件。
//
// ★ 比「名字没出现」更强:任何人新写一个 os.ReadFile 来干同样的事,
//
// 这条也会红。
//
// 已知例外:context.go 的上下文窗口持久化(savePath)属**记忆子系统**,
// 已在允许清单里 —— 它不是多模态,且属于内核既有的职责。
func TestKernelHasNoMediaFileReading(t *testing.T) {
// 允许的内核磁盘读:上下文窗口持久化(记忆面)
allowed := map[string]bool{
"context.go": true, // RelevanceContext.savePath 的 load/save
}
readFile := regexp.MustCompile(`os\.(ReadFile|Open)\(`)
for _, f := range kernelImplFiles(t) {
if allowed[f] {
continue
}
b, err := os.ReadFile(f)
if err != nil {
t.Fatalf("读 %s: %v", f, err)
}
for i, line := range strings.Split(string(b), "\n") {
if readFile.MatchString(line) && !strings.HasPrefix(strings.TrimSpace(line), "//") {
t.Errorf("internal/agent/core/%s:%d 出现磁盘读取 %q —— "+
"内核承诺零 IO(多模态读文件已移到内置插件 multimodal)",
f, i+1, strings.TrimSpace(line))
}
}
}
}
// TestMediaToolsRegisteredByPlugin 确认能力没丢:插件侧确实注册了这三个工具。
//
// ★ 与上一条成对:「内核不该有」+「插件要有」= 迁移而不是删除。
//
// 只断言前者会放过「直接删掉功能」这种退化。
func TestMediaToolsRegisteredByPlugin(t *testing.T) {
// 插件侧的注册清单在 internal/plugins/multimodal/plugin.go。
// 这里只做**跨包引用检查**(core 包不该 import multimodal,那是反向依赖)。
if strings.Contains(kernelSourceDump(t), `"github.com/JianFeeeee/HologramAgent/internal/plugins/multimodal"`) {
t.Error("core 反向 import 了 multimodal 插件 —— " +
"工具由插件注册进 StageHost,核心不应知道插件实现")
}
}
func kernelSourceDump(t *testing.T) string {
t.Helper()
var sb strings.Builder
for _, f := range kernelImplFiles(t) {
b, err := os.ReadFile(f)
if err != nil {
t.Fatalf("读 %s: %v", f, err)
}
sb.Write(b)
}
return sb.String()
}