1754 字
约 5 分钟
9
全流程智能体
无标签

系列:ToyAgent 智能体知识库 18/26

全流程智能体:把 17 块积木装成一台机器

一句话核心:守卫 → 记忆 → ReAct 循环 → 工具 → 保险丝 → 输出校验 → 回写记忆——零件是前面拆开讲的,这里装回同一台机器;骨架(chat 方法签名)永远不变,变厚的只是策略层。

为什么需要

前面十几章是教学拆解:每次只让你看清一个零件——守卫一章、记忆一章、循环一章。但真实系统里这些零件是同时在线的:

  • 守卫在模型之前拦一道(省钱的:不合规请求一次模型调用都不花);
  • 记忆在上下文两侧(读时垫进去,答完写回去);
  • 循环在中间转起来;
  • 保险丝贴着循环盯预算。

缺一个全流程视角,你就只会造零件,不会装机。全流程场景的价值不在发明新东西,而在组装顺序:守卫在最前、记忆在两侧、循环在核心、保险丝贴着循环。

核心机制

生命周期七环节

用户输入
  │
  ▼
① 输入守卫 ── 长度/敏感词拦截(不进模型,不烧钱)
  ▼
② 读取记忆 ── 历史 + 用户画像,垫进上下文
  ▼
③ 组装上下文 ── system(角色+画像) + 历史 + 本轮输入
  ▼
④ ReAct 循环 ── Thought → Action → Tool → Observation ↻(≤ MAX_STEPS 步)
  ▼
⑤ 保险丝 ──── 超出步数预算,熔断返回
  ▼
⑥ 输出校验 ── 敏感词替换 + 画像兜底问答
  ▼
⑦ 回写记忆 ── 本轮对话入档;超阈值触发上下文压缩
sequenceDiagram
    participant U as 用户
    participant A as Agent
    participant G as 守卫
    participant M as 记忆
    participant L as 大模型
    participant T as 工具集
    U->>A: 我叫王小明帮我查北京天气
    A->>G: ① 输入守卫检查
    G-->>A: 放行
    A->>M: ② 读历史与画像并提取 profile
    M-->>A: 历史 + 画像
    A->>L: ③④ 组装上下文并发起推理
    L-->>A: Thought 需要天气数据
    A->>T: Action get_weather 北京
    T-->>A: Observation 晴 12 到 22 度
    A->>L: 续步携带观察结果
    L-->>A: final 最终回答
    A->>A: ⑤ 步数 2/8 未触发保险丝
    A->>A: ⑥ 输出校验无敏感词
    A->>M: ⑦ 本轮对话回写并判断压缩
    A-->>U: 王小明你好 北京明天晴

五参与者时序全景:守卫先拦一道,记忆在读写两侧,模型与工具在循环里往返,保险丝与输出校验收尾——七环节在生产 trace 面板里正是这条链路。

主干代码

public String chat(String input) {
    // ① 输入守卫:长度与敏感意图,不通过直接返回,模型预算为零
    if (input.length() > MAX_INPUT_LEN) return "输入过长,请精简后重试";
    if (SENSITIVE.matcher(input).find()) return "该请求已被输入守卫拦截";

    // ② 记忆读取 + 画像提取(「我叫 XX」→ 存档案)
    extractProfile(input);

    // ③ 组装上下文:系统提示词(含画像)+ 历史 + 本轮输入
    List<Message> messages = new ArrayList<>();
    messages.add(Message.system(SYSTEM_PROMPT + userPortrait()));
    messages.addAll(history);
    messages.add(Message.user(input));

    // ④⑤ ReAct 循环 + 保险丝(朴素骨架原样复用)
    String answer = runReAct(messages);

    // ⑥ 输出校验 + 画像兜底(「我叫什么」不问模型,查档案)
    answer = outputGuard(answer, input);

    // ⑦ 回写记忆,超阈值触发上下文压缩
    history.add(Message.user(input));
    history.add(Message.assistant(answer));
    if (history.size() > COMPRESS_THRESHOLD) compressHistory();
    return answer;
}

注意 ① 和 ⑥:守卫不只管输入,也管输出。模型是不可靠的(可能被注入、可能胡说),所以工程上它被夹在两道校验中间——输入侧拦恶意请求,输出侧拦违规内容。这就是"模型聪明,运行时可靠"的落地形态。

画像兜底:容易被忽略的细节

用户问"我叫什么名字"时,这条信息可能已经在上下文压缩时被裁掉了,直接问模型必然失忆。正确做法是画像单独持久化,问答时先查档案:

if (input.contains("我叫什么")) {
    String name = profile.get("名字");
    if (name != null) return "你叫 " + name + "(来自画像记忆,不依赖模型)";
}

这就是记忆要分两层的原因:对话历史(易失、会压缩)与用户画像(持久、结构化)。生产记忆组件(mem0、LangGraph Store)都遵循这个分层。

举例

一次完整 chat() 的五参与者时序(用户 / Agent / 记忆 / 大模型 / 工具集):

用户:我叫王小明,帮我查一下北京明天的天气

① 输入守卫:长度 OK,敏感词未命中 → 放行(模型预算尚未消耗)
② 记忆读取:history=空;画像提取:profile["名字"]="王小明"
③ 上下文组装:system("你是助手。" + 画像) + 历史 + 本轮输入
④ ReAct 循环:
   Thought: 需要天气数据 → Action: get_weather(city=北京)
   [Agent → 工具集 → Agent] Observation: 晴,12~22°C
⑤ 步数 2/8,未触发保险丝
⑥ 输出校验:无敏感词
⑦ 回写记忆:本轮两条入档;history=2 < 阈值,不压缩

Agent:王小明你好,北京明天晴,12~22°C。

─── 若干轮后(历史已压缩,画像还在)───

用户:你还记得我叫什么吗?
⑥ 画像兜底:命中「我叫什么」→ 直接查档案,不问模型
Agent:你叫王小明(来自画像记忆,不依赖模型)——即使对话历史早已被压缩。

这个时序对应生产框架的 trace 面板:守卫是 Agent 的自环(A->>A),记忆读写是 Agent 与存储的往返,Thought 从模型流回 Agent,Action→Observation 是与工具集的往返。所谓可观测面板,只是把同样的信息可视化了。

教学版 vs 生产版

环节 教学版 生产版差距
流式输出 chat() 返回 String SSE 流式,边生成边吐字
并发会话 记忆是实例字段 按 sessionId 隔离 + 持久化
工具 switch 执行器 MCP 网关 / 插件市场,带超时重试
上下文预算 按条数压缩 按 Token 计费裁剪 + 重要度打分
可观测性 trace 内存列表 结构化日志 + trace 面板 + 告警
评测回归 无 用例集 + LLM-as-Judge,改提示词必须跑回归

生产化路径

  • 先把 chat() 的七环节顺序刻进肌肉记忆——任何生产 Agent 框架的请求处理链都能映射回这七步。
  • 优先补流式与并发会话:这两个不影响架构,但直接决定产品体验。
  • 补评测回归再调提示词:没有用例集的提示词优化等于盲改。
  • 进阶方向:RAG、CLI Agent、GUI Agent,以及工业级脚手架源码里这七环节的对应实现。
全流程智能体
https://clxhxhhr.top/posts/4718/
作者
clxstart
发布于
2026-10-13
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。