A single tool round-trip
Time to run it end to end. Start the REPL and ask a question that requires looking at a file. If the schema and dispatcher are right, the model will pick read_file, you will dispatch it, the file contents will come back as a tool message, and the next completion will answer the actual question.
make run
> read the go.mod file and tell me the module path
# Expected flow:
# 1. Assistant message with tool_calls[read_file({"path":"go.mod"})]
# 2. Dispatcher reads go.mod and appends a role: tool message
# 3. Next completion returns: github.com/learnwithparam/coding-agents-from-scratch-goThe first real agentic interaction. The model wants a file, so it asks for it. The dispatcher obliges. The model uses the result.
func runReadFile(args string) (string, error) {
var in struct {
Path string `json:"path"`
}
if err := json.Unmarshal([]byte(args), &in); err != nil {
return "", fmt.Errorf("read_file: invalid args: %w", err)
}
if in.Path == "" {
return "", fmt.Errorf("read_file: path is required")
}
clean := filepath.Clean(in.Path)
data, err := os.ReadFile(clean)
if err != nil {
return "", fmt.Errorf("read_file(%s): %w", clean, err)
}
return string(data), nil
}Arguments arrive as a JSON string, not a parsed map. Unmarshal into a local anonymous struct, validate, then read. filepath.Clean is normalization, not security. Real path guards arrive in the next phase.
Ordering exercise: Put the round-trip in order
Loading practice…
Checkpoint: Tool round-trip checkpoint
Loading practice…