Each task includes: - Scope: what to build - Implementation: code sketches + details - Verification: concrete test criteria - Done criteria: acceptance checklist
3.8 KiB
3.8 KiB
T0.5: LLM Agents & Prompts
Scope
Implement LLM activities (Planner, Judge, Implementer), LLM client, and prompt template registry.
Implementation
File: action/llm/client.go
type AnthropicClient struct {
apiKey string
}
func NewClient() *AnthropicClient
// Read ANTHROPIC_API_KEY from env
// Return client
func (c *AnthropicClient) CreateMessage(ctx context.Context, in MessageInput) (string, error)
// Call Anthropic API messages.create
// Respect model.ModelID, model.Thinking, model.Effort
// Return response text
File: action/planner.go
type PlanningInput struct {
Config OrchestratorConfig
BoardState string // JSON or markdown of task board
Milestone string
}
type TaskDispatch struct {
TaskID string
Prompt PromptSpec
BaseTimeout time.Duration // can override default
}
func PlanningActivity(ctx context.Context, in PlanningInput) ([]TaskDispatch, error)
// Read target repo's tasks/INDEX.md + board from shared FS
// Render prompt: in.Config.SystemPrompt + in.Config.RolePrompts["planner"] template
// Call LLM (Planner model)
// Parse response: which tasks to dispatch next, optional tuning overrides
// Return task dispatch list
File: action/judge.go
type JudgeInput struct {
Config OrchestratorConfig
Diff string // git diff output
IntegrationTestLogs string // test output
}
type JudgeOutput struct {
Verdict string // "pass" or "fail"
Critique string // explanation if fail
}
func JudgeActivity(ctx context.Context, in JudgeInput) (JudgeOutput, error)
// Render prompt: in.Config.SystemPrompt + in.Config.RolePrompts["judge"]
// Call LLM (Judge model, reasoning)
// Parse response: verdict + critique
// Return JudgeOutput
File: action/implementer.go
type ImplementerInput struct {
Config OrchestratorConfig
TaskID string
WorktreePath string
Lessons string // "known errors — do not repeat" section
}
type ImplementerOutput struct {
Success bool
Changes string // summary of changes made
}
func ImplementerActivity(ctx context.Context, in ImplementerInput) (ImplementerOutput, error)
// Render prompt: in.Config.SystemPrompt + in.Config.RolePrompts["implementer"]
// Inject in.Lessons into Variables
// Start tool-call agent loop (run git/cargo/pnpm/etc as needed)
// After each tool call, activity.RecordHeartbeat(ctx, progress)
// Return success/changes
File: prompts/registry.go
// go:embed prompts/*.tmpl
func Render(templateRef string, variables map[string]any) (string, error)
// Load embedded template via go:embed + text/template
// Render with variables
// Return rendered string
Files: prompts/planner/default.tmpl, etc.
Empty templates for now; will be filled in by Planner/Judge/Implementer activities.
You are a Planner agent. Your job: reconcile task state, dispatch work.
System prompt: {{.SystemPrompt}}
Current board:
{{.BoardState}}
Current config:
{{.Config | json}}
What tasks should we dispatch next? (respond in JSON: {"tasks": [{"id": "T0.1", "timeout_override_ms": null}, ...]})
Verification
cd /Users/rockliang/workplace/Poimen/workflows
go test -v ./tests -run TestPrompts
# Test file: tests/prompts_test.go
Test cases:
- Render template with system prompt + variables → output includes system prompt prefix
- Render with RawTemplate override → uses raw template, not embedded
- Render with Variables substitution → all {{.Var}} replaced
- Mock LLM client responses → activities parse correctly
Done Criteria
go test ./tests -run TestPromptspasses- All templates render without errors
- LLM client reads ANTHROPIC_API_KEY from env (or uses mock in tests)
- Activities parse LLM responses into structured output