diff --git a/internal/ai/runtime/internal/impl/factory/instruction_providers.go b/internal/ai/runtime/internal/impl/factory/instruction_providers.go index be4b521..ce3427c 100644 --- a/internal/ai/runtime/internal/impl/factory/instruction_providers.go +++ b/internal/ai/runtime/internal/impl/factory/instruction_providers.go @@ -1,6 +1,8 @@ package factory import ( + "os" + "path/filepath" "strings" einoadapter "cs-agent/internal/ai/runtime/internal/impl/adapter" @@ -8,16 +10,57 @@ import ( "cs-agent/internal/pkg/toolx" ) -type ProjectInstructionProvider struct{} +type ProjectInstructionProvider struct { + fileName string +} +// TODO 这个要读取AGENTS.md文件,后面考虑还要不要 func NewProjectInstructionProvider() *ProjectInstructionProvider { - return &ProjectInstructionProvider{} + return &ProjectInstructionProvider{fileName: "AGENTS.md"} } func (p *ProjectInstructionProvider) Resolve() string { + if text := p.loadFromFile(); text != "" { + return text + } return strings.TrimSpace(DefaultProjectInstruction) } +func (p *ProjectInstructionProvider) loadFromFile() string { + path := p.resolvePath() + if path == "" { + return "" + } + data, err := os.ReadFile(path) + if err != nil { + return "" + } + return strings.TrimSpace(string(data)) +} + +func (p *ProjectInstructionProvider) resolvePath() string { + fileName := "AGENTS.md" + if p != nil && strings.TrimSpace(p.fileName) != "" { + fileName = strings.TrimSpace(p.fileName) + } + wd, err := os.Getwd() + if err != nil { + return "" + } + dir := wd + for { + candidate := filepath.Join(dir, fileName) + if stat, statErr := os.Stat(candidate); statErr == nil && !stat.IsDir() { + return candidate + } + parent := filepath.Dir(dir) + if parent == dir { + return "" + } + dir = parent + } +} + type ToolAppendixProvider struct{} func NewToolAppendixProvider() *ToolAppendixProvider { diff --git a/internal/ai/runtime/internal/impl/factory/instruction_providers_test.go b/internal/ai/runtime/internal/impl/factory/instruction_providers_test.go new file mode 100644 index 0000000..e77d473 --- /dev/null +++ b/internal/ai/runtime/internal/impl/factory/instruction_providers_test.go @@ -0,0 +1,48 @@ +package factory + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestProjectInstructionProviderResolveFromAgentsFile(t *testing.T) { + tmpDir := t.TempDir() + nestedDir := filepath.Join(tmpDir, "nested", "child") + if err := os.MkdirAll(nestedDir, 0o755); err != nil { + t.Fatalf("mkdir failed: %v", err) + } + content := "# AGENTS.md\n\nfrom temp file" + if err := os.WriteFile(filepath.Join(tmpDir, "AGENTS.md"), []byte(content), 0o644); err != nil { + t.Fatalf("write file failed: %v", err) + } + wd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd failed: %v", err) + } + defer func() { _ = os.Chdir(wd) }() + if err := os.Chdir(nestedDir); err != nil { + t.Fatalf("chdir failed: %v", err) + } + got := NewProjectInstructionProvider().Resolve() + if !strings.Contains(got, "from temp file") { + t.Fatalf("expected provider to load AGENTS.md from file, got: %s", got) + } +} + +func TestProjectInstructionProviderFallbacksToDefault(t *testing.T) { + tmpDir := t.TempDir() + wd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd failed: %v", err) + } + defer func() { _ = os.Chdir(wd) }() + if err := os.Chdir(tmpDir); err != nil { + t.Fatalf("chdir failed: %v", err) + } + got := NewProjectInstructionProvider().Resolve() + if !strings.Contains(got, "本文件定义本项目内 AI Agent 的强制开发规则") { + t.Fatalf("expected fallback project instruction, got: %s", got) + } +} diff --git a/internal/ai/runtime/internal/impl/factory/project_instruction.go b/internal/ai/runtime/internal/impl/factory/project_instruction.go index 97f3528..65e6522 100644 --- a/internal/ai/runtime/internal/impl/factory/project_instruction.go +++ b/internal/ai/runtime/internal/impl/factory/project_instruction.go @@ -2,8 +2,7 @@ package factory // DefaultProjectInstruction 为当前项目统一注入的全局项目规则。 // -// 当前阶段不再从文件系统读取 AGENTS.md,也不依赖 SystemConfig; -// 所有客服 Agent 运行时默认使用本常量作为项目级规则来源。 +// 默认回退到内置常量;运行时优先由 ProjectInstructionProvider 读取仓库中的 AGENTS.md。 // TODO 这个内容不合适,需要重新整理内容,内容需要面向客服系统 const DefaultProjectInstruction = `# AGENTS.md