loop verbessert
This commit is contained in:
@@ -18,20 +18,16 @@ const (
|
|||||||
maxTurns = 10
|
maxTurns = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
var systemPrompt = `Du bist ein Coding-Agent. Erledige den gegebenen Task.
|
var systemPrompt = `Du bist ein Coding-Agent und programmierst Go.
|
||||||
|
Erledige deine Aufgabe mit folgenden Tools:
|
||||||
TOOLS:
|
|
||||||
TOOL:READ_FILE:pfad
|
TOOL:READ_FILE:pfad
|
||||||
TOOL:WRITE_FILE:pfad
|
TOOL:WRITE_FILE:pfad:<<<inhalt>>>
|
||||||
<<<
|
|
||||||
inhalt
|
|
||||||
>>>
|
|
||||||
TOOL:LIST_FILES:pfad
|
TOOL:LIST_FILES:pfad
|
||||||
|
|
||||||
REGELN:
|
REGELN:
|
||||||
- Nutze relative Pfade
|
- Nutze relative Pfade
|
||||||
- Kein Markdown in Dateiinhalten
|
- Kein Markdown in Dateiinhalten
|
||||||
- Task erledigt: schreibe TASK_COMPLETE`
|
- Wenn Task erledigt: schreibe nur TASK_COMPLETE`
|
||||||
|
|
||||||
type AgentLoop struct {
|
type AgentLoop struct {
|
||||||
client *openai.Client
|
client *openai.Client
|
||||||
@@ -131,8 +127,7 @@ func (a *AgentLoop) runTask(task prd.Task) error {
|
|||||||
totalChars += len(fmt.Sprintf("%v", m))
|
totalChars += len(fmt.Sprintf("%v", m))
|
||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
a.log.Debug("MODEL REQUEST: model=%s totalChars=%d messages=%#v", a.model, totalChars, messages)
|
a.log.Debug("MODEL REQUEST: model=%s ~%d Zeichen\n%s", a.model, totalChars, formatMessages(messages))
|
||||||
|
|
||||||
resp, err := a.client.Chat.Completions.New(
|
resp, err := a.client.Chat.Completions.New(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
openai.ChatCompletionNewParams{
|
openai.ChatCompletionNewParams{
|
||||||
@@ -141,7 +136,9 @@ func (a *AgentLoop) runTask(task prd.Task) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
a.log.Debug("MODEL RESPONSE (elapsed=%s): %#v", elapsed, resp)
|
if resp != nil && len(resp.Choices) > 0 {
|
||||||
|
a.log.Debug("MODEL RESPONSE\n%s", formatResponse(resp, elapsed))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("API-Fehler (~%d Zeichen im Kontext): %w", totalChars, err)
|
return fmt.Errorf("API-Fehler (~%d Zeichen im Kontext): %w", totalChars, err)
|
||||||
}
|
}
|
||||||
@@ -152,6 +149,13 @@ func (a *AgentLoop) runTask(task prd.Task) error {
|
|||||||
|
|
||||||
// Completion Detection
|
// Completion Detection
|
||||||
if isTaskComplete(response) {
|
if isTaskComplete(response) {
|
||||||
|
if turn == 0 {
|
||||||
|
// LLM hat sofort TASK_COMPLETE ohne Tool-Call → nichts wurde getan
|
||||||
|
nudge := "Du hast die Datei noch nicht erstellt! Nutze zuerst WRITE_FILE, dann schreibe TASK_COMPLETE."
|
||||||
|
a.log.ChatMessage("user", nudge)
|
||||||
|
messages = append(messages, openai.UserMessage(nudge))
|
||||||
|
continue // nächster Turn
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,3 +196,71 @@ func isTaskComplete(response string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// formatMessages gibt die Chat-History lesbar aus
|
||||||
|
func formatMessages(messages []openai.ChatCompletionMessageParamUnion) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
for i, m := range messages {
|
||||||
|
var role, content string
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case m.OfSystem != nil:
|
||||||
|
role = "system"
|
||||||
|
if len(m.OfSystem.Content.OfString.Value) > 0 {
|
||||||
|
content = m.OfSystem.Content.OfString.Value
|
||||||
|
}
|
||||||
|
case m.OfUser != nil:
|
||||||
|
role = "user"
|
||||||
|
if len(m.OfUser.Content.OfString.Value) > 0 {
|
||||||
|
content = m.OfUser.Content.OfString.Value
|
||||||
|
}
|
||||||
|
case m.OfAssistant != nil:
|
||||||
|
role = "assistant"
|
||||||
|
if len(m.OfAssistant.Content.OfString.Value) > 0 {
|
||||||
|
content = m.OfAssistant.Content.OfString.Value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
role = "unknown"
|
||||||
|
content = fmt.Sprintf("%+v", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inhalt auf 120 Zeichen kürzen für Übersicht
|
||||||
|
preview := content
|
||||||
|
if len(preview) > 120 {
|
||||||
|
preview = preview[:120] + "..."
|
||||||
|
}
|
||||||
|
// Zeilenumbrüche für einzeilige Darstellung ersetzen
|
||||||
|
preview = strings.ReplaceAll(preview, "\n", "↵")
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf(" [%d] %-10s : %s\n", i, role, preview))
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatResponse(resp *openai.ChatCompletion, elapsed time.Duration) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf(" ID : %s\n", resp.ID))
|
||||||
|
sb.WriteString(fmt.Sprintf(" Modell : %s\n", resp.Model))
|
||||||
|
sb.WriteString(fmt.Sprintf(" Elapsed : %s\n", elapsed.Round(time.Millisecond)))
|
||||||
|
sb.WriteString(fmt.Sprintf(" Finish-Reason : %s\n", resp.Choices[0].FinishReason))
|
||||||
|
sb.WriteString(fmt.Sprintf(" Tokens : prompt=%d completion=%d total=%d\n",
|
||||||
|
resp.Usage.PromptTokens,
|
||||||
|
resp.Usage.CompletionTokens,
|
||||||
|
resp.Usage.TotalTokens,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Tokens/Sekunde aus den Timing-Daten (Ollama-spezifisch)
|
||||||
|
if timings, ok := resp.JSON.ExtraFields["timings"]; ok {
|
||||||
|
sb.WriteString(fmt.Sprintf(" Timings : %s\n", timings.Raw()))
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf(" Content :\n"))
|
||||||
|
// Inhalt eingerückt und vollständig ausgeben
|
||||||
|
content := resp.Choices[0].Message.Content
|
||||||
|
for _, line := range strings.Split(content, "\n") {
|
||||||
|
sb.WriteString(fmt.Sprintf(" %s\n", line))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user