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平台上開通和續費套餐計劃。選擇套餐、確認訂單、完成支付的全過程,並提供續費操作指南,幫助用戶輕鬆管理服務訂閱。