JVS Crew API 文档

更新时间:
复制为 MD 格式

概述

JVS Crew 是阿里云无影团队开发的 AI 智能助手平台。通过JVS Crew API,可以将 JVS Crew 的 AI 对话能力集成到自有应用中。

API 分为四大类:

  • 会话交互——获取令牌、发起对话、上传文件

  • 会话管理——列举/查看/删除历史会话

  • 定时任务——创建/管理/监控定时执行任务

  • 计费查询——查看用量和消耗明细

快速开始

集成 JVS Crew 最少只需两步:

  1. 获取令牌——使用 AK/SK 调用 GetAccessToken,获取 AccessToken。

  2. 发起对话——携带 AccessToken 调用 Chat,通过 SSE 流接收回复。

完整 Python 示例参见使用示例章节。

基础信息

  • 协议:HTTPS

  • Base URLhttps://wuyingai.cn-shanghai.aliyuncs.com

  • API 版本2026-03-11(所有接口的 x-acs-version 或 POP Version 参数)

认证方式

本文档的 API 采用两种认证方式:

认证方式

适用 API

说明

AK/SK 签名

GetAccessToken、GetChatFileUploadUrl、SyncContext、GetBillingOverview、ListUserConsumption

使用阿里云 AccessKey 进行 POP V1 签名,详见使用示例

JWT 令牌

Chat、ListSessions、ListSessionHistory、StopSession、DeleteSession、GetSandboxInfo、CreateScheduledTask、UpdateScheduledTask、GetScheduledTask、ListScheduledTasks、DeleteScheduledTask、ListScheduledTaskRuns

先调用 GetAccessToken 获取 AccessToken,放入 Query 参数 Authorization=Bearer <token>

通用请求格式

两种认证方式的请求格式不同:

AK/SK 签名接口:所有参数(包括 Action、业务参数、签名参数)均通过 Query String 拼接在 URL 中,使用 POST 方法发送。

POST https://wuyingai.cn-shanghai.aliyuncs.com/?Action=GetAccessToken&ExternalUserId=user-38764&Signature=...&其他签名参数

JWT 令牌接口Authorization 放在 Query String 中,操作名称通过 x-acs-action Header 指定,业务参数放在 JSON Body 中。

POST https://wuyingai.cn-shanghai.aliyuncs.com/?Authorization=Bearer%20<token>
Headers: x-acs-action: ListSessions, x-acs-version: 2026-03-11, x-acs-date: <UTC 时间>
Body: {"ExternalUserId": "user-38764"}
说明

Chat 接口的 URL 路径为 /api/agent/chat(非根路径 /),其余 JWT 接口均使用根路径。

API 一览

API

分类

认证

说明

GetAccessToken

会话交互

AK/SK

获取 JWT 访问令牌

Chat

会话交互

JWT

流式对话(SSE)

GetChatFileUploadUrl

会话交互

AK/SK

获取文件上传地址

SyncContext

会话交互

AK/SK

同步文件到沙箱

ListSessions

会话管理

JWT

列举会话列表

ListSessionHistory

会话管理

JWT

获取会话历史

StopSession

会话管理

JWT

中止正在执行的对话

DeleteSession

会话管理

JWT

删除对话记录

GetSandboxInfo

会话管理

JWT

获取沙箱信息

GetBillingOverview

计费查询

AK/SK

租户当月计费概览

ListUserConsumption

计费查询

AK/SK

按用户查询消耗明细

CreateScheduledTask

定时任务

JWT

创建定时任务

UpdateScheduledTask

定时任务

JWT

更新定时任务

GetScheduledTask

定时任务

JWT

查询单个任务详情

ListScheduledTasks

定时任务

JWT

分页列举任务

DeleteScheduledTask

定时任务

JWT

删除任务

ListScheduledTaskRuns

定时任务

JWT

查询执行记录

会话交互接口

GetAccessToken - 获取对话令牌

接口描述

获取用户进行对话所需访问令牌(AccessToken),供后续调用 Chat 接口验证身份。

令牌格式:AccessToken 为 JWT,由 Header.Payload.Signature 三段经 Base64URL 编码后以 . 连接成一行;下表示例为脱敏示意,实际 RequestId、JWT 各段均更长。

令牌有效期:AccessToken 在一段时间内有效,过期后须重新调用本接口获取新令牌。

请求信息

  • 认证方式:POP V1 签名(AK/SK),详见使用示例

  • ActionGetAccessToken

请求参数

名称

类型

必填

描述

示例值

ExternalUserId

string

外部系统用户唯一标识

"user-38764"

请求示例

params = {
    "Format": "JSON",
    "Version": "2026-03-11",
    "AccessKeyId": ak,
    "SignatureMethod": "HMAC-SHA1",
    "Timestamp": _now_utc(),
    "SignatureVersion": "1.0",
    "SignatureNonce": str(uuid.uuid4()),
    "Action": "GetAccessToken",
    "RegionId": "cn-shanghai",
    "ExternalUserId": "user-38764",
}
params["Signature"] = _sign_v1(params, sk)

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

AccessToken

string

JWT 访问令牌,用于 Chat 等接口的 Authorization 参数

"eyJhbGc****.eyJ********.****TCk"

AccessDeniedDetail

string

鉴权失败详情

null

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "AccessToken": "eyJhbGc****.eyJ********.****TCk"
}

失败

{
  "Success": false,
  "Code": "400",
  "Message": "Invalid ExternalUserId",
  "HttpStatusCode": 400
}

错误码

HttpCode

Error Code

错误信息

说明

400

InvalidParameter

参数错误

ExternalUserId 格式不正确

401

Unauthorized

未授权

缺少必要的认证信息

500

InternalError

服务内部错误

服务端异常,请稍后重试

Chat - 流式对话

接口描述

与 JVS Crew 发起流式对话,采用 Server-Sent Events(SSE) 协议实时推送对话内容。

特性

  • 实时流式响应,降低首字延迟

  • 支持多模态输入(文本、图片、文件)

  • 会话保持,支持多轮对话

  • 事件驱动,精确控制消息状态

请求信息

  • 请求方法POST

  • Content-Typeapplication/json

  • 响应 Content-Typetext/event-stream

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

TemplateId

string

Agent 模板 ID

"template-abc123"

请求头(Header)

名称

类型

必填

描述

示例值

Content-Type

string

请求内容类型

application/json

Accept

string

接受的响应类型

text/event-stream

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

Chat

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-18T06:21:31Z

