feat: coordinator mode — system prompt injection for orchestration tasks

This commit is contained in:
2026-04-05 23:06:23 +02:00
parent dd9f4e390a
commit 8d3d498245
2 changed files with 65 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
package engine
import (
"strings"
"testing"
"somegit.dev/Owlibou/gnoma/internal/router"
)
func TestCoordinatorSystemPrompt_InjectedForOrchestration(t *testing.T) {
prompt := coordinatorPrompt()
if !strings.Contains(prompt, "spawn_elfs") {
t.Error("coordinator prompt must mention spawn_elfs")
}
if !strings.Contains(prompt, "list_results") {
t.Error("coordinator prompt must mention list_results")
}
}
func TestShouldInjectCoordinatorPrompt(t *testing.T) {
cases := []struct {
prompt string
want bool
}{
{"orchestrate the migration", true},
{"coordinate the refactor", true},
{"dispatch tasks to elfs", true},
{"fix the bug in main.go", false},
{"explain this function", false},
{"write unit tests for auth", false},
}
for _, c := range cases {
task := router.ClassifyTask(c.prompt)
got := task.Type == router.TaskOrchestration
if got != c.want {
t.Errorf("prompt %q: want orchestration=%v, got %v (type=%s)", c.prompt, c.want, got, task.Type)
}
}
}

View File

@@ -301,9 +301,35 @@ func (e *Engine) buildRequest(ctx context.Context) provider.Request {
)
}
// Inject coordinator guidance for orchestration tasks
if e.cfg.Router != nil {
prompt := ""
for i := len(e.history) - 1; i >= 0; i-- {
if e.history[i].Role == message.RoleUser {
prompt = e.history[i].TextContent()
break
}
}
if router.ClassifyTask(prompt).Type == router.TaskOrchestration {
req.SystemPrompt = coordinatorPrompt() + "\n\n" + req.SystemPrompt
}
}
return req
}
// coordinatorPrompt returns the system prompt block injected for orchestration tasks.
func coordinatorPrompt() string {
return `You are operating in coordinator mode. Your role is to decompose complex work into parallel tasks and orchestrate elfs.
Rules:
- Use spawn_elfs to dispatch N tasks in parallel when they don't share write state.
- Use list_results to discover outputs produced by prior tool calls in this session.
- Pass result file paths to elfs in their prompts so they can read prior outputs with read_result or fs.read.
- Writes are serial: if two elfs would write the same file, sequence them.
- Synthesize elf outputs into a coherent final answer.`
}
func (e *Engine) executeTools(ctx context.Context, calls []message.ToolCall, cb Callback) ([]message.ToolResult, error) {
// Partition into read-only (parallel) and write (serial) batches
type toolCallWithTool struct {