Agent 流式对话接口 SSE 实时输出 iTick 金融 AI 文档 SDK & 开发工具 iTick Agent 流式对话接口文档,基于 SSE 提供实时 AI 回复输出,支持思考过程展示、心跳保活、thread_id 会话保持及多语言请求示例。

Agent 流式对话接口文档

基于 SSE(Server-Sent Events)的流式对话接口,按 Token 逐字输出 AI 回复内容,支持思考过程展示和心跳保活。

接口信息

项目说明
请求方式POST
接口路径/agent/stream/v1
通信协议SSE(text/event-stream
认证方式Header token 字段校验

请求参数

Headers

参数名类型必填说明
tokenstring用户认证令牌,用于验权和配额管理

Body(JSON)

{
  "input": {
    "messages": [
      {
        "type": "human",
        "content": "你好,请帮我分析一下..."
      }
    ]
  },
  "config": {
    "configurable": {
      "thread_id": "会话ID(可选,不传则自动生成UUID)"
    }
  }
}
字段类型必填说明
input.messagesarray对话消息列表,支持多轮历史
input.messages[].typestring消息角色:human / user / ai / assistant
input.messages[].contentstring消息文本内容
config.configurable.thread_idstring会话线程 ID,不传则自动生成 UUID

响应格式

响应为 SSE 流,每行以 data: 开头,包含 JSON 数据。

思考内容事件

模型在推理过程中输出的思考内容,由 thinkingresponse 标签包裹:

{"type": "think", "content": " thinking用户的问题是..."}
{"type": "think", "content": " response"}
  • 首次 think 事件内容以 thinking 开头,表示进入思考阶段
  • 思考结束后,发送 content: " response" 表示思考结束,转入正式回复

正式回复事件

{
  "type": "content",
  "content": "你好!",
  "node": "agent",
  "run_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
字段类型说明
typestring固定值 "content"
contentstringAI 回复的 Token 片段
nodestringLangGraph 节点名称
run_idstring当前运行 ID(UUID)

心跳包

每隔约 1 秒,如果模型仍在处理中,会发送空的心跳数据以维持连接:

data:

流结束标记

data: [DONE]

错误事件

模型安全策略拦截:

{
  "type": "error",
  "code": 3,
  "message": "模型生成内容被安全策略阻止,请尝试调整输入内容。"
}

通用异常:

{
  "error": "ExceptionClassName",
  "message": "错误详情"
}

Token 认证失败:

{
  "type": "package permission not enough",
  "code": 2002
}
错误码说明
2002token 无效、不存在或配额已用完
3内容被安全策略拦截

认证逻辑

  1. 从请求 Header 中获取 token
  2. 通过 TokenAuthManager 校验 token 是否在 Redis 中存在有效授权记录。
  3. 校验 token 是否过期(expire_time)。
  4. 校验 token 的 credits 配额是否耗尽(基于输入/输出 token 用量估算)。

任一校验失败返回 code: 2002 错误。

请求示例

cURL(快速测试)

curl -X POST https://agent.itick.org/agent/stream/v1 \
  -H "Content-Type: application/json" \
  -H "token: your_token_here" \
  -d '{
    "input": {
      "messages": [
        {"type": "human", "content": "你好,请介绍一下自己"}
      ]
    },
    "config": {
      "configurable": {
        "thread_id": "session-001"
      }
    }
  }'

Go

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strings"
)

const (
    apiURL = "https://agent.itick.org/agent/stream/v1"
    token  = "your_token_here"
)

type RequestBody struct {
    Input struct {
        Messages []Message `json:"messages"`
    } `json:"input"`
    Config struct {
        Configurable struct {
            ThreadID string `json:"thread_id"`
        } `json:"configurable"`
    } `json:"config"`
}

type Message struct {
    Type    string `json:"type"`
    Content string `json:"content"`
}