Cache-Control

string

缓存控制

no-cache

Connection

string

连接类型

keep-alive

请求参数

名称

类型

必填

描述

示例值

SessionId

string

会话 ID,用于保持多轮对话上下文

"test-session-001"

ExternalUserId

string

外部系统用户 ID

"test-user"

Input

string

消息列表 JSON 字符串,按时间顺序排列

"[{\"Role\":\"user\",...}]"

RoutingKey

string

路由键,指定后端实例

""

Input 结构说明

Input 参数是一个 JSON 字符串(非对象数组),包含 Message 数组,需先序列化为字符串再传递。

Message 结构(JSON 字符串内的数组元素):

名称

类型

必填

描述

示例值

枚举值

Role

string

消息角色

"user"

user / assistant / system / tool

Content

array

内容块列表

见下方 Content 结构

-

Content 结构(Message 中的 Content 数组元素):

名称

类型

必填

描述

示例值

枚举值

Type

string

内容类型

"text"

text / image / file

Text

string

文本内容(Type=text)

"帮我分析这张图片"

-

ImageUrl

string

图片 URL 或 base64(Type=image)

"https://example.com/img.jpg"

-

FileUrl

string

文件路径或 URL(Type=file)

"/workspace/report.pdf"

-

请求示例

curl -N -X POST \
  'https://wuyingai.cn-shanghai.aliyuncs.com/api/agent/chat?Authorization=Bearer%20<access_token>&TemplateId=<template_id>' \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'x-acs-version: 2026-03-11' \
  -H 'x-acs-action: Chat' \
  -H 'x-acs-date: 2026-03-18T06:21:31Z' \
  -d '{
    "Input": "[{\"Role\":\"user\",\"Content\":[{\"Type\":\"text\",\"Text\":\"你好\"}]}]",
    "ExternalUserId": "test-user",
    "SessionId": "test-session-001"
  }
说明
  • -N 参数用于禁用缓冲,实时接收 SSE 流。

  • x-acs-date 需要设置为当前 UTC 时间(ISO 8601 格式)。

  • Input 是 JSON 字符串,需要转义引号。

  • <access_token> 替换为实际的访问令牌,令牌过期(HTTP 401)时,需重新调用 GetAccessToken。

响应参数(SSE 事件)

SSE 响应格式为 data: <JSON>\n\n,每行为一个 JSON 事件对象。服务端定期发送 :ping 心跳行(无 data: 前缀)以保持连接,客户端忽略即可。

通用字段

名称

类型

描述

示例值

Object

string

事件对象类型

"response" / "message" / "content" / "error"

Id

string

消息唯一标识

"msg_xxx"

SessionId

string

会话 ID

"176405663****961"

SequenceNumber

string

事件序号,保证处理顺序

"1"

Response 事件字段

Object=response 事件标记整个回复的生命周期(开始与结束)。

名称

类型

描述

枚举值

Status

string

回复状态

created / in_progress / completed

Message 事件字段

Object=message 事件表示一条具体消息,通过 Type 字段区分消息类型。

名称

类型

描述

枚举值

Role

string

角色

user / assistant / system / tool

Type

string

消息类型

reasoning(模型思考过程) / message(正式回复)

Status

string

运行状态

in_progress / completed

Content

array

内容块列表(仅 completed 时携带)

见下方 Content 结构

CreatedAt

string

创建时间戳(Unix 秒)

"1773380609"

Content 结构(响应)

名称

类型

描述

示例值

Type

string

内容类型

"text" / "data"

Status

string

内容状态

"in_progress"(增量片段) / "completed"(聚合完整文本)

Text

string

文本内容

"您好"

Data

object

结构化数据(如工具调用)

{"call_id":"call_xxx","name":"get_weather"}

事件类型说明

事件类型

Object

描述

触发时机

回复创建

response

Status=created,整个回复的生命周期开始

SSE 流开始

回复进行中

response

Status=in_progress

开始生成内容

思考开始

message

Type=reasoning, Status=in_progress

模型开始推理

思考内容增量

content

