Skip to content

Commit b175be6

Browse files
authored
Merge pull request volcengine#20 from liyuxuan-bd/demo_deepdoubao
feat: deepdoubao demo
2 parents 41d99c1 + 54b0187 commit b175be6

File tree

9 files changed

+2901
-179
lines changed

9 files changed

+2901
-179
lines changed

demohouse/deepdoubao/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# DeepDoubao
2+
## 应用介绍
3+
这是一款结合 DeepSeek R1 模型的强大推理能力与 Doubao 模型的高效对话能力的应用,为用户提供智能问答服务。结合 Deepseek R1 模型的推理能力与 Doubao 自然流畅的对话总结能力,为用户带来精准、专业且具有互动性的智能体验。
4+
5+
### 应用优势
6+
DeepSeek R1 模型强大的思考能力,搭配豆包模型扎实的对话总结基础能力,优势互补。在处理复杂流程、参考问答、信息抽取等任务时,两者协作,能更精准高效地执行,取得比纯 R1 模型更好的结果。
7+
8+
### 相关模型
9+
- 思考模型:使用 DeepSeek-R1/250120,生成推理步骤和依据,辅助回答用户问题。
10+
- 回答模型:结合思考步骤,对用户的初始问题进行回答总结。不同场景可以选择不同的回答模型:
11+
- 综合任务:Doubao-1.5-pro-32k/250115
12+
- 角色扮演:Doubao-pro-32k/character-241215
13+
14+
## 环境准备
15+
16+
- Python 版本要求大于等于 3.8,小于 3.12
17+
- 已获取火山方舟 API Key [参考文档](https://www.volcengine.com/docs/82379/1298459#api-key-%E7%AD%BE%E5%90%8D%E9%89%B4%E6%9D%83)
18+
- 已创建 DeepSeek-R1 的 endpoint [参考文档](https://www.volcengine.com/docs/82379/1099522#594199f1)
19+
- 已创建 Douba 的endpoint [参考文档](https://www.volcengine.com/docs/82379/1099522#594199f1)
20+
21+
## 快速开始
22+
23+
1. 下载代码库
24+
25+
```bash
26+
git clone https://github.com/volcengine/ai-app-lab.git
27+
cd demohouse/deepdoubao
28+
```
29+
2. 修改配置
30+
31+
- 修改`backend/code/main.py` 中配置,填入
32+
```text
33+
| 配置变量名 | 说明 |
34+
| ------------------------ | -------------------------------|
35+
| DEEPSEEK_R1_ENDPOINT | DeepSeek-R1 endpoint id |
36+
| DOUBAO_ENDPOINT | Doubao 模型 endpoint id |
37+
```
38+
39+
- 修改 `backend/run.sh` 中配置,填入获取的API key
40+
```text
41+
| 配置变量名 | 说明 |
42+
| ----------- | --------------- |
43+
| ARK_API_KEY | 火山方舟 API Key |
44+
```
45+
46+
47+
48+
3. 安装后端依赖
49+
50+
```bash
51+
cd demohouse/deepdoubao/backend
52+
53+
python -m venv .venv
54+
source .venv/bin/activate
55+
pip install poetry==1.6.1
56+
57+
poetry install
58+
```
59+
4. 启动后端
60+
61+
```bash
62+
cd demohouse/deepdoubao/backend
63+
bash run.sh
64+
```
65+
66+
5. 测试
67+
68+
```bash
69+
curl --location 'http://localhost:8888/api/v3/bots/chat/completions' \
70+
--header 'Content-Type: application/json' \
71+
--data '{
72+
"model": "test",
73+
"stream": true,
74+
"messages": [
75+
{
76+
"role": "user",
77+
"content": "写一个适合3岁宝宝的睡前故事"
78+
}
79+
]
80+
}'
81+
```
82+
## 技术实现
83+
<img src="./assets/技术路线.png" alt="技术路线">
84+
85+
本项目结合深度思考模型DeepSeek R1的思考能力,以及 Doubao 模型的对话能力,提供 Chat Completion API。API在接收到用户问题以后有两个步骤:
86+
1. DeepSeek R1 模型输入用户问题进行思考,输出思考内容(reasoning content)。
87+
2. Doubao 模型输入用户问题以及 R1 模型的思考过程,输出最终回答用户的结果。
88+
89+
## 目录结构
90+
```text
91+
./
92+
├── README.md
93+
├── assets
94+
│   └── 技术路线.png
95+
└── backend
96+
├── code
97+
│   ├── __init__.py
98+
│   └── main.py # 入口函数
99+
├── poetry.lock
100+
├── pyproject.toml
101+
└── run.sh # 启动脚本
102+
```
22.3 KB
Loading
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2+
# Licensed under the 【火山方舟】原型应用软件自用许可协议
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# https://www.volcengine.com/docs/82379/1433703
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2+
# Licensed under the 【火山方舟】原型应用软件自用许可协议
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# https://www.volcengine.com/docs/82379/1433703
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
12+
"""
13+
DeepDoubao
14+
"""
15+
16+
import logging
17+
import os
18+
from typing import AsyncIterable, Union
19+
20+
from arkitect.core.component.llm import BaseChatLanguageModel
21+
from arkitect.core.component.llm.model import (
22+
ArkChatCompletionChunk,
23+
ArkChatParameters,
24+
ArkChatRequest,
25+
ArkChatResponse,
26+
Response,
27+
ArkMessage,
28+
BotUsage,
29+
)
30+
from arkitect.launcher.local.serve import launch_serve
31+
from arkitect.telemetry.trace import task
32+
from volcenginesdkarkruntime.types.completion_usage import (
33+
CompletionUsage,
34+
PromptTokensDetails,
35+
CompletionTokensDetails,
36+
)
37+
38+
logger = logging.getLogger(__name__)
39+
40+
DEEPSEEK_R1_ENDPOINT = "<ENDPOINT_ID_FOR_DEEPSEEK_R1>"
41+
DOUBAO_ENDPOINT = "<ENDPOINT_ID_FOR_DOUBAO>"
42+
43+
44+
def merge_usage(usage1: CompletionUsage, usage2: CompletionUsage) -> CompletionUsage:
45+
usage = CompletionUsage(
46+
prompt_tokens=usage1.prompt_tokens + usage2.prompt_tokens,
47+
completion_tokens=usage1.completion_tokens + usage2.completion_tokens,
48+
total_tokens=usage1.total_tokens + usage2.total_tokens,
49+
)
50+
if usage1.prompt_tokens_details or usage2.prompt_tokens_details:
51+
usage.prompt_tokens_details = PromptTokensDetails(
52+
cached_tokens=usage1.prompt_tokens_details.cached_tokens
53+
+ usage2.prompt_tokens_details.cached_tokens
54+
)
55+
if usage1.completion_tokens_details or usage2.completion_tokens_details:
56+
r1 = usage1.completion_tokens_details.reasoning_tokens
57+
r2 = usage2.completion_tokens_details.reasoning_tokens
58+
usage.completion_tokens_details = CompletionTokensDetails(
59+
reasoning_tokens=r1 if r2 is None else r2 if r1 is None else r1 + r2
60+
)
61+
return usage
62+
63+
64+
@task()
65+
async def default_model_calling(
66+
request: ArkChatRequest,
67+
) -> AsyncIterable[Union[ArkChatCompletionChunk, ArkChatResponse]]:
68+
parameters_r1 = ArkChatParameters(**request.__dict__)
69+
parameters_r1.max_tokens = (
70+
1 # Set max_tokens to 1, so R1 model will only output reasoning content.
71+
)
72+
deepseek = BaseChatLanguageModel(
73+
endpoint_id=DEEPSEEK_R1_ENDPOINT,
74+
messages=request.messages,
75+
parameters=parameters_r1,
76+
)
77+
reasoning_content = ""
78+
reasoning_usage = CompletionUsage(
79+
completion_tokens=0,
80+
total_tokens=0,
81+
prompt_tokens=0,
82+
)
83+
if request.stream:
84+
async for chunk in deepseek.astream():
85+
if chunk.usage:
86+
reasoning_usage = chunk.usage
87+
if len(chunk.choices) > 0 and chunk.choices[0].delta.reasoning_content:
88+
yield chunk
89+
reasoning_content += chunk.choices[0].delta.reasoning_content
90+
else:
91+
response = await deepseek.arun()
92+
reasoning_content = response.choices[0].message.reasoning_content
93+
if response.usage:
94+
reasoning_usage = response.usage
95+
96+
parameters_doubao = ArkChatParameters(**request.__dict__)
97+
doubao = BaseChatLanguageModel(
98+
endpoint_id=DOUBAO_ENDPOINT,
99+
messages=request.messages
100+
+ [
101+
ArkMessage(
102+
role="assistant",
103+
content="思考过程如下:\n"
104+
+ reasoning_content
105+
+ "\n请根据以上思考过程,给出完整的回答:\n",
106+
)
107+
],
108+
parameters=parameters_doubao,
109+
)
110+
if request.stream:
111+
async for chunk in doubao.astream():
112+
if chunk.usage:
113+
chunk.bot_usage = BotUsage(model_usage=[reasoning_usage, chunk.usage])
114+
chunk.usage = merge_usage(chunk.usage, reasoning_usage)
115+
yield chunk
116+
else:
117+
response = await doubao.arun()
118+
response.choices[0].message.reasoning_content = reasoning_content
119+
if response.usage:
120+
response.bot_usage = BotUsage(model_usage=[reasoning_usage, response.usage])
121+
response.usage = merge_usage(response.usage, reasoning_usage)
122+
yield response
123+
124+
125+
@task()
126+
async def main(request: ArkChatRequest) -> AsyncIterable[Response]:
127+
async for resp in default_model_calling(request):
128+
yield resp
129+
130+
131+
if __name__ == "__main__":
132+
port = os.getenv("_FAAS_RUNTIME_PORT")
133+
launch_serve(
134+
package_path="main",
135+
port=int(port) if port else 8888,
136+
health_check_path="/v1/ping",
137+
endpoint_path="/api/v3/bots/chat/completions",
138+
)

0 commit comments

Comments
 (0)