feat: enhance ProjectInstructionProvider to read from AGENTS.md and update default instruction logic
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package factory
|
package factory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
einoadapter "cs-agent/internal/ai/runtime/internal/impl/adapter"
|
einoadapter "cs-agent/internal/ai/runtime/internal/impl/adapter"
|
||||||
@@ -8,16 +10,57 @@ import (
|
|||||||
"cs-agent/internal/pkg/toolx"
|
"cs-agent/internal/pkg/toolx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProjectInstructionProvider struct{}
|
type ProjectInstructionProvider struct {
|
||||||
|
fileName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 这个要读取AGENTS.md文件,后面考虑还要不要
|
||||||
func NewProjectInstructionProvider() *ProjectInstructionProvider {
|
func NewProjectInstructionProvider() *ProjectInstructionProvider {
|
||||||
return &ProjectInstructionProvider{}
|
return &ProjectInstructionProvider{fileName: "AGENTS.md"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProjectInstructionProvider) Resolve() string {
|
func (p *ProjectInstructionProvider) Resolve() string {
|
||||||
|
if text := p.loadFromFile(); text != "" {
|
||||||
|
return text
|
||||||
|
}
|
||||||
return strings.TrimSpace(DefaultProjectInstruction)
|
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{}
|
type ToolAppendixProvider struct{}
|
||||||
|
|
||||||
func NewToolAppendixProvider() *ToolAppendixProvider {
|
func NewToolAppendixProvider() *ToolAppendixProvider {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,7 @@ package factory
|
|||||||
|
|
||||||
// DefaultProjectInstruction 为当前项目统一注入的全局项目规则。
|
// DefaultProjectInstruction 为当前项目统一注入的全局项目规则。
|
||||||
//
|
//
|
||||||
// 当前阶段不再从文件系统读取 AGENTS.md,也不依赖 SystemConfig;
|
// 默认回退到内置常量;运行时优先由 ProjectInstructionProvider 读取仓库中的 AGENTS.md。
|
||||||
// 所有客服 Agent 运行时默认使用本常量作为项目级规则来源。
|
|
||||||
// TODO 这个内容不合适,需要重新整理内容,内容需要面向客服系统
|
// TODO 这个内容不合适,需要重新整理内容,内容需要面向客服系统
|
||||||
const DefaultProjectInstruction = `# AGENTS.md
|
const DefaultProjectInstruction = `# AGENTS.md
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user