逆向豆包(Doubao)客户端 API,为 AI 智能体提供免费的多模态能力。通过 OpenAI 兼容接口,让任何纯文本模型也能识图、读文件、生成图片/音乐/视频。
起初只是想给自己的 Hermes Agent(基于 DeepSeek V4 Flash)补上识图能力,结果越写越多,索性做成了完整的逆向客户端和 API 服务。
目录
- 这个项目能做什么
- 原理
- 快速开始
- 统一 API 服务(OpenAI 兼容)
- Bot ID
- 底层模型与路由
- 技术细节
- 项目结构
这个项目能做什么
本项目适合为通用 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
前置条件
- Python 3.10+
- 已安装依赖(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,
)
注意: 图片功能需要使用
EXTENSION_BOT_ID(7338286299411103781)。
通过 REST API 上传大图片(推荐,无需 base64):
# 先上传图片,获取 CDN URL
curl -F "[email protected]" 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-CRC32header(小写 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}")
水印限制: 所有 API 返回的图片 URL 均带有水印(CDN 层面通过 ImageX tplv 模板渲染),无法绕过。
视频生成(文生视频)
使用 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 |
点击展开: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
不想听的电话就按掉
不想见的人就让他走开
不想装傻就尽情发泄吧
失败的梦想就让它算了吧
我要我要我要一个不眠夜
鼓点敲击我的大脑 吞没我的疯狂
那个瞬间 我好像看到了天堂。
哪怕只能暂时逃避 我也只想逃避
返回结果字段
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 tokenDOUBAO_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 "[email protected]"
# 上传代码文件
curl http://localhost:9090/v1/files \
-F "[email protected]"
# 上传 CSV 数据
curl http://localhost:9090/v1/files \
-F "[email protected]"
响应:
{
"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 "[email protected]")
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/代码/图片等,见支持列表) |
存储持久性说明:根据 veImageX 官方文档,标准存储类型的文件默认永久保留,不会自动删除(除非服务方配置了生命周期策略)。实测中 URI 可无限次刷新获取新的 7 天下载链接,未观察到文件被删除的情况。但由于我们使用的是豆包内部的 veImageX 服务,无法排除字节跳动未来调整内部策略的可能性,建议重要文件自行备份。
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 "[email protected]"
响应:
{
"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 "[email protected]" | 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
}"
已上传的图片 URL(含
tos-cn-i-)会被自动识别,不会重复上传。
在 /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 "[email protected]"
# -> {"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": "对比图表和数据是否一致"}
]
}]
}
注意:
- 文件上传受速率限制(与聊天共享 RPM 配额)
- 上传的文件存储在 ByteDance TOS,URI 有效期较长但非永久
- 单次对话可附带多个文件,但总量建议不超过 5 个
- 超大文件(>10MB)上传可能较慢,建议设置较长的请求超时
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 "[email protected]" | 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 "[email protected]"
# 带文件的聊天(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 |
说明:
client.py中仍保留旧版DoubaoChatClient的 Bot ID 常量;当前统一服务主路径使用BrowserClient,默认 Bot ID 为7338286299411103781。
底层模型与路由
模型家族
豆包底层使用字节跳动自研的 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 持久化浏览器上下文:
- 请求 CSRF token(
GET https://www.doubao.com) - 获取 QR 码(
POST /passport/web/scan_qrcode/) - 用户用豆包 App 扫码确认
- 获取
sessionid、ttwid、passport_csrf_token等 Cookie - 注入 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": "<timestamp>_<uuid>",
"local_message_id": "<timestamp>_<uuid>"
}
关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
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
Comments