思考阶段的文本片段(Status=in_progress

每生成一段思考文本

思考完成

message

Type=reasoning, Status=completed

推理结束

消息开始

message

Type=message, Status=in_progress

开始生成正式回复

消息内容增量

content

正式回复的文本片段(Status=in_progress

每生成一段回复文本

消息内容完成

content

聚合后的完整文本(Status=completed

正式回复文本生成完毕

消息完成

message

Type=message, Status=completed

正式回复结束

回复完成

response

Status=completed

SSE 流结束

错误

error

错误信息

发生错误时

心跳

:ping(注释行)

保持长连接,客户端忽略即可

空闲时每隔数秒

说明

思考阶段(reasoning)的 content 是模型内部推理过程,一般不向终端用户展示。客户端应根据当前 message 的 Type 来决定是否展示对应的 content。

SSE 响应示例

完整流程

data: {"Object":"response","Status":"created","SequenceNumber":"0"}

data: {"Object":"response","Status":"in_progress","SequenceNumber":"1"}

: ping

data: {"Object":"message","Id":"msg_001","SessionId":"176405663****961","Type":"reasoning","Status":"in_progress","SequenceNumber":"2"}

data: {"Object":"content","Id":"msg_001","Type":"text","Status":"in_progress","Text":"用户在打招呼","SequenceNumber":"3"}

data: {"Object":"content","Id":"msg_001","Type":"text","Status":"in_progress","Text":",我简单回复即可","SequenceNumber":"4"}

data: {"Object":"message","Id":"msg_001","Type":"message","Status":"in_progress","SequenceNumber":"5"}

data: {"Object":"content","Id":"msg_001","Type":"text","Status":"in_progress","Text":"您好","SequenceNumber":"6"}

data: {"Object":"content","Id":"msg_001","Type":"text","Status":"in_progress","Text":"!","SequenceNumber":"7"}

data: {"Object":"content","Id":"msg_001","Type":"text","Status":"completed","Text":"用户在打招呼,我简单回复即可","SequenceNumber":"8"}

data: {"Object":"message","Id":"msg_001","Type":"reasoning","Status":"completed","SequenceNumber":"9"}

data: {"Object":"message","Id":"msg_001","Type":"message","Status":"completed","Content":[{"Type":"text","Text":"您好!"}],"SequenceNumber":"10"}

data: {"Object":"response","Status":"completed","SequenceNumber":"11"}
重要
  • SequenceNumber=3~4 的 content 属于 reasoning(思考)阶段,SequenceNumber=6~7 的 content 属于 message(正式回复)阶段。客户端应只展示 message 阶段的 content。

  • SequenceNumber=8Status=completed 的聚合 content,包含某阶段的完整文本;它可能出现在后续阶段的增量事件之后(异步聚合),客户端应通过跟踪最近一个 Object=message 事件的 Type 来判定当前 content 归属。

  • : ping 是 SSE 协议注释行,浏览器 EventSource 和主流 SSE 库会自动忽略。

GetChatFileUploadUrl - 获取文件上传地址

接口描述

获取会话中文件上传 URL、沙箱内路径及 FileKey,用于将文件上传至 Agentbay 为用户提供的 Context 存储空间。文件上传分三步完成:

  1. 调用本接口获取 UploadUrlFileKey

  2. 使用 HTTP PUT 请求将文件内容上传到 UploadUrl(直传至对象存储,无需额外签名)

  3. 使用该 FileKey 调用 SyncContext 将文件同步到沙箱,同步后 Agent 才能使用该文件

请求信息

  • 认证方式:POP V1 签名(AK/SK),与 GetAccessToken 相同

  • ActionGetChatFileUploadUrl

请求参数

名称

类型

必填

描述

示例值

FileName

string

要上传的文件名称

"report.pdf"

ExternalUserId

string

外部系统用户唯一标识

"user-38764"

TemplateId

string

Agent 模板 ID

"template-abc123"

请求示例

params = {
    "Format": "JSON",
    "Version": "2026-03-11",
    "AccessKeyId": ak,
    "SignatureMethod": "HMAC-SHA1",
    "Timestamp": _now_utc(),
    "SignatureVersion": "1.0",
    "SignatureNonce": str(uuid.uuid4()),
    "Action": "GetChatFileUploadUrl",
    "RegionId": "cn-shanghai",
    "FileName": "report.pdf",
    "ExternalUserId": "user-38764",
    "TemplateId": "template-abc123",
}
params["Signature"] = _sign_v1(params, sk)

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

错误码

"200"

HttpStatusCode

integer

状态码

200

RequestId

string

请求 ID

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

UploadUrl

string

文件上传 URL,用于将文件上传至对象存储

"https://..."

UploadHeadersHint

string

上传请求的 Header 提示信息

"..."

SandboxPath

string

文件在沙箱内的路径

"/home/wuying/jvscrew/uploads/report_a1b2c3d4.pdf"

FileKey

string

文件标识,调用 SyncContext 时传入。格式为 uploads/<原文件名>_<随机后缀>.<扩展名>,与原始文件名不同

"uploads/report_a1b2c3d4.pdf"

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "UploadUrl": "https://oss-example.aliyuncs.com/upload/...",
  "UploadHeadersHint": "...",
  "SandboxPath": "/home/wuying/jvscrew/uploads/report_a1b2c3d4.pdf",
  "FileKey": "uploads/report_a1b2c3d4.pdf"
}

SyncContext - 同步 Context 的指定文件到沙箱

接口描述

同步指定用户的 Context 中指定文件内容到沙箱环境,使沙箱中的用户数据与 Context 保持同步。

请求信息

  • 认证方式:POP V1 签名(AK/SK),与 GetAccessToken 相同

  • ActionSyncContext

请求参数

名称

类型

必填

描述

示例值

ExternalUserId

string

外部系统用户唯一标识

"user-38764"

FileKey

string

文件标识,由 GetChatFileUploadUrl 返回

"uploads/report_a1b2c3d4.pdf"

TemplateId

string

Agent 模板 ID

"template-abc123"

请求示例

params = {
    "Format": "JSON",
    "Version": "2026-03-11",
    "AccessKeyId": ak,
    "SignatureMethod": "HMAC-SHA1",
    "Timestamp": _now_utc(),
    "SignatureVersion": "1.0",
    "SignatureNonce": str(uuid.uuid4()),
    "Action": "SyncContext",
    "RegionId": "cn-shanghai",
    "ExternalUserId": "user-38764",
    "FileKey": "uploads/report_a1b2c3d4.pdf",
    "TemplateId": "template-abc123",
}
params["Signature"] = _sign_v1(params, sk)

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

错误码

"200"

HttpStatusCode

integer

状态码

200

RequestId

string

请求 ID

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C"
}

会话管理接口

ListSessions - 列举对话列表

接口描述

列举指定用户的会话列表。每个元素包含 SessionId,可用于查询历史、中止或删除会话。

请求信息

  • 请求方法GET(也接受 POST

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

TemplateId

string

Agent 模板 ID,用于筛选

"template-abc123"

ExternalUserId

string

外部系统用户唯一标识

"user-38764"

Channel

string

按渠道筛选

"console" / "dingtalk" / "feishu"

请求头(Header)

名称

类型

必填

描述

示例值

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

ListSessions

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-25T06:00:00Z

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

Chats

array

会话基本信息列表

见下方

AccessDeniedDetail

string

鉴权失败详情

null

Chats 数组元素

名称

类型

描述

示例值

Id

string

会话 ID

"4bd0****02f"

Name

string

会话标题

"你好"

SessionId

string

会话标识,用于删除/停止等操作

"curl-test-session-****"

UserId

string

用户 ID

"u-open-****"

Channel

string

渠道名称

"console"

CreatedAt

string

会话创建时间(ISO 8601)

"2026-03-26T03:51:42.071603Z"

UpdatedAt

string

会话最后更新时间(ISO 8601)

"2026-03-26T03:52:03.000000Z"

Meta

map

扩展元数据

{}

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "Chats": [
    {
      "Id": "4bd0****02f",
      "Name": "你好",
      "SessionId": "curl-test-session-********",
      "UserId": "u-open-************",
      "Channel": "console",
      "CreatedAt": "2026-03-26T03:51:42.071603Z",
      "UpdatedAt": "2026-03-26T03:52:03.000000Z",
      "Meta": {}
    }
  ]
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AccessToken 无效或已过期

ListSessionHistory - 列举对话历史明细

接口描述

根据 SessionId 获取指定会话的历史消息列表,包含用户消息和 AI 回复。

请求信息

  • 请求方法GET(也接受 POST

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

TemplateId

string

Agent 模板 ID

"template-abc123"

SessionId

string

要获取历史的会话 ID

"curl-test-session-****"

ExternalUserId

string

外部系统用户唯一标识

"user-38764"

请求头(Header)

名称

类型

必填

描述

示例值

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

ListSessionHistory

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-25T06:00:00Z

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

Messages

array

会话消息列表

见下方

AccessDeniedDetail

string

鉴权失败详情

null

Messages 数组元素

名称

类型

描述

示例值

Id

string

消息唯一标识

"msg_5e8d****aa1d"

Role

string

消息角色

"user" / "assistant"

Type

string

消息类型

"message"

Object

string

对象类型

"message"

Status

string

消息状态

"completed"

Error

string

错误信息(如有)

null

SequenceNumber

string

序号

null

Content

array

内容块列表

见下方

Metadata

map

扩展元数据

{}

Content 数组元素

名称

类型

描述

示例值

Object

string

对象类型

"content"

Status

string

状态

"completed"

Error

string

错误信息

null

MsgId

string

所属消息 ID

"msg_5e8d****aa1d"

Text

string

文本内容

"你好"

Data

string

结构化数据(工具调用等)

null

SequenceNumber

string

序号

null

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "123***456",
  "Messages": [
    {
      "Id": "msg_5e8d****aa1d",
      "Role": "user",
      "Type": "message",
      "Object": "message",
      "Status": "completed",
      "Error": null,
      "SequenceNumber": null,
      "Content": [
        {
          "Object": "content",
          "Status": "completed",
          "Error": null,
          "MsgId": "msg_5e8d****aa1d",
          "Text": "你好",
          "Data": null,
          "SequenceNumber": null
        }
      ],
      "Metadata": {
        "OriginalId": "msg_20e8****8ff3",
        "OriginalName": "user"
      }
    },
    {
      "Id": "msg_8574****0563",
      "Role": "assistant",
      "Type": "message",
      "Object": "message",
      "Status": "completed",
      "Error": null,
      "SequenceNumber": null,
      "Content": [
        {
          "Object": "content",
          "Status": "completed",
          "Error": null,
          "MsgId": "msg_8574****0563",
          "Text": "你好,小伙伴!有什么我可以帮你的吗?",
          "Data": null,
          "SequenceNumber": null
        }
      ],
      "Metadata": {
        "OriginalId": "dXRT****MUV",
        "OriginalName": "JVSCrewAgent"
      }
    }
  ]
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AccessToken 无效或已过期

StopSession - 中止对话

接口描述

中止正在执行的 Agent 对话任务。调用后 Agent 将立即停止推理和工具执行,不再产生新的 Token 输出。适用于主动取消长时间运行的任务,或需要立即终止 Agent 的场景。

中止机制:服务端取消 Agent 执行任务,断开与大模型的连接,确保不再产生 Token。已完成的对话历史保留不变,后续可在同一 SessionId 下继续对话。

请求信息

  • 请求方法POST(也接受 GET

  • Content-Typeapplication/json

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

TemplateId

string

Agent 模板 ID

"template-abc123"

请求头(Header)

名称

类型

必填

描述

示例值

Content-Type

string

请求内容类型

application/json

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

StopSession

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-25T06:00:00Z

请求参数

名称

类型

必填

描述

示例值

SessionId

string

要中止的会话 ID

"test-session-001"

请求体示例

{
  "SessionId": "test-session-001"
}

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

Stopped

boolean

是否成功中止了正在运行的任务。true 表示找到并取消了任务,false 表示该会话当前没有正在运行的任务

true

响应示例

成功(任务已中止)

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "Stopped": true
}

成功(当前无运行中任务)

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "FA23****-****-****-****-****D4B",
  "Stopped": false
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AccessToken 无效或已过期

503

ServiceUnavailable

服务不可用

Task tracker 未初始化

DeleteSession - 删除对话

接口描述

根据 SessionId 删除对话记录,包括对话元数据和会话状态文件。删除后该会话的历史消息清空且无法恢复。

请求信息

  • 请求方法POST

  • Content-Typeapplication/json

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

TemplateId

string

Agent 模板 ID

"template-abc123"

请求头(Header)

名称

类型

必填

描述

示例值

Content-Type

string

请求内容类型

application/json

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

DeleteSession

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-25T06:00:00Z

请求参数

名称

类型

必填

描述

示例值

SessionId

string

要删除的会话 ID

"test-session-001"

请求体示例

{
  "SessionId": "test-session-001"
}

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

Deleted

boolean

是否成功删除

true

响应示例

成功

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "Deleted": true
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AccessToken 无效或已过期

500

InternalError

会话不存在或删除失败

指定的 SessionId 未找到对应的对话记录,或服务端异常

GetSandboxInfo - 获取沙箱会话信息

接口描述

获取当前用户沙箱会话状态与资源地址。可判断沙箱是否已激活,并获取沙箱的资源访问 URL 和会话标识。

请求信息

  • 请求方法GET(也接受 POST

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

请求头(Header)

名称

类型

必填

描述

示例值

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

GetSandboxInfo

x-acs-date

string

请求时间(ISO 8601 格式)

2026-03-25T06:00:00Z

请求参数

无额外请求参数。用户身份通过 JWT 令牌自动携带。

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"200"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

ResourceUrl

string

沙箱流化画面访问 URL

"https://..."

SandboxSessionId

string

沙箱会话 ID

"session-abc123"

SessionActive

string

沙箱会话是否激活

"true"

响应示例

成功(沙箱已激活)

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "ResourceUrl": "https://jvscrew.example.com/resource/...",
  "SandboxSessionId": "session-abc123",
  "SessionActive": "true"
}

成功(沙箱未激活)

{
  "Success": true,
  "Code": "200",
  "HttpStatusCode": 200,
  "RequestId": "FA23****-****-****-****-****D4B",
  "ResourceUrl": null,
  "SandboxSessionId": null,
  "SessionActive": "false"
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AccessToken 无效或已过期

计费查询接口

说明

计费查询接口使用 AK/SK 签名认证(与 GetAccessToken 相同),无需先获取 AccessToken。

GetBillingOverview - 获取计费概览

接口描述

获取租户当月计费概览,包含消耗积分、会话数和平均消耗等指标。

请求信息

  • 认证方式:POP V1 签名(AK/SK),与 GetAccessToken 相同

  • ActionGetBillingOverview

请求参数

无额外请求参数。租户身份通过 AK/SK 自动识别。

请求示例

params = {
    "Format": "JSON",
    "Version": "2026-03-11",
    "AccessKeyId": ak,
    "SignatureMethod": "HMAC-SHA1",
    "Timestamp": _now_utc(),
    "SignatureVersion": "1.0",
    "SignatureNonce": str(uuid.uuid4()),
    "Action": "GetBillingOverview",
    "RegionId": "cn-shanghai",
}
params["Signature"] = _sign_v1(params, sk)

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"ok"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

MonthlyCredit

float

当月已消耗积分(Credit)

125.50

MonthlySessions

integer

当月会话总数

42

AvgCreditPerSession

float

单会话平均消耗

2.99

CycleStart

string

账单周期起始日(ISO 8601)

"2026-04-01T00:00:00Z"

CycleEnd

string

账单周期结束日(ISO 8601)

"2026-04-30T23:59:59Z"

响应示例

成功

{
  "Success": true,
  "Code": "ok",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "MonthlyCredit": 125.50,
  "MonthlySessions": 42,
  "AvgCreditPerSession": 2.99,
  "CycleStart": "2026-04-01T00:00:00Z",
  "CycleEnd": "2026-04-30T23:59:59Z"
}

错误码

HttpCode

Error Code

错误信息

说明

401

Unauthorized

未授权

AK/SK 无效

500

InternalError

服务内部错误

服务端异常,请稍后重试

ListUserConsumption - 查询用户消耗明细

接口描述

按用户查询当月消耗明细,支持分页和按外部用户 ID 过滤。

请求信息

  • 认证方式:POP V1 签名(AK/SK),与 GetAccessToken 相同

  • ActionListUserConsumption

请求参数

名称

类型

必填

描述

示例值

ExternalUserIds

string

按外部用户 ID 过滤,多个用逗号分隔

"alice,bob"

PageSize

integer

每页数量,默认 20,最大 100

20

PageNumber

integer

页码,从 1 开始,默认 1

1

请求示例

params = {
    "Format": "JSON",
    "Version": "2026-03-11",
    "AccessKeyId": ak,
    "SignatureMethod": "HMAC-SHA1",
    "Timestamp": _now_utc(),
    "SignatureVersion": "1.0",
    "SignatureNonce": str(uuid.uuid4()),
    "Action": "ListUserConsumption",
    "RegionId": "cn-shanghai",
    "ExternalUserIds": "alice,bob",
    "PageSize": "20",
    "PageNumber": "1",
}
params["Signature"] = _sign_v1(params, sk)

响应参数

名称

类型

描述

示例值

Success

boolean

是否成功

true

Code

string

业务状态码

"ok"

Message

string

错误详情(失败时返回)

null

HttpStatusCode

integer

HTTP 状态码

200

RequestId

string

请求唯一标识

"EA12****-****-****-****-****E5C"

AccessDeniedDetail

string

鉴权失败详情

null

Users

array

用户消耗明细列表

见下方

TotalCount

integer

总用户数

15

PageSize

integer

每页数量

20

PageNumber

integer

当前页码

1

Users 数组元素

名称

类型

描述

示例值

UserId

string

用户内部标识

"u-open-abc123"

ExternalUserId

string

外部用户标识(无则回退为 UserId)

"alice"

InstanceId

string

实例 ID(用于售卖侧对账)

"jvscrew-u-open-abc123"

MonthlyCredit

float

当月消耗积分

28.50

MonthlySessions

integer

当月会话数

12

MonthlyDurationHours

float

当月使用时长(小时)

3.25

MonthlyDurationMinutes

float

当月使用时长(分钟)

195.00

响应示例

成功

{
  "Success": true,
  "Code": "ok",
  "HttpStatusCode": 200,
  "RequestId": "EA12****-****-****-****-****E5C",
  "Users": [
    {
      "UserId": "u-open-abc123",
      "ExternalUserId": "alice",
      "InstanceId": "jvscrew-u-open-abc123",
      "MonthlyCredit": 28.50,
      "MonthlySessions": 12,
      "MonthlyDurationHours": 3.25,
      "MonthlyDurationMinutes": 195.00
    },
    {
      "UserId": "u-open-def456",
      "ExternalUserId": "bob",
      "InstanceId": "jvscrew-u-open-def456",
      "MonthlyCredit": 15.00,
      "MonthlySessions": 8,
      "MonthlyDurationHours": 1.50,
      "MonthlyDurationMinutes": 90.00
    }
  ],
  "TotalCount": 2,
  "PageSize": 20,
  "PageNumber": 1
}

错误码

HttpCode

Error Code

错误信息

说明

400

InvalidPageSize

参数错误

PageSize 不能为负数

401

Unauthorized

未授权

AK/SK 无效

500

InternalError

服务内部错误

服务端异常,请稍后重试

定时任务接口

说明

定时任务 API 使用 JWT 令牌认证,与 Chat、ListSessions 等接口相同:先调用 GetAccessToken 获取 AccessToken,放入 Query 参数 Authorization=Bearer <token>

CreateScheduledTask - 创建定时任务

接口描述

创建定时任务,支持 cron 表达式和固定间隔调度。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数)

  • ActionCreateScheduledTask

Query 参数

名称

类型

必填

描述

示例值

Authorization

string

GetAccessToken 返回的 JWT,格式为 Bearer + token,整段 URL 编码后放入 Query

Bearer%20eyJhb****...****k

请求头(Header)

名称

类型

必填

描述

示例值

Content-Type

string

请求内容类型

application/json

x-acs-version

string

API 版本

2026-03-11

x-acs-action

string

API 操作名称

CreateScheduledTask

x-acs-date

string

请求时间(ISO 8601 格式)

2026-04-22T06:00:00Z

请求参数

名称

类型

必填

描述

示例值

Name

string

任务名称

"每日技术简报"

Instruction

string

任务指令,Agent 按此指令执行

"帮我总结技术新闻"

TemplateId

string

模板 ID

"template-abc123"

Schedule

object

调度规则,见 Schedule 对象

-

Sinks

array

推送渠道,见 Sinks 对象

-

请求示例

curl -X POST \
  'https://wuyingai.cn-shanghai.aliyuncs.com/?Authorization=Bearer%20<access_token>' \
  -H 'Content-Type: application/json' \
  -H 'x-acs-version: 2026-03-11' \
  -H 'x-acs-action: CreateScheduledTask' \
  -H 'x-acs-date: 2026-04-22T06:00:00Z' \
  -d '{
    "Name": "每日技术简报",
    "Instruction": "帮我总结今天的技术新闻",
    "Schedule": {"Type": "cron", "Expr": "0 9 * * *", "Timezone": "Asia/Shanghai"},
    "Sinks": [{"Sink": "im_push", "Channel": "dingtalk", "ChannelInstanceId": "", "TargetUserId": "<user>", "TargetSessionId": "", "Meta": {}}]
  }'

响应参数(Task 对象)

所有返回 Task 的接口(Create / Update / Get)共用以下结构:

名称

类型

描述

TaskId

string

任务唯一标识

TemplateId

string

模板 ID

ExternalUserId

string

外部用户 ID

Name

string

任务名称

Status

string

任务状态(active

Instruction

string

任务指令

Schedule

object

调度配置

Sinks

array

推送渠道配置

NextRunAt

string

下次执行时间(ISO 8601)

LastRunAt

string

上次执行时间(新创建时为 null)

LastError

string

最近错误信息(新创建时为 null)

CreatedAt

string

创建时间(ISO 8601)

UpdatedAt

string

更新时间(ISO 8601)

说明

POP 网关自动过滤值为 null 的字段,因此 LastRunAtLastError 在新创建的任务响应中可能不出现。

响应示例

{
  "TaskId": "bd0f7607-538c-47b4-9b1e-380e12947640",
  "TemplateId": "template-zc9ecvdf",
  "ExternalUserId": "verify-schedule-user",
  "Name": "每日技术简报",
  "Status": "active",
  "Instruction": "帮我总结今天的技术新闻",
  "Schedule": {"Type": "cron", "Expr": "0 9 * * *", "Timezone": "Asia/Shanghai"},
  "Sinks": [],
  "NextRunAt": "2026-04-22T09:00:00",
  "CreatedAt": "2026-04-21T23:32:53",
  "UpdatedAt": "2026-04-21T23:32:53"
}

UpdateScheduledTask - 更新定时任务

接口描述

更新指定定时任务的配置。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数),与 CreateScheduledTask 相同

  • ActionUpdateScheduledTask

Query 参数和请求头与 CreateScheduledTask 相同,x-acs-action 改为 UpdateScheduledTask

请求参数

名称

类型

必填

描述

TaskId

string

要更新的任务 ID

Name

string

新的任务名称

Instruction

string

新的任务指令

TemplateId

string

模板 ID

Schedule

object

新的调度规则

Sinks

array

新的推送渠道配置

响应参数

同 CreateScheduledTask 响应(Task 对象)。

GetScheduledTask - 查询任务详情

接口描述

根据 TaskId 查询定时任务详情。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数),与 CreateScheduledTask 相同

  • ActionGetScheduledTask

Query 参数和请求头与 CreateScheduledTask 相同,x-acs-action 改为 GetScheduledTask

请求参数

名称

类型

必填

描述

TaskId

string

任务 ID

TemplateId

string

模板 ID

响应参数

同 CreateScheduledTask 响应(Task 对象)。

ListScheduledTasks - 分页列举任务

接口描述

分页列举当前用户的定时任务。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数),与 CreateScheduledTask 相同

  • ActionListScheduledTasks

