当LLM从"对话工具"进化为"自主决策的智能体",软件架构的设计范式正在被重新定义。2023年AutoGPT引爆Agent浪潮,2025年Google、OpenAI相继发布Agent协议标准,到今天,AI Agent已成为从代码生成到业务自动化领域最核心的技术方向之一。

然而,面对层出不穷的Agent框架和概念,很多开发者感到困惑:一个Agent到底由哪些核心组件组成? 抛开框架的封装,我们能否用自己熟悉的语言从零构建一个Agent?

本文尝试回答这个问题。我将Agent拆解为五个核心组件——感知层、规划层、工具层、记忆层、执行层,逐一剖析其设计原理,并用Go代码给出最小可用的实现参考。

一、Agent的工作模式:感知-思考-行动的循环在拆解组件之前,先理解Agent的工作模式。Agent本质上是一个能够感知环境、自主思考、执行动作并不断迭代的智能系统,可以类比为Go中的goroutine调度循环:不断从"通道"(感知输入)中读取数据,根据当前状态(记忆)做出决策,执行操作(行动),然后继续下一轮循环。

代码语言:javascript复制感知 → 规划 → 工具调用 → 执行 → 反思 → 记忆存储

理解了这个循环,再来看五个组件各自的职责就清晰了。

二、五大核心组件1. 感知层:信息的入口职责:接收和解析各种输入(用户问题、工具返回结果、环境状态变化等),将原始数据转换为Agent可处理的结构化格式。

感知层是Agent与外部世界交互的第一道关卡。在Go中,可以把它理解为HTTP中间件——对原始请求做清洗、解析和标准化。好的设计应具备良好的扩展性,支持新输入类型时只需添加解析逻辑,无需修改核心代码。

代码语言:javascript复制type Perception struct {

UserMessage string

Images []string

Attachments []string

Metadata map[string]interface{}

}

func NewPerception(rawInput string) *Perception {

return &Perception{

UserMessage: rawInput,

Images: make([]string, 0),

Attachments: make([]string, 0),

Metadata: make(map[string]interface{}),

}

}

2. 规划层:Agent的大脑职责:分析任务、制定执行计划并动态调整策略。

规划层是Agent的"决策中枢",包含三个关键能力:

任务拆解:将复杂问题分解为可执行的子任务。比如"帮我分析销售数据并生成报告"→获取数据、分析趋势、生成报告。推理引擎:基于LLM推理能力,结合上下文和可用工具决定下一步行动,类似Go中的策略模式。自我反思:Agent在执行中不断评估状态,发现行动未达预期时主动调整策略重新尝试。代码语言:javascript复制type Planner struct {

llm LLMClient

tools []Tool

maxSteps int

}

type Plan struct {

Steps []Step

CurrentStep int

}

type Step struct {

Action, Tool string

Input map[string]interface{}

Expected string

}

func (p *Planner) CreatePlan(ctx context.Context, task string) (*Plan, error) {

// 遍历 p.tools 构建工具描述列表,拼装 prompt 并限制最大步数

// response, err := p.llm.Chat(ctx, prompt)

// return parsePlan(response), nil

returnnil, nil// 省略具体实现

}

建议设置最大步数限制(比如10步)避免死循环,每个步骤应有明确的预期结果以便后续评估。

3. 工具层:能力的延伸职责:为Agent提供调用外部系统能力的标准化接口。

纯粹的LLM能力有限,但能调用各种工具时,能力边界就大大扩展了。工具层是Agent连接外部世界的桥梁,常见类型包括搜索、数据库查询、API调用、代码执行、文件操作等。通常我们会维护一个工具注册表,Agent根据任务需求自动选择合适的工具,类似Go中的插件机制。

代码语言:javascript复制type Tool interface {

Name() string

Description() string

Parameters() map[string]Parameter

Execute(ctx context.Context, params map[string]interface{}) (*Result, error)

}

type WebSearchTool struct { apiKey string }

// Name() 返回 "web_search",Description() 返回工具描述

// Parameters() 返回 {"query": {Type: "string", Required: true}}

func (w *WebSearchTool) Execute(ctx context.Context, params map[string]interface{}) (*Result, error) {

query, ok := params["query"].(string)

if !ok {

returnnil, fmt.Errorf("missing or invalid parameter: query")

}

// 调用搜索API并返回结构化结果...

return &Result{Content: "搜索结果..."}, nil

}

4. 记忆层:知识的沉淀职责:在多轮交互中保持上下文一致性,存储和检索关键信息。

记忆层通常分为短期记忆和长期记忆:

