Agent 的基本架构由哪些核心组件构成?
把 Agent 拆开看,它不是一个模型,而是一套围绕模型的"外骨骼":模型负责决策,其余组件负责让决策变得可执行、可约束、可观测。 不同框架(LangGraph、AutoGen、Coze 等)的命名五花八门,但骨架基本一致,缺了哪一层都会在线上以特定方式出问题。
1. 结论:七层骨架,三层是刚需
最小可用 Agent 只需要 模型 + 工具 + 循环;要做到生产可用,还得补上 上下文/记忆、规划、护栏、观测。 按重要性排序:模型与提示、工具层、运行时循环(刚需)→ 记忆、规划(提质)→ 护栏、观测与评测(保命)。
2. 机制:分层结构
┌───────────────────────────────────────────────┐
│ 接入层:CLI / HTTP API / IM 机器人 / 定时任务 │
├───────────────────────────────────────────────┤
│ 编排层:运行时循环、状态机、中断与恢复 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 规划器 │ │ 反思器 │ │ 路由器 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├───────────────────────────────────────────────┤
│ 能力层:工具/函数调用、代码执行、检索(RAG) │
├───────────────────────────────────────────────┤
│ 状态层:短期上下文、长期记忆、任务黑板 │
├───────────────────────────────────────────────┤
│ 模型层:主模型 + 小模型(路由/摘要) + 兜底模型 │
├───────────────────────────────────────────────┤
│ 治理层:权限、护栏、限流预算、日志追踪、评测 │
└───────────────────────────────────────────────┘
关键点:模型层之上的一切都是为了让模型少犯错。把上下文裁剪好、把工具描述写准、把错误信息写清,效果提升往往比换更大的模型明显。
3. 组件职责与典型故障
| 组件 | 职责 | 常见实现 | 缺少/做错时的表现 |
|---|---|---|---|
| 模型(Model) | 决策、生成、解析 | 通用大模型 + 小模型分流 | 复杂任务跑不动,或成本失控 |
| 提示模板(Prompt) | 角色、约束、输出格式 | 系统提示 + few-shot 示例 | 输出格式飘、工具名记错 |
| 工具层(Tool) | 把能力暴露给模型 | JSON Schema 描述的函数、MCP 服务 | 参数类型错、选错工具、越权 |
| 运行时(Runtime) | 执行动作、管理循环 | 手写 while、LangGraph 图 | 死循环、超时、状态丢失 |
| 记忆(Memory) | 跨轮次保存信息 | 上下文窗口 + 向量库 + 关系库 | 多轮失忆、重复问同样的问题 |
| 规划器(Planner) | 拆解目标、排步骤 | 结构化输出生成 DAG | 长任务中途跑偏、漏步骤 |
| 护栏(Guardrail) | 输入输出校验、权限 | 规则 + 分类模型 + 白名单 | 提示注入、误删数据、泄露 |
| 观测(Tracing) | 记录每步输入输出 | OpenTelemetry、Langfuse | 出问题无法定位到哪一步 |
4. 用配置描述一个 Agent
把架构写成声明式配置,比散落在代码里更容易 review 和灰度:
# agent.yaml
name: ops-assistant
model:
primary: gpt-4o-mini # 主决策模型
router: gpt-4o-mini # 意图分类用小模型即可
temperature: 0.2 # 工具型 Agent 要低温度
prompt:
system: prompts/ops.md
max_context_tokens: 12000
tools:
- name: query_metrics
schema: schemas/query_metrics.json
side_effect: read # read / write,write 需人工确认
- name: restart_pod
schema: schemas/restart_pod.json
side_effect: write
require_approval: true
memory:
short_term: { max_turns: 20, strategy: summary }
long_term: { store: pgvector, top_k: 5 }
runtime:
max_steps: 12 # 硬上限,防死循环
timeout_seconds: 120
on_error: retry_once_then_ask
guardrails:
input: [prompt_injection_check, pii_redact]
output: [schema_validate, secret_scan]
observability:
trace: otel
sample_rate: 1.0
装配代码只做一件事:把配置翻译成运行时对象,缺省值全部显式化。
def build_agent(cfg):
tools = {t["name"]: load_tool(t) for t in cfg["tools"]}
for t in cfg["tools"]:
if t.get("side_effect") == "write":
tools[t["name"]] = require_approval(tools[t["name"]])
return Agent(
llm=load_model(cfg["model"]["primary"]),
system=render(cfg["prompt"]["system"]),
tools=tools,
max_steps=cfg["runtime"]["max_steps"],
memory=build_memory(cfg["memory"]),
hooks=[guard(cfg["guardrails"]), trace(cfg["observability"])],
)
hooks 这一层是区别 demo 与生产的分水岭:所有输入输出都过一遍护栏和埋点,而不是靠开发者在每个工具里手动加日志。
5. 常见追问
问:哪些组件可以省? 记忆可以先用滑动窗口顶替,规划器可以先用"每步重新决策"顶替,观测绝对不能省——没有 trace 的 Agent 等于线上盲飞。护栏在只读场景可以简化,写场景必须有。
问:规划器和 ReAct 冲突吗? 不冲突。规划器负责粗粒度步骤,每个步骤内部仍可以用 ReAct 边想边做。区别只是"步骤在什么时候确定"。
问:工具层怎么设计才算合格? 四条:命名动词开头且语义唯一;参数用 JSON Schema 严格约束(枚举、必填、范围);错误返回可读的原因和修复建议;写操作幂等或带确认。工具描述是给模型看的 API 文档,写得含糊,模型就一定会用错。
问:状态放在哪? 短期状态(当前任务、已执行步骤)放运行时内存,进程重启即失效;需要跨会话的放外部存储。别把状态塞进提示词里靠模型自己记,那是最不可靠的存储。
小结
Agent 架构可以概括成"一个模型 + 一圈外骨骼":工具让它能动手,记忆让它不失忆,规划让它不跑偏,护栏和观测让它不出事、出事能查。面试时按"刚需三层、提质两层、保命两层"讲,比罗列框架组件清单更有层次。