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

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

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

一、Agent的工作模式:感知-思考-行动的循环

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

感知 → 规划 → 工具调用 → 执行 → 反思 → 记忆存储

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

二、五大核心组件

1. 感知层:信息的入口

职责:接收和解析各种输入(用户问题、工具返回结果、环境状态变化等),将原始数据转换为Agent可处理的结构化格式。

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

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在执行中不断评估状态,发现行动未达预期时主动调整策略重新尝试。
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
    return nil, nil // 省略具体实现
}

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

3. 工具层:能力的延伸

职责:为Agent提供调用外部系统能力的标准化接口。

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

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 {
        return nil, fmt.Errorf("missing or invalid parameter: query")
    }
    // 调用搜索API并返回结构化结果...
    return &Result{Content: "搜索结果..."}, nil
}

4. 记忆层:知识的沉淀

职责:在多轮交互中保持上下文一致性,存储和检索关键信息。

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

  • 短期记忆:存储当前对话的上下文信息(历史消息、回复、中间状态),数据量大但生命周期短,类似Go中的局部变量。
  • 长期记忆:存储Agent积累的知识和经验(工作流程、用户偏好、领域知识),需要持久化存储。记忆的检索通常通过向量相似度搜索实现。
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)
    if len(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. 执行层:动作的落地

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

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

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 {
        return nil, fmt.Errorf("unknown tool: %s", action.ToolName)
    }
    // 循环重试:调用 tool.Execute(),失败时使用 select+timer 监听 ctx.Done()
    // 成功则返回 ExecutionResult{Success: true, Output: result.Content}
    return nil, nil // 省略具体实现
}

三、五个组件如何协作

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

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系统提供一个清晰的认知框架。