# 运行第一个智能体 (/docs/developers/agent/quickstart)



用 API 建立一段服务智能体对话，并从事件中验证真实回复。本页只在服务端运行，不需要网页组件或嵌入策略。

准备 Bun 或支持原生 `fetch` 的 Node.js、Develop 组织的 API Key，以及该组织可用的 `deepseek-flash` 模型和余额。在服务端进程设置 `PRAXIS_API_KEY`；密钥创建见[API Key](/docs/developers/account/api-keys)。如果你的 Key 属于其他环境，先统一 API 地址、组织和模型，不能混用环境。

## 1. 创建智能体 [#create-agent]

新建 `first-agent.mjs`，从下面的代码开始。后续三段 JavaScript 按顺序追加到同一个文件，不要分别执行。助手只回答一个算术问题，便于判断首次调用是否完成；业务工具在接通后再添加。

`POST /agent/v1/agents` 使用名称、类型、模型和指令创建配置。`tools: []` 不声明额外工具。请求辅助函数为每次调用检查状态并设置超时，失败时只输出状态和请求标识，不打印 Key。

```js
import assert from "node:assert/strict";
import { setTimeout as delay } from "node:timers/promises";

const { PRAXIS_API_KEY } = process.env;
assert(PRAXIS_API_KEY, "Set PRAXIS_API_KEY in the server process");
const base = "https://api-develop.deepraxis.com/agent/v1";
async function request(path, body) {
  const response = await fetch(`${base}${path}`, {
    method: body === undefined ? "GET" : "POST",
    headers: {
      Authorization: `Bearer ${PRAXIS_API_KEY}`,
      "Content-Type": "application/json",
    },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
    signal: AbortSignal.timeout(30_000),
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}; request=${response.headers.get("X-Request-Id") ?? response.headers.get("request-id")}`);
  }
  return response.json();
}
const agent = await request("/agents", {
  name: "First support agent", kind: "service", model: "deepseek-flash",
  system: "Answer the user's question briefly. Do not use tools for this exercise.",
  tools: [],
});
assert(agent.id.startsWith("agent_"));
console.log({ agent: agent.id, version: agent.version });
```

**预期结果：** 创建响应为 HTTP 200，包含公开 `id` 和 `version`。运行脚本时会打印这两个值，后续直接复用，不手写猜测 ID。

## 2. 建立会话 [#create-session]

追加以下代码。`POST /agent/v1/sessions` 把刚创建的版本固定在会话中。省略 `environment_id` 使用轻型机器；这一步不需要先创建沙箱。示例显式设置 100 美分的会话预算，不把预算金额当作预计费用。

```js
const session = await request("/sessions", {
  agent: { type: "agent", id: agent.id, version: agent.version },
  title: "First API conversation",
  budget: { type: "limit", max_list_cost: { amount: "100", currency: "USD" } },
});
assert(session.id);
console.log({ session: session.id });
```

**预期结果：** HTTP 201，响应包含会话 `id`。此时还没有发送用户问题；即使状态为 `idle`，也不代表已经得到回复。预算决定后续请求能否继续，不保证生成特定答案。

## 3. 发送第一条消息 [#send-message]

追加以下代码，调用 `POST /agent/v1/sessions/{session_id}/events`。`events` 是输入数组；这里发送一条 `user.message`，其中只有一个非空文本块。

```js
const accepted = await request(`/sessions/${session.id}/events`, {
  events: [{ type: "user.message", content: [{ type: "text", text: "What is 2 + 2?" }] }],
});
assert(accepted.data.some(event => event.type === "user.message"));
```

**预期结果：** HTTP 200，`data` 中出现已接收的用户事件。这是接收确认；不要把它当作智能体答案，也不要因为暂时没有答案就再次发送同一条消息。

## 4. 读取并验证回复 [#verify]

追加以下代码，然后执行 `node first-agent.mjs` 或 `bun first-agent.mjs`。它用 `GET /agent/v1/sessions/{session_id}/events` 分页读取事件，先找到 `agent.message`，再找到其后的 `session.status_idle`。初始空闲状态不会被误判为成功。

```js
let completed = false;
const deadline = Date.now() + 120_000;
while (Date.now() < deadline) {
  const events = [];
  let page;
  do {
    const query = new URLSearchParams({ order: "asc", limit: "100" });
    if (page) query.set("page", page);
    const result = await request(`/sessions/${session.id}/events?${query}`);
    events.push(...result.data);
    page = result.next_page;
  } while (page && Date.now() < deadline);
  const reply = events.findIndex(event => event.type === "agent.message");
  const idle = events.findIndex((event, i) => i > reply && event.type === "session.status_idle");
  if (reply >= 0 && idle > reply) {
    const text = events.filter(event => event.type === "agent.message")
      .flatMap(event => event.content ?? []).filter(block => block.type === "text")
      .map(block => block.text).join("\n");
    assert(text.trim(), "Expected a nonempty reply");
    console.log({ reply: text, completed: true });
    completed = true;
    break;
  }
  await delay(1500);
}
assert(completed, `No completed reply before timeout; inspect session ${session.id}`);
```

**完成判定：** 进程正常退出，输出非空 `reply` 和 `completed: true`，答案应表达 4。脚本的 120 秒是本例等待期限，不是服务运行上限。只通过文本非空断言还不足以验证业务答案；上线前为实际问题准备预期结果。

## 失败时检查 [#troubleshoot]

| 症状               | 下一步                                                            |
| ---------------- | -------------------------------------------------------------- |
| HTTP 401         | 检查服务端 Key 是否存在、有效，是否对应当前环境                                     |
| HTTP 400         | 核对字段与模型；错误详情见[限制与错误](/docs/developers/agent/limits-and-errors) |
| HTTP 404         | 使用创建响应里的 ID，确认组织与所选版本一致                                        |
| HTTP 429 或临时上游失败 | 按响应重试提示退避；先查询会话，避免重新创建资源或重复发消息                                 |
| 超时仍无完成回复         | 保留会话 ID，读取事件检查工具等待、预算或运行错误；不要仅依据创建响应的状态判断                      |

重新运行整份脚本会新建智能体和会话。排错时优先查询已输出的 ID。测试记录可在确认不再需要后按 [API 参考](/docs/api/native/agent)处理，不要把仍在运行的会话当作可直接删除的记录。

## 下一步 [#next]

继续[配置智能体](/docs/developers/agent/configure)、了解[智能体与会话](/docs/developers/agent/concepts)，或用 [PraxisKit 接入网页](/docs/developers/agent/embed/quickstart)。

交给你的代理：

```text
按 /docs/developers/agent/quickstart 在服务端运行首次对话。
需要我提供 Develop 组织 API Key，并确认 deepseek-flash 可用。
完成标准：收到非空 agent.message，其后出现 session.status_idle。
不要把 Key 放进浏览器或日志；记录智能体 ID、版本和会话 ID。
```
