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
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | string | 是 | 用户认证令牌,用于验权和配额管理 |
Body(JSON)
{
"input": {
"messages": [
{
"type": "human",
"content": "你好,请帮我分析一下..."
}
]
},
"config": {
"configurable": {
"thread_id": "会话ID(可选,不传则自动生成UUID)"
}
}
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
input.messages | array | 是 | 对话消息列表,支持多轮历史 |
input.messages[].type | string | 是 | 消息角色:human / user / ai / assistant |
input.messages[].content | string | 是 | 消息文本内容 |
config.configurable.thread_id | string | 否 | 会话线程 ID,不传则自动生成 UUID |
响应格式
响应为 SSE 流,每行以 data: 开头,包含 JSON 数据。
思考内容事件
模型在推理过程中输出的思考内容,由 thinking 和 response 标签包裹:
{"type": "think", "content": " thinking用户的问题是..."}
{"type": "think", "content": " response"}
- 首次
think事件内容以thinking开头,表示进入思考阶段 - 思考结束后,发送
content: " response"表示思考结束,转入正式回复
正式回复事件
{
"type": "content",
"content": "你好!",
"node": "agent",
"run_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 固定值 "content" |
content | string | AI 回复的 Token 片段 |
node | string | LangGraph 节点名称 |
run_id | string | 当前运行 ID(UUID) |
心跳包
每隔约 1 秒,如果模型仍在处理中,会发送空的心跳数据以维持连接:
data:
流结束标记
data: [DONE]
错误事件
模型安全策略拦截:
{
"type": "error",
"code": 3,
"message": "模型生成内容被安全策略阻止,请尝试调整输入内容。"
}
通用异常:
{
"error": "ExceptionClassName",
"message": "错误详情"
}
Token 认证失败:
{
"type": "package permission not enough",
"code": 2002
}
| 错误码 | 说明 |
|---|---|
2002 | token 无效、不存在或配额已用完 |
3 | 内容被安全策略拦截 |
认证逻辑
- 从请求 Header 中获取
token。 - 通过
TokenAuthManager校验 token 是否在 Redis 中存在有效授权记录。 - 校验 token 是否过期(
expire_time)。 - 校验 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 秒发送心跳包,防止代理(如 Nginx)超时断开连接。Nginx 需配置
proxy_buffering off或X-Accel-Buffering: no。 - 客户端断开检测:服务端会实时检测客户端是否断开,一旦断开会立即取消 LangGraph 任务,节省 LLM 算力。
- 多轮对话:
messages数组支持传入完整的历史消息,服务端会自动将dict格式的消息转换为 LangChain 消息对象。 - thread_id:建议客户端维护
thread_id以保持多轮对话的上下文连续性;不传则每次生成新会话。