Query 参数和请求头与 CreateScheduledTask 相同,x-acs-action 改为 ListScheduledTasks

请求参数

名称

类型

必填

描述

TemplateId

string

按模板过滤

PageNumber

integer

页码,默认 1

PageSize

integer

每页条数,默认 20,最大 100

响应参数

名称

类型

描述

Tasks

array

任务列表,每个元素为 Task 对象

TotalCount

integer

总数

PageNumber

integer

当前页码

PageSize

integer

每页条数

说明

POP 网关过滤值为 0 或 null 的字段,空列表时 TotalCountPageNumberPageSize 可能不出现。

响应示例

{
  "Tasks": [
    {
      "TaskId": "bd0f7607-538c-47b4-9b1e-380e12947640",
      "Name": "每日技术简报",
      "Status": "active",
      "NextRunAt": "2026-04-22T09:00:00"
    }
  ],
  "TotalCount": 1,
  "PageNumber": 1,
  "PageSize": 20
}

DeleteScheduledTask - 删除任务

接口描述

删除指定的定时任务。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数),与 CreateScheduledTask 相同

  • ActionDeleteScheduledTask

Query 参数和请求头与 CreateScheduledTask 相同,x-acs-action 改为 DeleteScheduledTask

请求参数

名称

类型

必填

