agentLoop is the inner loop: call the model, dispatch any tool calls, loop. Two exits — the model returns plain text, or it returns a non-tool stop reason. Either way we return the updated messages slice so the outer REPL keeps the conversation going.
(ctx context.Context, messages []anthropic.MessageParam)
| 94 | // stop reason. Either way we return the updated messages slice so the |
| 95 | // outer REPL keeps the conversation going. |
| 96 | func agentLoop(ctx context.Context, messages []anthropic.MessageParam) []anthropic.MessageParam { |
| 97 | for { |
| 98 | resp, err := client.Messages.New(ctx, anthropic.MessageNewParams{ |
| 99 | Model: anthropic.ModelClaudeOpus4_7, |
| 100 | MaxTokens: 8192, |
| 101 | System: []anthropic.TextBlockParam{{Text: systemPrompt}}, |
| 102 | Messages: messages, |
| 103 | Tools: tools, |
| 104 | }) |
| 105 | if err != nil { |
| 106 | fmt.Printf("api error: %v\n", err) |
| 107 | return messages |
| 108 | } |
| 109 | messages = append(messages, resp.ToParam()) |
| 110 | |
| 111 | var toolResults []anthropic.ContentBlockParamUnion |
| 112 | for _, block := range resp.Content { |
| 113 | switch v := block.AsAny().(type) { |
| 114 | case anthropic.TextBlock: |
| 115 | fmt.Println(v.Text) |
| 116 | case anthropic.ToolUseBlock: |
| 117 | result, isErr := executeTool(v.Name, v.JSON.Input.Raw()) |
| 118 | toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, result, isErr)) |
| 119 | } |
| 120 | } |
| 121 | if resp.StopReason != anthropic.StopReasonToolUse { |
| 122 | return messages |
| 123 | } |
| 124 | messages = append(messages, anthropic.NewUserMessage(toolResults...)) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | // executeTool is the switch that runs whatever the model asked for. The |
| 129 | // (string, bool) return — content plus is-error flag — is the "errors as |