From 95e659a9159fcae48f5f5f2370b169010a70a1f2 Mon Sep 17 00:00:00 2001 From: agent-bot Date: Tue, 29 Sep 2026 07:17:32 +0000 Subject: [PATCH] feat(assets): embedded prompt and xlsx asset fallback (U10a slice 2) - configs package embeds agents/ + shared/ prompt assets; templates package embeds xlsx/ (cdo_quote.xlsx) - template.GetAgentInstructionFile: disk-first with embedded fallback for instructions and writing.md (dev behavior unchanged) - quote.CalculateAndFill: embedded xlsx fallback when the on-disk template walk misses (single-binary deployments) - presence test guards the embedded asset set - verified: vet + template/quote/bizdev/squad/arch green --- configs/embed.go | 10 ++++++++++ internal/quote/quote.go | 15 +++++++++++++-- internal/template/loader.go | 16 ++++++++++++++-- internal/template/loader_test.go | 23 +++++++++++++++++++++++ templates/embed.go | 5 +++++ 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 configs/embed.go diff --git a/configs/embed.go b/configs/embed.go new file mode 100644 index 00000000..8c9dd9d4 --- /dev/null +++ b/configs/embed.go @@ -0,0 +1,10 @@ +// configs 包内嵌运行提示词资产(U10a:单文件应用无源码目录时回退使用)。 +// 磁盘路径存在时仍优先磁盘(开发态行为不变)。 +package configs + +import "embed" + +// Assets 内嵌 configs/agents 与 configs/shared 提示词。 +// +//go:embed agents shared +var Assets embed.FS diff --git a/internal/quote/quote.go b/internal/quote/quote.go index 51b32be3..d64e44a3 100644 --- a/internal/quote/quote.go +++ b/internal/quote/quote.go @@ -1,10 +1,13 @@ package quote import ( + "bytes" "fmt" "os" "path/filepath" + "strings" + "chat-agent-system/templates" "github.com/xuri/excelize/v2" ) @@ -112,8 +115,16 @@ func CalculateAndFill(in Input, templateXlsx string, outPath string) (*Summary, } } - f, err := excelize.OpenFile(templateXlsx) - if err != nil { + var f *excelize.File + if tf, err := excelize.OpenFile(templateXlsx); err == nil { + f = tf + } else if eb, eerr := templates.XlsxAssets.ReadFile(strings.TrimPrefix(filepath.ToSlash(templateXlsx), "templates/")); eerr == nil { + ef, oerr := excelize.OpenReader(bytes.NewReader(eb)) + if oerr != nil { + return nil, fmt.Errorf("open embedded template %s: %w", templateXlsx, oerr) + } + f = ef + } else { return nil, fmt.Errorf("open template %s: %w", templateXlsx, err) } defer f.Close() diff --git a/internal/template/loader.go b/internal/template/loader.go index c33ef283..f883f6c3 100644 --- a/internal/template/loader.go +++ b/internal/template/loader.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "chat-agent-system/configs" "chat-agent-system/internal/workflowdef" "chat-agent-system/templates" ) @@ -90,12 +91,23 @@ func GetAgentInstructionFile(rel string) (string, error) { path := filepath.Join(root, filepath.FromSlash(rel)) b, err := os.ReadFile(path) if err != nil { - return "", fmt.Errorf("read agent instruction file %s: %w", rel, err) + // U10a:磁盘缺失(单文件部署)时回退内嵌 configs 资产。 + eb, eerr := configs.Assets.ReadFile(strings.TrimPrefix(rel, "configs/")) + if eerr != nil { + return "", fmt.Errorf("read agent instruction file %s: %w", rel, err) + } + b = eb } inst := string(b) writingPath := filepath.Join(root, "configs", "shared", "writing.md") - if wb, err := os.ReadFile(writingPath); err == nil { + wb, werr := os.ReadFile(writingPath) + if werr != nil { + if eb, eerr := configs.Assets.ReadFile("shared/writing.md"); eerr == nil { + wb = eb + } + } + if wb != nil { inst += "\n\n" + string(wb) } return inst, nil diff --git a/internal/template/loader_test.go b/internal/template/loader_test.go index 8f22e7ad..0cb0f185 100644 --- a/internal/template/loader_test.go +++ b/internal/template/loader_test.go @@ -4,7 +4,9 @@ import ( "strings" "testing" + "chat-agent-system/configs" "chat-agent-system/internal/template" + "chat-agent-system/templates" ) func TestLoadDocTemplatesAndAgents(t *testing.T) { @@ -67,3 +69,24 @@ func TestGetDocTemplate_WorksWithoutDiskCheckout(t *testing.T) { } } } + +// TestEmbeddedPromptAssetsPresent 校验 U10a 内嵌资产齐备(单文件部署回退源)。 +// embed 在编译期捕获源码树,这里防止资产被意外移出嵌入范围。 +func TestEmbeddedPromptAssetsPresent(t *testing.T) { + for _, name := range []string{"agents/leader.md", "agents/engineer.md", "shared/writing.md"} { + b, err := configs.Assets.ReadFile(name) + if err != nil { + t.Fatalf("embedded configs asset %s: %v", name, err) + } + if len(b) == 0 { + t.Fatalf("embedded configs asset %s is empty", name) + } + } + xl, err := templates.XlsxAssets.ReadFile("xlsx/cdo_quote.xlsx") + if err != nil { + t.Fatalf("embedded xlsx asset: %v", err) + } + if len(xl) == 0 { + t.Fatal("embedded xlsx asset is empty") + } +} diff --git a/templates/embed.go b/templates/embed.go index ce0204a7..0e8402b1 100644 --- a/templates/embed.go +++ b/templates/embed.go @@ -7,3 +7,8 @@ import "embed" // //go:embed docs/*.md var DocTemplates embed.FS + +// XlsxAssets 内嵌运行 xlsx 资产(如 cdo_quote.xlsx);磁盘路径存在时优先磁盘。 +// +//go:embed xlsx +var XlsxAssets embed.FS -- 2.54.0