Skip to content

Agent 引擎

Agent 是 MateClaw 的核心推理组件。它接收用户输入,决定是直接回答、调用工具还是将任务拆解为多步执行计划。MateClaw 基于 spring-ai-alibaba-graph-core 的 StateGraph 引擎实现了两种 Agent 模式:ReActPlan-and-Execute

架构总览

AgentGraphBuilder
  |
  |-- buildReActAgent()         --> StateGraphReActAgent (CompiledGraph)
  |-- buildPlanExecuteAgent()   --> StateGraphPlanExecuteAgent (CompiledGraph)
  |
  +-- buildRuntimeChatModel()   --> ChatModel (DashScope / OpenAI-compatible / Anthropic)

所有 Agent 的构建由 AgentGraphBuilder 统一负责。它根据 AgentEntity 中的配置(类型、迭代上限等),选择模型、注册工具、编译状态图,最终返回一个可用的 BaseAgent 实例。运行时统一使用全局默认模型,不依赖 Agent 实体上的 modelName 字段。

核心抽象

BaseAgent

所有 Agent 继承自 BaseAgent,它定义了统一的对话接口和状态管理:

方法说明
chat(userMessage, conversationId)同步对话,返回完整文本
chatStream(userMessage, conversationId)流式对话,返回 Flux<String>
execute(goal, conversationId)执行任务(Plan-Execute 模式入口,ReAct 下退化为 chat)
chatWithReplay(userMessage, conversationId, toolCallPayload)审批通过后重放工具调用
chatWithReplayStream(...)审批通过后的流式重放

BaseAgent 还负责加载会话历史(buildConversationHistory)、过滤审批占位消息、转换消息格式等基础工作。

AgentState

Agent 在生命周期中经历以下状态:

状态说明
IDLE空闲,等待新消息
PLANNING规划中,正在生成执行计划
EXECUTING执行中,正在执行工具调用或子任务
RUNNING运行中(ReAct 循环或 Plan-Execute 图执行期间)
WAITING_USER_INPUT等待用户输入
DONE已完成
FAILED执行失败
ERROR错误状态(流式调用异常等)

FinishReason

状态图终止时会标记终止原因,供前端和日志区分:

说明
NORMALLLM 直接给出最终回答
SUMMARIZED经过 SummarizingNode 压缩后完成
MAX_ITERATIONS_REACHED达到最大迭代次数后强制收束
ERROR_FALLBACK发生错误后降级回答

ReAct Agent

StateGraphReActAgent 是默认的 Agent 类型,基于 StateGraph v2 实现显式可控的 Thought-Action-Observation 循环。

状态图拓扑

START
  |
  v
ReasoningNode -----(ReasoningDispatcher)----> 4 路分支
  |                  |                |               |
  v                  v                v               v
ActionNode     SummarizingNode  LimitExceededNode  FinalAnswerNode
  |                  |                |               |
  v                  |                v               v
ObservationNode      |          FinalAnswerNode      END
  |                  |                |
  | (ObservationDispatcher)          v
  |    |         |        |         END
  v    v         v        v
  回到  Summ-   Limit-   Final-
 Reason arizing Exceeded Answer

完整路由规则:

ReasoningNode --> ReasoningDispatcher:
  1. 迭代超限      --> LimitExceededNode(最高优先级)
  2. 需要工具调用   --> ActionNode
  3. 需要总结压缩   --> SummarizingNode
  4. 可直接回答     --> FinalAnswerNode

ActionNode --> ObservationNode(固定边)

ObservationNode --> ObservationDispatcher:
  0. 审批等待       --> FinalAnswerNode(Graph 终止,由 Replay 继续)
  1. 迭代超限       --> LimitExceededNode(强制终止)
  2. 有错误         --> LimitExceededNode
  3. 需要总结       --> SummarizingNode
  4. 继续循环       --> ReasoningNode

SummarizingNode --> ReasoningNode(固定边,压缩后重新推理)
LimitExceededNode --> FinalAnswerNode(固定边)
FinalAnswerNode --> END

节点详解