描述

TaskId

string

要删除的任务 ID

响应参数

名称

类型

描述

Deleted

boolean

是否删除成功

响应示例

{"Deleted": true}

ListScheduledTaskRuns - 查询执行记录

接口描述

查询定时任务的执行记录列表,支持游标分页。

请求信息

  • 认证方式:JWT 令牌(Authorization Query 参数),与 CreateScheduledTask 相同

  • ActionListScheduledTaskRuns

Query 参数和请求头与 CreateScheduledTask 相同,x-acs-action 改为 ListScheduledTaskRuns

请求参数

名称

类型

必填

描述

TaskId

string

按任务 ID 过滤

Since

string

起始时间戳(毫秒),仅返回之后的记录

Cursor

string

分页游标,来自上一次响应的 NextCursor

PageSize

integer

每页条数,默认 50,最大 200

响应参数

名称

类型

描述

Runs

array

执行记录列表

NextCursor

string

下一页游标,为 null 时表示无更多数据

Runs 数组元素

名称

类型

描述

RunId

string

执行记录唯一标识

TaskId

string

所属任务 ID

TemplateId

string

模板 ID

Status

string

执行状态:running / succeeded / failed

ResultPayload

string

执行结果内容(仅 succeeded 时有值)

ErrorMessage

string

错误信息(仅 failed 时有值)

