agent/pkg/loop/prompt.go
Snider e90a84eaa0 feat: merge go-agent + go-agentic + php-devops into unified agent repo
Combines three repositories into a single workspace:
- go-agent → pkg/orchestrator (Clotho), pkg/jobrunner, pkg/loop, cmd/
- go-agentic → pkg/lifecycle (allowance, sessions, plans, dispatch)
- php-devops → repos.yaml, setup.sh, scripts/, .core/

Module path: forge.lthn.ai/core/agent

All packages build, all tests pass.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-06 15:23:00 +00:00

71 lines
2.2 KiB
Go

package loop
import (
"encoding/json"
"fmt"
"strings"
)
// BuildSystemPrompt constructs the system prompt that instructs the model how
// to use the available tools. When no tools are registered it returns a plain
// assistant preamble without tool-calling instructions.
func BuildSystemPrompt(tools []Tool) string {
if len(tools) == 0 {
return "You are a helpful assistant."
}
var b strings.Builder
b.WriteString("You are a helpful assistant with access to the following tools:\n\n")
for _, tool := range tools {
b.WriteString(fmt.Sprintf("### %s\n", tool.Name))
b.WriteString(fmt.Sprintf("%s\n", tool.Description))
if tool.Parameters != nil {
schema, _ := json.MarshalIndent(tool.Parameters, "", " ")
b.WriteString(fmt.Sprintf("Parameters: %s\n", schema))
}
b.WriteString("\n")
}
b.WriteString("To use a tool, output a fenced block:\n")
b.WriteString("```tool\n")
b.WriteString("{\"name\": \"tool_name\", \"args\": {\"key\": \"value\"}}\n")
b.WriteString("```\n\n")
b.WriteString("You may call multiple tools in one response. After tool results are provided, continue reasoning. When you have a final answer, respond normally without tool blocks.\n")
return b.String()
}
// BuildFullPrompt assembles the complete prompt string from the system prompt,
// conversation history, and current user message. Each message is tagged with
// its role so the model can distinguish turns. Tool results are annotated with
// the tool name for traceability.
func BuildFullPrompt(system string, history []Message, userMessage string) string {
var b strings.Builder
if system != "" {
b.WriteString(system)
b.WriteString("\n\n")
}
for _, msg := range history {
switch msg.Role {
case RoleUser:
b.WriteString(fmt.Sprintf("[user]\n%s\n\n", msg.Content))
case RoleAssistant:
b.WriteString(fmt.Sprintf("[assistant]\n%s\n\n", msg.Content))
case RoleToolResult:
toolName := "unknown"
if len(msg.ToolUses) > 0 {
toolName = msg.ToolUses[0].Name
}
b.WriteString(fmt.Sprintf("[tool_result: %s]\n%s\n\n", toolName, msg.Content))
}
}
if userMessage != "" {
b.WriteString(fmt.Sprintf("[user]\n%s\n\n", userMessage))
}
return b.String()
}