---
title: "Hello world：构建第一个 Agent Graph"
---

本篇使用两个 `SimpleAgent`（或者 `ReActAgent`） 构建最小 Graph：分析员先提炼需求，作者再读取分析结果并生成提纲。

```text
start -> analyst -> writer -> end
```

### 1、引入依赖

```xml
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-ai-agent</artifactId>
</dependency>
```

`solon-ai-agent` 已包含 Agent Graph 所需的 Solon Flow 依赖。

### 2、创建 ChatModel

```java
ChatModel chatModel = ChatModel.of("https://api.example.com/v1/chat/completions")
        .apiKey(System.getenv("LLM_API_KEY"))
        .model("your-model")
        .build();
```

密钥应由环境变量或安全配置注入，不要直接写进源码。

### 3、创建 Agent

```java
Agent analyst = SimpleAgent.of(chatModel)
        .name("analyst")
        .role("需求分析师")
        .instruction("提炼目标、受众和三个关键要点。")
        .outputKey("analysis_result")
        .build();

Agent writer = SimpleAgent.of(chatModel)
        .name("writer")
        .role("技术文章作者")
        .instruction("根据用户任务和以下分析生成文章提纲：\n"
                + "#{analysis_result}")
        .outputKey("draft_result")
        .build();
```

`analyst` 完成后把文本写入 `analysis_result`。`writer` 的系统指令在执行时使用 Session Context 渲染，因此能显式消费这个结果。

> `outputKey` 只负责把响应回填到 Context，不会自动把它加入下一个 Agent 的 Prompt。这里真正完成交接的是 `#{analysis_result}` 模板。

### 4、构建 Graph

```java
TeamAgent team = TeamAgent.of(null)
        .name("article_graph")
        .role("文章生产 Graph")
        .protocol(TeamProtocols.NONE)
        .agentAdd(analyst, writer)
        .outputKey("article_result")
        .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();
```

这里有四个要点：

1. `NONE` 不预建节点，必须自行添加 Start、Activity、End 和连接；
2. Agent 名称成为节点 ID，在同一 Graph 中必须唯一；
3. `NONE` 仍有 TeamAgent、TeamTrace 和团队原始 Prompt，但不会自动把成员输出接力给后续 Agent；
4. 未显式设置 `TeamTrace.finalAnswer` 时，TeamAgent 会回退到最后一条 Agent 记录的文本作为结果。

### 5、执行

```java
AgentSession session = InMemoryAgentSession.of("article-job-001");

TeamResponse response = team.prompt(
                "为 Java 开发者设计一篇 Graph 与 Agent 结合的入门文章")
        .session(session)
        .call();

System.out.println(response.getContent());
```

读取各阶段结果：

```java
FlowContext context = session.getContext();

String analysis = context.getAs("analysis_result");
String draft = context.getAs("draft_result");
String finalResult = context.getAs("article_result");
```

`draft_result` 是作者输出；`article_result` 是 TeamAgent 的最终回填值。在这个线性 Graph 中，两者通常相同。

### 6、查看结构和协作记录

```java
System.out.println(team.getGraph().toYaml());
System.out.println(team.getGraph().toPlantuml());

TeamTrace trace = team.getTrace(session);
trace.getRecords().forEach(record ->
        System.out.println(record.getSource() + ": " + record.getContent()));
```

正常情况下，Agent 记录包含：

```text
analyst
writer
```

`TeamTrace` 记录 Agent 协作，不等于所有 Graph 节点的完整执行历史；普通 Activity 和 Gateway 应通过 Flow 拦截器或业务状态验证。

### 7、如果不想使用模板

复杂交接可以插入显式适配节点。下面的定义用于替换前面 `analyst -> writer` 的两段连接：

```java
spec.addActivity(analyst)
        .linkAdd("write_with_context");

spec.addActivity("write_with_context")
        .task((ctx, node) -> {
            AgentSession current = ctx.getAs(Agent.KEY_SESSION);
            String analysis = ctx.getAs("analysis_result");

            String result = writer.prompt("请根据以下分析生成提纲：\n" + analysis)
                    .session(current)
                    .call()
                    .getContent();

            ctx.put("draft_result", result);
            TeamTrace trace = TeamTrace.getCurrent(ctx);
            if (trace != null) {
                trace.setFinalAnswer(result);
            }
        })
        .linkAdd(Agent.ID_END);
```

这种写法让 Prompt 来源、结果键和最终答案都完全显式，适合普通独立 Graph 或复杂聚合场景。
