wangchuxiaoji-oss/doubao2api
GitHub: wangchuxiaoji-oss/doubao2api
Stars: 27 | Forks: 8
# doubao2api
逆向豆包(Doubao)客户端 API,为 AI 智能体提供免费的多模态能力。通过 OpenAI 兼容接口,让任何纯文本模型也能识图、读文件、生成图片/音乐/视频。
起初只是想给自己的 Hermes Agent(基于 DeepSeek V4 Flash)补上识图能力,结果越写越多,索性做成了完整的逆向客户端和 API 服务。
## 目录
- [这个项目能做什么](#这个项目能做什么)
- [原理](#原理)
- [快速开始](#快速开始)
- [安装](#安装)
- [Docker 部署(可选)](#docker-部署可选)
- [QR 扫码登录(推荐,跨平台)](#qr-扫码登录推荐跨平台)
- [从 Session 文件创建客户端](#从-session-文件创建客户端)
- [流式输出](#流式输出)
- [三模式对话(快速/思考/专家)](#三模式对话快速思考专家)
- [图片上传 + 多模态对话](#图片上传--多模态对话)
- [文件上传 + 文档对话](#文件上传--文档对话)
- [图片生成(文生图)](#图片生成文生图)
- [视频生成(文生视频)](#视频生成文生视频)
- [音乐生成(文生音乐)](#音乐生成文生音乐)
- [统一 API 服务(OpenAI 兼容)](#统一-api-服务openai-兼容)
- [部署](#部署)
- [环境变量](#环境变量)
- [认证](#认证)
- [会话管理](#会话管理)
- [Admin Dashboard](#admin-dashboard)
- [模型列表](#模型列表)
- [端点详细规范](#端点详细规范)
- [GET /health](#get-health)
- [GET /v1/models](#get-v1models)
- [POST /v1/chat/completions](#post-v1chatcompletions)
- [POST /v1/files](#post-v1files)
- [GET /v1/files/download](#get-v1filesdownload)
- [POST /v1/images/upload](#post-v1imagesupload)
- [POST /v1/images/generations](#post-v1imagesgenerations)
- [POST /v1/video/generations](#post-v1videogenerations)
- [POST /v1/audio/generations](#post-v1audiogenerations)
- [GET /auth/status](#get-authstatus)
- [POST /v1/session/qr-login](#post-v1sessionqr-login)
- [GET /admin](#get-admin)
- [使用 OpenAI Python SDK](#使用-openai-python-sdk)
- [使用 curl](#使用-curl)
- [Bot ID](#bot-id)
- [底层模型与路由](#底层模型与路由)
- [模型家族](#模型家族)
- [seed_intention 路由表](#seed_intention-路由表)
- [模式切换参数](#模式切换参数)
- [API 响应中的模型元数据](#api-响应中的模型元数据)
- [火山引擎 ARK API 模型名称参考](#火山引擎-ark-api-模型名称参考)
- [技术细节](#技术细节)
- [认证流程](#认证流程)
- [请求格式](#请求格式)
- [SSE 事件类型](#sse-事件类型)
- [content_type 枚举](#content_type-枚举)
- [思考链提取](#思考链提取)
- [Session 过期与风控检测](#session-过期与风控检测)
- [msToken 与风控](#mstoken-与风控)
- [搜索工具调用捕获](#搜索工具调用捕获)
- [项目结构](#项目结构)
## 这个项目能做什么
本项目适合为通用 AI 智能体(如 OpenClaw、Hermes 等对话/任务型 Agent)补全多模态能力。
举个实际例子:我的 Hermes Agent 接入的是 DeepSeek V4 Flash——一个纯文本模型,看不了图、读不了文件、更不能生成多媒体内容。接入 doubao2api 之后,相当于给它装上了"眼睛"和"手":
- **多模态对话**:多轮对话、深度思考(思维链)、联网搜索,完整的 ChatCompletion 能力
- **多模态理解**:识图、读 PDF/Word/Excel/代码等 60+ 种文件格式,纯文本模型也能"看懂"图片和文档
- **多媒体生成**:免费生成图片(文生图、图生图)、音乐、视频,Agent 的输出不再局限于文字
- **文件中转站**(奇淫技巧):通过 `/v1/files` 可上传任意文件(最大 1GB)获得一个永久 TOS URI,之后随时凭这个 URI 调用 `/v1/files/download` 换取 7 天有效的下载链接,过期了再换一个就行。这意味着你可以把它当作**免费的跨机器文件传输通道**——Agent A 在服务器 A 上传文件拿到 URI,把 URI 传给 Agent B,Agent B 在另一台服务器上凭 URI 获取下载链接直接拉取文件。无需自建 OSS,无需打通内网,单文件最大 1GB,存储不过期。有兴趣的兄弟可以基于这个开做个文件中转,感觉会很不错
⚠️ **不适合编程智能体**:豆包客户端模型**不支持 Function Calling / Tool Use**(无法调用外部工具如文件读写、终端命令、代码搜索等),因此**不适合**作为编程智能体(Claude Code、Codex、OpenCode 等)的后端模型。如果你需要的是能操作代码仓库的 coding agent,请选择原生支持工具调用的模型 API。
## 原理
通过 QR 扫码登录(全平台)获取 `sessionid` 等认证 Cookie,然后调用豆包内部 SSE 流式端点实现对话、图片/视频/音乐生成。
| 端点 | 协议 | 思考链 | 状态 |
|------|------|--------|------|
| `POST /samantha/chat/completion` | JSON 明文 sentEvent | **有** — `block_type=10040` + `10000` | ✅ 推荐主用 |
| `POST /alice/message/stream_call_bot` | base64 编码 payload | **无** | 旧端点,已废弃 |
- 认证: Cookie (`sessionid`, `ttwid`, `passport_csrf_token`)
- 响应: Server-Sent Events 流
## 快速开始
### 安装
# 方式一:pip 安装(推荐)
pip install git+https://github.com/wangchuxiaoji-oss/doubao2api.git
# 方式二:从源码
git clone https://github.com/wangchuxiaoji-oss/doubao2api.git
cd doubao2api
pip install -e .
### Docker 部署(可选)
docker build -t doubao2api .
docker run -d -p 9090:9090 -v ./. doubao_session.json:/app/.doubao_session.json doubao2api
### 前置条件
1. Python 3.10+
2. 已安装依赖(pip install 会自动处理)
### QR 扫码登录(推荐,跨平台)
不需要安装豆包桌面客户端:
from doubao2api.qr_login import QRLogin
result = QRLogin.login_and_save(".doubao_session.json")
### 从 Session 文件创建客户端
import asyncio
from doubao2api import DoubaoChatClient
async def main():
client = DoubaoChatClient.from_session()
async with client:
result = await client.chat("你好,请介绍一下你自己")
print(result.text)
asyncio.run(main())
### 流式输出
async with DoubaoChatClient.from_session() as client:
async for msg in client.chat_stream("讲个笑话"):
if msg.is_text_chunk:
print(msg.text, end="", flush=True)
### 三模式对话(快速/思考/专家)
from doubao2api import DoubaoChatClient, EXTENSION_BOT_ID
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
# 快速模式 (need_deep_think=0)
result = await client.chat_completion("1+1=?")
# 思考模式 (need_deep_think=1) — 带思维链
result = await client.chat_completion("解释量子纠缠", need_deep_think=1)
print(f"思考: {result.thinking_text}")
print(f"回答: {result.text}")
# 专家模式 (need_deep_think=3) — 深度推理
result = await client.chat_completion("证明勾股定理", need_deep_think=3)
### 图片上传 + 多模态对话
from doubao2api import DoubaoChatClient, EXTENSION_BOT_ID
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
image_bytes = open("photo.png", "rb").read()
att = await client.upload_image(image_bytes, "photo.png")
result = await client.chat_completion(
text="描述这张图片的内容",
image_attachments=[att],
need_deep_think=0,
)
**通过 REST API 上传大图片(推荐,无需 base64)**:
# 先上传图片,获取 CDN URL
curl -F "file=@photo.jpg" http://localhost:9090/v1/images/upload
# -> {"url": "https://...", "key": "tos-cn-i-.../xxx.png", ...}
# 然后在聊天中直接引用 URL
curl http://localhost:9090/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"doubao-expert","messages":[{"role":"user","content":[
{"type":"text","text":"这是什么?"},
{"type":"image_url","image_url":{"url":"上一步返回的url"}}
]}],"stream":true}'
### 文件上传 + 文档对话
支持 PDF、TXT、DOCX、XLSX、PPTX、CSV、Markdown、代码文件等 60+ 种格式。
from doubao2api import DoubaoChatClient, UploadedFile, EXTENSION_BOT_ID
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
# 上传文件
file_bytes = open("report.pdf", "rb").read()
uploaded = await client.upload_file(file_bytes, "report.pdf")
# uploaded: UploadedFile(uri='tos-cn-i-ik7evvg4ik/xxx.pdf', name='report.pdf', size=102400, file_type='pdf')
# 带文件引用的对话
result = await client.chat_completion(
text="总结这份文档的要点",
file_attachments=[uploaded],
)
print(result.text)
#### 多文件上传
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
# 同时上传多个文件
files = []
for path in ["data.csv", "readme.md", "config.json"]:
data = open(path, "rb").read()
uploaded = await client.upload_file(data, path)
files.append(uploaded)
# 一次对话引用多个文件
result = await client.chat_completion(
text="对比这三个文件的内容,找出关键差异",
file_attachments=files,
)
#### 文件 + 图片混合
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
# 同时附带文件和图片
img_att = await client.upload_image(open("chart.png", "rb").read(), "chart.png")
file_att = await client.upload_file(open("data.csv", "rb").read(), "data.csv")
result = await client.chat_completion(
text="图表和数据是否一致?",
image_attachments=[img_att],
file_attachments=[file_att],
)
#### 流式输出 + 文件
async with DoubaoChatClient.from_session(bot_id=EXTENSION_BOT_ID) as client:
uploaded = await client.upload_file(open("paper.pdf", "rb").read(), "paper.pdf")
async for chunk in client.chat_stream_completion(
text="逐段翻译这篇论文",
file_attachments=[uploaded],
need_deep_think=1, # 思考模式
):
if chunk.thinking:
print(f"[思考] {chunk.thinking}", end="")
if chunk.text:
print(chunk.text, end="", flush=True)
#### UploadedFile 数据结构
@dataclass
class UploadedFile:
uri: str = "" # TOS 存储路径,如 "tos-cn-i-ik7evvg4ik/xxx.pdf"
name: str = "" # 原始文件名
size: int = 0 # 文件大小(字节)
file_type: str = "" # 文件扩展名(不含点号)
#### 上传流程详解
`upload_file()` 内部自动完成以下 4 步:
┌─────────────────────────────────────────────────────────────────────┐
│ Step 1: POST /alice/resource/prepare_upload │
│ 请求: {"tenant_id":"5","scene_id":"5","resource_type":1} │
│ 响应: service_id, upload_auth_token (AK/SK/SessionToken) │
├─────────────────────────────────────────────────────────────────────┤
│ Step 2: GET /top/v1?Action=ApplyImageUpload&ServiceId=xxx │
│ 签名: AWS Signature V4 (使用 Step 1 的 STS 凭证) │
│ 响应: StoreUri, UploadHosts, Auth (TOS token), SessionKey │
├─────────────────────────────────────────────────────────────────────┤
│ Step 3: POST https://{tos_host}/upload/v1/{store_uri} │
│ Headers: Authorization={TOS Auth}, Content-CRC32={crc32_hex} │
│ Body: 文件二进制内容 │
│ 响应: {"code":2000,"message":"Success","data":{"crc32":"xxx"}} │
├─────────────────────────────────────────────────────────────────────┤
│ Step 4: POST /top/v1?Action=CommitImageUpload&ServiceId=xxx │
│ 签名: AWS Signature V4 │
│ 请求: {"SessionKey":"..."} │
│ 响应: UriStatus=2000 (成功) │
└─────────────────────────────────────────────────────────────────────┘
关键技术点:
- `/top/v1` 是豆包对 ByteDance ImageX API 的反向代理,避免了直接调用 `imagex.volcengineapi.com` 的 PSM 条件限制
- 签名使用标准 AWS Signature V4 算法(region=`cn-north-1`, service=`imagex`)
- TOS 上传需要 `Content-CRC32` header(小写 hex,8 位)
- STS 凭证有效期约 1 小时,每次上传重新获取
#### 支持的文件格式完整列表
| 类别 | 扩展名 |
|------|--------|
| 文档 | pdf, txt, csv, docx, doc, xlsx, xls, pptx, ppt, md, mobi, epub |
| Web/标记 | html, css, xml, json, yaml, yml |
| Python | py |
| JavaScript/TypeScript | js, ts, tsx, jsx |
| Java/Kotlin | java, kt |
| C/C++ | c, cpp, h, hpp |
| Go | go, mod, sum |
| Rust | rs |
| Swift | swift |
| C# | cs, xaml |
| Ruby | rb |
| PHP | php |
| Perl | pl |
| Shell | sh, bash, bat, cmd, ps1 |
| Lua | lua |
| Dart | dart |
| Scala | scala |
| Vue | vue |
| Protocol Buffers | proto |
| Docker | dockerfile |
| 配置 | env, ini, toml, plist, feature, vbs, vmx, vbox |
| 图片 | png, jpeg, jpg, webp |
#### 文件大小限制
- 单文件最大约 50MB(受 TOS 单次上传限制)
- 超大文件建议分片或使用 URL 引用方式
#### 在 chat/completion 中引用文件的 content_block 结构
文件在消息中以 `block_type=10052` 的 attachment block 传递,attachment `type=3` 表示文件:
{
"block_type": 10052,
"content": {
"attachment_block": {
"attachments": [{
"type": 3,
"identifier": "uuid",
"file": {
"uri": "tos-cn-i-ik7evvg4ik/xxx.pdf",
"url": "",
"file_type": 0,
"name": "report.pdf",
"size": 102400
},
"parse_state": 1,
"review_state": 1,
"upload_status": 1,
"progress": 100,
"src": ""
}]
},
"pc_event_block": ""
},
"block_id": "uuid",
"parent_id": "",
"meta_info": [],
"append_fields": []
}
attachment type 枚举:
| type | 含义 |
|------|------|
| 1 | 图片 |
| 3 | 文件(PDF/TXT/代码等) |
### 图片生成(文生图)
async with DoubaoChatClient.from_session() as client:
result = await client.generate_image(
prompt="一只柴犬在樱花树下",
ratio="16:9", # 支持 "1:1", "16:9", "9:16"
)
for img in result.images:
print(f"下载: {img.ori_url}")
### 视频生成(文生视频)
使用 Seedance 2.0 全能视频模型,每日约 10 次免费额度。
async with DoubaoChatClient.from_session() as client:
result = await client.generate_video(
prompt="一只柴犬在雪地里奔跑",
ratio="16:9",
timeout=300,
)
for v in result.videos:
print(f"视频: {v.video_url}")
print(f"时长: {v.duration}s")
#### 图生视频(img2video)
async with DoubaoChatClient.from_session() as client:
att = await client.upload_image(open("ref.png", "rb").read(), "ref.png")
result = await client.generate_video(
prompt="让画面动起来,镜头缓慢推进",
ref_image_key=att["uri"],
ratio="16:9",
)
#### 视频参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `prompt` | str | 视频描述文本(必需) |
| `ratio` | str | 宽高比:`"1:1"`, `"16:9"`, `"9:16"` |
| `camera_movement` | str | 镜头运动方式(可选) |
| `ref_image_key` | str | 参考图片 key(img2video,可选) |
| `timeout` | float | 最长等待秒数,默认 300 |
#### 异步流程
1. POST /samantha/chat/completion (content_type=2020)
↓ SSE 返回文本确认 + fin_reason.async_task.id
2. POST /samantha/chat/async/stream (body: {task_id, event_id: 0})
↓ SSE 长连接等待 1-3 分钟
3. 收到 content_type=2021 (SamanthaVideoGenerationOutput) → 视频 URL
### 音乐生成(文生音乐)
音乐生成是同步的,通常 30-60 秒内返回。返回的音频为 AAC 格式,时长约 60-140 秒。
#### 最简用法(AI 全自动)
async with DoubaoChatClient.from_session() as client:
result = await client.generate_music(prompt="一首轻快的夏日流行歌曲")
for track in result.tracks:
print(f"标题: {track.title}")
print(f"时长: {track.duration}s")
print(f"音频: {track.audio_url}")
#### 精细控制参数
result = await client.generate_music(
prompt="一首关于星空的浪漫民谣",
genre="Folk",
mood="Romantic",
gender="Male",
)
#### 自定义歌词模式
result = await client.generate_music(
prompt="星空之歌",
lyric="星光洒满夜空 我在这里等你\n银河流淌不息 照亮回家的路",
genre="Pop",
gender="Female",
generation_type="custome_lyric", # 注意拼写,API 原始值
)
#### 参数有效值参考
所有参数值来自 `/samantha/skill/pack` API(`skill_type=9`),为**首字母大写英文**。
**genre(音乐风格)— 11 种:**
| API value | 中文 |
|-----------|------|
| `Pop` | 流行 |
| `Rock` | 摇滚 |
| `Folk` | 民谣 |
| `Electronic` | 电音 |
| `Hip Hop/Rap` | 嘻哈 |
| `Chinese Style` | 国风 |
| `DJ` | DJ |
| `R&B/Soul` | R&B |
| `Reggae` | 雷鬼 |
| `Punk` | 朋克 |
| `Jazz` | 爵士 |
**mood(情绪)— 9 种:**
| API value | 中文 |
|-----------|------|
| `Happy` | 快乐 |
| `Chill` | 放松 |
| `Dynamic/Energetic` | 活力 |
| `Excited` | 兴奋 |
| `Sentimental/Melancholic/Lonely` | 忧郁 |
| `Inspirational/Hopeful` | 鼓舞 |
| `Sorrow/Sad` | 伤感 |
| `Nostalgic/Memory` | 怀旧 |
| `Romantic` | 浪漫 |
**gender(歌手性别):** `Male` / `Female`
**generation_type(歌词模式):** `AI_lyric`(AI 写词,默认) / `custome_lyric`(自定义歌词)
#### 预设模板
豆包提供 31 个预设音乐模板(通过 `/samantha/skill/pack` API 获取,`skill_type=9`)。
| # | 标题 | 风格 | 情绪 | 性别 |
|---|------|------|------|------|
| 1 | 醒来 | Rock | Chill | Male |
| 2 | 关于你 | Folk | Sentimental/Melancholic/Lonely | Female |
| 3 | 玉碎银鞍 | Chinese Style | Sorrow/Sad | Female |
| 4 | 漫漫 | R&B/Soul | Chill | Male |
| 5 | 在全宇宙与你相遇 | Rock | Chill | Male |
| 6 | 牙买加的偶遇 | Reggae | Dynamic/Energetic | Male |
| 7 | 爱的具象化 | Pop | Happy | Female |
| 8 | 戏中梦 | Chinese Style | Sentimental/Melancholic/Lonely | Female |
| 9 | 凤梨罐头 | Jazz | Nostalgic/Memory | Male |
| 10 | 沉溺的戏 | Jazz | Sentimental/Melancholic/Lonely | Male |
| 11 | 坠 | Electronic | Chill | Male |
| 12 | 追 | Electronic | Dynamic/Energetic | Female |
| 13 | 寻音 | Electronic | Dynamic/Energetic | Female |
| 14 | 就粉碎我 | Punk | Excited | Male |
| 15 | 樱花树下的秘密 | Pop | Nostalgic/Memory | Female |
| 16 | 江南闲梦 | Chinese Style | Chill | Female |
| 17 | 局外人 | Folk | Nostalgic/Memory | Female |
| 18 | 归家 | Folk | Sentimental/Melancholic/Lonely | Male |
| 19 | 格桑花的等待 | DJ | Sorrow/Sad | Male |
| 20 | 丢了月亮 | Folk | Sentimental/Melancholic/Lonely | Male |
| 21 | 夏日微风的低语 | Folk | Nostalgic/Memory | Female |
| 22 | 迟到 | Pop | Chill | Female |
| 23 | 荆棘尽头 | Pop | Happy | Female |
| 24 | 空空 | Pop | Sentimental/Melancholic/Lonely | Male |
| 25 | 梦里梦外 | Pop | Sorrow/Sad | Female |
| 26 | 思卿 | Chinese Style | Sorrow/Sad | Male |
| 27 | 白瓦 | Chinese Style | Chill | Male |
| 28 | 一镜千年 | Chinese Style | Nostalgic/Memory | Male |
| 29 | 留学生 | Hip Hop/Rap | Inspirational/Hopeful | Male |
| 30 | 她的故事 | Jazz | Chill | Female |
| 31 | 不眠夜 | Punk | Excited | Male |
#### 返回结果字段
`MusicGenerationResult.tracks` 列表中每个 `GeneratedMusic` 包含:
| 字段 | 类型 | 说明 |
|------|------|------|
| `audio_url` | str | 音频下载 URL(AAC/.mp4,抖音 CDN) |
| `title` | str | AI 生成的歌曲标题 |
| `duration` | float | 时长(秒),通常 60-140s |
| `lyrics` | str | 完整歌词文本 |
| `cover_url` | str | 封面图 URL |
| `vid` | str | 抖音视频 ID(内部标识) |
## 统一 API 服务(OpenAI 兼容)
一个服务暴露所有能力,兼容 OpenAI SDK 格式。设计为在 Linux 无头服务器上长期运行。
### 部署
#### 安装依赖
pip install aiohttp fastapi pydantic uvicorn httpx playwright playwright-stealth python-multipart
playwright install chromium
#### 本地开发启动
python -m doubao2api
# 默认监听 0.0.0.0:9090,无认证
#### 生产部署(Ubuntu)
DOUBAO_API_KEY=your-secret-key \
DOUBAO_HOST=0.0.0.0 \
DOUBAO_PORT=9090 \
DOUBAO_RPM_LIMIT=30 \
DOUBAO_BROWSER_DATA=/opt/doubao2api/.browser_data \
python -m doubao2api
#### systemd 服务(推荐)
# /etc/systemd/system/doubao-api.service
[Unit]
Description=Doubao Chat API
After=network.target
[Service]
Type=simple
User=doubao
WorkingDirectory=/opt/doubao
Environment=DOUBAO_API_KEY=your-secret-key
Environment=DOUBAO_HOST=0.0.0.0
Environment=DOUBAO_PORT=9090
Environment=DOUBAO_BROWSER_DATA=/opt/doubao2api/.browser_data
ExecStart=/usr/bin/python3 -m doubao2api
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now doubao-api
#### Nginx 反向代理(可选)
server {
listen 443 ssl;
server_name doubao-api.example.com;
location / {
proxy_pass http://127.0.0.1:9090;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off; # SSE 流式必须关闭缓冲
proxy_read_timeout 300s; # 视频生成需要长超时
}
}
### 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `DOUBAO_PORT` | `9090` | 监听端口 |
| `DOUBAO_HOST` | `0.0.0.0` | 监听地址 |
| `DOUBAO_API_KEY` | (空=无认证) | Bearer token。设为 `any` 接受任意非空 key |
| `DOUBAO_RPM_LIMIT` | `20` | 每分钟请求限制(所有端点共享) |
| `DOUBAO_HEADLESS` | `true` | Chromium 是否无头运行 |
| `DOUBAO_BROWSER_DATA` | `~/.doubao_browser` | Chromium 持久化用户目录 |
| `DOUBAO_NOVNC_URL` | 自动推断 | Admin 面板中的 noVNC 地址 |
| `DOUBAO_NOVNC_PASSWORD` | 空 | 自动拼接到 noVNC URL 的密码参数 |
### 认证
设置 `DOUBAO_API_KEY` 后,API 和 Admin 端点需要 Bearer token;不设置时不启用认证:
Authorization: Bearer your-api-key
- `DOUBAO_API_KEY` 未设置时:无认证,所有请求直接通过
- `DOUBAO_API_KEY=any`:接受任意非空 Bearer token
- `DOUBAO_API_KEY=sk-xxx`:仅接受完全匹配的 token
### 会话管理
**启动行为**:服务启动时打开持久化 Chromium 用户目录(`DOUBAO_BROWSER_DATA`),如果浏览器目录内已有登录态会自动复用。
**浏览器 watchdog**:后台每 30 秒检查浏览器是否响应,如果 Chromium 进程异常会自动重启。
**风控处理**:如果接口返回 `710022004`,服务会在 `/health` 中标记 `needs_captcha=true`,此时需要通过 noVNC 或重新登录处理风控,再调用 `/auth/reset_captcha` 恢复服务。
**首次部署 Session 获取**:
# 方式 1:通过 Dashboard 扫码(推荐)
# 访问 http://host:port/admin?key=YOUR_API_KEY → 登录 Tab → 扫码
# 登录成功后 3 秒自动跳转概览页
# 方式 2:API 扫码登录
curl -X POST http://localhost:9090/v1/session/qr-login \
-H "Authorization: Bearer YOUR_KEY"
# → 返回 base64 QR 码图片,用豆包 App 扫码
# 轮询状态:
curl http://localhost:9090/v1/session/qr-login \
-H "Authorization: Bearer YOUR_KEY"
# 如遇验证码/风控,可打开 Admin 登录页里的 noVNC 手动处理
### Admin Dashboard
内置 Web 管理面板(Vue 3 单文件应用,零构建依赖)。
**访问**:`http://host:port/admin?key=YOUR_API_KEY`
| 页面 | 功能 |
|------|------|
| **概览** | Session 状态、系统配置、Cookie 详情表格、测活按钮 |
| **登录** | QR 扫码登录、noVNC 手动登录、浏览器截图 |
| **API 测试** | 选择模型发送请求,支持流式/非流式,思维链折叠展示 |
| **请求日志** | 最近 100 条请求记录(5s 自动刷新) |
- 如果设置了 `DOUBAO_API_KEY`,HTML 页面和数据 API 都需要认证
- 概览页每 10 秒自动刷新 session 状态
- "测活"按钮主动发消息验证 session 有效性
### 模型列表
| 模型 ID | 类型 | need_deep_think | 说明 |
|---------|------|-----------------|------|
| `doubao` | chat | 0 | 快速模式(默认) |
| `doubao-think` | chat | 1 | 思考模式(带思维链) |
| `doubao-expert` | chat | 3 | 专家模式(深度推理) |
| `doubao-pro` | chat | 0 | 快速模式别名 |
| `doubao-image` | image | — | 图片生成 |
| `doubao-video` | video | — | 视频生成 |
| `doubao-music` | audio | — | 音乐生成 |
### 端点详细规范
#### GET /health
健康检查,无需认证。
**响应**:
{
"status": "ok",
"logged_in": true,
"consecutive_failures": 0,
"needs_captcha": false,
"last_error_code": 0
}
#### GET /v1/models
返回所有可用模型列表。
**响应**:
{
"object": "list",
"data": [
{"id": "doubao", "object": "model", "owned_by": "doubao", "created": 0},
{"id": "doubao-think", "object": "model", "owned_by": "doubao", "created": 0}
]
}
#### POST /v1/chat/completions
OpenAI 兼容的聊天补全端点。
**请求体**:
{
"model": "doubao-think",
"messages": [
{"role": "system", "content": "你是一个助手"},
{"role": "user", "content": "你好"}
],
"stream": true
}
**文件输入**:当前统一服务支持 OpenAI 风格的 `file_url`,仅支持非流式请求。图片输入的 `image_url` 结构暂未在统一服务中解析。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `model` | string | 否 | 模型 ID,默认 `doubao` |
| `messages` | array | 是 | 消息数组,支持 `system`/`user`/`assistant` 角色 |
| `stream` | bool | 否 | 是否流式返回,默认 `false` |
| `conversation_id` | string | 否 | 多轮对话 ID。首次不传,响应中返回新 ID,后续请求带上即可续接上下文 |
| `bot_id` | string | 否 | 切换 Bot,默认 `7338286299411103781` |
| `temperature` | float | 否 | 温度参数(保留字段,当前不影响行为) |
| `max_tokens` | int | 否 | 最大 token 数(保留字段) |
**非流式响应**:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "doubao-think",
"conversation_id": "38427336493867522",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "回答内容",
"reasoning_content": "思维链内容(仅思考/专家模式)",
"conversation_id": "38427336493867522"
},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
}
**多轮对话示例**:
# 第一轮:不传 conversation_id,响应中返回新 ID
RESP=$(curl -s http://localhost:9090/v1/chat/completions \
-H "Authorization: Bearer sk-xxx" \
-H "Content-Type: application/json" \
-d '{"model":"doubao","messages":[{"role":"user","content":"我叫小明"}]}')
CONV_ID=$(echo $RESP | jq -r '.conversation_id')
# 第二轮:带上 conversation_id,模型记住上下文
curl http://localhost:9090/v1/chat/completions \
-H "Authorization: Bearer sk-xxx" \
-H "Content-Type: application/json" \
-d "{\"model\":\"doubao\",\"messages\":[{\"role\":\"user\",\"content\":\"我叫什么名字?\"}],\"conversation_id\":\"$CONV_ID\"}"
# → "你叫小明"
**流式响应**(SSE):
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1700000000,"model":"doubao-think","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1700000000,"model":"doubao-think","choices":[{"index":0,"delta":{"reasoning_content":"思考中..."},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1700000000,"model":"doubao-think","choices":[{"index":0,"delta":{"content":"回答"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1700000000,"model":"doubao-think","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
**错误码**:
| HTTP 状态 | 说明 |
|-----------|------|
| 400 | 无效模型或空消息 |
| 401 | 认证失败 |
| 503 | 未登录、浏览器未初始化或需要验证码 |
| 502 | 上游错误(豆包 API 异常) |
#### POST /v1/files
文件上传端点。上传文件后可在 `/v1/chat/completions` 中通过 `file_url` 内容类型引用。
**请求格式**:`multipart/form-data`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | 要上传的文件 |
**curl 示例**:
# 上传 PDF 文件
curl http://localhost:9090/v1/files \
-F "file=@document.pdf"
# 上传代码文件
curl http://localhost:9090/v1/files \
-F "file=@main.py"
# 上传 CSV 数据
curl http://localhost:9090/v1/files \
-F "file=@data.csv"
**响应**:
{
"id": "tos-cn-i-ik7evvg4ik/3d1fe926a54849ebaa8f69943889393a.pdf",
"object": "file",
"filename": "document.pdf",
"bytes": 102400,
"uri": "tos-cn-i-ik7evvg4ik/3d1fe926a54849ebaa8f69943889393a.pdf",
"file_type": "pdf",
"purpose": "assistants"
}
| 响应字段 | 类型 | 说明 |
|----------|------|------|
| `id` | string | 与 `uri` 相同,兼容 OpenAI 文件对象格式 |
| `object` | string | 固定 `"file"` |
| `filename` | string | 原始文件名 |
| `bytes` | int | 文件大小(字节) |
| `uri` | string | TOS 存储路径(可用于后续引用) |
| `file_type` | string | 文件扩展名 |
| `purpose` | string | 固定 `"assistants"` |
**错误码**:
| HTTP 状态 | 说明 |
|-----------|------|
| 400 | 缺少 `file` 字段或非 multipart 请求 |
| 401 | 认证失败 |
| 429 | 速率限制 |
| 502 | 上传失败(TOS 存储异常) |
#### GET /v1/files/download
获取已上传文件的临时 CDN 下载链接。配合 `/v1/files` 使用,实现完整的上传→下载流程。
**Query 参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `uri` | string | 是 | `/v1/files` 返回的 TOS URI |
| `expire` | int | 否 | 期望有效期秒数(实际固定 7 天,此参数无效) |
**curl 示例**:
curl "http://localhost:9090/v1/files/download?uri=tos-cn-i-ik7evvg4ik/xxx.txt"
**响应**:
{
"url": "https://p3-flow-sign.byteimg.com/tos-cn-i-ik7evvg4ik/xxx.txt?x-expires=...",
"uri": "tos-cn-i-ik7evvg4ik/xxx.txt",
"expires_in": 3600
}
| 响应字段 | 类型 | 说明 |
|----------|------|------|
| `url` | string | CDN 下载链接(有效期 7 天) |
| `uri` | string | 原始 TOS URI |
| `expires_in` | int | 请求的过期时间(实际由服务端决定) |
**完整上传→下载流程**:
# 1. 上传文件
RESPONSE=$(curl -s http://localhost:9090/v1/files -F "file=@myfile.pdf")
URI=$(echo $RESPONSE | jq -r '.uri')
echo "上传成功: $URI"
# 2. 获取下载链接(可反复调用,每次返回新的 7 天有效 URL)
DOWNLOAD_URL=$(curl -s "http://localhost:9090/v1/files/download?uri=$URI" | jq -r '.url')
# 3. 下载文件
curl -o downloaded.pdf "$DOWNLOAD_URL"
**存储特性**:
| 项目 | 限制 |
|------|------|
| 单文件大小上限 | **1 GB** |
| 上传速度 | ~7 MB/s(取决于网络) |
| 下载 URL 有效期 | **固定 7 天**(expire 参数无效) |
| URI 持久性 | 可无限次重新获取下载 URL |
| 底层存储 | ByteDance veImageX 标准存储(默认永久保留) |
| 支持格式 | 60+ 种(PDF/DOCX/代码/图片等,见支持列表) |
#### POST /v1/images/upload
图片上传端点。上传图片后返回 CDN URL,可直接用于 `/v1/chat/completions` 的 `image_url` 内容类型,**无需 base64 编码**。
适用场景:本地大图片、避免 base64 膨胀(+33% 体积)。
**请求格式**:`multipart/form-data`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | 图片文件(png/jpg/webp) |
**curl 示例**:
# 上传图片
curl http://localhost:9090/v1/images/upload \
-F "file=@photo.jpg"
**响应**:
{
"url": "https://p-vcloud.byteimg.com/tos-cn-i-ik7evvg4ik/xxx.png~tplv-...",
"key": "tos-cn-i-ik7evvg4ik/xxx.png",
"filename": "photo.jpg",
"bytes": 2048576
}
| 响应字段 | 类型 | 说明 |
|----------|------|------|
| `url` | string | CDN URL,直接用于 `image_url.url` |
| `key` | string | TOS 存储路径 |
| `filename` | string | 原始文件名 |
| `bytes` | int | 文件大小(字节) |
**完整使用流程**:
# 1. 上传图片
URL=$(curl -s http://localhost:9090/v1/images/upload \
-F "file=@photo.jpg" | jq -r '.url')
# 2. 在聊天中引用(无需 base64)
curl http://localhost:9090/v1/chat/completions \
-H "Content-Type: application/json" \
-d "{
\"model\": \"doubao-expert\",
\"messages\": [{\"role\": \"user\", \"content\": [
{\"type\": \"text\", \"text\": \"描述这张图片\"},
{\"type\": \"image_url\", \"image_url\": {\"url\": \"$URL\"}}
]}],
\"stream\": true
}"
#### 在 /v1/chat/completions 中使用文件
有两种方式在聊天中附带文件:
**方式 1:使用 `file_url` 内容类型(自动上传)**
服务器会自动下载并上传文件到豆包存储:
# 通过 base64 data URI 传递文件内容
curl http://localhost:9090/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "doubao",
"messages": [{
"role": "user",
"content": [
{"type": "file_url", "file_url": {"url": "data:text/plain;base64,SGVsbG8gV29ybGQ="}},
{"type": "text", "text": "这个文件里写了什么?"}
]
}]
}'
# 通过 HTTP URL 传递文件(服务器会下载)
curl http://localhost:9090/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "doubao",
"messages": [{
"role": "user",
"content": [
{"type": "file_url", "file_url": {"url": "https://example.com/report.pdf"}},
{"type": "text", "text": "总结这份报告的要点"}
]
}]
}'
**方式 2:先上传再引用(适合大文件或重复引用)**
先通过 `/v1/files` 上传,然后在多次对话中复用同一个文件 URI:
# Step 1: 上传文件(只需一次)
curl -s http://localhost:9090/v1/files -F "file=@report.pdf"
# -> {"uri": "tos-cn-i-ik7evvg4ik/xxx.pdf", "filename": "report.pdf", "bytes": 102400, ...}
# Step 2: 在对话中直接引用 TOS URI(可多次复用,不会重复上传)
curl http://localhost:9090/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-expert",
"messages": [{"role": "user", "content": [
{"type": "file_url", "file_url": {
"url": "tos-cn-i-ik7evvg4ik/xxx.pdf",
"name": "report.pdf",
"size": 102400
}},
{"type": "text", "text": "总结这份报告的要点"}
]}]
}'
**`file_url` 内容类型参数**:
{
"type": "file_url",
"file_url": {
"url": "data:application/pdf;base64,...",
"name": "report.pdf",
"size": 102400
}
}
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `url` | string | 是 | 文件来源,支持三种格式:
• `tos-cn-i-xxx/yyy.pdf` — 已上传的 TOS URI(不会重复上传)
• `data:{mime};base64,{data}` — 直接传递文件内容
• `https://example.com/file.pdf` — HTTP(S) URL,服务器会下载 | | `name` | string | 否 | 文件名(使用 TOS URI 时建议提供) | | `size` | int | 否 | 文件大小字节数(使用 TOS URI 时建议提供) | **支持混合多种内容类型**: { "model": "doubao", "messages": [{ "role": "user", "content": [ {"type": "file_url", "file_url": {"url": "data:text/csv;base64,..."}}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}, {"type": "text", "text": "对比图表和数据是否一致"} ] }] } #### POST /v1/images/generations 图片生成端点。 **请求体**: { "prompt": "一只猫在月球上", "model": "doubao-image", "ratio": "16:9", "size": "1792x1024" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 图片描述 | | `model` | string | 否 | 固定 `doubao-image` | | `ratio` | string | 否 | 豆包比例:`1:1`/`16:9`/`9:16`/`4:3`/`3:4`;优先级高于 `size` | | `size` | string | 否 | OpenAI 风格尺寸:`1024x1024`/`1792x1024`/`1024x1792`/`1024x768`/`768x1024`;也可直接传比例字符串 | | `ref_image_key` | string | 否 | 参考图片 key(图生图)。通过 `/v1/images/upload` 上传后获取 `uri` 字段 | **图生图示例**(先上传参考图,再生成): # 1. 上传参考图片,获取 uri URI=$(curl -s http://localhost:9090/v1/images/upload \ -F "file=@ref.jpg" | jq -r '.uri') # 2. 以参考图为基础生成新图 curl http://localhost:9090/v1/images/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d "{\"prompt\":\"将这张图片转为水彩画风格\",\"ref_image_key\":\"$URI\",\"ratio\":\"16:9\"}" **响应**: { "created": 1700000000, "data": [ { "url": "https://p-vcloud.byteimg.com/...", "revised_prompt": "一只猫在月球上" } ] } #### POST /v1/video/generations 视频生成端点。当前接口会在请求内等待任务结果,成功后直接返回视频列表。 **请求体**: { "model": "doubao-video", "prompt": "一只柴犬在雪地奔跑", "ratio": "16:9" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 视频描述 | | `model` | string | 否 | 固定 `doubao-video` | | `ratio` | string | 否 | `1:1`/`16:9`/`9:16`,也可传 OpenAI 风格 `size` 后自动映射 | **响应**: { "created": 1700000000, "data": [ {"video_url": "https://...", "cover_url": "https://...", "duration": 5.0, "width": 1920, "height": 1080} ] } #### POST /v1/audio/generations 音乐生成端点。 **请求体**: { "model": "doubao-music", "prompt": "一首关于夏天的轻快流行歌", "genre": "Pop", "lyric": "可选歌词" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 歌曲描述 | | `model` | string | 否 | 固定 `doubao-music` | | `lyric` | string | 否 | 自定义歌词 | | `genre` | string | 否 | 流派 | **响应**: { "created": 1700000000, "data": [ { "audio_url": "https://v3-web.douyinvod.com/...", "title": "夏日微风", "duration": 95.5, "lyrics": "完整歌词...", "cover_url": "https://..." } ] } #### GET /auth/status 查看当前 session 健康状态。 **响应**: { "logged_in": true, "is_ready_flag": true, "login_button_visible": false, "page_url": "https://www.doubao.com/chat/", "device_id": "...", "web_id": "..." } `/admin/api/status` 返回同样结构。 #### POST /v1/session/qr-login 启动 QR 扫码登录流程。 **响应**: { "status": "qr_ready", "qr_image_base64": "iVBORw0KGgo...", "message": "Scan QR code with Doubao mobile app." } #### GET /v1/session/qr-login 轮询扫码状态。 **响应**: // 等待扫码 {"status": "pending"} // 登录成功 {"status": "success", "message": "Login successful, session updated", "cookies_count": 8} // 失败 {"status": "failed", "error": "二维码已过期"} #### GET /admin 管理面板 HTML 页面(无需认证)。 #### GET /admin/api/logs 最近 100 条请求日志。 #### GET /admin/api/system 系统信息(Python 版本、运行时间、配置等)。 #### GET /admin/api/cookies 当前 session 的 Cookie 详情。 ### 使用 OpenAI Python SDK from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:9090/v1", api_key="your-key") # 流式聊天 stream = client.chat.completions.create( model="doubao-think", messages=[{"role": "user", "content": "解释量子纠缠"}], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta if delta.content: print(delta.content, end="") # 非流式聊天 response = client.chat.completions.create( model="doubao-expert", messages=[{"role": "user", "content": "证明勾股定理"}], ) print(response.choices[0].message.content) # 图片生成 images = client.images.generate( model="doubao-image", prompt="一只宇航员猫咪", extra_body={"ratio": "1:1"}, ) print(images.data[0].url) # 带文件的聊天(通过 base64 data URI) import base64 file_data = open("report.pdf", "rb").read() b64 = base64.b64encode(file_data).decode() response = client.chat.completions.create( model="doubao", messages=[{ "role": "user", "content": [ {"type": "file_url", "file_url": {"url": f"data:application/pdf;base64,{b64}"}}, {"type": "text", "text": "总结这份文档的核心观点"}, ], }], ) print(response.choices[0].message.content) ### 使用 curl # 流式聊天 curl -N http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"model":"doubao-think","messages":[{"role":"user","content":"你好"}],"stream":true}' # 上传文件 curl http://localhost:9090/v1/files \ -H "Authorization: Bearer sk-xxx" \ -F "file=@document.pdf" # 带文件的聊天(base64 方式) FILE_B64=$(base64 -w0 document.pdf) curl http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d "{\"model\":\"doubao\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"file_url\",\"file_url\":{\"url\":\"data:application/pdf;base64,$FILE_B64\"}},{\"type\":\"text\",\"text\":\"总结文档\"}]}]}" # 带文件的聊天(URL 方式,服务器自动下载) curl http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"model":"doubao","messages":[{"role":"user","content":[{"type":"file_url","file_url":{"url":"https://example.com/report.pdf"}},{"type":"text","text":"总结这份报告"}]}]}' # 图片生成 curl http://localhost:9090/v1/images/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一只猫在月球上","ratio":"16:9"}' # 视频生成(请求内等待结果) curl http://localhost:9090/v1/video/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一只柴犬在雪地奔跑","ratio":"16:9"}' # 音乐生成 curl http://localhost:9090/v1/audio/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一首轻快的夏日歌曲","genre":"Pop"}' ## Bot ID | Bot ID | 说明 | 多模态 | 备注 | |--------|------|--------|------| | `7338286299411103781` | 当前统一服务默认 Bot | 文件问答/多媒体生成 | `BrowserClient.DEFAULT_BOT_ID` | ## 底层模型与路由 ### 模型家族 豆包底层使用字节跳动自研的 **Seed** 大模型。三模式(快速/思考/专家)共用同一个 Seed 模型,区别在于思考/专家模式注入了 Chain-of-Thought 提示。 ### seed_intention 路由表 豆包采用内容驱动的智能路由,收到消息后先分析意图再分发到对应 Agent: | intention | detail | agent | 触发条件 | |-----------|--------|-------|---------| | `seed_main` | `default` | Agent-Chat | 闲聊、推理、翻译 | | `seed_main` | `knowledge` | Agent-Knowledge | 知识问答 | | `seed_main` | `writing` | Agent-InnerCreation | 创作 | | `browsing` | `complex_browsing` | Agent-Knowledge | 实时搜索 | | `multi_agent` | `Agent-Code` | Agent-Code | 代码执行 | ### 模式切换参数 | need_deep_think | 模式 | completion_option | 思考链 | |-----------------|------|-------------------|--------| | 0 | 快速 | `use_deep_think: false` | 无 | | 1 | 思考 | `use_deep_think: true` | 有(10040+10000) | | 3 | 专家 | `use_deep_think: true, use_auto_cot: true` | 有(10040+10000) | ### API 响应中的模型元数据 SSE 事件的 `message.ext` 字段包含: | 字段 | 含义 | 示例值 | |------|------|--------| | `llm_model_type` | 模型内部 ID | `38`, `1733208237` | | `llm_intention` | 调度意图 | `seed_main` / `browsing` | | `llm_intention_detail` | 细分意图 | `default` / `Agent-Code` | | `input_tokens` | 输入 token 数 | 动态 | | `output_tokens` | 输出 token 数 | 动态 | ### 火山引擎 ARK API 模型名称参考 | 系列 | model_id | 说明 | |------|----------|------| | 豆包 2.0 Pro | `doubao-seed-2-0-pro` | 专家模式对应 | | 豆包 2.0 Lite | `doubao-seed-2-0-lite` | 快速模式对应 | | 豆包 2.0 Mini | `doubao-seed-2-0-mini` | 低延迟高并发 | | 豆包 2.0 Code | `doubao-seed-2-0-code-preview-260215` | 编程场景 | | 豆包 1.5 Pro 32K | `doubao-1-5-pro-32k-250115` | 上一代主力 | | 豆包 1.6 思考 | `doubao-seed-1-6-thinking-250715` | 深度推理 | | 豆包 1.6 快速思考 | `doubao-seed-1-6-flash-250615` | 快速推理 | ## 技术细节 ### 认证流程 通过 QR 扫码登录获取完整 session,并注入到 Playwright 持久化浏览器上下文: 1. 请求 CSRF token(`GET https://www.doubao.com`) 2. 获取 QR 码(`POST /passport/web/scan_qrcode/`) 3. 用户用豆包 App 扫码确认 4. 获取 `sessionid`、`ttwid`、`passport_csrf_token` 等 Cookie 5. 注入 Chromium 用户目录,后续启动复用 `DOUBAO_BROWSER_DATA` ### 请求格式 #### `/chat/completion`(聊天主端点) 当前统一服务的文本聊天、文件问答使用 `/chat/completion`。请求通过 Playwright 页面内的 `fetch()` 发出,ByteDance 前端 JS hook 自动拦截并注入 `a_bogus` + `msToken` 签名,确保请求指纹与浏览器完全一致。 关键点: | 项目 | 值 | |------|-----| | URL | `/chat/completion?aid=497858&device_platform=web&...`(hook 自动追加 a_bogus) | | Method | POST | | Content-Type | `application/json` | | 签名 | 浏览器 fetch hook 自动注入(无需手动调用) | | CSRF | 从 `passport_csrf_token` Cookie 自动提取并设为 header | | msToken | fetch hook 自动从 Cookie 读取并注入 | | 传输方式 | `page.evaluate()` + `expose_function` 回调桥接 SSE 流 | #### `/samantha/chat/completion`(图片/视频/音乐生成端点) 图片、视频、音乐生成使用 `/samantha/chat/completion`。请求体为 JSON 明文对象: { "messages": [ { "content": "{\"text\":\"你的问题\"}", "content_type": 2001, "attachments": [], "references": [] } ], "completion_option": { "is_regen": false, "with_suggest": true, "need_create_conversation": true, "launch_stage": 1, "is_replace": false, "is_delete": false, "is_ai_playground": false, "memory_type": 2, "message_from": 0, "use_deep_think": true, "use_auto_cot": false, "resend_for_regen": false, "enable_commerce_credit": false }, "evaluate_option": {"web_ab_params": ""}, "local_conversation_id": "_",
"local_message_id": "_"
}
关键字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `messages[].content` | string | `JSON.stringify({text: "..."})` |
| `messages[].content_type` | int | `2001` = SamanthaText |
| `completion_option.use_deep_think` | bool | 启用深度思考 |
| `completion_option.use_auto_cot` | bool | 自动 CoT |
| `completion_option.launch_stage` | int | `1` = Release |
| `completion_option.memory_type` | int | `2` = ToolMemory |
### SSE 事件类型
| event_type | 枚举名 | 说明 |
|------------|--------|------|
| 1 | HEARTBEAT | 心跳 |
| 2001 | CMPL | 文本补全块 |
| 2002 | ACK | 消息确认(含 conversation_id) |
| 2003 | FIN | 流结束 |
| 2004 | CMD | 命令 |
| 2005 | ERR | 错误 |
| 2010 | VERBOSE | 详细元数据(seed_intention 等) |
### content_type 枚举
| 值 | 枚举名 | 说明 |
|----|--------|------|
| 2001 | SamanthaText | 普通文本 |
| 2002 | SamanthaSuggest | 推荐问题 |
| 2003 | SamanthaLoading | 加载状态("深度思考中") |
| 2005 | SamanthaMusicGenInput | 音乐生成输入 |
| 2008 | SamanthaSearchText | 思考链增量文本 |
| 2009 | SamanthaImageInput | 图片输入 |
| 2010 | SamanthaImageOutput | 图片输出 |
| 2020 | SamanthaVideoGenerationInput | 视频生成输入 |
| 2021 | SamanthaVideoGenerationOutput | 视频生成输出 |
| 10000 | SamanthaTextV2 | 主回答文本(思考/专家模式) |
| 10040 | BlockTypeThink | 思考内容块 |
### 思考链提取
聊天流中通过 `block_type=10040` 切换思考状态,`block_type=10000` 或 `CHUNK_DELTA` 承载文本:
SSE event_type=2001 (CMPL)
├─ content_type=10040 → 思考状态块(BLOCK_THINKING)
├─ content_type=10000 → 主回答文本
├─ content_type=2008 → 思考链增量文本 (content.think)
├─ content_type=2001 → 最终回答文本
└─ content_type=2002 → 推荐问题
SSE event_type=2010 (VERBOSE) → 模型调度意图
SSE event_type=2002 (ACK) → conversation_id
SSE event_type=2003 (FIN) → 流结束
### Session 过期与风控检测
| 端点 | 过期信号 |
|------|----------|
| `/chat/completion` | SSE `error_code` / `STREAM_ERROR` |
| `/samantha/chat/completion` | JSON 错误或 SSE `event_type=2005` |
| 风控验证码 | `710022004` |
触发 `710022004` 后,服务会标记 `needs_captcha=true`,`/health` 会暴露该状态,`_get_client()` 会拒绝继续请求,直到人工完成验证并调用 `/auth/reset_captcha`。
### msToken 与风控
`msToken` 是字节跳动前端 JSSDK(BDMS/Slardar)生成的设备指纹 token,存储在 `.bytedance.com` 域下。格式为 136 字节随机数据的 base64url 编码(184 字符)。
**本项目策略**:所有 API 请求通过浏览器内 `fetch()` 发出,ByteDance 的 fetch hook 自动从 Cookie 读取 `msToken` 并注入请求参数,无需手动管理。文件上传端点(`upload_file`/`upload_image`)仍使用 httpx + `frontierSign` 签名,启动时从 Cookie 读取 msToken。
### 搜索工具调用捕获
当豆包模型判断需要联网搜索时,SSE 流中会出现 `block_type=10025`(`search_query_result_block`)事件。本项目完整捕获这些事件并通过 API 暴露。
**SSE 流中的搜索事件结构**:
{
"block_type": 10025,
"content": {
"search_query_result_block": {
"summary": "搜索 3 个关键词,参考 23 篇资料",
"queries": ["关键词1", "关键词2", "关键词3"],
"results": [
{"text_card": {"title": "...", "url": "...", "summary": "...", "source_name": "..."}}
]
}
},
"is_finish": true
}
**API 响应中的搜索结果**:
流式响应通过 `delta.search_results` 传递:
{
"choices": [{
"delta": {
"search_results": {
"summary": "搜索 3 个关键词,参考 23 篇资料",
"queries": ["关键词1", "关键词2"],
"results": [
{"title": "...", "url": "...", "summary": "...", "source": "..."}
]
}
}
}]
}
Admin 面板会合并多次增量搜索结果,并显示关键词、来源和摘要。
**已知 block_type 列表**:
| block_type | 含义 | 处理方式 |
|-----------|------|---------|
| 10000 | 文本块(思维链/回答) | 拼接为 text/thinking |
| 10024 | 通用工具块(generic_tool_block) | 提取 title → tool_info |
| 10025 | 搜索结果块(search_query_result_block) | 完整解析 → search_info |
| 10040 | 思维链分隔符 | 状态机切换 thinking/answer |
| 10052 | 附件块(图片上传) | 仅用于请求构建 |
| 10101 | 加载状态块(loading_block) | 提取 text → tool_info |
## 项目结构
doubao2api/
├── __init__.py # 公开 API
├── __main__.py # CLI 入口
├── browser_client.py # BrowserClient(浏览器内 fetch + expose_function 流式桥接)
├── client.py # DoubaoChatClient(旧版独立客户端,保留兼容)
├── unified_server.py # 统一 API 服务(FastAPI,OpenAI 兼容)
├── session.py # Cookie 加载(JSON 文件)
├── sse.py # Server-Sent Events 解析器
├── qr_login.py # QR 扫码登录(纯 HTTP)
├── captcha_handler.py # 验证码处理
├── captcha_server.py # 验证码本地 Web 服务
└── static/
└── admin.html # Admin Dashboard(Vue 3)
## Keywords / 关键词
doubao api, 豆包 api, 豆包逆向, doubao reverse engineer, openai compatible api, free chatgpt api, free multimodal api, ai agent tools, doubao chat api, bytedance doubao, 豆包免费接口, text to image api free, text to video api free, text to music api, doubao2api, coze alternative, 免费大模型 api, 智能体多模态, deepseek agent tools, python openai server
点击展开:31 首模板完整歌词
**1. 醒来** — Rock / Chill / Male 提线木偶般扭动四肢把自己叫醒在清晨 眼神在五光十色的屏幕前聚焦又涣散 划过精心包装的视频 掉进虚幻失真的世界 你在里面嬉笑怒骂 脸孔变换 曾经大言不惭的梦想还记得吗 不如冲破这茧房跃入山川湖海的怀抱 狂奔向苍穹去寻找许巍歌里的蓝莲花 让自由肆意主宰 你也终将野蛮生长 **2. 关于你** — Folk / Sentimental / Female 你是天边揉碎的白云 轻盈啊 你是擦肩落下的细雨 醉人啊 你是高悬天空的明月 遥远啊 我在夜里寂静 你在眼底蔓延 我多想 轻握着你的手 告诉你我所有最深沉的忧愁 告诉你我的秘密我的温柔 能否允许我 再陪你走过山高水长 **3. 玉碎银鞍** — Chinese Style / Sorrow / Female 往事如烟 何日重现 燃尽大殿 天人相隔不复见 银鞍白马 断弦琵琶 此生永别玉兰花 泥沼深陷 万丈深渊 半生飘摇 恨意涛涛 刀山火海走一遭 幽冥地狱催人老 谁将故人悼 **4. 漫漫** — R&B/Soul / Chill / Male 漫漫 怎么让我遇到了你 好巧你也爱在湖边看下雨 看行人走走停停 我们算不算是心有灵犀 人生一直漫漫慢慢 漫无目的地将你装在眼底 慢慢我已离不开你 慢慢我们一起变老下去 **5. 在全宇宙与你相遇** — Rock / Chill / Male 如戏般的剧本无聊的探索 宇宙的边缘是未知的轮廓 人们的生活是否有所掩饰 灵魂之外还有无尽的渴求 浩瀚 孤寂 波光粼粼 和你互相辉映 捍卫 不朽 这样足够 还好有你懂我 **6. 牙买加的偶遇** — Reggae / Dynamic / Male 我绕着加勒比海 来到牙买加 快乐如同棕榈叶 随海风飘动 偶遇了海边老人 却没有聊天 他说不要再回头看 你也可以奔跑起来 Stand up hurry up Run up fly up 我沿着海风飘啊飘啊 就算双脚打结也不累 我看着浪潮翻啊翻啊 感受这无尽自由时间 **7. 爱的具象化** — Pop / Happy / Female 咖啡香氤氲了晨曦的窗 你在身旁陪我翻阅着时光 细雨轻敲温柔乡 我们的心跳合奏同频的交响 午后阳光洒落 捧一束浪漫与你对坐 街灯下影子交错 爱人啊再把那情话轻讲 爱的具象化 是漫步宇宙却发现你比星辰明亮 是化身仓颉 也造不出你名字的分量 是你看宏大的意象 无法写好爱的具象 就像我爱你这件事 早已成为我生命的征象 **8. 戏中梦** — Chinese Style / Sentimental / Female 梦是虚实的界限 跨越时间一步千年 那时笔墨还未染史篇 你仍是少年 你英年魂归 我迟生千年难吊唁 骤然清醒 前尘皆散独坐镜前描妆勾面 金戈铁马吞山河 封狼居胥凯歌旋 我在台上将戏演 你唯剩史书字里行间 我唱云遮月 扮将军当年风光无限 一戏唱罢英雄事 只憾今生前尘无缘相见 **9. 凤梨罐头** — Jazz / Nostalgic / Male 我打开那瓶凤梨罐头 才发现它已过了期 尝一口过期的希望 酸苦肆意侵袭 寂静的夜里回忆如潮涌 心却在游离 从一数到五百的钟响 笑我又唏嘘 **10. 沉溺的戏** — Jazz / Sentimental / Male 我以为我能轻易触碰你 我真想和你走到时间尽头 可是啊对你来说 我只是弃之可惜 这说不清的友情或是爱情 我只能沉溺 **11. 坠** — Electronic / Chill / Male 我把灵魂锁进思念的茧 放任自己深潜你眸中的海洋 荒芜的梦中挂满喝醉的星 摇摇欲坠向你的坐标落下 **12. 追** — Electronic / Dynamic / Female 我透过月亮 看朦胧的未来 在逐梦的途中歇脚 画幅印象派 那么忘我啊 连风都听不见 月光见证我 追在梦的后面 **13. 寻音** — Electronic / Dynamic / Female 在这个潮湿的夜晚风停住不动 树林也静默 只剩我和你相拥 就一起到遥远的梦境去找寻 那回响在灵魂深处的声音 **14. 就粉碎我** — Punk / Excited / Male 每天早上一杯速溶就随便喝 再像可怜人一样嘶吼怒骂一下 夜晚在不属于我的城市喝醉游荡 被生活粉碎被一切粉碎 就粉碎我的骄傲 就粉碎我的愤怒 就粉碎我的执着 就粉碎我的热情 **15. 樱花树下的秘密** — Pop / Nostalgic / Female 阳光洒落在旧课桌旁 我偷偷看你侧脸的光 书页间藏着的小心思 和樱花一起悄悄绽放 小卖部门口故意徘徊 只为和你多一秒遇见 其实偶尔我也会吃醋 看那女孩与你肩并肩 听说你喜欢晴天和海 我默默记在心里面 那些未曾说出口的话 藏在心底成了诗篇 这粉色秘密微酸又甜 遗憾总让机会擦肩 但愿未来会有那一天 你能听见我的心弦 **16. 江南闲梦** — Chinese Style / Chill / Female 小桥流水 轻舟摇曳过柳岸 烟雨蒙蒙 青瓦白墙映江南 茶香氤氲 书卷轻翻旧时言 江南闲梦 悠悠岁月慢慢看 诗意画卷 花开水乡间 清风拂面 心事皆释然 岁月静好 笑谈人世间 在这江南里 梦回旧时年 **17. 局外人** — Folk / Nostalgic / Female 走过林立的高楼 喧闹的街道如此空旷 逆行汹涌的人潮 犹豫着前进的方向 我在这城市中漂泊 是拼搏还是挣扎 舞台上的局外人啊 是坚强还是擅长伪装 我说理想不重 只压弯了月光 道路过于宽广 难免迷失方向 我和这城市不熟 只顾得上闯荡 白天藏好迷茫 夜晚在梦里飘荡 **18. 归家** — Folk / Sentimental / Male 坐在空空的房间 陪着我的只有吉他 城市灯火明明灭灭 孤独的人走在长街 心中的家像远方的月 看得见却触不及边界 我四处张望 眼眸藏不住失望 我想有一个家 有小小的沙发和她种的花 她会抚平我所有伤疤 她会爱我不论冬夏 心中灯塔忽暗又忽烁 归家梦从未凋落 异乡霓虹照不亮寂寞 漂泊的心还渴望着落 **19. 格桑花的等待** — DJ / Sorrow / Male 我问你是谁,那心中的爱难猜 一朵格桑花,她留守在旷野外 你说爱不在,往事深情成空白 惊鸿过一瞥,原来寂寞是天籁 什么样的情欠下何种相思债 什么样的爱等到下一次花开 缘尽情难在,还会有什么期待 相遇即离开,月光洒落秋水外 **20. 丢了月亮** — Folk / Sentimental / Male 他说想要去流浪,像一匹野马 在草原奔波辗转,听晚风歌唱 也许他真能找到,这样的地方 总好过在城市的角落,孤单流放 这一生啊,埋藏着太多的遗憾 渴望光啊,却又被现实打得遍体鳞伤 这青春啊,到底是年少的轻狂 月亮它啊,终究抵不过六便士的分量 **21. 夏日微风的低语** — Folk / Nostalgic / Female 想你像树荫下的微风 轻轻吹过我心头的夏天 盼你如晨曦穿过小窗 一丝丝光芒映在旧墙 你是我日复一日的期待 是每条小路上的远方 在每个普通的时刻里 你是无尽思念的风光 **22. 迟到** — Pop / Chill / Female 床头放着闹钟 怀里抱着猫 喂食器一工作 猫就喵喵叫 可是为什么 我还是睡过头 难道神明在说 我注定迟到 相遇迟到相恋迟到 分手也迟到 追赶不上挽留不了 只剩下苦笑 我与你的happyend 总是差一点 如果抵达不了 干脆逃开你的怀抱 **23. 荆棘尽头** — Pop / Happy / Female 多少次满身污泥,多少次人海沉浮 你从我背后走来,带动温热的晚风 水雾稀薄了,空气旋转了 绝望消散了,荡气回肠了 破除了荆棘,将苦化作了甜蜜 站在我身后,为累赋格了意义 星垂低语耳边呢喃 说千万别再走丢了 **24. 空空** — Pop / Sentimental / Male 关了灯在屋中 心太空 失去所有联络 没行踪 模糊的空气里 要发疯 乌云密布的我 是黑洞 比一块冰更寂更冷 比一尾鱼更哑更聋 沉溺在自我世界 无视情感涌动 深陷于太空四季 梦境与诗中 **25. 梦里梦外** — Pop / Sorrow / Female 在梦里爱上你 所有斑驳都重生奕奕 在梦外离开你 爱的忍耐都化为灰燃 梦中阳光耀眼 醒来只见黑暗幽远 走不出望不穿 爱的语言都成为云烟 **26. 思卿** — Chinese Style / Sorrow / Male 提笔绘,浮生卷卷落画,雕琢我心扉 叹只叹,此景难入你眼,空留了伤悲 骤雨催,车马滚滚印辙,装下离人泪 却思归,自此山高水远,恐是再难回 且看那风月笑,知是情深不可往 饮浊酒一杯,在红尘染尽爱恨憔悴 又闻那落花飞,点了流水尚余味 此一生,只一生,凭回忆将卿魂牵梦追 **27. 白瓦** — Chinese Style / Chill / Male 轻踏白瓦巷 雨后晴空长 柳絮沾露珠 釉色染霜凉 古道旁桃花香 纸伞下人相望 岁月轻抚过 留下一抹妆 白瓦映月光 故事在回响 烟雨水墨里 勾勒过往 青石板街旁 谁家炊烟扬 一幕幕画卷 藏匿旧时芳华香 **28. 一镜千年** — Chinese Style / Nostalgic / Male 铜镜轻拂尘 岁月留痕深 镜中映出旧时颜 笑语盈盈似花绽 镜中影似水流年 恍若隔世梦一场 镜外人诉尽沧桑 昔日风华已成殇 精雕细琢星云纹 凤鸟欲飞蟠龙腾 镜里千秋照出历史悠悠 汉唐宋明时光转 故事长长已泛黄 你看那谁的手拂过往事幽幽 **29. 留学生** — Hip Hop/Rap / Inspirational / Male 打包20刀的一荤一素 回到尽头那间standard room 进门先和室友打个招呼 不是Sam是袋鼠和蜘蛛 没有尖叫也不会被吓哭 我早就对此熟视无睹 比起危险还是essay更痛苦 我埋头在房间极限赶due 都说留学必然经历四个阶段 兴奋焦虑疯癫和麻木感 陌生的地域,毕业的压力 连吃饱饭都那么艰难 在异国看同一轮太阳落下 其实我也很想家 还好心态和胃很抗压 留学生的信念不会轻易崩塌 **30. 她的故事** — Jazz / Chill / Female 玫瑰会奔向远方 伴着荒郊月亮 夜莺为她歌唱 南风也为她奔忙 她会始终绽放 她会给你芳香 她会一路歌唱 去她自己的乡 **31. 不眠夜** — Punk / Excited / Male 不想听的电话就按掉 不想见的人就让他走开 不想装傻就尽情发泄吧 失败的梦想就让它算了吧 我要我要我要一个不眠夜 鼓点敲击我的大脑 吞没我的疯狂 那个瞬间 我好像看到了天堂。 哪怕只能暂时逃避 我也只想逃避• `tos-cn-i-xxx/yyy.pdf` — 已上传的 TOS URI(不会重复上传)
• `data:{mime};base64,{data}` — 直接传递文件内容
• `https://example.com/file.pdf` — HTTP(S) URL,服务器会下载 | | `name` | string | 否 | 文件名(使用 TOS URI 时建议提供) | | `size` | int | 否 | 文件大小字节数(使用 TOS URI 时建议提供) | **支持混合多种内容类型**: { "model": "doubao", "messages": [{ "role": "user", "content": [ {"type": "file_url", "file_url": {"url": "data:text/csv;base64,..."}}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}, {"type": "text", "text": "对比图表和数据是否一致"} ] }] } #### POST /v1/images/generations 图片生成端点。 **请求体**: { "prompt": "一只猫在月球上", "model": "doubao-image", "ratio": "16:9", "size": "1792x1024" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 图片描述 | | `model` | string | 否 | 固定 `doubao-image` | | `ratio` | string | 否 | 豆包比例:`1:1`/`16:9`/`9:16`/`4:3`/`3:4`;优先级高于 `size` | | `size` | string | 否 | OpenAI 风格尺寸:`1024x1024`/`1792x1024`/`1024x1792`/`1024x768`/`768x1024`;也可直接传比例字符串 | | `ref_image_key` | string | 否 | 参考图片 key(图生图)。通过 `/v1/images/upload` 上传后获取 `uri` 字段 | **图生图示例**(先上传参考图,再生成): # 1. 上传参考图片,获取 uri URI=$(curl -s http://localhost:9090/v1/images/upload \ -F "file=@ref.jpg" | jq -r '.uri') # 2. 以参考图为基础生成新图 curl http://localhost:9090/v1/images/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d "{\"prompt\":\"将这张图片转为水彩画风格\",\"ref_image_key\":\"$URI\",\"ratio\":\"16:9\"}" **响应**: { "created": 1700000000, "data": [ { "url": "https://p-vcloud.byteimg.com/...", "revised_prompt": "一只猫在月球上" } ] } #### POST /v1/video/generations 视频生成端点。当前接口会在请求内等待任务结果,成功后直接返回视频列表。 **请求体**: { "model": "doubao-video", "prompt": "一只柴犬在雪地奔跑", "ratio": "16:9" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 视频描述 | | `model` | string | 否 | 固定 `doubao-video` | | `ratio` | string | 否 | `1:1`/`16:9`/`9:16`,也可传 OpenAI 风格 `size` 后自动映射 | **响应**: { "created": 1700000000, "data": [ {"video_url": "https://...", "cover_url": "https://...", "duration": 5.0, "width": 1920, "height": 1080} ] } #### POST /v1/audio/generations 音乐生成端点。 **请求体**: { "model": "doubao-music", "prompt": "一首关于夏天的轻快流行歌", "genre": "Pop", "lyric": "可选歌词" } | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `prompt` | string | 是 | 歌曲描述 | | `model` | string | 否 | 固定 `doubao-music` | | `lyric` | string | 否 | 自定义歌词 | | `genre` | string | 否 | 流派 | **响应**: { "created": 1700000000, "data": [ { "audio_url": "https://v3-web.douyinvod.com/...", "title": "夏日微风", "duration": 95.5, "lyrics": "完整歌词...", "cover_url": "https://..." } ] } #### GET /auth/status 查看当前 session 健康状态。 **响应**: { "logged_in": true, "is_ready_flag": true, "login_button_visible": false, "page_url": "https://www.doubao.com/chat/", "device_id": "...", "web_id": "..." } `/admin/api/status` 返回同样结构。 #### POST /v1/session/qr-login 启动 QR 扫码登录流程。 **响应**: { "status": "qr_ready", "qr_image_base64": "iVBORw0KGgo...", "message": "Scan QR code with Doubao mobile app." } #### GET /v1/session/qr-login 轮询扫码状态。 **响应**: // 等待扫码 {"status": "pending"} // 登录成功 {"status": "success", "message": "Login successful, session updated", "cookies_count": 8} // 失败 {"status": "failed", "error": "二维码已过期"} #### GET /admin 管理面板 HTML 页面(无需认证)。 #### GET /admin/api/logs 最近 100 条请求日志。 #### GET /admin/api/system 系统信息(Python 版本、运行时间、配置等)。 #### GET /admin/api/cookies 当前 session 的 Cookie 详情。 ### 使用 OpenAI Python SDK from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:9090/v1", api_key="your-key") # 流式聊天 stream = client.chat.completions.create( model="doubao-think", messages=[{"role": "user", "content": "解释量子纠缠"}], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta if delta.content: print(delta.content, end="") # 非流式聊天 response = client.chat.completions.create( model="doubao-expert", messages=[{"role": "user", "content": "证明勾股定理"}], ) print(response.choices[0].message.content) # 图片生成 images = client.images.generate( model="doubao-image", prompt="一只宇航员猫咪", extra_body={"ratio": "1:1"}, ) print(images.data[0].url) # 带文件的聊天(通过 base64 data URI) import base64 file_data = open("report.pdf", "rb").read() b64 = base64.b64encode(file_data).decode() response = client.chat.completions.create( model="doubao", messages=[{ "role": "user", "content": [ {"type": "file_url", "file_url": {"url": f"data:application/pdf;base64,{b64}"}}, {"type": "text", "text": "总结这份文档的核心观点"}, ], }], ) print(response.choices[0].message.content) ### 使用 curl # 流式聊天 curl -N http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"model":"doubao-think","messages":[{"role":"user","content":"你好"}],"stream":true}' # 上传文件 curl http://localhost:9090/v1/files \ -H "Authorization: Bearer sk-xxx" \ -F "file=@document.pdf" # 带文件的聊天(base64 方式) FILE_B64=$(base64 -w0 document.pdf) curl http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d "{\"model\":\"doubao\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"file_url\",\"file_url\":{\"url\":\"data:application/pdf;base64,$FILE_B64\"}},{\"type\":\"text\",\"text\":\"总结文档\"}]}]}" # 带文件的聊天(URL 方式,服务器自动下载) curl http://localhost:9090/v1/chat/completions \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"model":"doubao","messages":[{"role":"user","content":[{"type":"file_url","file_url":{"url":"https://example.com/report.pdf"}},{"type":"text","text":"总结这份报告"}]}]}' # 图片生成 curl http://localhost:9090/v1/images/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一只猫在月球上","ratio":"16:9"}' # 视频生成(请求内等待结果) curl http://localhost:9090/v1/video/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一只柴犬在雪地奔跑","ratio":"16:9"}' # 音乐生成 curl http://localhost:9090/v1/audio/generations \ -H "Authorization: Bearer sk-xxx" \ -H "Content-Type: application/json" \ -d '{"prompt":"一首轻快的夏日歌曲","genre":"Pop"}' ## Bot ID | Bot ID | 说明 | 多模态 | 备注 | |--------|------|--------|------| | `7338286299411103781` | 当前统一服务默认 Bot | 文件问答/多媒体生成 | `BrowserClient.DEFAULT_BOT_ID` | ## 底层模型与路由 ### 模型家族 豆包底层使用字节跳动自研的 **Seed** 大模型。三模式(快速/思考/专家)共用同一个 Seed 模型,区别在于思考/专家模式注入了 Chain-of-Thought 提示。 ### seed_intention 路由表 豆包采用内容驱动的智能路由,收到消息后先分析意图再分发到对应 Agent: | intention | detail | agent | 触发条件 | |-----------|--------|-------|---------| | `seed_main` | `default` | Agent-Chat | 闲聊、推理、翻译 | | `seed_main` | `knowledge` | Agent-Knowledge | 知识问答 | | `seed_main` | `writing` | Agent-InnerCreation | 创作 | | `browsing` | `complex_browsing` | Agent-Knowledge | 实时搜索 | | `multi_agent` | `Agent-Code` | Agent-Code | 代码执行 | ### 模式切换参数 | need_deep_think | 模式 | completion_option | 思考链 | |-----------------|------|-------------------|--------| | 0 | 快速 | `use_deep_think: false` | 无 | | 1 | 思考 | `use_deep_think: true` | 有(10040+10000) | | 3 | 专家 | `use_deep_think: true, use_auto_cot: true` | 有(10040+10000) | ### API 响应中的模型元数据 SSE 事件的 `message.ext` 字段包含: | 字段 | 含义 | 示例值 | |------|------|--------| | `llm_model_type` | 模型内部 ID | `38`, `1733208237` | | `llm_intention` | 调度意图 | `seed_main` / `browsing` | | `llm_intention_detail` | 细分意图 | `default` / `Agent-Code` | | `input_tokens` | 输入 token 数 | 动态 | | `output_tokens` | 输出 token 数 | 动态 | ### 火山引擎 ARK API 模型名称参考 | 系列 | model_id | 说明 | |------|----------|------| | 豆包 2.0 Pro | `doubao-seed-2-0-pro` | 专家模式对应 | | 豆包 2.0 Lite | `doubao-seed-2-0-lite` | 快速模式对应 | | 豆包 2.0 Mini | `doubao-seed-2-0-mini` | 低延迟高并发 | | 豆包 2.0 Code | `doubao-seed-2-0-code-preview-260215` | 编程场景 | | 豆包 1.5 Pro 32K | `doubao-1-5-pro-32k-250115` | 上一代主力 | | 豆包 1.6 思考 | `doubao-seed-1-6-thinking-250715` | 深度推理 | | 豆包 1.6 快速思考 | `doubao-seed-1-6-flash-250615` | 快速推理 | ## 技术细节 ### 认证流程 通过 QR 扫码登录获取完整 session,并注入到 Playwright 持久化浏览器上下文: 1. 请求 CSRF token(`GET https://www.doubao.com`) 2. 获取 QR 码(`POST /passport/web/scan_qrcode/`) 3. 用户用豆包 App 扫码确认 4. 获取 `sessionid`、`ttwid`、`passport_csrf_token` 等 Cookie 5. 注入 Chromium 用户目录,后续启动复用 `DOUBAO_BROWSER_DATA` ### 请求格式 #### `/chat/completion`(聊天主端点) 当前统一服务的文本聊天、文件问答使用 `/chat/completion`。请求通过 Playwright 页面内的 `fetch()` 发出,ByteDance 前端 JS hook 自动拦截并注入 `a_bogus` + `msToken` 签名,确保请求指纹与浏览器完全一致。 关键点: | 项目 | 值 | |------|-----| | URL | `/chat/completion?aid=497858&device_platform=web&...`(hook 自动追加 a_bogus) | | Method | POST | | Content-Type | `application/json` | | 签名 | 浏览器 fetch hook 自动注入(无需手动调用) | | CSRF | 从 `passport_csrf_token` Cookie 自动提取并设为 header | | msToken | fetch hook 自动从 Cookie 读取并注入 | | 传输方式 | `page.evaluate()` + `expose_function` 回调桥接 SSE 流 | #### `/samantha/chat/completion`(图片/视频/音乐生成端点) 图片、视频、音乐生成使用 `/samantha/chat/completion`。请求体为 JSON 明文对象: { "messages": [ { "content": "{\"text\":\"你的问题\"}", "content_type": 2001, "attachments": [], "references": [] } ], "completion_option": { "is_regen": false, "with_suggest": true, "need_create_conversation": true, "launch_stage": 1, "is_replace": false, "is_delete": false, "is_ai_playground": false, "memory_type": 2, "message_from": 0, "use_deep_think": true, "use_auto_cot": false, "resend_for_regen": false, "enable_commerce_credit": false }, "evaluate_option": {"web_ab_params": ""}, "local_conversation_id": "