func main() {
    body := RequestBody{}
    body.Input.Messages = []Message{
        {Type: "human", Content: "你好,请介绍一下自己"},
    }
    body.Config.Configurable.ThreadID = "session-001"

    jsonBody, _ := json.Marshal(body)

    req, _ := http.NewRequest("POST", apiURL, strings.NewReader(string(jsonBody)))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("token", token)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()

    reader := bufio.NewReader(resp.Body)
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            fmt.Println("读取失败:", err)
            break
        }
        line = strings.TrimSpace(line)
        if line == "" {
            continue
        }
        if strings.HasPrefix(line, "data: ") {
            dataStr := line[6:]
            if dataStr == "[DONE]" {
                fmt.Println("=== 流结束 ===")
                break
            }
            fmt.Println(dataStr)
        }
    }
}

Python

import json
import requests

url = "https://agent.itick.org/agent/stream/v1"
headers = {
    "Content-Type": "application/json",
    "token": "your_token_here"
}
body = {
    "input": {
        "messages": [
            {"type": "human", "content": "你好,请介绍一下自己"}
        ]
    },
    "config": {
        "configurable": {
            "thread_id": "session-001"
        }
    }
}

response = requests.post(url, headers=headers, json=body, stream=True)

for line in response.iter_lines():
    if not line:
        continue
    line = line.decode("utf-8")
    if line.startswith("data: "):
        data_str = line[6:]
        if data_str == "[DONE]":
            print("=== 流结束 ===")
            break
        data = json.loads(data_str)
        print(data)

Java

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;

public class AgentStreamClient {

    private static final String API_URL = "https://agent.itick.org/agent/stream/v1";
    private static final String TOKEN = "your_token_here";

    public static void main(String[] args) throws Exception {
        String jsonBody = """
            {
                "input": {
                    "messages": [
                        {"type": "human", "content": "你好,请介绍一下自己"}
                    ]
                },
                "config": {
                    "configurable": {
                        "thread_id": "session-001"
                    }
                }
            }
            """;

        URL url = new URI(API_URL).toURL();
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("token", TOKEN);
        conn.setDoOutput(true);
        conn.setConnectTimeout(10000);
        conn.setReadTimeout(300000);

        try (OutputStream os = conn.getOutputStream()) {
            os.write(jsonBody.getBytes("UTF-8"));
            os.flush();
        }

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.isEmpty()) {
                    continue;
                }
                if (line.startsWith("data: ")) {
                    String dataStr = line.substring(6);
                    if ("[DONE]".equals(dataStr)) {
                        System.out.println("=== 流结束 ===");
                        break;
                    }
                    System.out.println(dataStr);
                }
            }
        }

        conn.disconnect();
    }
}

Node.js

const API_URL = "https://agent.itick.org/agent/stream/v1";
const TOKEN = "your_token_here";

async function streamChat() {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "token": TOKEN,
    },
    body: JSON.stringify({
      input: {
        messages: [{ type: "human", content: "你好,请介绍一下自己" }]
      },
      config: {
        configurable: { thread_id: "session-001" },
      },
    }),
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const text = decoder.decode(value, { stream: true });
    const lines = text.split("\n");
    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const dataStr = line.slice(6);
        if (dataStr === "[DONE]") {
          console.log("=== 流结束 ===");
          return;
        }
        if (dataStr) {
          console.log(JSON.parse(dataStr));
        }
      }
    }
  }
}

streamChat().catch(console.error);

注意事项

  1. 连接保活:接口每 1 秒发送心跳包,防止代理(如 Nginx)超时断开连接。Nginx 需配置 proxy_buffering offX-Accel-Buffering: no
  2. 客户端断开检测:服务端会实时检测客户端是否断开,一旦断开会立即取消 LangGraph 任务,节省 LLM 算力。
  3. 多轮对话messages 数组支持传入完整的历史消息,服务端会自动将 dict 格式的消息转换为 LangChain 消息对象。
  4. thread_id:建议客户端维护 thread_id 以保持多轮对话的上下文连续性;不传则每次生成新会话。
  1. Agent SDK

    iTick 官方 Agent SDK,为金融资讯平台提供即插即用的 AI 对话能力,支持 ShadowDOM 样式隔离、虚拟滚动、Markdown 渲染、流式 SSE 输出、React/Vue 3 多框架封装。

  2. 如何开通和续费套餐计划

    如何在iTick平台上开通和续费套餐计划。选择套餐、确认订单、完成支付的全过程,并提供续费操作指南,帮助用户轻松管理服务订阅。