Unified Agent + reusable Go agent core.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add template-based system prompt rendering

Lyric e10cc5e9 8c78c53f

+103 -1
+8
agent/prompt.go
··· 45 45 } 46 46 47 47 func BuildSystemPrompt(registry *tools.Registry, spec PromptSpec) string { 48 + rendered, err := renderSystemPrompt(registry, spec) 49 + if err == nil && strings.TrimSpace(rendered) != "" { 50 + return rendered 51 + } 52 + return buildSystemPromptLegacy(registry, spec) 53 + } 54 + 55 + func buildSystemPromptLegacy(registry *tools.Registry, spec PromptSpec) string { 48 56 var b strings.Builder 49 57 b.WriteString(spec.Identity) 50 58 if len(spec.Blocks) > 0 {
+60
agent/prompts/system.tmpl
··· 1 + {{.Identity}} 2 + {{if .Blocks}} 3 + 4 + ## Skills & Context 5 + Skills are not tools. They provide extra context and may include scripts to run via tools like bash. 6 + {{range .Blocks}} 7 + 8 + ### {{.Title}} 9 + {{.Content}} 10 + {{end}}{{end}} 11 + 12 + ## Available Tools 13 + {{.ToolSummaries}} 14 + 15 + ## Response Format 16 + 17 + {{if .HasPlanCreate}}When not calling tools, you MUST respond with JSON in one of two formats: 18 + 19 + ### Option 1: Plan 20 + ```json 21 + { 22 + "type": "plan", 23 + "plan": { 24 + "thought": "brief reasoning (optional)", 25 + "summary": "1-2 sentence overview", 26 + "steps": [ 27 + {"step": "step 1", "status": "in_progress"}, 28 + {"step": "step 2", "status": "pending"}, 29 + {"step": "step 3", "status": "pending"} 30 + ], 31 + "risks": ["optional"], 32 + "questions": ["optional clarifying questions"], 33 + "completion": "what done looks like (optional)" 34 + } 35 + } 36 + ``` 37 + 38 + ### Option 2: Final 39 + {{else}} 40 + When not calling tools, you MUST respond with JSON in the following format: 41 + 42 + ### Final 43 + {{end}}```json 44 + { 45 + "type": "final", 46 + "final": { 47 + "thought": "brief reasoning", 48 + "output": "your final answer" 49 + } 50 + } 51 + ``` 52 + 53 + {{if .Rules}} 54 + ## Rules 55 + {{range .Rules}} 56 + {{.}} 57 + 58 + {{end}} 59 + {{end}} 60 +
+5 -1
docs/prompt.md
··· 21 21 ### 1) Base PromptSpec 22 22 23 23 - File: `agent/prompt.go` 24 + - Template/Renderer: 25 + - `agent/prompts/system.tmpl` 26 + - `agent/prompt_template.go` 27 + - `internal/prompttmpl/prompttmpl.go` 24 28 - Definitions: 25 29 - `DefaultPromptSpec()`: base `Identity` and `Rules` 26 - - `BuildSystemPrompt(...)`: renders identity, blocks, available tools, response schema, and rules 30 + - `BuildSystemPrompt(...)`: renders identity, blocks, available tools, response schema, and rules (template-driven, with legacy fallback) 27 31 28 32 ### 2) Persona identity injection 29 33
+30
internal/prompttmpl/prompttmpl.go
··· 1 + package prompttmpl 2 + 3 + import ( 4 + "bytes" 5 + "text/template" 6 + ) 7 + 8 + func Parse(name, source string, funcs template.FuncMap) (*template.Template, error) { 9 + t := template.New(name).Option("missingkey=error") 10 + if funcs != nil { 11 + t = t.Funcs(funcs) 12 + } 13 + return t.Parse(source) 14 + } 15 + 16 + func MustParse(name, source string, funcs template.FuncMap) *template.Template { 17 + t, err := Parse(name, source, funcs) 18 + if err != nil { 19 + panic(err) 20 + } 21 + return t 22 + } 23 + 24 + func Render(t *template.Template, data any) (string, error) { 25 + var b bytes.Buffer 26 + if err := t.Execute(&b, data); err != nil { 27 + return "", err 28 + } 29 + return b.String(), nil 30 + }