ReasoningNode(推理节点)

调用 LLM 进行单次推理,判断是否需要工具调用。关键设计:通过 internalToolExecutionEnabled=false 禁用 ChatModel 内部的工具循环,让 StateGraph 完全控制 ReAct 迭代。

功能:

  • 构建包含系统提示词、会话历史、观察历史的 Prompt
  • 调用 LLM 获取响应,提取 ToolCall 列表
  • 支持 forced_tool_call 机制:审批通过后跳过 LLM 调用,直接发出预批准的工具调用
  • 通过 NodeStreamingChatHelper 实时推送 content/thinking 增量
  • 检查停止标志(ChatStreamTracker.isStopRequested

ActionNode(工具执行节点)

委托 ToolExecutionExecutor 执行工具调用。

功能:

  • 支持并发工具执行和审批 barrier
  • 两阶段执行:先顺序检查 ToolGuard,再分段并发执行
  • forced_replay 模式下跳过 ToolGuard 检查
  • 审批等待时设置 AWAITING_APPROVAL=true,Graph 将在下一个 Dispatcher 终止
  • 检查停止标志

ObservationNode(观察节点)

处理工具执行结果,是迭代控制的核心节点之一。

功能:

  • 通过 ObservationProcessor 对工具结果进行标准化和截断
  • 递增迭代计数器
  • 判断是否需要进入 summarizing 阶段(上下文过长时)

SummarizingNode(总结压缩节点)

当满足以下条件时被路由至此:

  • observationHistory 条目过多
  • 单次工具结果超过阈值
  • 多轮观察结果过于冗长

调用 LLM 对已有观察结果进行压缩总结,压缩后回到 ReasoningNode 继续推理。

LimitExceededNode(超限处理节点)

当迭代达到 maxIterations 时被路由至此。不会抛异常,而是:

  1. 对过长的 observationHistory 做内联压缩
  2. 注入"停止工具调用"系统指令
  3. 让 LLM 基于已有信息生成简洁最终回答
  4. 标记 finishReason = MAX_ITERATIONS_REACHED

FinalAnswerNode(最终回答节点)

汇聚所有终止路径,设置 finalAnswerfinalThinkingfinishReason。优先级:

  1. finalAnswerDraft(来自 LimitExceeded/Summarizing 路径)
  2. finalAnswer(来自 Reasoning 直接回答路径)

审批等待路径下返回空 finalAnswer,内容已通过 streaming 推送。

交互示例

用户发送:"帮我查一下北京今天的天气"

[ReasoningNode]    LLM 分析意图 --> 需要工具调用 (WebSearchTool)
[ActionNode]       执行 WebSearchTool("北京今天天气") --> ToolGuard 检查通过
[ObservationNode]  结果:北京今天晴,15-26C --> 迭代 1/10,继续
[ReasoningNode]    LLM 判断信息充分 --> 直接回答
[FinalAnswerNode]  汇聚回答:"北京今天天气晴朗,气温 15-26 C..."

Plan-and-Execute Agent

StateGraphPlanExecuteAgent 适用于需要多步骤才能完成的复杂任务。它先评估是否需要规划,简单问题直接回答,复杂任务则生成计划并逐步执行。

状态图拓扑

START
  |
  v
PlanGenerationNode ---(PlanGenerationDispatcher)--> 2 路分支
  |                                                    |
  v                                                    v
StepExecutionNode                              DirectAnswerNode
  |                                                    |
  | (StepProgressDispatcher)                           v
  |    |              |          |                    END
  v    v              v          v
 继续  PlanSummaryNode  END
 执行       |       (审批终止)
 下一步     v
          END

节点详解

PlanGenerationNode(计划生成节点)

职责:

  1. 判断是否需要规划(简单问答快速退出到 DirectAnswerNode)
  2. 需要规划时生成计划 JSON(步骤数 2-6 个)
  3. PlanningService.createPlan() 持久化到 mate_planmate_sub_plan
  4. 发布 plan_created 事件

StepExecutionNode(步骤执行节点)

执行当前步骤,内置显式工具执行循环(internalToolExecutionEnabled=false)。

  • 单步最大工具调用次数限制为 5 次
  • 支持审批流程:需要审批的工具调用创建 pending,发出 SSE 事件后非阻塞返回
  • 审批通过后通过 replay 机制重新执行

PlanSummaryNode(计划汇总节点)

汇总所有步骤的执行结果,调 LLM 生成最终总结,调 planningService.completePlan() 标记计划完成。

DirectAnswerNode(直接回答节点)

当 PlanGenerationNode 判定不需要规划时,将 direct_answer 透传为 final_summary,直接结束图执行。

数据模型

计划持久化到两张表:

mate_plan

字段说明
id计划 ID
conversation_id关联会话
goal用户原始请求
statuspending / running / completed / failed

mate_sub_plan

字段说明
id子步骤 ID
plan_id父计划
step_order执行顺序(1, 2, 3...)
description步骤描述
statuspending / running / completed / failed
result步骤执行结果

交互示例

用户发送:"帮我调研 Spring AI 框架,对比各主要实现,并写一份简要报告"

[PlanGenerationNode]  判断需要规划 --> 生成 4 步计划
  步骤 1: 搜索 Spring AI 框架的主要实现和特性
  步骤 2: 搜索各实现的优缺点和社区活跃度
  步骤 3: 对比分析,形成结构化表格
  步骤 4: 撰写简要调研报告

[StepExecutionNode]   执行步骤 1
  LLM --> WebSearchTool("Spring AI framework implementations") --> 结果
  标记步骤 1 完成

[StepExecutionNode]   执行步骤 2 (携带步骤 1 的上下文)
  ...

[StepExecutionNode]   执行步骤 3
  ...

[StepExecutionNode]   执行步骤 4
  ...

[PlanSummaryNode]     汇总所有步骤结果,生成最终调研报告

DynamicAgent

DynamicAgent 从数据库(mate_agent 表)动态加载配置,支持在运行时通过 UI 或 API 创建和修改 Agent,无需重启应用。

AgentGraphBuilder.build(AgentEntity) 每次构建时读取最新的 AgentEntity,组装系统提示词、工具集和状态图。

数据库字段

字段类型说明
nameStringAgent 名称
descriptionStringAgent 描述
agent_typeStringreactplan_execute
system_promptText系统提示词
max_iterationsInteger最大迭代次数(默认 10)
enabledBoolean是否启用
iconString图标(emoji 或 URL)
tagsString标签(逗号分隔)

注意:model_name 字段为历史残留,运行时统一使用全局默认模型(通过"设置 - 模型"页面配置)。

Agent 选型指南

何时使用 ReAct

  • 简单问答:一轮推理即可回答,无需工具
  • 信息检索:搜索 + 总结,通常 2-3 次循环
  • 单一工具操作:读文件、查时间等明确操作
  • 实时对话:需要快速响应的场景

何时使用 Plan-and-Execute

  • 多步骤任务:需要按顺序完成多个操作
  • 调研报告:涉及多次搜索、对比和汇总
  • 复杂分析:需要先收集信息再综合判断
  • 需要可追踪进度的任务(每步结果持久化到数据库)
场景推荐模式原因
简单问答ReAct快速响应,直接回答路径无额外开销
信息检索ReAct搜索 + 总结,2-3 次循环
多步骤任务Plan-and-Execute计划拆解 + 逐步执行 + 结果汇总
调研报告Plan-and-Execute多次搜索、对比、汇总
文件读写ReAct操作明确,单次工具调用
数据对比Plan-and-Execute分别收集再对比

系统提示词编写建议

系统提示词通过 AgentGraphBuilder.buildEnhancedPrompt() 增强,会自动注入技能指令和工作区上下文。编写自定义提示词时注意:

  1. 明确角色和职责:告诉 Agent 它是谁、能做什么
  2. 说明可用工具:列出工具名称和使用场景(工具描述会自动注入,但额外说明有助于引导 LLM)
  3. 设定输出格式:如需结构化输出,在提示词中明确说明
  4. 避免冲突指令:不要与 MateClaw 的内置行为(如工具调用格式)产生矛盾

示例:

你是一名专业的技术文档助手。你的职责是:
1. 根据用户需求搜索和整理技术资料
2. 使用清晰的结构化格式回答问题
3. 提供代码示例时确保语法正确
4. 如果不确定,先搜索再回答,不要凭空编造

注意事项:
- 优先使用中文回答
- 引用信息时标注来源
- 对于时效性强的问题,先获取当前日期再搜索

配置参考

Agent 级配置

配置项默认值说明
maxIterations10ReAct 最大循环次数,Plan-Execute 中单步最大工具调用为 5 次
agentTypereactAgent 类型:reactplan_execute
enabledtrue是否启用

图级配置

配置项说明
recursionLimitStateGraph 编译时设置,ReAct 为 maxIterations * 2 + 5,Plan-Execute 为 maxIterations * 3 + 10
ObservationProcessor控制工具结果截断阈值和观察历史压缩策略

流式事件

Agent 在结构化流式模式(chatStructuredStream)下会通过 SSE 推送以下事件类型:

事件类型说明
phase当前执行阶段(reasoning / action / observation / summarizing 等)
tool_call_start工具调用开始
tool_call_end工具调用结束(含结果摘要)
plan_created计划已创建(Plan-Execute 模式)
step_start / step_end步骤开始/结束(Plan-Execute 模式)
approval_required工具调用需要审批
_usage_finalToken 使用量统计(流结束时)

通过 API 管理 Agent

创建 Agent

bash
curl -X POST http://localhost:18088/api/v1/agents \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "技术助手",
    "description": "专业的技术文档助手",
    "agentType": "react",
    "systemPrompt": "你是一名专业的技术文档助手...",
    "maxIterations": 10
  }'

