Sync v2.0 version of code to github repo

This commit is contained in:
Jiang-Jia-Jun
2025-06-29 23:29:37 +00:00
parent d151496038
commit 92c2cfa2e7
597 changed files with 78776 additions and 22905 deletions

View File

@@ -0,0 +1,25 @@
# Chunked Prefill 与 128K 长文推理部署
Chunked Prefill 采用分块策略将预填充Prefill阶段请求拆解为小规模子任务与解码Decode请求混合批处理执行。可以更好地平衡计算密集型Prefill和访存密集型Decode操作优化GPU资源利用率减少单次Prefill的计算量和显存占用从而降低显存峰值避免显存不足的问题。
在启用 Chunked Prefill 机制后,调度策略采用以下优先级规则:
- 解码请求优先处理系统会优先将所有待处理的解码请求Decode进行批量整合确保生成类任务获得即时响应能力。
- 弹性分块机制:当存在等待处理的预填充任务时,系统会在 `max_num_batched_tokens` 预算范围内(即当前批次可容纳的最大 token 数量调度预填充Prefill操作。若单个预填充请求的 token 量超过剩余预算容量系统会自动将其拆分为多个符合预算限制的子块Chunks
通过 Chunked Prefill 机制,可以有效降低 Token 间时延Inter-Token Latency同时优化显存占用支持更大长度的输入。
更多信息请查阅相关论文 [https://arxiv.org/pdf/2308.16369](https://arxiv.org/pdf/2308.16369)。
## 使用与调优
在 FastDeploy 中开启 Chunked Prefill 时,可以通过 `max_num_batched_tokens` 参数调节服务性能:
- 较小的 `max_num_batched_tokens` 可以获得更好的 Token 间时延Inter-Token Latency
- 更大的 `max_num_batched_tokens` 可以获得更好的首 Token 时延Time To First Token
- 建议配置 `max_num_batched_tokens > 8096`,当在算力充足的硬件上部署小模型时,可以进一步增大 `max_num_batched_tokens` 来获得更高的吞吐量。
## 适用场景
1. 长文推理部署,如 128K 长文推理部署等场景。可以通过 Chunked prefill 技术降低显存峰值,避免显存不足的问题。
2. 生成类任务,通过 Chunked prefill 技术可以降低 Token 间时延Inter-Token Latency提高生成类任务的响应速度。

View File

@@ -0,0 +1,173 @@
# 分离式部署
大模型推理分为两个部分Prefill和Decode阶段分别为计算密集型Prefill和计算密集型Decode两部分。将Prefill 和 Decode 分开部署在一定场景下可以提高硬件利用率,有效提高吞吐,降低整句时延,
* Prefill阶段处理输入的全部Token如用户输入的Prompt完成模型的前向传播Forward生成首token。
* Decode阶段从生成第首token后采用自回归一次生成一个token直到生成到stop token结束设输出N✖tokenDecode阶段需要执行N-1次前向传播只能串行执行并且在生成过程中需要关注的token数越来越多计算量也逐渐增大。
分离式部署核心是将Prefill 和 Decode 部署在不同的计算资源上提高各自的利用率。要想实现分离式部署不可避免的需要考虑Prefill 和 Decode 之间的通信问题。
在实际推理过程中Prefill 需要将其计算得到的KV Cache 传输至Decode 实例Decode 读取KV Cache 进行续推。
## KV Cache 传输方式
针对KV Cache 传输我们提供了2种传输方式分别针对单机内与多机间的场景。
### 单机内传输
通过cudaMemcpyPeer进行单机内两个GPU之间KV Cache传输时延低且吞吐高
### 多机间传输
针对多机之间的传输通过高速网络RDMA传输KV Cache。 针对RDMA传输我们提供了高速传输的网络库`rdma_comm` 实现跨机的KV Cache传输。
## PD 分离调度
![Splitwise Scheduler](images/disaggregated.png)
在全局调度器的基础上FastDeploy 支持 PD 分离调度策略,专为大语言模型推理场景设计,将推理流程中的两个阶段解耦:
* Prefill 阶段:构建 KV 缓存,计算密集,显存占用高但延迟低;
* Decode 阶段:进行自回归解码,过程串行、耗时长但显存占用低。
多实例情况下每收到一条请求需要根据不同的策略将请求分配到不同的Prefill实例和Decode实例。通过角色分离prefill 节点负责接收并处理请求decode节点完成后续生成可以更细粒度地控制资源分配、提高吞吐量与 GPU 利用率。
## 使用说明
### 单机分离式部署
#### 在线推理服务
使用如下命令进行服务部署
**prefill 实例**
```bash
export FD_LOG_DIR="log_prefill"
export CUDA_VISIBLE_DEVICES=0,1,2,3
python -m fastdeploy.entrypoints.openai.api_server \
--model ERNIE-4.5-300B-A47B-BF16 \
--port 8180 --metrics-port 8181 \
--engine-worker-queue-port 8182 \
--cache-queue-port 8183 \
--tensor-parallel-size 4 \
--quantization wint4 \
--splitwise-role "prefill"
```
**decode 实例**
```bash
export FD_LOG_DIR="log_decode"
export CUDA_VISIBLE_DEVICES=4,5,6,7
# 注意innode-prefill-ports指定为Prefill服务的engine-worker-queue-port
python -m fastdeploy.entrypoints.openai.api_server \
--model ERNIE-4.5-300B-A47B-BF16 \
--port 8184 --metrics-port 8185 \
--engine-worker-queue-port 8186 \
--cache-queue-port 8187 \
--tensor-parallel-size 4 \
--quantization wint4 \
--innode-prefill-ports 8182 \
--splitwise-role "decode"
```
注意在请求单机PD分离服务时**用户需请求Decode服务的端口**。
#### 离线推理服务
参考`fastdeploy/demo` 目录下 `offline_disaggregated_demo.py` 示例代码,进行离线推理服务部署
### 多机分离式部署
#### 前置依赖 Redis
- 使用`conda`安装
```bash
# 安装
conda install redis
# 启动
nohup redis-server > redis.log 2>&1 &
```
- 使用`apt`安装
```bash
# 安装
sudo apt install redis-server -y
# 启动
sudo systemctl start redis-server
```
- 使用`yum`安装
```bash
# 安装
sudo yum install redis -y
# 启动
sudo systemctl start redis
```
#### 在线推理服务
多机部署时需要确认当前网卡是否支持RDMA并且需要集群中所有节点网络互通。
**注意**
* `KVCACHE_RDMA_NICS` 指定当前机器的RDMA网卡多个网卡用逗号隔开。
**prefill 实例**
```bash
export FD_LOG_DIR="log_prefill"
export CUDA_VISIBLE_DEVICES=0,1,2,3
export KVCACHE_RDMA_NICS="mlx5_2,mlx5_3,mlx5_4,mlx5_5"
python -m fastdeploy.entrypoints.openai.api_server \
--model ERNIE-4.5-300B-A47B-BF16 \
--port 8180 --metrics-port 8181 \
--engine-worker-queue-port 8182 \
--cache-queue-port 8183 \
--tensor-parallel-size 4 \
--quantization wint4 \
--cache-transfer-protocol "rdma,ipc" \
--rdma-comm-ports "7671,7672,7673,7674" \
--pd-comm-port "2334" \
--splitwise-role "prefill" \
--scheduler-name "splitwise" \
--scheduler-host "127.0.0.1" \
--scheduler-port 6379 \
--scheduler-ttl 9000
```
**decode 实例**
```bash
export FD_LOG_DIR="log_decode"
export CUDA_VISIBLE_DEVICES=4,5,6,7
export KVCACHE_RDMA_NICS="mlx5_2,mlx5_3,mlx5_4,mlx5_5"
python -m fastdeploy.entrypoints.openai.api_server \
--model ERNIE-4.5-300B-A47B-BF16 \
--port 8184 --metrics-port 8185 \
--engine-worker-queue-port 8186 \
--cache-queue-port 8187 \
--tensor-parallel-size 4 \
--quantization wint4 \
--scheduler-name "splitwise" \
--cache-transfer-protocol "rdma,ipc" \
--rdma-comm-ports "7671,7672,7673,7674" \
--pd-comm-port "2334" \
--scheduler-host "127.0.0.1" \
--scheduler-port 6379 \
--scheduler-ttl 9000
--splitwise-role "decode"
```
### 参数说明
* --splitwise-role: 指定当前服务为prefill还是decode
* --cache-queue-port: 指定cache服务的端口用于prefill和decode服务通信
#### 单机参数说明
* --inner-prefill-ports: 仅需Decode实例填写指定需要连接的prefill实例的端口列表
#### 多机参数说明
* --cache-transfer-protocol: 指定KV Cache传输协议支持ipc和rdma默认ipc
* --scheduler-name: PD分离情况下为splitwise
* --scheduler-host: 连接的redis地址
* --scheduler-port: 连接的redis端口
* --scheduler-ttl: 指定redis的ttl时间单位为秒
* --pd-comm-port: 指定pd通信的端口
* --rdma-comm-ports: 指定RDMA通信的端口多个端口用逗号隔开数量与卡数一致

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

View File

@@ -0,0 +1,69 @@
# 全局调度器: 多实例负载均衡
## 设计方案
集群中的各个节点根据自身负载情况,空闲时主动从其他节点偷取任务,然后将任务的执行结果推送回原节点。
### 全局调度器解决了什么问题?
![Local Scheduler](./images/LocalScheduler.png)
在常规负载均衡策略中,集群按照轮询策略分发请求,可以确保每个推理实例接受到的请求个数是均匀的。
在LLM场景中每个请求的处理时间和请求各自的输入输出token数有关。即使请求均匀的分发到每个推理实例上每个推理实例完成推理的时间也会相差很大。
所以,我们希望通过全局调度器进一步优化集群负载。
### 全局调度器是如何工作的?
![Global Scheduler](./images/GlobalScheduler.png)
如上图所示节点1,2,n均收到了3个请求。在T时刻节点1已经完成了对所有请求的处理节点2和节点n正在处理第二个请求各自的请求队列中还剩余1个请求。此时节点1窃取到了节点2的一个请求进行处理并将结果推送回节点2的响应队列中。
全局调度器在集群内完成二次负载均衡可以有效提高集群整体资源利用率降低TTFT(Time To First Token)。
## 如何使用全局调度器
### 前置依赖 Redis
- 使用`conda`安装
```bash
# 安装
conda install redis
# 启动
nohup redis-server > redis.log 2>&1 &
```
- 使用`apt`安装
```bash
# 安装
sudo apt install redis-server -y
# 启动
sudo systemctl start redis-server
```
- 使用`yum`安装
```bash
# 安装
sudo yum install redis -y
# 启动
sudo systemctl start redis
```
### 启动FastDeploy
```bash
python -m fastdeploy.entrypoints.openai.api_server \
--port 8801 \
--metrics-port 8802 \
--engine-worker-queue-port 8803 \
--scheduler-name global \
--scheduler-ttl 900 \
--scheduler-host "127.0.0.1" \
--scheduler-port 6379 \
--scheduler-db 0 \
--scheduler-password "" \
--scheduler-topic "default" \
--scheduler-min-load_score 3 \
--scheduler-load-shards-num 1
```
[启动参数说明](../online_serving/scheduler.md)
可以将上述启动命令在多个机器执行,启动多个推理实例(如果是在一个机器中启动多个推理实例,注意端口不要冲突)。
集群外部的负载均衡可以使用`Nginx`进行搭建,集群内部的负载均衡由全局调度器负责。

View File

@@ -0,0 +1,40 @@
# Prefix Caching
Prefix Caching前缀缓存是一种优化生成式模型推理效率的技术核心思想是通过缓存输入序列的中间计算结果KV Cache避免重复计算从而加速具有相同前缀的多个请求的响应速度。
**工作原理**
前缀识别当多个请求共享相同的输入前缀如提示词或上下文开头部分系统会缓存该前缀对应的中间状态KV Cache
增量计算:对于后续请求,只需计算新增部分(如用户追加的输入)并复用缓存的中间结果,显著减少计算量。
## 服务化部署开启 Prefix Caching
启动服务增加以下参数 `enable-prefix-caching`默认只开启一级缓存GPU 缓存)。
若需要开启 CPU 缓存,需要指定参数 `swap-space` 指定开启的 CPU Cache 的空间大小,单位为 GB。具体大小根据加载完模型机器可用内存大小设置。
> ERNIE-4.5-VL 多模态模型暂不支持开启 prefix caching。
具体参数说明可参考文档[参数说明](../parameters.md)。
启动示例:
```shell
python -m fastdeploy.entrypoints.openai.api_server \
--model "baidu/ERNIE-4.5-21B-A3B-Paddle" \
--port 8180 --engine-worker-queue-port 8181 \
--metrics-port 8182 \
--cache-queue-port 8183 \
--enable-prefix-caching \
--swap-space 50 \
--max-model-len 8192 \
--max-num-seqs 32
```
## 离线推理开启Prefix Caching
FastDeploy 启动时设置 `enable_prefix_caching=True`CPU Cache 根据机器内存选择开启 `swap_space`
提供了测试示例 `demo/offline_prefix_caching_demo.py`

View File

@@ -0,0 +1,76 @@
# 思考链内容
思考模型在输出中返回 `reasoning_content` 字段,表示思考链内容,即得出最终结论的思考步骤.
##目前支持思考链的模型
| 模型名称 | 解析器名称 | 默认开启思考链 |
|---------------|-------------|---------|
| ernie-45-vl | ernie-45-vl | ✓ |
| ernie-lite-vl | ernie-45-vl | ✓ |
思考模型需要指定解析器,以便于对思考内容进行解析. 通过`enable_thinking=False` 参数可以关闭模型思考模式.
可以支持思考模式开关的接口:
1. OpenAI 服务中 `/v1/chat/completions` 请求.
2. OpenAI Python客户端中 `/v1/chat/completions` 请求.
3. Offline 接口中 `llm.chat`请求.
同时在思考模型中,支持通过```reasoning_max_tokens```控制思考内容的长度,在请求中添加```metadata={"reasoning_max_tokens": 1024}```即可。
### 快速使用
在启动模型服务时, 通过`--reasoning-parser`参数指定解析器名称.
该解析器会解析思考模型的输出, 提取`reasoning_content`字段.
```bash
python -m fastdeploy.entrypoints.openai.api_server \
--model /path/to/your/model \
--enable-mm \
--tensor-parallel-size 8 \
--port 8192 \
--quantization wint4 \
--reasoning-parser ernie-45-vl
```
接下来, 向模型发送 `chat completion` 请求
```bash
curl -X POST "http://0.0.0.0:8192/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_images/example2.jpg"}},
{"type": "text", "text": "图中的文物属于哪个年代"}
]}
],
"metadata": {"enable_thinking": true}
}'
```
字段`reasoning_content`包含得出最终结论的思考步骤,而`content`字段包含最终结论。
### 流式会话
在流式会话中, `reasoning_content`字段会可以在`chat completion response chunks`中的 `delta` 中获取
```python
from openai import OpenAI
# Set OpenAI's API key and API base to use vLLM's API server.
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8192/v1"
client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)
chat_response = client.chat.completions.create(
messages=[
{"role": "user", "content": [ {"type": "image_url", "image_url": {"url": "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_images/example2.jpg"}},
{"type": "text", "text": "图中的文物属于哪个年代"}]}
],
model="vl",
stream=True,
metadata={"enable_thinking": True}
)
for chunk in chat_response:
if chunk.choices[0].delta is not None:
print(chunk.choices[0].delta, end='')
print("\n")
```

View File

@@ -0,0 +1,120 @@
# 🔮 投机解码
本项目基于 PaddlePaddle 实现了高效的 **投机解码Speculative Decoding** 推理框架,支持多 Token 预测Multi-token Proposing, MTP用于加速大语言模型LLM的生成显著降低时延并提升吞吐量。
## ✅ 投机解码方法支持
### ✅ 支持列表
- **Ngram**
- **MTP (Multi-Token Prediction)**
- ✅ 已支持TP 切分
- ✅ 已支持:共享前缀
- ✅ 已支持:单机 TP 切分 + PD 分离
- ⏳ 即将支持EP + DP + PD 分离
- ⏳ 即将支持:兼容 Chunk Prefill
- ⏳ 即将支持:多层 MTP layer
---
### ⏳ 规划中
- Draft Model
- Eagle
- Hydra
- Medusa
- ...
## ⚙️ 高效投机解码框架设计
- **Attention机制**:采用 [Cascade Append Attention](https://flashinfer.ai/2024/02/02/cascade-inference.html) 的 Attention 机制,支持变长查询统一处理,一次前向推理即可完成所有验证。此外,我们对 Kernel 实现进行了深度定制,以最大化 Tensor Core 的利用率,并在高并发场景下仍然保持高吞吐。
- **虚拟填充机制**:采用虚拟填充快速定位输出 Token 的批次 ID避免了高开销的数据拷贝与切片操作。
- **并行采样与验证**:我们开发了多个融合 Cuda Kernel用于同时执行采样与验证操作。该 Kernel 支持对每个 batch 样本进行并行处理,避免了显式循环的开销。
- **高效 DraftModel/MTP 框架**:开发多个融合 Cuda Kernel统一完成模型类方法的前后处理相比传统的循环、切片方法性能高效且易维护
## 🔧 参数说明
- `method`: 解码策略,可选值为 `"mtp"``"ngram"`
- `num_speculative_tokens`: 每轮预测的 Token 数,最大支持 5当前 MTP 仅支持 1
- `model`: 若选择 MTP则需指定 MTP 模型路径
- `quantization`: 模型量化方式,推荐使用 `wint8`
- `batch_size`: 当前支持最大值为 256
## 🚀 使用 Multi-Token-Prediction(MTP) 解码
详见论文:[DeepSeek-V3](https://arxiv.org/pdf/2412.19437)
### TP 并行部署
> 使用 4×H100量化方式选择 WINT4
> 配置文件:`benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml`
```
python -m fastdeploy.entrypoints.openai.api_server \
--model ${path_to_main_model} \
--tensor-parallel-size 4 \
--config ${path_to_FastDeploy}benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml \
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": "${path_to_mtp_model}"}'
```
### PD 分离式部署1P1D
> 在8×H100上部署1P1DP、D节点 分别使用 4×H100量化方式选择 WINT4
> 与常规 PD 分离部署一致,仅需替换配置文件并新增 speculative_config
详情请参考[PD分离式部署](./disaggregated.md)。
- P 节点Prefill
> 配置文件: `benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml`
```
export FD_LOG_DIR="log_prefill"
rm -rf ${FD_LOG_DIR}
export CUDA_VISIBLE_DEVICES=0,1,2,3
python -m fastdeploy.entrypoints.openai.api_server \
--model ${path_to_main_model} \
--port 8180 \
--metrics-port 8181 \
--engine-worker-queue-port 8182 \
--cache-queue-port 8183 \
--workers 2 \
--tensor-parallel-size 4 \
--quantization wint4 \
--splitwise-role "prefill" \
--scheduler-name "splitwise" \
--scheduler-host "127.0.0.1" \
--scheduler-port 6379 \
--scheduler-ttl 9000 \
--scheduler-topic mtp \
--config ${path_to_FastDeploy}/benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml \
--scheduler-password "scheduler_mtp" \
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": ""${path_to_mtp_model}"}' &
```
- D 节点Decode
> 配置文件: `benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-decode.yaml`
```
export FD_LOG_DIR="log_prefill"
rm -rf ${FD_LOG_DIR}
export CUDA_VISIBLE_DEVICES=0,1,2,3
python -m fastdeploy.entrypoints.openai.api_server \
--model ${path_to_main_model} \
--port 8180 \
--metrics-port 8181 \
--engine-worker-queue-port 8182 \
--cache-queue-port 8183 \
--workers 2 \
--tensor-parallel-size 4 \
--quantization wint4 \
--splitwise-role "prefill" \
--scheduler-name "splitwise" \
--scheduler-host "127.0.0.1" \
--scheduler-port 6379 \
--scheduler-ttl 9000 \
--scheduler-topic mtp \
--config ${path_to_FastDeploy}/benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml \
--scheduler-password "scheduler_mtp" \
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": ""${path_to_mtp_model}"}' &
```
## 🧠 使用 Ngram 解码
该算法通过 n-gram 窗口从 prompt 和已生成的 Token 中进行匹配生成草稿 Token适合输入和输出有很大 overlap 的场景如代码编辑、文档查询等查看论文地址。
> 使用 4×H100量化方式选择 WINT4
> 配置文件benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml
```
python -m fastdeploy.entrypoints.openai.api_server \
--model ${path_to_main_model} \
--tensor-parallel-size 4 \
--config ${path_to_FastDeploy}benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml \
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": "${mtp_model_path}"}'
```

View File

@@ -0,0 +1,332 @@
# Structured Outputs
## 概述
Structured Outputs 是指通过预定义格式约束使大模型生成内容严格遵循指定结构。该功能可显著提升生成结果的可控性适用于需要精确格式输出的场景如API调用、数据解析、代码生成等同时支持动态语法扩展平衡灵活性与规范性。
FastDeploy 支持使用 [XGrammar](https://xgrammar.mlc.ai/docs/) 后端生成结构化输出。
支持输出格式
- `json格式`: 输出内容为标准的 JSON 格式
- `正则表达式格式regex`: 精确控制文本模式(如日期、邮箱、身份证号等),支持复杂正则表达式语法
- `选项格式choice`: 输出严格限定在预定义选项中
- `语法格式grammar`: 使用扩展 BNF 语法定义复杂结构,支持字段名、类型及逻辑关系的联合约束
- `结构标签格式structural_tag`: 通过标签树定义结构化输出,支持嵌套标签、属性校验及混合约束
## 使用方式
服务启动时,可以通过 `--guided-decoding-backend` 参数指定期望使用的后端,如果指定 `auto`, FastDeploy 会自动选择合适的后端。
### OpenAI 接口
FastDeploy 支持 OpenAI 的 [Completions](https://platform.openai.com/docs/api-reference/completions) 和 [Chat Completions](https://platform.openai.com/docs/api-reference/chat) APIOpenAI 通过 `response_format` 参数指定 Structured Outputs 的输出格式。
FastDeploy 支持通过指定 `json_object` 来保证输出为json格式如果想指定 JSON 内部具体参数名、类型等信息,可以通过 `json_schema` 来指定详细信息,详细使用方法可以参考示例。
```python
import openai
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
completion = client.chat.completions.create(
model="null",
messages=[
{
"role": "user",
"content": "生成一个包含以下信息的JSON对象中国四大发明名称、发明朝代及简要描述每项不超过50字",
}
],
response_format={"type": "json_object"}
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
{"inventions": [{"name": "造纸术", "dynasty": "东汉", "description": "蔡伦改进造纸术,用树皮、麻头等为原料,成本低廉,推动文化传播。"}, {"name": "印刷术", "dynasty": "唐朝", "description": "雕版印刷术在唐朝出现,后毕昇发明活字印刷术,提高印刷效率。"}, {"name": "火药", "dynasty": "唐朝", "description": "古代炼丹家偶然发明,后用于军事,改变了战争方式和格局。"}, {"name": "指南针", "dynasty": "战国", "description": "最初称司南,后发展为指南针,为航海等提供方向指引。"}]}
```
以下示例要求模型返回一个 book 信息的 json 数据json 数据中必须包含`author、title、genre`三个字段,且`genre`必须在给定 `BookType` 中。
```python
import openai
from pydantic import BaseModel
from enum import Enum
class BookType(str, Enum):
romance = "Romance"
historical = "Historical"
adventure = "Adventure"
mystery = "Mystery"
dystopian = "Dystopian"
class BookDescription(BaseModel):
author: str
title: str
genre: BookType
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
completion = client.chat.completions.create(
model="null",
messages=[
{
"role": "user",
"content": "生成一个JSON描述一本中国的著作要包含作者、标题和书籍类型。",
}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "book-description",
"schema": BookDescription.model_json_schema()
},
},
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
{"author": "曹雪芹", "title": "红楼梦", "genre": "Historical"}
```
`structural_tag` 可以提取输入内容中的重点信息,并调用指定方法,返回预定义的结构化输出。以下示例展示了通过 `structural_tag` 获取指定时区并输出格式化结果。
```python
import openai
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
content_str = """
你有以下函数可以调用:
{
"name": "get_current_date",
"description": "根据给定的时区获取当前日期和时间",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "获取当前日期和时间的时区, 例如: Asia/Shanghai",
}
},
"required": ["timezone"],
}
}
如果你选择只调用函数,请按照以下格式回复:
<{start_tag}={function_name}>{parameters}{end_tag}
其中
start_tag => `<function`
parameters => 一个JSON字典其中函数参数名为键函数参数值为值。
end_tag => `</function>`
这是一个示例,
<function=example_function_name>{"example_name": "example_value"}</function>
注意:
- 函数调用必须遵循指定的格式
- 必须指定所需参数
- 一次只能调用一个函数
- 将整个函数调用回复放在一行上
你是一个人工智能助理,请回答下列问题。
"""
completion = client.chat.completions.create(
model="null",
messages=[
{
"role": "system",
"content": content_str,
},
{
"role": "user",
"content": "你今天去上海出差",
}
],
response_format={
"type": "structural_tag",
"structures": [
{
"begin": "<function=get_current_date>",
"schema": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "获取当前日期和时间的时区, 例如: Asia/Shanghai",
}
},
"required": ["timezone"],
},
"end": "</function>",
}
],
"triggers": ["<function="],
},
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
<function=get_current_date>{"timezone": "Asia/Shanghai"}</function>
```
对于 `choice、grammar、regex`格式FastDeploy 使用 `extra_body` 参数支持,`choice` 较为简单,指定后模型会在预定义选项中选择一个最优项。在 FastDeploy 中,可以通过 `guided_choice` 指定一组与定义选项,示例如下
```python
import openai
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
completion = client.chat.completions.create(
model="null",
messages=[
{"role": "user", "content": "深圳的地标建筑是什么?"}
],
extra_body={"guided_choice": ["深圳平安国际金融中心", "中国华润大厦(春笋大厦)", "深圳京基100", "深圳地王大厦"]},
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
深圳地王大厦
```
`regex` 允许用户通过正则表达式限制输出格式,通常用于需要精确控制文本格式的场景。以下示例展示了通过模型生成一个标准格式的网络地址的过程。
```python
import openai
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
completion = client.chat.completions.create(
model="null",
messages=[
{
"role": "user",
"content": "生成一个标准格式的网络地址,包括协议、域名。\n",
}
],
extra_body={"guided_regex": r"^https:\/\/www\.[a-zA-Z]+\.com\/?$\n"},
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
https://www.example.com/
```
`grammar` 允许用户通过 EBNF(Extended Backus-Naur Form) 语法描述一个限制规则,以下示例展示了通过 `guided_grammar` 限制模型输出一段html代码。
```python
import openai
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
html_h1_grammar = """
root ::= html_statement
html_statement ::= "<h1" style_attribute? ">" text "</h1>"
style_attribute ::= " style=" dq style_value dq
style_value ::= (font_style ("; " font_weight)?) | (font_weight ("; " font_style)?)
font_style ::= "font-family: '" font_name "'"
font_weight ::= "font-weight: " weight_value
font_name ::= "Arial" | "Times New Roman" | "Courier New"
weight_value ::= "normal" | "bold"
text ::= [A-Za-z0-9 ]+
dq ::= ["]
"""
completion = client.chat.completions.create(
model="null",
messages=[
{
"role": "user",
"content": "生成一段html的代码对以下标题加粗、Times New Roman字体。标题ERNIE Bot",
}
],
extra_body={"guided_grammar": html_h1_grammar},
)
print(completion.choices[0].message.content)
print("\n")
```
输出
```
<h1 style="font-family: 'Times New Roman'; font-weight: bold">ERNIE Bot</h1>
```
### OpenAI beta 接口
在 OpenAI Client 1.54.4 版本之后,提供了 `client.beta.chat.completions.parse` 接口,通过该接口,实现了与 Python 原生类型更好的支持。以下示例展示了使用该接口直接获取 `pydantic` 类型的结构化输出。
```python
from pydantic import BaseModel
import openai
class Info(BaseModel):
addr: str
height: int
port = "8170"
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
completion = client.beta.chat.completions.parse(
model="null",
messages=[
{"role": "user", "content": "上海东方明珠在上海市浦东新区世纪大道1号高度为468米请问上海东方明珠的地址和高度是多少"},
],
response_format=Info,
)
message = completion.choices[0].message
print(message)
print("\n")
assert message.parsed
print("地址:", message.parsed.addr)
print("高度:", message.parsed.height)
```
输出
```
ParsedChatCompletionMessage[Info](content='{"addr": "上海市浦东新区世纪大道1号", "height": 468}', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None, parsed=Info(addr='上海市浦东新区世纪大道1号', height=468), reasoning_content=None)
地址: 上海市浦东新区世纪大道1号
高度: 468
```