PushSink

string

推送渠道类型

PushStatus

string

推送状态:pending / succeeded / failed / skipped

StartedAt

string

开始时间

FinishedAt

string

结束时间

CreatedAt

string

记录创建时间

响应示例

{
  "Runs": [
    {
      "RunId": "run-abc123",
      "TaskId": "bd0f7607-538c-47b4-9b1e-380e12947640",
      "TemplateId": "template-zc9ecvdf",
      "Status": "succeeded",
      "ResultPayload": "今日技术要闻:...",
      "PushStatus": "succeeded",
      "StartedAt": "2026-04-22T09:00:01",
      "FinishedAt": "2026-04-22T09:02:35",
      "CreatedAt": "2026-04-22T09:00:00"
    }
  ],
  "NextCursor": null
}

定时任务数据结构

Schedule 对象

字段

类型

描述

Type

string

调度类型:cron / interval

Expr

string

调度表达式,格式取决于 Type

Timezone

string

时区,如 Asia/Shanghai

Type 与 Expr 对照表

Type

Expr 格式

示例

说明

cron

标准 cron 表达式

0 9 * * *

每天早上 9 点

cron

标准 cron 表达式

0 9 * * 1-5

工作日早上 9 点

interval

数字+单位

10m

每 10 分钟

interval