查询 Agent 列表

bash
curl http://localhost:18088/api/v1/agents \
  -H "Authorization: Bearer <token>"

获取 Agent 详情

bash
curl http://localhost:18088/api/v1/agents/1 \
  -H "Authorization: Bearer <token>"

更新 Agent

bash
curl -X PUT http://localhost:18088/api/v1/agents/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "技术助手 v2",
    "systemPrompt": "更新后的提示词...",
    "maxIterations": 15
  }'

删除 Agent

bash
curl -X DELETE http://localhost:18088/api/v1/agents/1 \
  -H "Authorization: Bearer <token>"

流式对话

bash
curl -N "http://localhost:18088/api/v1/agents/1/chat/stream?message=你好&conversationId=default" \
  -H "Authorization: Bearer <token>"

调试指南

开启 DEBUG 日志

application.yml 中添加:

yaml
logging:
  level:
    vip.mate.agent: DEBUG
    vip.mate.agent.graph: DEBUG

这将输出每个节点的执行详情,包括:

  • Agent 状态变迁(IDLE -> RUNNING -> IDLE
  • ReasoningDispatcher / ObservationDispatcher 的路由决策
  • 迭代计数(Iteration 1/10
  • 工具调用和结果摘要
  • ToolGuard 检查结果

常见问题排查

Agent 无响应或超时

  • 检查模型配置是否正确("设置 - 模型"页面)
  • 查看日志中是否有 StateGraph chat failedERROR 状态
  • 确认 API Key 有效且有额度

Agent 陷入循环

  • 查看日志中的迭代计数,确认 maxIterations 是否合理
  • 检查是否有工具持续返回错误导致 LLM 反复重试
  • 如果经常触发 MAX_ITERATIONS_REACHED,考虑增大 maxIterations 或优化提示词

工具调用不生效

  • 确认工具已在 ToolRegistry 中注册
  • 检查 ToolGuard 是否拦截了调用
  • 查看 ActionNode 的日志输出

审批流程中断

  • 审批等待时 Graph 会正常终止(AWAITING_APPROVAL=true
  • 用户决策后通过 chatWithReplay / chatWithReplayStream 继续执行
  • 如果 replay 失败,检查 toolCallPayload 格式是否正确

下一步