从零搭建电商智能导购 Agent:LangGraph 编排 + 三层记忆 + 熔断降级 + 生产部署全链路实战

从零搭建电商智能导购 Agent:LangGraph 编排 + 三层记忆 + 熔断降级 + 生产部署全链路实战

本文记录了一个完整的电商消费决策 Agent 从骨架到生产部署的全过程,涵盖 LangGraph 图编排、三层记忆体系、MCP 工具调用、熔断降级、人工审批(HITL)、nginx 反向代理等环节,并附上全程踩坑记录。代码已跑通,生产环境一键部署。


📌 目录


一、项目背景与目标

做一个能帮用户找商品、比价、查风险、盯价的智能导购 Agent,核心诉求:

  1. 自然语言交互:用户说"帮我找个 2000 以内的降噪耳机",Agent 自动搜货、比价、评估风险。
  2. 有记忆:同一会话连续对话能感知上下文;跨会话能记住用户偏好("只买索尼")。
  3. 扛得住故障:外部工具挂了不能整条链路崩,要能降级、重试、进死信。
  4. 人工可干预:下单这种关键操作要等人审批。
  5. 可生产部署 :一条 docker compose up 跑全套,HTTPS 对外。

二、技术栈总览

层级 技术选型
Agent 编排 LangGraph(StateGraph + interrupt 做 HITL)
后端 FastAPI + SSE 流式输出
工作记忆 Redis
情景记忆 PostgreSQL
长期画像 ChromaDB(向量检索)
工具层 MCP(独立 HTTP 服务,契约驱动)
韧性 三态熔断器 + 指数退避重试 + 死信表
前端 React + Vite + TypeScript
部署 Docker Compose + nginx 反向代理 + HTTPS
检查点 PostgreSQL(AsyncPostgresSaver,支持断点恢复)

三、整体架构设计

复制代码
用户 → nginx(80/443) → frontend(/) 
                    → backend(/api/) → LangGraph 图
                                        ├─ supervisor(意图路由,LLM + 关键词兜底)
                                        ├─ search(MCP 商品搜索)
                                        ├─ compare(MCP 比价)
                                        ├─ risk(MCP 风险评估)
                                        ├─ prepare_order → approval(interrupt 挂起等人)
                                        ├─ archive(归档入库)
                                        └─ monitor(盯价调度)
                                        
记忆层:Redis(工作记忆) ←→ PG(情景记忆) ←→ ChromaDB(长期画像)
韧性层:熔断器 + 重试 + 死信,串在每个节点的工具调用前

LangGraph 的图结构是核心,所有业务节点都是图上的一个 node,supervisor 根据意图决定走哪条边。


四、核心模块实现

4.1 LangGraph 图编排

用 StateGraph 定义状态机,interrupt() 实现 HITL 挂起。

State 定义:

python 复制代码
class ShoppingState(TypedDict):
    query: dict              # {raw, category, budget, constraints, order_requested}
    intent: str
    product_ids: list[str]
    comparison_table: list
    risk_report: dict
    selected_product: dict
    completed_nodes: list[str]
    memory_context: dict     # 三层记忆组装后的上下文

Supervisor 意图路由(LLM 优先,关键词兜底):

python 复制代码
VALID_INTENTS = {"search", "compare", "risk_check", "archive", "monitor", "unknown"}

async def run(state: ShoppingState) -> dict:
    text = (state.get("query") or {}).get("raw", "")
    # LLM 解析失败自动走关键词兜底,主流程永远不会因 LLM 挂掉而中断
    parsed = await _llm_route(state, text) or _keyword_route(text)
    return {
        "intent": parsed["intent"],
        "next_node": parsed["intent"],  # intent 直接当节点名
        "query": {...},
        "completed_nodes": ["supervisor"],
    }

关键设计 :next_node = intent,六个意图对应六个分支节点,图的边天然对齐意图枚举,加新意图只需加节点 + 加边。

4.2 三层记忆体系

记忆类型 存储 用途 生命周期
工作记忆 Redis 当前会话上下文快照 会话级
情景记忆 PostgreSQL 会话结构化摘要 长期
长期画像 ChromaDB 用户偏好向量检索 长期

记忆组装(Token Budget 控制):