数字+单位

2h

每 2 小时

Sinks 对象

字段

类型

描述

Sink

string

推送方式,当前支持 im_push

Channel

string

渠道类型,如 dingtalk

ChannelInstanceId

string

渠道实例 ID

TargetUserId

string

目标用户 ID

TargetSessionId

string

目标会话 ID

Meta

object

渠道扩展元数据

说明

未配置 Sinks 时,任务仍正常执行,结果通过 ListScheduledTaskRuns 的 ResultPayload 字段拉取。

Run 状态流转

running → succeeded(执行成功)
        → failed(执行失败)

推送状态(PushStatus)

状态

说明

pending

等待推送

succeeded

推送成功

failed

推送失败

skipped

未配置 Sinks,跳过推送

定时任务错误码

HttpCode

说明

排查建议

400

请求参数错误

检查必填参数、Schedule/Sinks 格式

401

认证失败

检查 Bearer Token 是否有效或已过期

404

资源不存在

确认 TaskId 是否正确

使用示例

Python

完整的示例代码请参考:

#!/usr/bin/env python3
""" JVS Crew Chat 示例 - 使用 POP V1 签名

使用前需要提供以下信息:

1. 阿里云 AK/SK(必需)
   - 获取方式:阿里云 RAM 控制台 → AccessKey 管理 → 创建 AccessKey
   - 配置方式:在项目根目录 .env 文件中添加:
       ALIBABA_CLOUD_ACCESS_KEY_ID=你的 AK
       ALIBABA_CLOUD_ACCESS_KEY_SECRET=你的 SK
   - 注意:建议使用 RAM 子用户的 AK/SK,避免使用主账号

2. 可选配置项(修改下方代码中的常量)
   - ENDPOINT: API 端点地址,默认 cn-shanghai
   - REGION_ID: 地域 ID,默认 cn-shanghai
   - EXTERNAL_USER_ID: 外部用户 ID,用于标识对话用户
   - USER_MESSAGE: 要发送的消息内容

运行方式:
  pip install requests python-dotenv
  python jvscrew_chat_example.py
"""
import base64
import hashlib
import hmac
import json
import os
import time
import urllib.parse
import uuid
from datetime import datetime, timezone

import requests
from dotenv import load_dotenv

# 加载当前目录的 .env 文件
load_dotenv()

# ============ 配置 ============
ENDPOINT = "https://wuyingai.cn-shanghai.aliyuncs.com"
API_VERSION = "2026-03-11"
REGION_ID = "cn-shanghai"
EXTERNAL_USER_ID = "test-user-example"
USER_MESSAGE = "你好"


# ============ V1 签名实现 ============
def _pct(s: str) -> str:
    """RFC 3986 URL 编码"""
    return urllib.parse.quote(str(s), safe="-_.~")


def _now_utc() -> str:
    """UTC 时间戳"""
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


def _sign_v1(params: dict, sk: str) -> str:
    """计算阿里云 POP V1 签名"""
    # 1. 参数排序 + URL 编码
    pairs = [
        f"{_pct(k)}={_pct(str(v))}"
        for k, v in sorted(params.items())
        if k != "Signature"
    ]
    canonical = "&".join(pairs)

    # 2. 构造待签名字符串
    string_to_sign = f"POST&{_pct('/')}&{_pct(canonical)}"

    # 3. HMAC-SHA1 签名
    key = (sk + "&").encode("utf-8")
    signature = base64.b64encode(
        hmac.new(key, string_to_sign.encode("utf-8"), hashlib.sha1).digest()
    ).decode("ascii")

    return signature


def get_access_token(ak: str, sk: str, external_user_id: str) -> str:
    """获取 AccessToken(使用 V1 签名)"""
    params = {
        "Format": "JSON",
        "Version": API_VERSION,
        "AccessKeyId": ak,
        "SignatureMethod": "HMAC-SHA1",
        "Timestamp": _now_utc(),
        "SignatureVersion": "1.0",
        "SignatureNonce": str(uuid.uuid4()),
        "Action": "GetAccessToken",
        "RegionId": REGION_ID,
        "ExternalUserId": external_user_id,
    }

    # 计算签名
    params["Signature"] = _sign_v1(params, sk)

    # 发送请求
    url = f"{ENDPOINT}/?{urllib.parse.urlencode(params)}"
    resp = requests.post(
        url, headers={"Accept": "application/json"}, timeout=60)
    resp.raise_for_status()

    data = resp.json()
    if not data.get("Success") and data.get("Code") not in ("200", "Success", None):
        raise RuntimeError(f"GetAccessToken failed: {data}")

    token = data.get("AccessToken")
    if not token:
        raise RuntimeError(f"No AccessToken in response: {data}")

    return token


def chat_sse(jwt: str, external_user_id: str, session_id: str, text: str):
    """发起 Chat SSE 对话"""
    url = f"{ENDPOINT}/api/agent/chat?Authorization={urllib.parse.quote(f'Bearer {jwt}')}"

    payload = {
        "ExternalUserId": external_user_id,
        "SessionId": session_id,
        "Input": json.dumps(
            [{"Role": "user", "Content": [{"Type": "text", "Text": text}]}],
            ensure_ascii=False,
        ),
    }

    headers = {
        "Content-Type": "application/json",
        "Accept": "text/event-stream",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "x-acs-version": API_VERSION,
        "x-acs-action": "Chat",
        "x-acs-date": _now_utc(),
    }

    resp = requests.post(url, json=payload, headers=headers,
                         stream=True, timeout=300)
    resp.raise_for_status()

    # 处理 SSE 流
    # current_phase 用于区分 reasoning(思考)和 message(正式回复)阶段
    current_phase = None  # "reasoning" 或 "message"

    for line in resp.iter_lines():
        if not line:
            continue
        line = line.decode("utf-8")
        if not line.startswith("data:"):
            continue

        raw = line[5:].strip()
        if raw == "[DONE]":
            break

        try:
            ev = json.loads(raw)
        except json.JSONDecodeError:
            continue

        if not isinstance(ev, dict):
            continue

        obj = ev.get("Object") or ev.get("object")
        typ = ev.get("Type") or ev.get("type")
        status = ev.get("Status") or ev.get("status")

        # 跟踪当前阶段:reasoning(思考)或 message(正式回复)
        if obj == "message" and typ in ("reasoning", "message"):
            current_phase = typ

        # 只输出正式回复阶段的增量 content,跳过 reasoning 内容
        if obj == "content" and typ == "text" and status == "in_progress":
            if current_phase == "message":
                text_content = ev.get("Text") or ev.get("text") or ""
                print(text_content, end="", flush=True)
        elif obj == "error":
            print(f"\n错误: {json.dumps(ev, ensure_ascii=False)}")
            break
        elif obj == "response" and status == "completed":
            print("\n对话完成")
            break

    print()


