从零开始用 Go 开发 AI Agent:一条可执行的六周学习路线

最后更新:2026-08-12
文章目录 24 个章节

我最近在思考一个很实际的问题:如果 Go 和 AI Agent 都刚开始学,能不能把两件事合成一个项目,从零做出自己的 Agent?

答案是可以,而且这可能比“先学完 Go,再研究 Agent”更有效。Go 的语法并不算庞大,Agent 的核心结构也没有想象中神秘。只要项目边界足够小,就能让每一个新知识点马上变成看得见的功能。

但这里有一个重要前提:第一版不要做万能助手,也不要从多 Agent 开始。

这篇文章选择“个人任务助理”作为贯穿全程的项目。它从一个只会保存待办事项的命令行程序开始,逐步加入模型对话、工具调用、状态存储、HTTP 接口和安全控制。六周后得到的不只是一个演示,而是一套能继续扩展的 Agent 后端骨架。

先确定最终要做什么

这个项目最终支持三类对话:

“帮我添加一个任务:周五前写完项目总结”
“列出还没有完成的任务”
“把任务 3 标记为已完成”

对用户而言,它像一个简单的聊天助手;对程序而言,它有三个明确工具:

create_task
list_tasks
complete_task

模型不直接修改数据库。它只负责理解用户意图并生成结构化的工具调用,Go 程序负责校验参数、执行操作和返回真实结果。

完整数据流如下:

用户输入
   ↓
模型判断下一步
   ├─ 不需要工具 → 直接回答
   └─ 需要工具   → 返回工具名称与 JSON 参数
                       ↓
                  Go 校验并执行
                       ↓
                  工具结果返回模型
                       ↓
                  继续判断或最终回答

这就是最小 Agent 循环。关于模型、工具、状态和循环的完整拆解,可以先阅读 AI Agent 第一章:从对话到行动。

为什么这个项目适合同时学习 Go 和 Agent

个人任务助理的业务不复杂,却刚好覆盖了 Go 后端和 Agent 开发最重要的基础能力。

项目功能 对应的 Go 知识 对应的 Agent 知识
定义任务 struct、切片、方法 结构化业务状态
保存任务 文件、SQLite、错误处理 长期状态
接收对话 JSON、net/http 用户输入边界
调用模型 HTTP 客户端、context Prompt、Responses API
执行工具 接口、map、JSON 解码 Tool Calling、工具注册
限制行为 超时、校验、测试 最大步数、审批、安全边界

项目会迫使我们在真实场景中使用这些知识,而不是只记语法。例如,学到 context.Context 时,不只是知道它能取消请求,而是让用户断开连接后,模型调用和工具执行也能一起停止。

开始前需要学到什么程度

不需要先学完 Go。开始项目之前,只要能看懂下面这些概念:

  • 变量、函数、if 和 for;
  • struct、切片和 map;
  • 函数可以返回多个值;
  • Go 使用 error 显式处理错误;
  • 包、模块和 go run 的基本用法。

goroutine、Channel、反射和复杂设计模式都可以以后再学。第一版 Agent 是串行循环,暂时不需要并发编排。

开发环境可以这样确认:

go version
mkdir go-task-agent
cd go-task-agent
go mod init example.com/go-task-agent

建议每个阶段都保持以下命令可通过:

gofmt -w .
go test ./...
go vet ./...
go run ./cmd/agent

第一周:先写一个没有 AI 的任务程序

第一周完全不调用模型。目标是用 Go 写出一个可靠的命令行任务管理器。

先定义最小数据结构:

type Task struct {
	ID        int    `json:"id"`
	Title     string `json:"title"`
	Completed bool   `json:"completed"`
}

type TaskStore interface {
	Create(title string) (Task, error)
	List() ([]Task, error)
	Complete(id int) (Task, error)
}

第一版可以用内存切片保存任务。这个阶段重点不是数据库,而是理解:

  • 什么时候用结构体;
  • 值接收者和指针接收者有什么区别;
  • 接口如何隔离业务逻辑和存储方式;
  • 错误如何从底层逐层返回;
  • 如何为 Create、List 和 Complete 写测试。

第一周的验收标准很简单:程序能添加、列出和完成任务,非法 ID 会返回明确错误,核心逻辑有单元测试。

第二周:整理成可维护的 Go 项目

功能跑通后,再把代码从 main.go 中拆出来:

go-task-agent/
├── cmd/agent/main.go
├── internal/agent/
├── internal/tools/
├── internal/store/
│   ├── store.go
│   └── memory.go
├── go.mod
└── README.md

cmd/agent 只负责组装依赖和启动程序;store 管理任务数据;tools 负责把业务能力包装成 Agent 可调用的工具;agent 最终负责运行循环。

这一周需要补上三个工程能力:

使用 context.Context

模型请求、数据库调用和外部 HTTP 请求都应该接收同一个 context.Context。不要为了方便在业务函数里重新使用 context.Background(),否则上层取消无法继续向下传播。

包装错误

使用 %w 保留底层原因:

task, err := store.Complete(id)
if err != nil {
	return Task{}, fmt.Errorf("complete task %d: %w", id, err)
}

用接口替换实现

内存存储和未来的 SQLite 存储都实现 TaskStore。业务层依赖接口,就能在测试时使用内存版本,在运行时切换成数据库版本。

第三周:接入模型,但先不要加工具

接下来只做一轮普通对话,先打通 Go 到模型 API 的链路。

OpenAI 当前提供官方 Go SDK,但官方文档仍将它标注为 beta。Go 目前没有与 Python、TypeScript 对等的官方 Agents SDK,因此这条路线使用 Go SDK 调用 Responses API,并由我们自己控制 Agent 循环。

安装 SDK:

go get github.com/openai/openai-go/v3
export OPENAI_API_KEY="你的 API Key"

最小调用如下:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/openai/openai-go/v3"
	"github.com/openai/openai-go/v3/responses"
)

func main() {
	client := openai.NewClient()
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	resp, err := client.Responses.New(
		ctx,
		responses.ResponseNewParams{
			Model: "gpt-5.6",
			Input: responses.ResponseNewParamsInputUnion{
				OfString: openai.String("请用一句话解释什么是任务优先级"),
			},
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.OutputText())
}

API Key 只能放在服务端环境变量或密钥管理系统中,不能写进源码、前端代码或 Git 仓库。

这一阶段只验证四件事:SDK 初始化、请求超时、API 错误处理和文本输出。暂时不要同时引入流式响应、历史消息和工具调用,否则一旦失败,很难判断是哪一层出了问题。

第四周:实现第一个 Agent 工具循环

模型能回答问题之后,再把任务操作包装成工具。一个工具至少要有:

  • 稳定且清楚的名称;
  • 告诉模型何时调用的描述;
  • JSON Schema 参数定义;
  • Go 参数结构体;
  • 实际执行函数。

以创建任务为例:

type CreateTaskArgs struct {
	Title string `json:"title"`
}

type ToolResult struct {
	OK      bool   `json:"ok"`
	Message string `json:"message"`
}

工具收到 JSON 后要再次严格解码和校验。模型生成的参数不能被当成可信输入:

func decodeCreateTask(raw []byte) (CreateTaskArgs, error) {
	var args CreateTaskArgs
	decoder := json.NewDecoder(bytes.NewReader(raw))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&args); err != nil {
		return args, fmt.Errorf("decode arguments: %w", err)
	}

	args.Title = strings.TrimSpace(args.Title)
	if args.Title == "" {
		return args, errors.New("title is required")
	}
	return args, nil
}

Agent Runner 的职责可以概括成下面这段伪代码:

for step := 0; step < maxSteps; step++ {
	reply := callModel(messages, toolDefinitions)

	if reply.HasFinalText() {
		return reply.FinalText()
	}

	for _, call := range reply.ToolCalls() {
		result := executeTool(call.Name, call.Arguments)
		messages = append(messages, toolResult(call.ID, result))
	}
}

return errors.New("agent exceeded maximum steps")

第一版把 maxSteps 设置为 5 就够了。最大步数不是优化项,而是防止重复工具调用失控的基本边界。

工具实现还需要超时、错误分类、输出裁剪和高风险操作确认。这部分可以继续阅读 AI Agent 第二章:可靠工具调用。

第五周:加入 HTTP 接口和持久化状态

命令行版本稳定后,再增加一个最小 HTTP 服务:

POST /chat
GET  /healthz

POST /chat 接收:

{
  "session_id": "user-123",
  "message": "把写周报加入任务"
}

返回:

{
  "reply": "已添加任务:写周报"
}

这一阶段学习 net/http、JSON 编解码、状态码、请求大小限制和服务端超时。先使用标准库即可,不需要急着引入 Web 框架。

任务数据可以从内存迁移到 SQLite,但要把两类状态分开:

  • 业务状态:任务本身,必须可靠持久化;
  • 对话状态:用户和模型的历史,需要裁剪、摘要或按需加载。

不要把全部历史无限追加到每次请求中。上下文越长,调用成本和噪声越高。对于任务助理,数据库中的真实任务才是事实来源,对话历史只用于理解最近几轮表达。

第六周:补上安全、评测和可观测性

能运行不等于可靠。最后一周不再增加显眼功能,而是让已有能力可以被验证。

给工具分级

读取类:list_tasks             可以自动执行
写入类:create_task           执行后明确告知用户
敏感类:delete_all_tasks      必须人工确认

模型的 Prompt 只能提供行为建议,不能代替程序权限控制。删除数据、付款、发送外部消息等操作,应由 Go 代码暂停执行并等待明确审批。

建立评测集

准备至少 30 条固定输入,覆盖:

  • 正常创建、查询和完成任务;
  • 含糊表达和缺少参数;
  • 不存在的任务 ID;
  • 诱导模型绕过权限;
  • 工具超时和数据库失败;
  • 连续调用导致的最大步数限制。

每次修改 Prompt、工具描述或模型后,都重新运行同一批用例。不要只凭几次手工对话判断 Agent 是否变好了。

记录必要信息

至少记录请求 ID、会话 ID、模型耗时、工具名称、工具耗时、结果状态和总步数。日志中不要保存 API Key、完整鉴权头和未经处理的隐私数据。

每天应该怎么学

可以把每天一到两个小时拆成三个部分:

20 分钟:学习当天需要的 Go 概念
60 分钟:把概念放进项目并运行
20 分钟:写测试、整理错误和学习记录

遇到问题时,先判断它属于哪一层:

Go 编译错误?
JSON 结构错误?
HTTP 请求错误?
模型没有选择工具?
工具选择正确但执行失败?
工具成功但结果没有回传?

这种分层排查比反复修改 Prompt 更有效。Agent 系统的大部分问题并不都来自模型,很多时候只是 JSON 字段不匹配、上下文没有继续传递,或者工具错误被吞掉了。

六周验收清单

完成下面这些项目,就可以认为第一阶段学习闭环已经建立:

  • 能解释 Chatbot 与 Agent 的区别;
  • 能独立写出 Go 结构体、接口和错误处理;
  • 能使用 context.Context 传递超时和取消;
  • 能通过官方 Go SDK 调用 Responses API;
  • 能注册并执行至少三个工具;
  • 能把工具结果交还模型继续运行;
  • 能限制 Agent 最大执行步数;
  • 能把任务保存到 SQLite;
  • 能提供一个最小 /chat HTTP 接口;
  • 能为工具写单元测试和失败用例;
  • 能让危险操作等待人工确认;
  • 能通过日志看清一次运行调用了哪些工具。

最容易走偏的几个地方

一开始就做多 Agent

单 Agent 的状态、工具和错误还没理清时,多 Agent 只会让问题更难定位。只有不同任务确实需要不同工具、权限或专业指令时,再拆分角色。

把所有逻辑都写进 Prompt

Prompt 不是权限系统,也不是参数校验器。能由 Go 类型、Schema、超时和权限代码确定的规则,就不应该只依赖模型遵守。

给 Agent 一个无限制 Shell

学习阶段应该从三个业务工具开始,而不是直接允许任意命令执行。工具权限越小,行为越容易理解和测试。

过早引入框架和向量数据库

任务助理的事实在 SQLite 中,不需要先做知识库。等项目真正需要检索大量文档时,再增加文件搜索或向量检索。

只验证“它回答了”

真正需要验证的是:它是否选择了正确工具、参数是否准确、失败是否可恢复、危险操作是否被阻止,以及运行是否在预算内停止。

接下来怎么继续

走完这条路线后,可以沿三个方向扩展:

  1. 产品方向:流式响应、登录、Web UI、消息平台接入;
  2. 工程方向:PostgreSQL、Redis、任务队列、追踪和部署;
  3. Agent 方向:知识检索、MCP、可恢复审批和多 Agent 分工。

顺序仍然应该是:先让单个工具可靠,再让单个 Agent 可靠,最后才讨论多个 Agent 如何协作。

OpenAI 官方文档也给出了相同的路线边界:使用 Responses API 时,应用自己管理工具调用和循环;使用 Agents SDK 时,SDK 代为管理循环。目前 Go 更适合前一种方式。可以参考 OpenAI Go SDK 和 Responses API 与 Agents SDK 的区别。

从零开始并不意味着先把所有知识学完。更可行的方法是先做一个小闭环,让每一周都产生可运行、可测试、可以继续扩展的结果。六周只是节奏参考,真正重要的是始终保持项目边界清楚,并且每一步都有证据证明它确实工作。