Agent 全栈开发实战:从零到生产级 Agent 应用
Agent 开发的完整技术栈
构建一个生产级的 Agent 应用不是简单地调用 LLM API。它涉及前端交互、后端编排、工具集成、记忆管理、RAG 检索、部署运维六个层面。本文梳理从零到一的完整开发路径和每个环节的关键决策。
┌─────────────────────────────────────────────────────────┐
│ 前端交互层 │
│ 聊天界面 · 流式渲染 · 工具调用可视化 · 会话管理 │
├─────────────────────────────────────────────────────────┤
│ API 网关层 │
│ 认证鉴权(JWT/API Key) · 限流 · 路由 · CORS · 日志 │
├─────────────────────────────────────────────────────────┤
│ Agent 编排层 │
│ ReAct 循环 · 任务规划 · 多Agent协调 · 错误恢复 │
├──────────────┬──────────────────┬────────────────────────┤
│ 工具网关层 │ 记忆管理层 │ RAG 检索层 │
│ Skill 调度 │ 短期/长期记忆 │ 向量检索 + 重排序 │
│ MCP 集成 │ 会话状态 │ 混合检索(关键词+语义) │
│ Function Call│ 用户画像 │ 检索增强生成 │
├──────────────┴──────────────────┴────────────────────────┤
│ 基础设施层 │
│ PostgreSQL · 向量数据库 · Redis · 消息队列 · 对象存储 │
└─────────────────────────────────────────────────────────┘
阶段一:Agent 核心引擎搭建
1.1 选择架构模式
Agent 的编排引擎是整个系统的核心。三种主流选择:
| 模式 | 适用场景 | 复杂度 | 代表实现 |
|---|---|---|---|
| ReAct 循环 | 需要工具调用的交互式 Agent | 中 | LangGraph AgentExecutor |
| Plan-then-Execute | 复杂的多步骤任务 | 高 | AutoGPT、CrewAI |
| Router + Worker | 多领域多能力的 Agent 平台 | 高 | OpenAI Swarm |
| Simple Chain | 确定性流程(如文档处理流水线) | 低 | LangChain LCEL |
1.2 ReAct 循环的核心实现
ReAct 循环是所有 Agent 系统的基础。一个健壮的实现必须包含:
class AgentExecutor:
def __init__(self, llm, tools, memory, max_steps=12, max_time=60):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.memory = memory
self.max_steps = max_steps
self.max_time = max_time
self.action_history = [] # 重复检测
async def run(self, user_input: str, session_id: str):
start_time = time.time()
context = await self.memory.load(session_id)
messages = [SystemMessage(self.build_system_prompt()),
HumanMessage(user_input)]
for step in range(self.max_steps):
# 超时熔断
if time.time() - start_time > self.max_time:
messages.append(HumanMessage("请基于已有信息给出最佳回答"))
return await self.llm.ainvoke(messages)
response = await self.llm.ainvoke(messages)
if response.tool_calls:
# 重复检测
for tc in response.tool_calls:
key = (tc.name, str(tc.args))
if self.action_history.count(key) >= 2:
messages.append(ToolMessage(
content="已重试2次,请换一种方式",
tool_call_id=tc.id))
continue
self.action_history.append(key)
# 执行工具
result = await self.tools[tc.name].arun(**tc.args)
messages.append(ToolMessage(content=result,
tool_call_id=tc.id))
messages.append(response) # 保留 assistant 消息
else:
# 没有工具调用 = 最终回答
await self.memory.save(session_id, messages)
return response.content
阶段二:工具系统设计
2.1 工具定义规范
工具是 Agent 的"手"。工具定义的质量直接影响 Agent 的可靠性:
# 工具描述的最佳实践
tool_spec = {
"name": "search_knowledge_base",
"description": (
"在内部知识库中搜索相关文档。"
"用于查找技术文档、操作手册、历史记录。"
"注意:只能搜索已索引的文档,不支持实时网页搜索。"
"如果查询结果为空,说明知识库中没有相关内容,"
"请告知用户并建议其他方式。"
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,建议用简短的短语而非完整句子"
},
"top_k": {
"type": "integer",
"description": "返回结果数量,默认5,最大20",
"default": 5,
"minimum": 1,
"maximum": 20
},
"category": {
"type": "string",
"enum": ["技术文档", "操作手册", "FAQ", "历史记录"],
"description": "限定搜索的范围分类"
}
},
"required": ["query"]
}
}
2.2 工具网关模式
当 Agent 拥有超过 10 个工具时,将所有工具定义注入 System Prompt 会消耗大量 Token 并降低选择准确率。工具网关解决这个问题:
# 两级路由:先分类再选择
class ToolGateway:
def __init__(self):
self.categories = {
"搜索类": [web_search, kb_search, image_search],
"数据类": [sql_query, api_fetch, file_read],
"操作类": [send_email, create_ticket, deploy_service],
"分析类": [code_review, data_analysis, text_summary]
}
async def resolve(self, intent: str) -> list[Tool]:
"""根据用户意图返回相关工具子集"""
category = await self.classify(intent)
return self.categories.get(category, [])
# 只在 System Prompt 中注入分类而非所有工具
# 分类确定后再注入该分类下的具体工具
阶段三:RAG 检索增强
3.1 RAG 的基本流程
RAG(Retrieval Augmented Generation)让 Agent 能够基于外部知识回答问题,而不是仅依赖模型训练数据:
# 完整的 RAG Pipeline
1. 文档加载 → 从各种来源加载文档(PDF、网页、数据库)
2. 文档切分 → 按语义边界切分为 chunk(通常 500-1500 token)
3. 向量化 → 用 Embedding 模型将 chunk 转为向量
4. 存储索引 → 存入向量数据库(Milvus/Pinecone/Weaviate)
5. 检索 → 用户查询向量化 → 向量相似度搜索 → 返回 top-k
6. 重排序 → 用 Cross-Encoder 对结果精排
7. 生成 → 将检索结果注入 LLM Prompt → 生成回答
3.2 混合检索:关键词 + 语义
纯向量检索对精确匹配(如错误码、API 名称)效果差。混合检索结合 BM25(关键词)和向量(语义),用 RRF(Reciprocal Rank Fusion)融合结果:
def hybrid_search(query, top_k=10):
# 向量检索(语义匹配)
vector_results = vector_db.search(embed(query), top_k=top_k*2)
# BM25 关键词检索(精确匹配)
bm25_results = bm25_index.search(query, top_k=top_k*2)
# RRF 融合
merged = reciprocal_rank_fusion(vector_results, bm25_results, k=60)
return merged[:top_k]
3.3 轻量级 RAG 实践:客户端搜索
对于静态站点或小型知识库,可以用前端搜索引擎替代完整 RAG 栈。例如 Fuse.js 实现客户端模糊搜索:
- 构建时生成 search-index.json(所有文档的纯文本 + 元数据)
- 前端加载 Fuse.js,配置模糊匹配参数(threshold: 0.4, distance: 100)
- 关键词高亮用
<mark>标签 - 降级方案:索引加载失败时退化为仅搜索标题/标签
实践要点:这是 2026-06-10 博客搜索功能的核心实现方式。适合文档量 < 500 篇的场景,零后端依赖。
阶段四:记忆系统设计
4.1 三层记忆架构
| 层级 | 存储 | 生命周期 | 用途 |
|---|---|---|---|
| 工作记忆 | 上下文窗口 | 单次会话 | 当前对话的完整上下文 |
| 短期记忆 | Redis / 会话存储 | 数小时-数天 | 跨轮对话的连续上下文 |
| 长期记忆 | 向量数据库 + 结构化存储 | 持久化 | 用户偏好、重要决策、知识积累 |
4.2 记忆压缩策略
上下文窗口有限(通常 4K-200K token),长对话需要压缩。常用策略:
- 摘要压缩:用 LLM 定期总结历史对话,用摘要替代原始消息
- 滑动窗口:只保留最近 N 轮完整对话,更早的用摘要
- 重要性评分:给每条消息打分,低分消息优先丢弃
- LCM(Lossless Context Management):DAG 结构存储压缩上下文,支持按需展开
阶段五:前后端集成
5.1 SSE 流式响应(推荐方案)
SSE 是 Agent 应用最主流的通信方式,支持:打字机效果的文字流、工具调用进度的实时展示、Thinking 过程的折叠显示。
# 后端 FastAPI 实现
@router.post("/chat/stream")
async def chat_stream(req: ChatRequest):
async def event_generator():
yield sse_event("start", {"session_id": req.session_id})
async for event in agent.stream(req.message, req.session_id):
match event.type:
case "thinking":
yield sse_event("thinking", {"content": event.content})
case "token":
yield sse_event("token", {"content": event.token})
case "tool_start":
yield sse_event("tool", {"name": event.tool_name,
"status": "started"})
case "tool_end":
yield sse_event("tool", {"name": event.tool_name,
"status": "done",
"result": event.result})
case "error":
yield sse_event("error", {"message": str(event.error)})
case "done":
yield sse_event("done", {})
return StreamingResponse(event_generator(),
media_type="text/event-stream")
5.2 前端消费 SSE
// 前端 EventSource 或 fetch + ReadableStream
const response = await fetch('/chat/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, session_id })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 按 \n\n 分割 SSE 事件
const events = buffer.split('\n\n');
buffer = events.pop(); // 保留不完整的事件
for (const event of events) {
const data = JSON.parse(event.replace('data: ', ''));
handleEvent(data); // 分发到 UI 更新
}
}
阶段六:部署与运维
6.1 部署方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Vercel/Netlify | 前端 + Serverless Function | 零配置、自动 SSL、CI/CD | Function 执行时间限制 |
| Railway/Render | 全栈应用 | 简单、支持长连接 | 成本随规模增长 |
| Docker + K8s | 大型生产系统 | 完全可控、弹性伸缩 | 运维复杂度高 |
| 云函数 + API Gateway | 事件驱动型 Agent | 按量付费、自动扩容 | 冷启动延迟 |
6.2 CI/CD 自动化部署
Agent 项目的部署应该全面自动化。以 Vercel + Git 为例(2026-06-10 实践):
- GitHub/GitLab 仓库关联 Vercel 项目
- main 分支 push 自动触发构建和部署
- 构建命令在
vercel.json或项目配置中定义 - 预览部署:每个 PR 自动生成独立的预览 URL
- 生产部署:合并到 main 自动更新生产环境
# vercel.json 示例(Agent 前端项目)
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"installCommand": "npm install",
"framework": "astro"
}
6.3 框架迁移的坑
2026-06-10 实践:Astro 4.x → 5.x 升级时 import.meta.glob 的 API 签名变更:
- 旧版:
import.meta.glob('./posts/*.md', true)(第二个参数是 boolean) - 新版:
import.meta.glob('./posts/*.md', { eager: true })(第二个参数是对象) - 教训:主版本升级前务必阅读 CHANGELOG 中的 Breaking Changes 部分
- 建议:用
npm outdated定期检查依赖,有计划的渐进升级而非一次性大跳
阶段七:可观测性
Agent 系统比传统应用更需要可观测性,因为 LLM 的非确定性让"复现问题"变得困难。
| 维度 | 监控内容 | 工具 |
|---|---|---|
| LLM 调用 | 输入/输出、Token 用量、延迟、模型 | LangSmith、Helicone |
| 工具调用 | 工具名、参数、结果、耗时、错误 | OpenTelemetry + 自建 |
| Agent 轨迹 | ReAct 每一步的 Thought/Action/Observation | LangSmith Traces |
| 业务指标 | 会话数、完成任务数、用户满意度 | Grafana + Prometheus |
| 成本 | 按模型/用户/会话维度的费用统计 | LLM Proxy 层统计 |
7.1 Trace 的重要性
Trace 是可观测性的核心。每个 Agent 会话的完整执行链路应该被记录下来:用户输入 → 每一步 Reasoning → 每个 Tool Call 的请求和响应 → 最终输出。当用户反馈"Agent 回答错了"时,Trace 是定位问题的唯一可靠手段。
关键决策清单
开始一个 Agent 项目时,你需要在这些节点上做选择:
- ✅ 架构模式:ReAct / Plan-Execute / Multi-Agent?
- ✅ 工具系统:直接 Function Call / Tool Gateway / MCP?
- ✅ RAG:需要吗?全量 RAG 还是轻量客户端搜索?
- ✅ 记忆:几层?压缩策略?
- ✅ 流式:SSE / WebSocket / 轮询?
- ✅ 部署:Serverless / 容器 / 传统服务器?
- ✅ 可观测性:用什么记录 Trace?成本如何监控?
- ✅ 认证:API Key / JWT / OAuth?