Solon v4.1.0

Solon AI Agent Graph 开发

</> markdown
2026年9月18日 下午12:02:03

本系列是 Solon AI Agent 与 Solon Flow Graph 的交叉进阶系列。它不定义新的 Agent 类型,也不重复讲通用 Graph API,而是说明如何把 SimpleAgentReActAgentTeamAgent 与确定性业务节点组织成可控制、可测试、可恢复的执行图。

Agent:理解、推理、调用工具并生成结果
Graph:定义顺序、分支、汇聚、循环和子图
FlowContext:传递 Graph 业务数据并保存 FlowTrace
AgentSession:定义会话边界,保存消息历史并持有 FlowContext
FlowEngine:执行 Graph

核心原则:让 Agent 处理不确定的认知任务,让 Graph 处理确定的业务约束。

1、前置阅读

建议先了解:

本系列不会重复介绍 ChatModel、各类 Agent 的完整配置,也不会重复 Graph Fluent API、网关和 DSL 的通用参考;这里只讲 Graph 与 Agent 结合后新增的数据、状态和治理问题

2、为什么需要 Agent Graph?

当系统出现以下要求时,不宜让一个大 Agent 自由决定全部步骤:

  • 必须先审核再发布;
  • 按金额、风险等级或问题类型选择 Agent;
  • 让多个专家分析,再统一汇聚;
  • 在敏感工具或业务阶段前等待人工批准;
  • 将复杂系统拆成团队和子图;
  • 保存执行位置,并在另一进程恢复。

典型结构:

Start
  -> 需求分析 Agent
  -> Parallel
       ├─ 技术 Agent ─┐
       ├─ 成本 Agent ─┼─> 汇聚 -> 决策 Agent
       └─ 风险 Agent ─┘
  -> 人工审核
  -> End

3、两种结合方式

3.1 TeamAgent 内部 Graph

TeamAgent 内部使用 Graph。TeamProtocols.NONE 不预建拓扑,用户通过 graphAdjuster 完整定义结构:

TeamAgent team = TeamAgent.of(null)
        .name("content_team")
        .protocol(TeamProtocols.NONE)
        .agentAdd(analyst, writer)
        .graphAdjuster(spec -> {
            spec.addStart(Agent.ID_START).linkAdd(analyst.name());
            spec.addActivity(analyst).linkAdd(writer.name());
            spec.addActivity(writer).linkAdd(Agent.ID_END);
            spec.addEnd(Agent.ID_END);
        })
        .build();

这种方式具备 TeamAgent 的 Prompt、TeamTrace、AgentEvent 和结果收敛语义。当系统主体是多个 Agent 的协作时,它是本系列优先讲解的主线方式;如果系统主体是通用业务流程,Agent 只是少量节点,则应选择后面的独立业务 Graph。

NONE 是本系列用于讲解“显式 Agent Graph”的默认选择,不是 TeamAgent 构建器的框架默认协议。未显式配置时,TeamAgent 仍使用 HIERARCHICALNONE 也不提供自动协作:它不创建节点、不传递成员结果、不汇总并行结论,只保留 TeamAgent 运行时并把 Graph 定义权交给用户。

3.2 独立业务 Graph 显式调用 Agent

业务 Graph 只在部分节点需要 AI 时,应显式完成 FlowContext -> Prompt -> Agent -> FlowContext 映射:

Graph graph = Graph.create("summary_graph", spec -> {
    spec.addStart("start").linkAdd("summary");

    spec.addActivity("summary")
            .task((ctx, node) -> {
                AgentSession session = ctx.getAs(Agent.KEY_SESSION);
                String input = ctx.getAs("input.document");

                String result = summarizer.prompt(input)
                        .session(session)
                        .call()
                        .getContent();

                ctx.put("summary.result", result);
            })
            .linkAdd("end");

    spec.addEnd("end");
});

FlowContext context = FlowContext.of("summary-job-001");
context.put("input.document", "需要摘要的正文");
InMemoryAgentSession.of(context);

FlowEngine.newInstance().eval(graph, context);

InMemoryAgentSession.of(context) 会让 Session 与 Graph 共用同一个 Context。

4、Agent 为什么可以成为节点?

Agent 继承 NamedTaskComponent,因此在 TeamAgent Graph 中可以直接使用:

spec.addActivity(agent);

节点 ID 来自 agent.name();未覆盖 title() 时,标题也默认使用名称;任务入口是 Agent.run(FlowContext, Node)

但不要由此推断普通独立 Graph 会自动把 context.get("input") 转换成 Prompt。外层不存在 TeamTrace 时,没有团队协议替 Agent 准备本次 Prompt;而且不应依赖 Agent.run 自动创建的临时 Session,它持有独立 Context。独立 Graph 应使用显式适配节点,并预先执行:

InMemoryAgentSession.of(context);

5、三个容易混淆的边界

Agent Graph 不是第四种 Agent

SimpleAgentReActAgentTeamAgent 是执行单元;Agent Graph 是组织这些执行单元的结构。

Agent Graph 不是另一套 Graph 引擎

底层仍是 Solon Flow 的 GraphFlowContextFlowEngine

Graph 拓扑不等于数据自动传递

spec.addActivity(analyst).linkAdd(writer.name());

只表示执行顺序。前一个 Agent 的 outputKey 不会自动进入后一个 Agent 的 Prompt,后续文章会专门定义输入输出契约。