短期记忆:存储当前对话的上下文信息(历史消息、回复、中间状态),数据量大但生命周期短,类似Go中的局部变量。长期记忆:存储Agent积累的知识和经验(工作流程、用户偏好、领域知识),需要持久化存储。记忆的检索通常通过向量相似度搜索实现。代码语言:javascript复制type Memory struct {

shortTerm *ShortTermMemory

longTerm *LongTermMemory

embedding EmbeddingClient

}

type ShortTermMemory struct {

messages []Message

maxSize int

}

type LongTermMemory struct {

facts, procedures []Fact

db *gorm.DB

}

func (m *Memory) AddMessage(msg Message) {

m.shortTerm.messages = append(m.shortTerm.messages, msg)

iflen(m.shortTerm.messages) > m.shortTerm.maxSize {

copy(m.shortTerm.messages, m.shortTerm.messages[1:])

m.shortTerm.messages = m.shortTerm.messages[:len(m.shortTerm.messages)-1]

}

}

// SaveImportantFact 先写数据库成功后再更新内存,保证状态一致性

5. 执行层:动作的落地职责:将规划层的决策转化为具体行动,处理调用、返回结果、重试和错误恢复。

执行层需与规划层紧密配合:动作成功后将结果反馈给规划层判断下一步,动作失败时规划层可能需要调整后续计划。核心关注点:执行的原子性、部分失败的处理、重试机制。

代码语言:javascript复制type Executor struct {

tools map[string]Tool

retryMax int

}

type Action struct {

ToolName string

Params map[string]interface{}

}

type ExecutionResult struct {

Action Action

Success bool

Output string

Error error

Duration time.Duration

}

func (e *Executor) Execute(ctx context.Context, action Action) (*ExecutionResult, error) {

tool, ok := e.tools[action.ToolName]

if !ok {

returnnil, fmt.Errorf("unknown tool: %s", action.ToolName)

}

// 循环重试:调用 tool.Execute(),失败时使用 select+timer 监听 ctx.Done()

// 成功则返回 ExecutionResult{Success: true, Output: result.Content}

returnnil, nil// 省略具体实现

}

三、五个组件如何协作了解各组件后,来看它们如何组装成一个完整的Agent:

代码语言:javascript复制type Agent struct {

name string

perception *Perception

planner *Planner

executor *Executor

memory *Memory

llm LLMClient

}

func (a *Agent) Run(ctx context.Context, input string) (string, error) {

perc := NewPerception(input) // 1. 感知阶段

plan, err := a.planner.CreatePlan(ctx, perc.UserMessage) // 2. 规划阶段

if err != nil {

return"", fmt.Errorf("plan creation failed: %w", err)

}

// 3. 执行循环:遍历 plan.Steps,选择工具并执行,结果存入记忆

// 4. 调用 a.generateResponse(ctx) 生成最终响应

return"", nil// 省略具体实现

}

实际生产环境中,还需考虑并发控制、超时处理、错误恢复、日志记录等横切关注点。

四、现有框架一览理解了底层组件后,再看主流框架会清晰很多:

LangChain:最早被广泛使用的LLM应用开发框架之一,包含Agent开发能力,但设计复杂且主要面向Python生态,Go开发者集成成本较高。AutoGPT:以Python为主的多语言项目(含TypeScript等),以自主任务分解能力著称,已演化为完整的AutoGPT平台,思想值得借鉴。Go生态:Agent框架正在快速发展,已有 langchaingo(8.2k+ Stars)和 Google ADK for Go(Google官方Agent Development Kit)等项目。相比Python生态仍有差距,但也意味着巨大机会——可以基于现有框架扩展,或参考本文架构思想从头设计,充分利用goroutine和channel的并发优势。写在最后回到开头的问题——一个Agent通常由哪几个核心组件组成?答案并不复杂:感知层负责"听",规划层负责"想",工具层负责"做",记忆层负责"记",执行层负责"管"。五个组件各司其职,通过循环协作涌现出智能行为。

真正困难的不是理解架构本身,而是在工程实践中做出合理的取舍:记忆的检索粒度、工具调用的安全边界、规划层的步数控制、执行层的重试策略……这些细节决定了Agent在生产环境中的可靠性。

对于Go开发者而言,Agent领域有着独特的优势。Go的接口体系天然适合定义工具层的抽象契约,goroutine和channel为并发执行与状态管理提供了轻量级的原语支持,而context机制则与Agent的取消、超时语义高度契合。随着langchaingo、Google ADK for Go等框架的成熟,Go在Agent生态中的位置正在从"可选项"变为"优选项"。

架构设计的价值不在于追逐最新的框架,而在于理解底层原理后的理性选择。希望本文的分析能为你构建自己的Agent系统提供一个清晰的认知框架。