def main():
    # 从环境变量读取 AK/SK
    ak = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "").strip()
    sk = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "").strip()

    if not ak or not sk:
        raise RuntimeError(
            "请设置环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET\n"
            "或在项目根目录 .env 文件中配置"
        )

    print(f"使用 AK: {ak[:8]}...")
    print(f"ExternalUserId: {EXTERNAL_USER_ID}")
    print(f"发送消息: {USER_MESSAGE}")
    print("-" * 50)

    # 1. 获取 Token
    print("正在获取 AccessToken...")
    token = get_access_token(ak, sk, EXTERNAL_USER_ID)
    print(f"Token 获取成功")

    # 2. 发起对话
    session_id = f"session-{int(time.time() * 1000)}"
    print(f"SessionId: {session_id}")
    print("-" * 50)
    print("AI 回复:")

    chat_sse(token, EXTERNAL_USER_ID, session_id, USER_MESSAGE)


if __name__ == "__main__":
    main()

文件上传完整示例

将本地文件上传到 Context 存储并同步到沙箱,需要依次完成三步:获取上传地址 → PUT 上传文件 → 同步到沙箱。

import hashlib
import hmac
import base64
import urllib.parse
import uuid
import requests
from datetime import datetime, timezone


def _pct(s: str) -> str:
    return urllib.parse.quote(str(s), safe="-_.~")


def _now_utc() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


def _sign_v1(params: dict, sk: str) -> str:
    pairs = [
        f"{_pct(k)}={_pct(str(v))}"
        for k, v in sorted(params.items())
        if k != "Signature"
    ]
    canonical = "&".join(pairs)
    string_to_sign = f"POST&{_pct('/')}&{_pct(canonical)}"
    key = (sk + "&").encode("utf-8")
    return base64.b64encode(
        hmac.new(key, string_to_sign.encode("utf-8"), hashlib.sha1).digest()
    ).decode("ascii")


ENDPOINT = "https://wuyingai.cn-shanghai.aliyuncs.com"
API_VERSION = "2026-03-11"
REGION_ID = "cn-shanghai"


def get_chat_file_upload_url(ak, sk, file_name, external_user_id, template_id=""):
    """步骤一:获取文件上传地址"""
    params = {
        "Format": "JSON", "Version": API_VERSION, "AccessKeyId": ak,
        "SignatureMethod": "HMAC-SHA1", "Timestamp": _now_utc(),
        "SignatureVersion": "1.0", "SignatureNonce": str(uuid.uuid4()),
        "Action": "GetChatFileUploadUrl", "RegionId": REGION_ID,
        "FileName": file_name, "ExternalUserId": external_user_id,
    }
    if template_id:
        params["TemplateId"] = template_id
    params["Signature"] = _sign_v1(params, sk)
    url = f"{ENDPOINT}/?{urllib.parse.urlencode(params)}"
    resp = requests.post(url, headers={"Accept": "application/json"}, timeout=60)
    resp.raise_for_status()
    data = resp.json()
    if not data.get("Success"):
        raise RuntimeError(f"GetChatFileUploadUrl failed: {data}")
    return data


def upload_file_to_oss(upload_url, file_path):
    """步骤二:将文件 PUT 上传到预签名 URL"""
    with open(file_path, "rb") as f:
        file_content = f.read()
    resp = requests.put(upload_url, data=file_content, timeout=120)
    resp.raise_for_status()


def sync_context(ak, sk, external_user_id, file_key, template_id=""):
    """步骤三:同步文件到沙箱"""
    params = {
        "Format": "JSON", "Version": API_VERSION, "AccessKeyId": ak,
        "SignatureMethod": "HMAC-SHA1", "Timestamp": _now_utc(),
        "SignatureVersion": "1.0", "SignatureNonce": str(uuid.uuid4()),
        "Action": "SyncContext", "RegionId": REGION_ID,
        "ExternalUserId": external_user_id, "FileKey": file_key,
    }
    if template_id:
        params["TemplateId"] = template_id
    params["Signature"] = _sign_v1(params, sk)
    url = f"{ENDPOINT}/?{urllib.parse.urlencode(params)}"
    resp = requests.post(url, headers={"Accept": "application/json"}, timeout=60)
    resp.raise_for_status()
    data = resp.json()
    if not data.get("Success"):
        raise RuntimeError(f"SyncContext failed: {data}")
    return data


def upload_and_sync(ak, sk, local_file_path, external_user_id, template_id=""):
    """完整的文件上传流程,返回文件在沙箱中的路径"""
    file_name = local_file_path.split("/")[-1]
    print(f"正在获取文件上传地址: {file_name}")
    upload_info = get_chat_file_upload_url(ak, sk, file_name, external_user_id, template_id)
    upload_url = upload_info["UploadUrl"]
    file_key = upload_info["FileKey"]
    sandbox_path = upload_info["SandboxPath"]
    print(f"FileKey: {file_key}, SandboxPath: {sandbox_path}")

    print(f"正在上传文件到对象存储...")
    upload_file_to_oss(upload_url, local_file_path)
    print("文件上传完成")

    print(f"正在同步文件到沙箱...")
    sync_context(ak, sk, external_user_id, file_key, template_id)
    print(f"文件已同步到沙箱路径: {sandbox_path}")
    return sandbox_path


if __name__ == "__main__":
    import os
    ak = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "").strip()
    sk = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "").strip()
    sandbox_path = upload_and_sync(
        ak=ak, sk=sk, local_file_path="/path/to/report.pdf",
        external_user_id="user-38764", template_id="template-abc123",
    )
    print(f"文件已就绪,沙箱路径: {sandbox_path}")