feat(agent): wire AGENTS.md discovery into the agent loop's system prompt
Export persona.DiscoverAgentsMD and add agent.Config.AgentsMD so project and global AGENTS.md rules actually reach the model. Previously buildInitialMessages always called AssembleSystemPrompt with an empty string, so no AGENTS.md content was ever injected despite the discovery logic already existing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
eeefe07809
commit
0f835a0802
4 changed files with 31 additions and 5 deletions
|
|
@ -42,6 +42,7 @@ type Config struct {
|
|||
OnIteration OnIterationHook
|
||||
ToolTimeout time.Duration
|
||||
ChatTemplateKwargs map[string]any // passed to the LLM provider (e.g. Qwen enable_thinking)
|
||||
AgentsMD string // discovered AGENTS.md content, folded into the system prompt
|
||||
}
|
||||
|
||||
// Iteration represents a single cycle of the agent loop.
|
||||
|
|
@ -230,7 +231,7 @@ func (l *Loop) RunStream(ctx context.Context, input string, history ...llm.Messa
|
|||
}
|
||||
|
||||
func (l *Loop) buildInitialMessages(input string, history []llm.Message) []llm.Message {
|
||||
systemPrompt := persona.AssembleSystemPrompt(l.cfg.Persona, "")
|
||||
systemPrompt := persona.AssembleSystemPrompt(l.cfg.Persona, l.cfg.AgentsMD)
|
||||
messages := make([]llm.Message, 0, len(history)+2)
|
||||
messages = append(messages, llm.Message{Role: llm.RoleSystem, Content: systemPrompt})
|
||||
messages = append(messages, history...)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"iter"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -116,6 +117,30 @@ func TestRun_NoToolCalls(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRun_IncludesAgentsMD(t *testing.T) {
|
||||
var capturedSystemPrompt string
|
||||
mockClient := &mockLLM{
|
||||
generateFunc: func(ctx context.Context, req llm.CompletionRequest) (llm.CompletionResponse, error) {
|
||||
capturedSystemPrompt = req.Messages[0].Content
|
||||
return llm.CompletionResponse{Content: "done"}, nil
|
||||
},
|
||||
}
|
||||
|
||||
loop := New(Config{
|
||||
LLM: mockClient,
|
||||
Persona: persona.DefaultPersona(),
|
||||
Tools: tools.NewRegistry(),
|
||||
AgentsMD: "Never edit go.mod directly.",
|
||||
})
|
||||
|
||||
if _, err := loop.Run(context.Background(), "Hello"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(capturedSystemPrompt, "Never edit go.mod directly.") {
|
||||
t.Errorf("expected system prompt to include AGENTS.md content, got %q", capturedSystemPrompt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun_ToolCalls(t *testing.T) {
|
||||
registry := tools.NewRegistry()
|
||||
registry.Register(tools.Tool{
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ func AssembleSystemPrompt(p Persona, agentsMD string) string {
|
|||
return strings.Join(parts, "\n\n")
|
||||
}
|
||||
|
||||
// discoverAgentsMD walks up the directory tree looking for AGENTS.md files.
|
||||
func discoverAgentsMD(root string) string {
|
||||
// DiscoverAgentsMD walks up the directory tree looking for AGENTS.md files.
|
||||
func DiscoverAgentsMD(root string) string {
|
||||
var parts []string
|
||||
current := root
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ func TestDiscoverAgentsMD(t *testing.T) {
|
|||
agentsPath := filepath.Join(tmpDir, "AGENTS.md")
|
||||
os.WriteFile(agentsPath, []byte("test instructions"), 0644)
|
||||
|
||||
result := discoverAgentsMD(tmpDir)
|
||||
result := DiscoverAgentsMD(tmpDir)
|
||||
if !contains(result, "test instructions") {
|
||||
t.Error("expected discoverAgentsMD to find AGENTS.md")
|
||||
t.Error("expected DiscoverAgentsMD to find AGENTS.md")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue