🦞 一只用 AI Agent 搭副业产线的程序员


给你看一段对话:

用户:帮我分析这个 Go 项目里有几个 SQL 注入风险
AI:好的,我来看看……额,我没法看你的代码。你把代码贴给我。
用户:(贴代码)
AI:发现 3 处风险。第 12 行、第 47 行、第 89 行。
用户:能帮我自动修复吗?
AI:我做不到,我只能给你建议。

这是聊天机器人。你问一句,它答一句。像个实习生——聪明但不主动。

Agent 不是这样的。Agent 是这样的:

用户:帮我检查 project-a 里的 SQL 注入风险,自动修好,然后跑一下测试确保没挂。
Agent:收到。
  → 读取 project-a 下所有 .go 文件
  → 逐个分析 SQL 拼接
  → 发现 3 处风险
  → 改成参数化查询
  → 执行 go test ./...
  → 测试通过 ✓
  → 输出修改报告

Agent 和聊天机器人的区别就一个词:自主行动。

这篇我们用 100 行 Go 代码,写一个最小的 Agent,让你看到它的核心骨架。


Agent 的骨架:感知-决策-执行循环

所有 Agent,不管多复杂,拆开就是三个步骤的循环:

┌─────────────────────────────────────────────┐
│                                             │
│   ① 感知(Perception)                       │
│   读取环境信息——文件、API 返回、用户输入       │
│         ↓                                   │
│   ② 决策(Decision)                         │
│   用 LLM 分析当前状态,决定下一步做什么         │
│         ↓                                   │
│   ③ 执行(Execution)                        │
│   调用工具、操作文件、发送请求,产生实际效果     │
│         ↓                                   │
│   回到 ①,直到任务完成                        │
│                                             │
└─────────────────────────────────────────────┘

这跟你每天上班一模一样:看 Jira → 决定做什么 → 写代码 → 看结果 → 决定下一步。Agent 只是把这个过程自动化了。


100 行 Go 实现

不废话,直接上代码。

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

// Message 对话消息
type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

// ToolCall 工具调用请求
type ToolCall struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"`
}

// LLMResponse LLM 返回
type LLMResponse struct {
	Content   string     `json:"content"`
	ToolCalls []ToolCall `json:"tool_calls,omitempty"`
}

// Agent 最小 Agent 实现
type Agent struct {
	apiKey   string
	messages []Message
	tools    map[string]func(string) string // 工具名 → 执行函数
	maxSteps int
}

func NewAgent(apiKey string) *Agent {
	return &Agent{
		apiKey:   apiKey,
		maxSteps: 10,
		tools:    make(map[string]func(string) string),
	}
}

// RegisterTool 注册一个工具
func (a *Agent) RegisterTool(name string, fn func(string) string) {
	a.tools[name] = fn
}

// Run 启动 Agent 循环
func (a *Agent) Run(task string) string {
	// 初始化:只告诉 Agent 任务
	a.messages = []Message{
		{Role: "system", Content: a.buildSystemPrompt()},
		{Role: "user", Content: task},
	}

	// 主循环:感知 → 决策 → 执行
	for step := 0; step < a.maxSteps; step++ {
		fmt.Printf("--- Step %d ---\n", step+1)

		// ① 决策:调用 LLM,让它决定下一步
		resp := a.callLLM()

		// ② 如果 LLM 决定调用工具 → 执行
		if len(resp.ToolCalls) > 0 {
			for _, tc := range resp.ToolCalls {
				fmt.Printf("🔧 调用工具: %s(%s)\n", tc.Name, tc.Arguments)
				result := a.executeTool(tc)
				// ③ 把执行结果反馈给 LLM(感知 → 下一轮决策)
				a.messages = append(a.messages, Message{
					Role:    "user",
					Content: fmt.Sprintf("工具 %s 的执行结果:%s", tc.Name, result),
				})
			}
			continue
		}

		// ④ 没有工具调用 → 任务完成,返回最终答案
		return resp.Content
	}

	return "达到最大步数限制"
}

func (a *Agent) buildSystemPrompt() string {
	var toolDescs []string
	for name := range a.tools {
		toolDescs = append(toolDescs, name)
	}
	return fmt.Sprintf(`你是一个自主 Agent。完成任务时,如果需要使用工具,
在回复中以 JSON 格式指定工具调用:
{"tool_calls": [{"name": "工具名", "arguments": "参数"}]}

可用工具:%s

当你不再需要工具时,直接输出最终答案。`, strings.Join(toolDescs, ", "))
}

func (a *Agent) callLLM() LLMResponse {
	body, _ := json.Marshal(map[string]interface{}{
		"model":    "deepseek-v4-pro",
		"messages": a.messages,
	})
	req, _ := http.NewRequest("POST",
		"https://api.deepseek.com/anthropic/v1/messages",
		bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+a.apiKey)
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	var llmResp LLMResponse
	json.Unmarshal(data, &llmResp)
	// 记录 LLM 的回复到历史
	a.messages = append(a.messages, Message{
		Role:    "assistant",
		Content: llmResp.Content,
	})
	return llmResp
}

func (a *Agent) executeTool(tc ToolCall) string {
	fn, ok := a.tools[tc.Name]
	if !ok {
		return fmt.Sprintf("错误:未知工具 %s", tc.Name)
	}
	return fn(tc.Arguments)
}

// ──────────── 示例工具 ────────────

func main() {
	agent := NewAgent(os.Getenv("DEEPSEEK_API_KEY"))

	// 注册工具:读取文件
	agent.RegisterTool("read_file", func(path string) string {
		content, err := os.ReadFile(strings.TrimSpace(path))
		if err != nil {
			return fmt.Sprintf("读取失败:%v", err)
		}
		return string(content)
	})

	// 注册工具:运行命令
	agent.RegisterTool("run_command", func(cmd string) string {
		// 简化版:实际生产环境需要安全沙箱
		return fmt.Sprintf("命令 %s 执行成功(模拟)", cmd)
	})

	result := agent.Run("读取 main.go 文件,检查里面有没有 TODO 注释")
	fmt.Println("\n✅ 最终结果:", result)
}

拆解这个循环

核心就 30 行——Run 方法里的 for 循环:

for step := 0; step < a.maxSteps; step++ {
    resp := a.callLLM()           // 决策:AI 想下一步做什么?
    if len(resp.ToolCalls) > 0 {
        result := a.executeTool() // 执行:AI 想用工具,就执行
        a.messages = append(...)  // 感知:把结果告诉 AI
        continue                  // 回到决策
    }
    return resp.Content           // 完成:AI 不想用工具了,输出答案
}

这就是 Agent 的全部秘密。不是什么黑科技,就是一个 for 循环包着一个 LLM 调用。


为什么这个模式这么强

普通聊天:用户 → AI → 用户 → AI,单向,一步一问。

Agent:用户 → AI → 工具 → AI → 工具 → AI → 答案,AI 能自己驱动流程。

AI 从一个「回答问题的人」变成了「完成任务的人」。

这个转变的威力有多大?想象一下——以前你让 AI 帮你修 Bug,你得:

  1. 把代码贴给 AI
  2. AI 告诉你哪里有问题
  3. 你手动改
  4. 把改完的代码贴给 AI 确认
  5. 重复 2-4

Agent 模式下:你一句话,它全干了。


一个真实的坑:死循环

我第一次写 Agent 的时候,忘了加 maxSteps。结果 AI 陷入了一个循环:读文件 → 觉得没读对 → 又读了一次 → 又觉得没读对……

10 秒钟烧了 15 次 API 调用,全在重复同一件事。

maxSteps 是 Agent 的安全带。永远都要设。

建议值:简单任务 5,中等任务 10,复杂任务 20。超过 20 步的任务建议拆成多个子任务单独跑。


本篇核心收获

概念 一句话
Agent 本质 感知-决策-执行 的循环
核心代码 for + callLLM + executeTool
与聊天机器人区别 Agent 能自主调用工具,驱动任务完成
必须的安全措施 maxSteps 防止死循环

100 行 Go 代码,一个 for 循环,你就有了一个能自主完成任务的 Agent。

下一篇我们深入 Agent 最核心的能力——Function Calling:AI 是怎么知道该调用哪个函数的?JSON Schema 怎么定义?函数执行出错了怎么办?完整实现,一行不跳。

关注我,别错过。


🦞 一只用 AI Agent 搭副业产线的程序员

全平台同名:虾哥不加班
需要定制 AI 工具?来聊聊 → lob_ai

源码:GitHub - lobster-bujiaban

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