python 复制代码
def build_memory_context(history, summaries, persona_docs):
    # 总预算 4000 token,画像最多占 60%,超了按行截断
    budget = {"total": 4000, "persona_max": 2400}
    ...

新会话开头:检索最近 5 条情景摘要 + 向量召回 Top6 画像,拼进 memory_context 给 Supervisor 当上下文。

去重与并发控制:

  • Redis Set 存消息 hash,TTL 7 天,重复消息直接回 {"duplicate": true}
  • 情景记忆写入前 setnx 分布式锁,防止并发写同一会话摘要

4.3 MCP 工具层

把所有外部电商 API 封装成独立 MCP 服务,契约驱动:

复制代码
POST /tools/{name}
Headers: X-Correlation-ID(原样回显)

三个工具:product_search / price_query / review_risk。

后端用 tool_gateway 统一调用,带语义断言 + 重试 + 降级:

python 复制代码
data, err = await call_tool_resilient(
    "product_search",
    {"category": "降噪耳机", "budget": 2000},
    node_id="search",
    degrade=MOCK,                          # 工具全挂时降级 mock
    assert_fn=lambda d: (len(d["product_ids"]) >= 1, "空结果"),
)

断言失败自动重试,重试耗尽进死信 + 返回降级结果,图继续往下跑。

4.4 韧性设计:熔断 / 重试 / 死信

每个节点调外部工具前先过熔断器:

python 复制代码
if not breaker.allow("search"):
    return {"product_ids": [], "mock": True}  # OPEN 状态直接降级

三态转换:CLOSED → 连续失败达阈值 → OPEN → 冷却时间到 → HALF_OPEN(放一个探测请求)→ 成功回 CLOSED / 失败回 OPEN。

重试 :@retry(max_attempts=3, base_delay=...) 指数退避。

死信 :耗尽重试后写入 dead_letter 表,带 retry_history,可人工排查重放。

4.5 HITL 人工审批

LangGraph 的 interrupt() 原生支持:

python 复制代码
def approval_node(state):
    decision = interrupt(
        {"product": state["selected_product"], "amount": ...},
        resumable=True,
    )
    if decision["approved"]:
        return {"order_status": "confirmed"}
    return {"order_status": "rejected"}

图跑到 interrupt() 自动挂起,前端收到 __interrupt__ 帧弹审批框,用户点通过/驳回后 POST /api/approval/{thread_id},图从断点续跑。

断点恢复 :checkpoint 存 PostgreSQL,用同一 thread_id 重启进程后能从挂起点继续,中间产出不丢。


五、生产部署实战

一条命令起全套:

bash 复制代码
docker compose -p shopping-agent -f docker-compose.yml -f docker-compose.prod.yml up -d

nginx 配置要点:

  • 独占 80/443,HTTP 301 跳 HTTPS
  • / → frontend,/api/ + /health → backend
  • SSE 关 buffering:proxy_buffering off + 1h 超时
  • limit_req 10r/s 突发 20(生产限流)
  • 动态 upstream 解析 :resolver 127.0.0.11 + 变量式 proxy_pass http://$backend$request_uri,后端晚启动不会把 nginx 拖崩

安全:

  • Bearer Token 认证(API_TOKEN 环境变量,不配=开发模式放行)
  • CORS 收紧(CORS_ORIGINS 环境变量)
  • 滑动窗口限流(RATE_LIMIT_PER_MIN)
  • 密码/Token 全走 .env,仓库只留 .env.example

内部端口隔离 :postgres/redis/chroma/mcp/backend/frontend 的端口在生产覆盖文件里 ports: !reset [] 全清空,只让 nginx 对外。


六、踩坑记录(重点)

坑 1:ChromaDB 嵌入模型冷启动卡死 ⭐

首个查画像的请求触发从 S3 懒下载 all-MiniLM-L6-v2(79MB),本机到 s3.amazonaws.com 只有 ~50KB/s,要等 20 分钟,SSE 流直接卡死无输出。

修法:

dockerfile 复制代码
# Dockerfile 构建期预下载,生产镜像自带模型
RUN mkdir -p /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2 && \
    curl -fsSL "https://chroma-onnx-models.s3.amazonaws.com/all-MiniLM-L6-v2/onnx.tar.gz" \
      -o /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2/onnx.tar.gz && \
    tar -xzf ... -C /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2/

