How Go AI Agents Think: Inside the Reasoning Loop and Tool Integration
This tutorial walks through the core architecture of a Golang AI Agent, detailing the ReAct reasoning loop, tool‑calling mechanism, web‑search implementation, metadata handling, and the complete closed‑loop workflow that turns a simple chatbot into a local AutoGPT‑style assistant.
Reasoning Loop Overview
A functional AI agent must go beyond a simple input → output pattern. It needs to analyze a problem, decide whether a tool is required, invoke the tool, observe the result, and iterate until the task is considered complete. This follows the ReAct (Reasoning + Action) paradigm: analyze → act → observe → re‑reason → repeat.
Agent Loop Implementation
The core loop runs a bounded number of iterations (six by default) to avoid infinite cycles. Each iteration generates a response from the LLM, checks for a structured function‑call request, executes the corresponding tool, and feeds the tool’s output back into the conversation.
for i := 0; i < 6; i++ {
resp, err := a.Model.Generate(ctx, a.conv)
if err != nil {
return "", err
}
fc := a.Model.ParseFunctionCall(resp)
if fc == nil {
finalResponse = resp
break
}
res, err := a.execTool(ctx, fc)
if err != nil {
return "", err
}
a.addSystem(fmt.Sprintf("工具 '%s' 执行结果: %s", fc.Name, res))
}Key steps:
Iteration limit – prevents endless thinking or erroneous tool calls.
Model inference – the LLM may return natural language or a function‑call payload.
Parse function call – extracts the tool name and arguments.
Execute tool – runs the Go implementation for the requested capability.
Feedback – the result is added to the system messages so the model can continue reasoning.
Tool Execution Dispatch
The execTool() function uses a switch statement on the tool name, making it straightforward to add new capabilities.
switch fc.Name {
case "read_file":
return readFileTool(fc.Args)
case "write_file":
return writeFileTool(fc.Args)
case "run_code":
return runCodeTool(fc.Args)
case "web_search":
var args WebSearchArgs
_ = json.Unmarshal(fc.Arguments, &args)
results, _ := WebSearch(args)
return MarshalArgs(results)
}Adding a new tool only requires a new case block and the implementation of the corresponding function.
Web Search Tool
The web_search tool demonstrates external information integration. It uses the free DuckDuckGo HTML search API, which is suitable for personal projects. The request is performed with a simple HTTP GET:
resp, err := http.Get("https://duckduckgo.com/html/?q=" + url.QueryEscape(query))The returned HTML is parsed to extract result titles, snippets, and destination URLs. For deeper analysis, the page body can be fetched and processed with goquery to collect all <p> texts, the main content, and the page title.
Tool Metadata for the LLM
The agent provides the language model with a JSON description of each available tool, mirroring the OpenAI Functions format. This enables the model to understand the tool’s purpose and required parameters.
{"name": "web_search", "description": "Search the web and return structured results", "parameters": { ... }}Complete Closed‑Loop Workflow
The overall process can be visualised as a sequential flow:
用户提问
↓
LLM 初次思考(是否需要搜索)
↓
请求调用工具(web_search)
↓
Golang 执行工具(实际进行搜索)
↓
把搜索结果回传给模型
↓
模型继续推理(结合搜索结果得出答案)
↓
输出最终总结Repository References
Source code for the agent is available at:
GitHub: https://github.com/louis-xie-programmer/easy-agent
Gitee: https://gitee.com/louis_xie/easy-agent
Code Wrench
Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
