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