同时 docker-compose 给 backend 挂 chromacache 命名卷,容器重建不丢缓存。

坑 2:checkpointer 必须用 AsyncPostgresSaver

同步版 PostgresSaver 的异步方法直接抛 NotImplementedError,而整条 API 是 astream/aget_state 异步链路。换成 AsyncPostgresSaver + psycopg_pool 的 AsyncConnectionPool。

坑 3:Windows 启动必须用 python run.py,别用 uvicorn

psycopg 异步模式要求 SelectorEventLoop,Windows 默认是 ProactorEventLoop,且新版 uvicorn 自建循环不理会全局 policy。run.py 里手动建循环再 serve。

坑 4:compose 必须带 -p shopping-agent

中文目录名(电商)导致 compose 项目名为空直接报错,必须显式指定 project name。

坑 5:nginx 静态 upstream 拖崩自己

用静态 upstream backend { server backend:8000; } 时,如果后端晚启动,nginx 解析失败直接 emerg 循环重启。改 resolver 127.0.0.11 + 变量式 proxy_pass 动态解析。

坑 6:测试里写死 session_id 必须 FakeRedis 隔离

联调残留的 wm:seen:{sid} 去重集合 TTL 7 天,直连真 Redis 再跑测试会命中 duplicate 帧。测试统一用 FakeRedis。


七、效果展示

发一条「帮我找个耳机」,SSE 流返回:

json 复制代码
// supervisor
{"intent": "search", "next_node": "search", "query": {"category": "耳机", ...}}

// search - 5 个真实商品
{"product_ids": ["xiaomi-buds-4-pro", "huawei-freebuds-pro-3", "airpods-pro-2-usbc", "bose-qc45-ii", "sony-wh1000xm5"]}

// compare - 15 条比价,自动选最低价
{"selected_product": {"product_id": "xiaomi-buds-4-pro", "platform": "拼多多", "price": 799, "price_verified": true}}

// risk - 5 份风险报告全评估
{"risk_report": {"xiaomi-buds-4-pro": {"negative_rate": 0.035, "shop_score": 4.6, "risk_assessed": true}, ...}}

生产环境验证:HTTPS 全链路通,内部端口宿主机不可达,SSE 经 nginx 流式正常。


八、总结与展望

已完成:LangGraph 编排骨架、三层记忆读写、MCP 工具层、熔断/重试/死信、HITL 审批、断点恢复、生产部署、前后端测试(后端 92 条全过覆盖率 89%,前端 12 条全过)。

下一步:

  1. LLM prompt 优化(Supervisor 意图解析 + 会话摘要),目前是草稿版
  2. 评测集扩充到 200 条
  3. checkpoint 版本迁移
  4. MCP 接真实电商 API(目前是 mock JSON 数据源,只换数据层,契约不动)

这个项目的核心思路是:把不确定性(LLM、外部工具)用确定性的工程手段兜住------熔断器、重试、死信、降级、关键词兜底,让 Agent 在任何环节出问题都能 graceful degradation,而不是直接崩。

如果觉得有帮助,欢迎点赞收藏 👍 有问题评论区交流。

相关推荐
for_ever_love__1 小时前
LangChain Text-to-SQL:让不会写 SQL 的人查数据库
数据库·sql·langchain·大模型·agent·rag·问答
Pniubi1 小时前
基于openclaw开发skill与自研agent中的skill开发区别
服务器·agent·skill·openclaw·龙虾
bksczm2 小时前
Redis基础篇之初识分布式
数据库·redis·分布式
jyOverQ2 小时前
Redis 主从复制、Sentinel 和 Cluster 到底有什么区别?
java·redis·sentinel
全栈弄潮儿10 小时前
Python实战第2期: 变量与数据类型
后端·python·agent
仍然.10 小时前
Redis---缓存
数据库·redis·缓存
EatFan13 小时前
2026 后端 AI 工程化:Spring AI 2.0、MCP 协议与 Agent 内嵌如何收进 Java 生产系统
java·人工智能·spring·agent·spring ai·spring boot 3·mcp
仍然.13 小时前
Redis---分布式锁
数据库·redis·分布式
七夜zippoe13 小时前
Agent 中间件架构:钩子链、插件注册与横切治理
ai·中间件·架构·agent·钩子链