Commit 00c8ddb9b138aaaf9e2e9d40bd555ead00226efb
1 parent
e8443ea0
suggest rank optimize
Showing
13 changed files
with
935 additions
and
44 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,417 @@ |
| 1 | +#!/bin/bash | |
| 2 | +start=$(date +%s%N) # 开始时间,纳秒级 | |
| 3 | + | |
| 4 | +time curl -X POST "http://localhost:6007/rerank" \ | |
| 5 | + -H "Content-Type: application/json" \ | |
| 6 | + -d '{ | |
| 7 | + "query": "健身女生T恤短袖", | |
| 8 | + "docs": [ "60 Jelly Bracelets 80 s Adult Size - MAQIHAN Neon Gummy Bracelets for Women 80s Jelly Bangles Glow Silicone Bands Jewelry Wristband Rainbow Jellies Bangle Girls Boys Colored Accessories Party Favor", | |
| 9 | +"MEROKEETY Women s 2025 Summer Square Neck Puff Sleeve Boho Midi Dress Swiss Dot Ruffle Flowy Tie Back Dress", | |
| 10 | +"FITORY Mens Sandals", | |
| 11 | +"Lefant 3 Packs Dust Bags Replacement Kit Suitable for Lefant Base Station of M3/M3 Max Robot Vacuum", | |
| 12 | +"Merrell Mens Hydro Moc", | |
| 13 | +"Lounge Sets for Women Summer Outfits Women 2 Piece Sets 2025 Sleeveless Matching Lounge Crop Top High Waisted Short", | |
| 14 | +"Men s Underwear", | |
| 15 | +"Executive Functioning Workbook for Teens: 101 Activities and Strategies for Enhancing Self-Discipline", | |
| 16 | +"LEVSOX Compression Socks Women and Men", | |
| 17 | +"MGparty 12 Pieces Christmas Headbands Christmas Parties Favors Decoration Supplies Xmas Gifts Photo Booth Xmas Tree Snowman Reindeer Antlers Santa Hat", | |
| 18 | +"10 Large Vacuum Storage Bags with Hand Pump", | |
| 19 | +"Disney Lilo and Stitch Boys Swim Set", | |
| 20 | +"Sterling Silver Hoop Earrings", | |
| 21 | +"23 Pcs Day of The Dead Altar Decorations Set", | |
| 22 | +"Travel Makeup Bag for Women Fashion Large Capacity Pouch Open Flat Cosmetic Portable Organizer Waterproof Large Opening Storage Toiletry Bags Vertical Free-Standing Brush Holder for Easy Access Blue", | |
| 23 | +"Iron Flame: Empyrean", | |
| 24 | +"Luxebell Luggage Straps Suitcase Belt TSA Approved Travel Accessories Gift 4-Pack 6.56ft (Green)", | |
| 25 | +"TONY & SANDY Christian Gifts for Women", | |
| 26 | +"Blue Birthday Party Supplies", | |
| 27 | +"Vionic Women s Coral Loafer Moccasin", | |
| 28 | +"LIQING 35L Large Picnic Basket 2 Layers of Internal Pockets Leak-Proof and Insulated ,Folding with Internal Support for enhansed Stability", | |
| 29 | +"40oz Softball Tumbler with Handle Softball Gifts Stuff for Women Girls Men Gift for Coach Lovers Fan Stainless Steel Cup", | |
| 30 | +"Crayola Colour & Erase Reusable Puzzle Set", | |
| 31 | +"Carry On Luggage with Front Compartment and Cup Holder", | |
| 32 | +"Interactive Cat Toy Rechargeable", | |
| 33 | +"Nike Air Rift", | |
| 34 | +"Portable Hookah Set for Travel - Premium Handheld Glass Aluminum Mini Hookah Real Metal Accessories", | |
| 35 | +"Clear Backpack for Boys", | |
| 36 | +"Women’s Knee High Boots Round Toe Chunky Heel Faux Leather Tall Riding Boots with Side Zipper", | |
| 37 | +"Golf Grip Trainer & Connection Band 2Set", | |
| 38 | +"Monster High Self Scare Day Cleo De Nile Doll Play Set", | |
| 39 | +"Fortnite eGift Card - Powered by the Epic Games Store", | |
| 40 | +"Mesh Beach Bags", | |
| 41 | +"Crowye Anime Cosplay Costume for Halloween Princess Costume Accessories Anime White Cosplay Wig Egypt Arm Cuff Bracelet Gold Earrings Greek Goddess Set for Halloween Dress up Princess", | |
| 42 | +"Premium Women s Leather Tote Handbag - Bag for Everyday Use", | |
| 43 | +"Ekouaer Maternity Nursing Gown and Robe Set Labor Delivery Nursing Nightgowns for Breastfeeding Pregnancy Clothes", | |
| 44 | +"Superband Mermaid Tails for Swimming for Women and Adults Without Monofin", | |
| 45 | +"Pink Queen Women s 2025 Casual Pullover Sweaters Sexy V Neck Long Sleeve Twist Knot Cropped Knit Sweater Tops", | |
| 46 | +"WDIRARA Girl s Bow Puff Sleeve A Line Midi Dress Cute Collared Ruffle Hem Swing Dresses", | |
| 47 | +"Funziez! Adult Onesie Halloween Costume Animal Dinosaur Shark Unisex Plush One Piece Cosplay Suit for Adults", | |
| 48 | +"Rockland Duffel Bag", | |
| 49 | +"Centipede Demon Baby Shoes Baby Boys Girls Walking Shoes Non Slip Booties Sock Shoe Infants Breathable Sneakers Lightweight Barefoot Slip On Sneakers", | |
| 50 | +"CYDREAM Long Sleeve Bodysuits for Women - Square Neck Shapewear Bodysuit Tops Going Out Body Suits Shirt Leotard", | |
| 51 | +"Men s Oversized Letter Graphic Tank Top Sleeveless Casual Summer Tops Y2K Streetwear", | |
| 52 | +"Flower Claw Clip 7 PCS Claw Clips", | |
| 53 | +"waist twister,waist twisting machine ab twister board with 300 lbs Weight Capacity", | |
| 54 | +"PAGE ONE Womens Winter Ribbed Beanie Crossed Cap Chunky Cable Knit Pompom Soft Warm Hat", | |
| 55 | +"5 Pack Cute Keychains for Girls", | |
| 56 | +"Dragon Ball Super - Complete Series - Blu-ray", | |
| 57 | +"VejiA Multifunctional Simple Shoe Cabinet Storage Shoe Rack Save Space Hallway Furniture", | |
| 58 | +"50Pcs Handbag Purse Feet Handbag Nailhead Brass Studs Screw-Back Feet Flat Head Stud Metal Studs Rivet Leather Craft DIY for DIY Purse Leather Craft", | |
| 59 | +"Wearable Blanket Hoodie with Letter A-Z - Oversized Blanket Hooded Personalized Birthday Christmas Gifts for Women Mom", | |
| 60 | +"On Women s Cloudnova Form 2 Sneakers", | |
| 61 | +"SANTINY 18 Skorts for Women with 4 Pockets High Waist Long Athletic Tennis Skirt Golf Skort Dressy Casual", | |
| 62 | +"Compatible with AirTag Case Keychain", | |
| 63 | +"Rod Holder Plugs", | |
| 64 | +"Protective Case Compatible with Have A Seat Figure-Clear PVC Portable Storage Box with Keychain", | |
| 65 | +"adidas Men s Swift Run 1.0 Running Shoes", | |
| 66 | +"M MOOHAM Cross Necklace for Women Teen Girls", | |
| 67 | +"Sportneer Adjustable Ankle Weights for Women and Men 7 lbs/Pair Adjustable Leg Weights with Secure Straps", | |
| 68 | +"PRETTYGARDEN Women s 2 Piece Outfits Sleeveless Suit Vest and Wide Leg Pants Business Casual Blazer Sets", | |
| 69 | +"Bouncer Seat for Babies 0-12 Months", | |
| 70 | +"Womens Crew Socks Cotton Long Gym Socks Lightweight Athletic Running Socks", | |
| 71 | +"Denior Magnetic Card Phone Wallet Holder for iPhone 17/16/15/14/13/12 Series", | |
| 72 | +"LIGHT DOT Women s Summer Dress Plisse Maxi Tube Bodycon Dress Back Tie Beach Resort Vacation", | |
| 73 | +"Vivresina UV Resin 400g (400.0", | |
| 74 | +"Wide Leg Pants High Waisted Pleated Trousers with 4 Colors", | |
| 75 | +"Osprey Daylite Shoulder Sling Bag – Compact Crossbody Backpack for Everyday Carry", | |
| 76 | +"Tote Bag for Women Large PVC Tote Bag Letters Print Plastic Handbag for Christmas Gift", | |
| 77 | +"Hello Kitty Giant Coloring & Activity Book 11x16", | |
| 78 | +"Skechers Mens Delson 3.0 - Roth 210606", | |
| 79 | +"3pcs Heart Badge Reel with Alligator Clip Cute Retractable Badge Holder Acrylic Nurse Badge Clip for Office Workers", | |
| 80 | +"Ortho Balance Hiking Shoes for Men Women", | |
| 81 | +"GOLDENMATE 1000VA/600W Lithium UPS Battery Backup and Surge Protector", | |
| 82 | +"Gelante Solid Color 100% Cotton Bucket Hat for Women and Men Packable Travel Summer Beach Hat", | |
| 83 | +"Sonic The Hedgehog 3 Movie Action Figures 2.5-Inch Movie Collector Toy Figure Multi-Pack Includes Sonic The Hedgehog Knuckles Shadow Buzz Bomber & Drone- Officially Licensed Toys", | |
| 84 | +"61 Pcs Nacho Libre Stickers Comedy Movie Graffiti Waterproof Vinyl for Adults for Birthday Party Supplies Decoration Favors for Water Bottles Laptop Suitcase Scrapbooking Choice", | |
| 85 | +"Neck Lift Tape", | |
| 86 | +"925 Sterling Silver Earrings for Womens Sparkly Colorful Full Diamond Simple Stylish Elegant Hypoallergenic Jewelry", | |
| 87 | +"Pink Ceramic Bow Vase for Flowers", | |
| 88 | +"Winter Coats For Men Winter Jackets Water Resistant Warm Thicken Parka Puffer Coat Long Down Jacket", | |
| 89 | +"Alarm Clocks for Bedrooms", | |
| 90 | +"KINURI Running Belt for Men & Women – Fits All Smartphones – Waterproof Waist Pack with Adjustable Strap – Ideal for Jogging", | |
| 91 | +"DREAM PAIRS Heels for Women Flip Flops Kitten Low Heels Open Square Toe Thong Heeled Sandals", | |
| 92 | +"Amazon Basics All Purpose Washable School Craft Liquid Glue for Making Slime", | |
| 93 | +"Inflatable Costume Adult Frog Full Body Deluxe Funny Air Blow Up Costume for Men Women Halloween", | |
| 94 | +"Mens Golf Pants Stretch Casual Dress Pants Elastic Drawstring Slacks for Men Lightweight Trousers with 5 Pockets", | |
| 95 | +"Lip Smacker Hello Kitty Lip Balm", | |
| 96 | +"Brown Sugar Keeper 3D – Terracotta Clay Bear Softener", | |
| 97 | +"MEETSUN Polarized Sunglasses for Women Men Trendy Classic Retro Designer Style", | |
| 98 | +"Corset Top Bustier Lingerie for Women Zipper Front Flower Sexy Burlesque Vintage", | |
| 99 | +"Pro Club Men s Heavyweight Mesh Basketball Shorts", | |
| 100 | +"Nike Tech Men s Full-Zip Windrunner Hoodie (HV0949-237", | |
| 101 | +"Ear Piercing Kit", | |
| 102 | +"Timberland Men s 6 Premium Boot", | |
| 103 | +"STAR WARS The Black Series Darth Maul", | |
| 104 | +"VZQI Halloween Cosplay Costumes Kamado Tanjir Kids Anime Kimono Halloween Green Cloak", | |
| 105 | +"Fringe Vest for Women Faux Suede Open Front Cardigan Sleeveless Tassels Fringed Vest Cardigan Hippie Jacket", | |
| 106 | +"Smart Health Ring 2.0 for Women Men", | |
| 107 | +"Fast Forward Kid s Licensed 15 Backpack With Lunch Box Combo Set (Hello Kitty)", | |
| 108 | +"Handmade Authentic Katana - 41-inch Full Tang Sharp Blade", | |
| 109 | +"Inateck Sling Bag X", | |
| 110 | +"EXLURA Women s Fashion Faux Wool Mini Skirt High Waisted Y2K Trendy Side Slit Tweed Plaid Skirts 2025 Fall Winter Outfits", | |
| 111 | +"LASLULU Womens Sexy Crossover Crop Top Long Sleeve Workout Tops Crewneck Athletic Yoga T-Shirts Fall Outfits", | |
| 112 | +"Wrangler Authentics Men s Classic Relaxed Fit Five Pocket Jean Short", | |
| 113 | +"ZeroBound Built in Bra Tank Tops for Women - High Neck Racerback Tank Tops", | |
| 114 | +"Nike Mens Air Max Alpha Trainer 6", | |
| 115 | +"MAZZERI Solid Gold Plated Sterling Silver Italian 1.3/1.6/2.2/2.8mm Diamond-Cut Braided Rope Chain Necklace for Men Women", | |
| 116 | +"Milumia Women s Polka Dots Twist Front Halter Top Dressy Casual Textured Peplum Going Out Tops", | |
| 117 | +"80s 90s Outfit for Women", | |
| 118 | +"EFAN Womens Sexy Sleeveless Double Lined Crop Tops Workout Cute Tight Racerback Tank Tops Summer Clothes Teen Girls 2025", | |
| 119 | +"Nike Mens Shorts Dri-Fit Flex Woven Shorts 7inch (US", | |
| 120 | +"top handle satchel Women", | |
| 121 | +"Kono Expandable Luggage 3 Piece Set Hardshell Lightweight 20in 24in 28in Carry On Suitcase with Spinner Wheels TSA Lock(Black & Brown)", | |
| 122 | +"Nations of The World | National Pride Flag Symbol Arms Tee Unisex T-Shirt for Men or Women", | |
| 123 | +"Jo & Bette Seamless Thongs for Women - High Waist Panties 6 Pack - Thong Underwear Pack Breathable No show Sports", | |
| 124 | +"eKids Disney Frozen 2 Bluetooth Headphones with Microphone", | |
| 125 | +"Arctix Kids Insulated Snow Bib Overalls", | |
| 126 | +"USA Flag Charlie Gift T-Shirt", | |
| 127 | +"CBKSUHBADE 15in×11in Anime One Piece Wanted Bounty Posters", | |
| 128 | +"Plus Size Underwear for Women XL-5XL Cotton High Waist Women Briefs Full Coverage Ladies Panties 4 Pack", | |
| 129 | +"Little Adventures Enchanted Rapunzel Dress-Up Costume for Adult Women", | |
| 130 | +"G Gradual Tennis Dress for Women Golf Outfits with Shorts and Pockets Sleeveless Active Exercise Athletic Dresses for Women", | |
| 131 | +"Pastoral Style Porch Goose Outfits", | |
| 132 | +"Vive Thigh High Compression Stockings for Women & Men - 15-20 mmHg Graduated Support Hose - Opaque Closed Toe Compression Tights - Stockings for Varicose Veins", | |
| 133 | +"Canada is Not for Sale Vintage Cotton Twill Cap", | |
| 134 | +"TomTiger Yoga Shorts for Women Tummy Control High Waist Biker Shorts Exercise Workout Butt Lifting Tights Women s Short Pants", | |
| 135 | +"4PCS GOD IS FIRST IM SECOND Bracelet: Faith Priority Bracelet - Engraved Cross Silicone Wristband for Daily Encouragement", | |
| 136 | +"Tahitian Black Pearl Pendant Necklace AAAA 18K White Gold Plated 925 Sterling Silver Black Pearl Jewelry Gift for Women Mother Wife Her for Anniversary Christmas Birthday", | |
| 137 | +"HOTOUCH Womens Short Sleeve Button Down Shirts Loose Fit V Neck Business Casual Blouses Summer Top with Pockets S-XXL", | |
| 138 | +"Men s Corduroy Short Sleeved Cargo Shirt Relaxed Fit Button Down Casual Wear Tops with Flap Pockets", | |
| 139 | +"Orange Blue Light Blocking Glasses for Better Sleep - 99.5% Premium Acetate Migraine Glasses for Women & Men", | |
| 140 | +"Disney Stitch Beach Towel for Kids Cotton Bath Towels with 2 Clothes Pins Travel Swimming Quick Dry Towel Beach Vacation Essentials", | |
| 141 | +"PGANDS Womens Crew Neck Solid/Color Block Sweatshirts Long Sleeve Casual Lightweight Pullover Tops", | |
| 142 | +"Premium Organic Whole Cloves 5.3 oz (150 grams)", | |
| 143 | +"habibee Bra for Women No Underwire Comfort Seamless Bras Push Up Wireless Bras Full Coverage Bralettes", | |
| 144 | +"Puma Mens Caven 2.0 Shoes", | |
| 145 | +"PRETTYGARDEN Women s Fall Button Down Shirts Dressy Casual Spring Long Puff Sleeve Eyelet Loose Fit Collared Blouse Top", | |
| 146 | +"TNNZEET 2 Pack Plus Size Biker Shorts for Women - 8 Black High Waisted Tummy Control Spandex Workout Shorts (XL-4XL)", | |
| 147 | +"Marvel Legends Series Captain America Shield", | |
| 148 | +"PAVOI 14K Gold AAA+ Handpicked White Freshwater Cultured Pearl Earrings Studs", | |
| 149 | +"Trendy Queen Long Skirts for Women Boho Maxi Skirt Winter Swing Tiered A-Line Elastic High Waist Dress with Pockets Fashion", | |
| 150 | +"Reebok Classic Leather Sneakers for Men", | |
| 151 | +"PRETTYGARDEN Women s Summer Bodycon Maxi Tube Dress Ribbed Strapless Side Slit Long Going Out Casual Elegant Party Dresses", | |
| 152 | +"Favorite Daughter Women s Classic Logo Baseball Cap", | |
| 153 | +"Reebok Men s Cotton Vital Fleece Sweatpant", | |
| 154 | +"COOFANDY Mens Hawaiian Shirt Short Sleeve Button Down Shirts Tropical Summer Beach Shirts Casual Floral Aloha Shirts", | |
| 155 | +"Columbia Mens Grander Marlin Iii Offshore Short", | |
| 156 | +"Satin One Shoulder Flower Girl Dress with Bow Wedding Princess Pageant Party Gown Puffy Formal First Communion", | |
| 157 | +"Nike Mens V5 RNR", | |
| 158 | +"Speed Cube 3x3", | |
| 159 | +"FOURSTEEDS Women s Cotton Zipper Front Multi-Pocket Twill Bermuda Women Cargo Shorts", | |
| 160 | +"Curly Hair Brush Defining", | |
| 161 | +"YQXCC Cooling Towels | 4 Pack 47x12 | Ice Cool for Neck | Microfiber Soft Breathable Chilly | for Yoga", | |
| 162 | +"Hot Wheels Toy Car Playset with Lights", | |
| 163 | +"Carhartt Men s Loose Fit Heavyweight Short-Sleeve Pocket Henley T-Shirt", | |
| 164 | +"Women s Mid-High Rise Ripped Denim Shorts Stretchy Distressed Jean Shorts with Pockets Folded Hem Casual Summer Jorts", | |
| 165 | +"Monster High Cleo De Nile Doll in Golden Blouse & Layered Skirt", | |
| 166 | +"Ariat Women’s Fatbaby Western Boot", | |
| 167 | +"UYYE Car Registration and Insurance Card Holder", | |
| 168 | +"365 by Whole Foods Market", | |
| 169 | +"Crystal Bracelet for Women Fashion 7 Inch Approximately Rainbow Sparkling Crystal Bracelet with Adjustable Elastic Cord", | |
| 170 | +"Samsung Galaxy Watch 7 (44mm) AI Smartwatch w/ 1.5 AMOLED", | |
| 171 | +"DOUKEN 4 Pair Sneaker Creases Protector", | |
| 172 | +"Elvis: The Legend music word search puzzle.: Great Country Music Word Scrambles about Elvis. Large print word puzzle for adults and rock music lovers. ... Great music gift for your friends or family.", | |
| 173 | +"Pinkfong Bebefinn Plush Toy - 12 (30cm) Stuffed Doll | Soft Cuddly Plush for Toddlers | Bebefinn Toy | Perfect Birthday", | |
| 174 | +"Thrusting Dildo Vibrator Sex Toys for Women", | |
| 175 | +"VANLOVEMAC Baseball Gifts for Boys 8-12 Baseball Stuff College Going Away Gifts Welcome Back to School Gifts Dorm Room Essentials for Guys Off to College", | |
| 176 | +"Hello Kitty and Friends - Cinnamoroll 12” Pink Monochrome Plush", | |
| 177 | +"BOBISUKA Pearl White Face Body Paint", | |
| 178 | +"OMKAGI 2 Piece Workout Sets for Women Halter Sports Bras Gym Sets Booty Leggings Outfits", | |
| 179 | +"Ivay Womens Scoop Neck Ribbed Knit Tank Top Sleeveless Cotton Wife Beater Camisole Shirts", | |
| 180 | +"SOLY HUX Women s Graphic Tee Shirts Novelty Funny Short Sleeve Summer Casual Tops", | |
| 181 | +"Wooden Taper Candle Holders: Wood Candlestick Holders Rustic Brown Farmhouse Fall Decor for Living Room Dinning Table Centerpiece Christmas Set of 2", | |
| 182 | +"PRETTYGARDEN Long Sleeve Shirts for Women 2025 Fall V Neck Waffle Basic Tee Dressy Casual Winter Blouses Knit Tunic Tops", | |
| 183 | +"Ray-Ban RB2140 Original Wayfarer Square Sunglasses", | |
| 184 | +"Lee Womens Ultra Lux Comfort with Flex-to-go Utility Skimmer Capri Pant", | |
| 185 | +"3D Pedometer for Walking", | |
| 186 | +"HiiFeuer Medieval Faux Leather Chest Armor", | |
| 187 | +"Pet Deadly Dog Costume", | |
| 188 | +"Western Chief Kids Freestyle Neoprene Outdoor Boot", | |
| 189 | +"SKECHERS Women s Ultra Flex 3.0-Brilliant Path Hands Free Slip-INS Sneaker", | |
| 190 | +"LUOBO Keychain Accessory Decor Keychain Decoration backpacks Bag Pendant", | |
| 191 | +"10inch Teddy Bear Stuffed Animal", | |
| 192 | +"Halloweentown University T-Shirt for Women Fall Pumpkin Shirts Funny Halloween Thanksgiving Gift Tops", | |
| 193 | +"Women s Sexy American Flag Crop Tank 4th of July Patriotic Sleeveless Tee Tops", | |
| 194 | +"Gillette Fusion5 ProGlide Men s Razor Blade Refills", | |
| 195 | +"Poppy Playtime - Mommy Long Legs Plush (14 Medium Plush", | |
| 196 | +"Women’s Heated Vest with 12V 20000mAh Battery – Cropped Stand Collar Lightweight Insulated Winter Vest.", | |
| 197 | +"toolant Winter Work Gloves for Men", | |
| 198 | +"192Pcs Halloween Favors Stationery Gift Set", | |
| 199 | +"20 Pcs Ultra Thin Tattoo Cover up Patch Waterproof Tattoo Cover up Tape Sweatproof Tattoos Covers Patches Cuttable Invisible Non-Woven Fabric Patches for Tattoos Scar Birthmark 4.72×3.35In(Light Skin)", | |
| 200 | +"Popcorns Maker", | |
| 201 | +"Paladone Kuromi GloBuddies Night Light", | |
| 202 | +"Creativity for Kids Sensory Minis Dinosaur Kit | Cloud Clay Sensory Toy for Toddlers | Squish", | |
| 203 | +"Mouse Ears Headband Fully Sewn Sturdy Headbands 2-Pcs, 4.6-Inch Sequin Big Ears 3D Silk Satin Bowknot Suitable for Women and Girls Theme Role Play Costume Accessories Party", | |
| 204 | +"Tanluhu Sweatbands Sport Headbands for Men & Women", | |
| 205 | +"Pilates Reformer Machine", | |
| 206 | +"Fossil Fenmore Analog Men Watch", | |
| 207 | +"Stray Kids Official Lightstick Ver 2", | |
| 208 | +"Zima Dental Pod PRO: New Ultrasonic Retainer Cleaner Machine. Market-Leading", | |
| 209 | +"2300pcs Polymer Clay Beads Bracelet Making Kit", | |
| 210 | +"AI ACCESSORY INNOVATIONS Bluey 4 Piece Backpack Set for Pre School Girls & Boys", | |
| 211 | +"MIRITY Women s High Waist Cotton Underwear - Soft Full Coverage Briefs with Double-Layer Waistedband", | |
| 212 | +"Plus Size Summer Dresses - Floral Beach Wedding Guest Semi Formal Tiered Flowy Long Sundress", | |
| 213 | +"AUTOMET Womens Tops Summer Sweater Long Tunic Dressy Casual Blouses Business Cute Trendy Short Sleeve Shirt 2025", | |
| 214 | +"Black Sabbath Sketch Band T-Shirt", | |
| 215 | +"Loomie Upgraded 6 Drawer White Dresser for Bedroom", | |
| 216 | +"Michael Kors Womens Zuma Trainer", | |
| 217 | +"Chunky Silver Bohemian Flower Bracelet For Wemen Men", | |
| 218 | +"Classic Black Western Felt Roll Up Brim Cowboy and Cowgirl Hat for Women and Men - Decoration with Western Belt Bukle", | |
| 219 | +"Jellycat Little Pig Bag Charm", | |
| 220 | +"LARNMERN Steel Toe Work Boots Men", | |
| 221 | +"3PCS Gold Hair Ties", | |
| 222 | +"Red Kap Men s Snap Front Cotton Coverall", | |
| 223 | +"Citizen Quartz Mens Watch", | |
| 224 | +"ATHMILE Long Sleeve Shirts for Women Tunic Fall Tops Loose Fit Dressy Crew Neck Basic Sweaters 2025", | |
| 225 | +"Narecte Summer Maxi Dresses for Women Back Strap Beach Dress Women s Casual Dress Long Flowy Dresses for Vacation", | |
| 226 | +"LIDHAY Cowboy Hat for Women and Men Western Cowgirl Hats Suede Cowboy Hat for Rodeo", | |
| 227 | +"BIC Classic Maxi Pocket Lighter", | |
| 228 | +"A + S Luxxe Diaper Bag Tote – Stylish", | |
| 229 | +"100pack Name Badge Holders Name Tag Holder Clear Plastic Badge Holder ID Holders for Lanyard (100Pcs Vertical)", | |
| 230 | +"MOOSEA Christmas Gifts for Women Wife - Love Knot Moissanite Necklace 1-3ct D Color VVS1 Clarity Moissanite 925 Sterling Silver Necklace Anniversary Birthday Gifts for Women Wife Mom Girlfriend", | |
| 231 | +"Solid Wood Retangle End Table with Drawer and Storage Shelf", | |
| 232 | +"Madden Girl womens Beella Heeled SandalHeeled Sandal", | |
| 233 | +"Ekouaer 2 Pack Womens Pajama Sets Short Sleeve Sleepwear Soft Crew Neck Pj Shorts Set Printed Loungewear Set S-XXL", | |
| 234 | +"NPQQUAN Original Classic Low Profile Baseball Cap Golf Dad Hat Adjustable Cotton Hats Men Women Unconstructed Plain Cap", | |
| 235 | +"YEOREO Women Workout Biker Shorts Impact 4.5 No Front Seam Hidden Scrunch Lifting Seamless Yoga Gym Shorts", | |
| 236 | +"Merino Wool Underwear Men by Thermowave - Sport & Everyday Men s Merino Wool Boxer Brief - 150 GSM Stretchy & Soft", | |
| 237 | +"COACH Women s Leah Platform Loafers", | |
| 238 | +"Doodle Me Happy Kids Thank You Cards - 25 Cards With Envelopes - Cute", | |
| 239 | +"Spring Summer Women Pleated Casual Denim V Neck Ruffle Sleeve Dress Light Blue XL", | |
| 240 | +"Disney Hooded Matching Family Cosplay T-Shirt Infant to Adult Sizes (12 Months - 2XL)", | |
| 241 | +"Leather CPR Cleaner & Conditioner 18oz - Cleans", | |
| 242 | +"Baseball Shirts Women Baseball Mom Tshirt Baseball Heart Graphic Tee Game Day Gifts Funny Short Sleeve Tops", | |
| 243 | +"4 Pack Cooling Towels", | |
| 244 | +"ZEEPORTE Mask Fin Snorkel Set", | |
| 245 | +"60 Pcs Bride Tribe Bachelorette Party Favors Bulk Friendship Bridesmaid Gifts 12 Set Friendship Bracelets Heart Sunglasses Satin Scrunchie for Engagement Bridal Shower Wedding Favor", | |
| 246 | +"AUSELILY Summer Dress Sundress Beach Cover up Swing Dresses", | |
| 247 | +"Loungefly Disney Minnie Mouse Crossbody Satchel Handbag", | |
| 248 | +"Tactical Gym Bag for Men,50L Large 3 in 1 Sports Duffle Bag with Shoes Compartment for Travel", | |
| 249 | +"YETI Rambler 42 oz Tumbler with Handle and Straw Lid", | |
| 250 | +"Samsonite Classic Leather Slim Backpack", | |
| 251 | +"Vive Thigh High Compression Stockings for Women & Men - 15-20 mmHg Graduated Support Hose - Opaque Closed Toe Compression Tights - Stockings for Varicose Veins", | |
| 252 | +"Canada is Not for Sale Vintage Cotton Twill Cap", | |
| 253 | +"TomTiger Yoga Shorts for Women Tummy Control High Waist Biker Shorts Exercise Workout Butt Lifting Tights Women s Short Pants", | |
| 254 | +"4PCS GOD IS FIRST IM SECOND Bracelet: Faith Priority Bracelet - Engraved Cross Silicone Wristband for Daily Encouragement", | |
| 255 | +"Tahitian Black Pearl Pendant Necklace AAAA 18K White Gold Plated 925 Sterling Silver Black Pearl Jewelry Gift for Women Mother Wife Her for Anniversary Christmas Birthday", | |
| 256 | +"HOTOUCH Womens Short Sleeve Button Down Shirts Loose Fit V Neck Business Casual Blouses Summer Top with Pockets S-XXL", | |
| 257 | +"Men s Corduroy Short Sleeved Cargo Shirt Relaxed Fit Button Down Casual Wear Tops with Flap Pockets", | |
| 258 | +"Orange Blue Light Blocking Glasses for Better Sleep - 99.5% Premium Acetate Migraine Glasses for Women & Men", | |
| 259 | +"Disney Stitch Beach Towel for Kids Cotton Bath Towels with 2 Clothes Pins Travel Swimming Quick Dry Towel Beach Vacation Essentials", | |
| 260 | +"PGANDS Womens Crew Neck Solid/Color Block Sweatshirts Long Sleeve Casual Lightweight Pullover Tops", | |
| 261 | +"Premium Organic Whole Cloves 5.3 oz (150 grams)", | |
| 262 | +"habibee Bra for Women No Underwire Comfort Seamless Bras Push Up Wireless Bras Full Coverage Bralettes", | |
| 263 | +"Puma Mens Caven 2.0 Shoes", | |
| 264 | +"PRETTYGARDEN Women s Fall Button Down Shirts Dressy Casual Spring Long Puff Sleeve Eyelet Loose Fit Collared Blouse Top", | |
| 265 | +"TNNZEET 2 Pack Plus Size Biker Shorts for Women - 8 Black High Waisted Tummy Control Spandex Workout Shorts (XL-4XL)", | |
| 266 | +"Marvel Legends Series Captain America Shield", | |
| 267 | +"PAVOI 14K Gold AAA+ Handpicked White Freshwater Cultured Pearl Earrings Studs", | |
| 268 | +"Trendy Queen Long Skirts for Women Boho Maxi Skirt Winter Swing Tiered A-Line Elastic High Waist Dress with Pockets Fashion", | |
| 269 | +"Reebok Classic Leather Sneakers for Men", | |
| 270 | +"PRETTYGARDEN Women s Summer Bodycon Maxi Tube Dress Ribbed Strapless Side Slit Long Going Out Casual Elegant Party Dresses", | |
| 271 | +"Favorite Daughter Women s Classic Logo Baseball Cap", | |
| 272 | +"Reebok Men s Cotton Vital Fleece Sweatpant", | |
| 273 | +"COOFANDY Mens Hawaiian Shirt Short Sleeve Button Down Shirts Tropical Summer Beach Shirts Casual Floral Aloha Shirts", | |
| 274 | +"Columbia Mens Grander Marlin Iii Offshore Short", | |
| 275 | +"Satin One Shoulder Flower Girl Dress with Bow Wedding Princess Pageant Party Gown Puffy Formal First Communion", | |
| 276 | +"Nike Mens V5 RNR", | |
| 277 | +"Speed Cube 3x3", | |
| 278 | +"FOURSTEEDS Women s Cotton Zipper Front Multi-Pocket Twill Bermuda Women Cargo Shorts", | |
| 279 | +"Curly Hair Brush Defining", | |
| 280 | +"YQXCC Cooling Towels | 4 Pack 47x12 | Ice Cool for Neck | Microfiber Soft Breathable Chilly | for Yoga", | |
| 281 | +"Hot Wheels Toy Car Playset with Lights", | |
| 282 | +"Carhartt Men s Loose Fit Heavyweight Short-Sleeve Pocket Henley T-Shirt", | |
| 283 | +"Women s Mid-High Rise Ripped Denim Shorts Stretchy Distressed Jean Shorts with Pockets Folded Hem Casual Summer Jorts", | |
| 284 | +"Monster High Cleo De Nile Doll in Golden Blouse & Layered Skirt", | |
| 285 | +"Ariat Women’s Fatbaby Western Boot", | |
| 286 | +"UYYE Car Registration and Insurance Card Holder", | |
| 287 | +"365 by Whole Foods Market", | |
| 288 | +"Crystal Bracelet for Women Fashion 7 Inch Approximately Rainbow Sparkling Crystal Bracelet with Adjustable Elastic Cord", | |
| 289 | +"Samsung Galaxy Watch 7 (44mm) AI Smartwatch w/ 1.5 AMOLED", | |
| 290 | +"DOUKEN 4 Pair Sneaker Creases Protector", | |
| 291 | +"Elvis: The Legend music word search puzzle.: Great Country Music Word Scrambles about Elvis. Large print word puzzle for adults and rock music lovers. ... Great music gift for your friends or family.", | |
| 292 | +"Pinkfong Bebefinn Plush Toy - 12 (30cm) Stuffed Doll | Soft Cuddly Plush for Toddlers | Bebefinn Toy | Perfect Birthday", | |
| 293 | +"Thrusting Dildo Vibrator Sex Toys for Women", | |
| 294 | +"VANLOVEMAC Baseball Gifts for Boys 8-12 Baseball Stuff College Going Away Gifts Welcome Back to School Gifts Dorm Room Essentials for Guys Off to College", | |
| 295 | +"Hello Kitty and Friends - Cinnamoroll 12” Pink Monochrome Plush", | |
| 296 | +"BOBISUKA Pearl White Face Body Paint", | |
| 297 | +"OMKAGI 2 Piece Workout Sets for Women Halter Sports Bras Gym Sets Booty Leggings Outfits", | |
| 298 | +"Ivay Womens Scoop Neck Ribbed Knit Tank Top Sleeveless Cotton Wife Beater Camisole Shirts", | |
| 299 | +"SOLY HUX Women s Graphic Tee Shirts Novelty Funny Short Sleeve Summer Casual Tops", | |
| 300 | +"Wooden Taper Candle Holders: Wood Candlestick Holders Rustic Brown Farmhouse Fall Decor for Living Room Dinning Table Centerpiece Christmas Set of 2", | |
| 301 | +"PRETTYGARDEN Long Sleeve Shirts for Women 2025 Fall V Neck Waffle Basic Tee Dressy Casual Winter Blouses Knit Tunic Tops", | |
| 302 | +"Ray-Ban RB2140 Original Wayfarer Square Sunglasses", | |
| 303 | +"Lee Womens Ultra Lux Comfort with Flex-to-go Utility Skimmer Capri Pant", | |
| 304 | +"3D Pedometer for Walking", | |
| 305 | +"HiiFeuer Medieval Faux Leather Chest Armor", | |
| 306 | +"Pet Deadly Dog Costume", | |
| 307 | +"Western Chief Kids Freestyle Neoprene Outdoor Boot", | |
| 308 | +"SKECHERS Women s Ultra Flex 3.0-Brilliant Path Hands Free Slip-INS Sneaker", | |
| 309 | +"LUOBO Keychain Accessory Decor Keychain Decoration backpacks Bag Pendant", | |
| 310 | +"10inch Teddy Bear Stuffed Animal", | |
| 311 | +"Halloweentown University T-Shirt for Women Fall Pumpkin Shirts Funny Halloween Thanksgiving Gift Tops", | |
| 312 | +"Women s Sexy American Flag Crop Tank 4th of July Patriotic Sleeveless Tee Tops", | |
| 313 | +"Gillette Fusion5 ProGlide Men s Razor Blade Refills", | |
| 314 | +"Poppy Playtime - Mommy Long Legs Plush (14 Medium Plush", | |
| 315 | +"Women’s Heated Vest with 12V 20000mAh Battery – Cropped Stand Collar Lightweight Insulated Winter Vest.", | |
| 316 | +"toolant Winter Work Gloves for Men", | |
| 317 | +"192Pcs Halloween Favors Stationery Gift Set", | |
| 318 | +"20 Pcs Ultra Thin Tattoo Cover up Patch Waterproof Tattoo Cover up Tape Sweatproof Tattoos Covers Patches Cuttable Invisible Non-Woven Fabric Patches for Tattoos Scar Birthmark 4.72×3.35In(Light Skin)", | |
| 319 | +"Popcorns Maker", | |
| 320 | +"Paladone Kuromi GloBuddies Night Light", | |
| 321 | +"Creativity for Kids Sensory Minis Dinosaur Kit | Cloud Clay Sensory Toy for Toddlers | Squish", | |
| 322 | +"Mouse Ears Headband Fully Sewn Sturdy Headbands 2-Pcs, 4.6-Inch Sequin Big Ears 3D Silk Satin Bowknot Suitable for Women and Girls Theme Role Play Costume Accessories Party", | |
| 323 | +"Tanluhu Sweatbands Sport Headbands for Men & Women", | |
| 324 | +"Pilates Reformer Machine", | |
| 325 | +"Fossil Fenmore Analog Men Watch", | |
| 326 | +"Stray Kids Official Lightstick Ver 2", | |
| 327 | +"Zima Dental Pod PRO: New Ultrasonic Retainer Cleaner Machine. Market-Leading", | |
| 328 | +"2300pcs Polymer Clay Beads Bracelet Making Kit", | |
| 329 | +"AI ACCESSORY INNOVATIONS Bluey 4 Piece Backpack Set for Pre School Girls & Boys", | |
| 330 | +"MIRITY Women s High Waist Cotton Underwear - Soft Full Coverage Briefs with Double-Layer Waistedband", | |
| 331 | +"Plus Size Summer Dresses - Floral Beach Wedding Guest Semi Formal Tiered Flowy Long Sundress", | |
| 332 | +"AUTOMET Womens Tops Summer Sweater Long Tunic Dressy Casual Blouses Business Cute Trendy Short Sleeve Shirt 2025", | |
| 333 | +"Black Sabbath Sketch Band T-Shirt", | |
| 334 | +"Loomie Upgraded 6 Drawer White Dresser for Bedroom", | |
| 335 | +"Michael Kors Womens Zuma Trainer", | |
| 336 | +"Chunky Silver Bohemian Flower Bracelet For Wemen Men", | |
| 337 | +"Classic Black Western Felt Roll Up Brim Cowboy and Cowgirl Hat for Women and Men - Decoration with Western Belt Bukle", | |
| 338 | +"Jellycat Little Pig Bag Charm", | |
| 339 | +"LARNMERN Steel Toe Work Boots Men", | |
| 340 | +"3PCS Gold Hair Ties", | |
| 341 | +"Red Kap Men s Snap Front Cotton Coverall", | |
| 342 | +"Citizen Quartz Mens Watch", | |
| 343 | +"ATHMILE Long Sleeve Shirts for Women Tunic Fall Tops Loose Fit Dressy Crew Neck Basic Sweaters 2025", | |
| 344 | +"Narecte Summer Maxi Dresses for Women Back Strap Beach Dress Women s Casual Dress Long Flowy Dresses for Vacation", | |
| 345 | +"LIDHAY Cowboy Hat for Women and Men Western Cowgirl Hats Suede Cowboy Hat for Rodeo", | |
| 346 | +"BIC Classic Maxi Pocket Lighter", | |
| 347 | +"A + S Luxxe Diaper Bag Tote – Stylish", | |
| 348 | +"100pack Name Badge Holders Name Tag Holder Clear Plastic Badge Holder ID Holders for Lanyard (100Pcs Vertical)", | |
| 349 | +"MOOSEA Christmas Gifts for Women Wife - Love Knot Moissanite Necklace 1-3ct D Color VVS1 Clarity Moissanite 925 Sterling Silver Necklace Anniversary Birthday Gifts for Women Wife Mom Girlfriend", | |
| 350 | +"Solid Wood Retangle End Table with Drawer and Storage Shelf", | |
| 351 | +"Madden Girl womens Beella Heeled SandalHeeled Sandal", | |
| 352 | +"Ekouaer 2 Pack Womens Pajama Sets Short Sleeve Sleepwear Soft Crew Neck Pj Shorts Set Printed Loungewear Set S-XXL", | |
| 353 | +"NPQQUAN Original Classic Low Profile Baseball Cap Golf Dad Hat Adjustable Cotton Hats Men Women Unconstructed Plain Cap", | |
| 354 | +"YEOREO Women Workout Biker Shorts Impact 4.5 No Front Seam Hidden Scrunch Lifting Seamless Yoga Gym Shorts", | |
| 355 | +"Merino Wool Underwear Men by Thermowave - Sport & Everyday Men s Merino Wool Boxer Brief - 150 GSM Stretchy & Soft", | |
| 356 | +"COACH Women s Leah Platform Loafers", | |
| 357 | +"Doodle Me Happy Kids Thank You Cards - 25 Cards With Envelopes - Cute", | |
| 358 | +"Spring Summer Women Pleated Casual Denim V Neck Ruffle Sleeve Dress Light Blue XL", | |
| 359 | +"Disney Hooded Matching Family Cosplay T-Shirt Infant to Adult Sizes (12 Months - 2XL)", | |
| 360 | +"Leather CPR Cleaner & Conditioner 18oz - Cleans", | |
| 361 | +"Baseball Shirts Women Baseball Mom Tshirt Baseball Heart Graphic Tee Game Day Gifts Funny Short Sleeve Tops", | |
| 362 | +"4 Pack Cooling Towels", | |
| 363 | +"ZEEPORTE Mask Fin Snorkel Set", | |
| 364 | +"60 Pcs Bride Tribe Bachelorette Party Favors Bulk Friendship Bridesmaid Gifts 12 Set Friendship Bracelets Heart Sunglasses Satin Scrunchie for Engagement Bridal Shower Wedding Favor", | |
| 365 | +"AUSELILY Summer Dress Sundress Beach Cover up Swing Dresses", | |
| 366 | +"Loungefly Disney Minnie Mouse Crossbody Satchel Handbag", | |
| 367 | +"Tactical Gym Bag for Men,50L Large 3 in 1 Sports Duffle Bag with Shoes Compartment for Travel", | |
| 368 | +"YETI Rambler 42 oz Tumbler with Handle and Straw Lid", | |
| 369 | +"Samsonite Classic Leather Slim Backpack", | |
| 370 | +"Fabletics Men s Only Short", | |
| 371 | +"3pcs Heart Badge Reel with Alligator Clip Cute Retractable Badge Holder Acrylic Nurse Badge Clip for Office Workers", | |
| 372 | +"Ortho Balance Hiking Shoes for Men Women", | |
| 373 | +"GOLDENMATE 1000VA/600W Lithium UPS Battery Backup and Surge Protector", | |
| 374 | +"Gelante Solid Color 100% Cotton Bucket Hat for Women and Men Packable Travel Summer Beach Hat", | |
| 375 | +"Sonic The Hedgehog 3 Movie Action Figures 2.5-Inch Movie Collector Toy Figure Multi-Pack Includes Sonic The Hedgehog Knuckles Shadow Buzz Bomber & Drone- Officially Licensed Toys", | |
| 376 | +"61 Pcs Nacho Libre Stickers Comedy Movie Graffiti Waterproof Vinyl for Adults for Birthday Party Supplies Decoration Favors for Water Bottles Laptop Suitcase Scrapbooking Choice", | |
| 377 | +"Neck Lift Tape", | |
| 378 | +"925 Sterling Silver Earrings for Womens Sparkly Colorful Full Diamond Simple Stylish Elegant Hypoallergenic Jewelry", | |
| 379 | +"Pink Ceramic Bow Vase for Flowers", | |
| 380 | +"Winter Coats For Men Winter Jackets Water Resistant Warm Thicken Parka Puffer Coat Long Down Jacket", | |
| 381 | +"Alarm Clocks for Bedrooms", | |
| 382 | +"KINURI Running Belt for Men & Women – Fits All Smartphones – Waterproof Waist Pack with Adjustable Strap – Ideal for Jogging", | |
| 383 | +"DREAM PAIRS Heels for Women Flip Flops Kitten Low Heels Open Square Toe Thong Heeled Sandals", | |
| 384 | +"Amazon Basics All Purpose Washable School Craft Liquid Glue for Making Slime", | |
| 385 | +"Inflatable Costume Adult Frog Full Body Deluxe Funny Air Blow Up Costume for Men Women Halloween", | |
| 386 | +"Mens Golf Pants Stretch Casual Dress Pants Elastic Drawstring Slacks for Men Lightweight Trousers with 5 Pockets", | |
| 387 | +"Lip Smacker Hello Kitty Lip Balm", | |
| 388 | +"Brown Sugar Keeper 3D – Terracotta Clay Bear Softener", | |
| 389 | +"MEETSUN Polarized Sunglasses for Women Men Trendy Classic Retro Designer Style", | |
| 390 | +"Corset Top Bustier Lingerie for Women Zipper Front Flower Sexy Burlesque Vintage", | |
| 391 | +"Pro Club Men s Heavyweight Mesh Basketball Shorts", | |
| 392 | +"Nike Tech Men s Full-Zip Windrunner Hoodie (HV0949-237", | |
| 393 | +"Ear Piercing Kit", | |
| 394 | +"Timberland Men s 6 Premium Boot", | |
| 395 | +"Nike Air Rift", | |
| 396 | +"Portable Hookah Set for Travel - Premium Handheld Glass Aluminum Mini Hookah Real Metal Accessories", | |
| 397 | +"Clear Backpack for Boys", | |
| 398 | +"Women’s Knee High Boots Round Toe Chunky Heel Faux Leather Tall Riding Boots with Side Zipper", | |
| 399 | +"Golf Grip Trainer & Connection Band 2Set", | |
| 400 | +"Monster High Self Scare Day Cleo De Nile Doll Play Set", | |
| 401 | +"Fortnite eGift Card - Powered by the Epic Games Store", | |
| 402 | +"Mesh Beach Bags", | |
| 403 | +"Crowye Anime Cosplay Costume for Halloween Princess Costume Accessories Anime White Cosplay Wig Egypt Arm Cuff Bracelet Gold Earrings Greek Goddess Set for Halloween Dress up Princess", | |
| 404 | +"Premium Women s Leather Tote Handbag - Bag for Everyday Use", | |
| 405 | +"Ekouaer Maternity Nursing Gown and Robe Set Labor Delivery Nursing Nightgowns for Breastfeeding Pregnancy Clothes", | |
| 406 | +"Superband Mermaid Tails for Swimming for Women and Adults Without Monofin", | |
| 407 | +"Pink Queen Women s 2025 Casual Pullover Sweaters Sexy V Neck Long Sleeve Twist Knot Cropped Knit Sweater Tops" | |
| 408 | + ], | |
| 409 | + "top_n":386, | |
| 410 | + "normalize": true | |
| 411 | + }' | |
| 412 | + | |
| 413 | +end=$(date +%s%N) # 结束时间,纳秒级 | |
| 414 | +duration=$(( (end - start) / 1000000 )) # 转换为毫秒 | |
| 415 | +echo "Command took $duration milliseconds." | |
| 416 | + | |
| 417 | + | ... | ... |
| ... | ... | @@ -0,0 +1,35 @@ |
| 1 | +#!/bin/bash | |
| 2 | +start=$(date +%s%N) # 开始时间,纳秒级 | |
| 3 | + | |
| 4 | +time curl -X POST "http://localhost:6007/rerank" \ | |
| 5 | + -H "Content-Type: application/json" \ | |
| 6 | + -d '{ | |
| 7 | + "query": "健身女生T恤短袖", | |
| 8 | + "docs": [ "60 Jelly Bracelets 80 s Adult Size - MAQIHAN Neon Gummy Bracelets for Women 80s Jelly Bangles Glow Silicone Bands Jewelry Wristband Rainbow Jellies Bangle Girls Boys Colored Accessories Party Favor", | |
| 9 | +"FITORY Mens Sandals", | |
| 10 | +"Lefant 3 Packs Dust Bags Replacement Kit Suitable for Lefant Base Station of M3/M3 Max Robot Vacuum", | |
| 11 | +"Merrell Mens Hydro Moc", | |
| 12 | +"FITORY Mens Sandals", | |
| 13 | +"Lefant 3 Packs Dust Bags Replacement Kit Suitable for Lefant Base Station of M3/M3 Max Robot Vacuum", | |
| 14 | +"Merrell Mens Hydro Moc", | |
| 15 | + | |
| 16 | +"FITORY Mens Sandals", | |
| 17 | +"Lefant 3 Packs Dust Bags Replacement Kit Suitable for Lefant Base Station of M3/M3 Max Robot Vacuum", | |
| 18 | +"Merrell Mens Hydro Moc", | |
| 19 | + | |
| 20 | + | |
| 21 | +"FITORY Mens Sandals", | |
| 22 | +"Lefant 3 Packs Dust Bags Replacement Kit Suitable for Lefant Base Station of M3/M3 Max Robot Vacuum", | |
| 23 | +"Merrell Mens Hydro Moc", | |
| 24 | +Superband Mermaid Tails for Swimming for Women and Adults Without Monofin", | |
| 25 | +"Pink Queen Women s 2025 Casual Pullover Sweaters Sexy V Neck Long Sleeve Twist Knot Cropped Knit Sweater Tops" | |
| 26 | + ], | |
| 27 | + "top_n":386, | |
| 28 | + "normalize": true | |
| 29 | + }' | |
| 30 | + | |
| 31 | +end=$(date +%s%N) # 结束时间,纳秒级 | |
| 32 | +duration=$(( (end - start) / 1000000 )) # 转换为毫秒 | |
| 33 | +echo "Command took $duration milliseconds." | |
| 34 | + | |
| 35 | + | ... | ... |
config/config.yaml
| ... | ... | @@ -275,26 +275,26 @@ services: |
| 275 | 275 | max_docs: 1000 |
| 276 | 276 | normalize: true |
| 277 | 277 | # 服务内后端(reranker 进程启动时读取) |
| 278 | - backend: "qwen3_vllm" # bge | qwen3_vllm | qwen3_transformers | dashscope_rerank | |
| 278 | + backend: "qwen3_transformers" # bge | qwen3_vllm | qwen3_transformers | dashscope_rerank | |
| 279 | 279 | backends: |
| 280 | 280 | bge: |
| 281 | 281 | model_name: "BAAI/bge-reranker-v2-m3" |
| 282 | 282 | device: null |
| 283 | 283 | use_fp16: true |
| 284 | 284 | batch_size: 64 |
| 285 | - max_length: 512 | |
| 285 | + max_length: 160 | |
| 286 | 286 | cache_dir: "./model_cache" |
| 287 | 287 | enable_warmup: true |
| 288 | 288 | qwen3_vllm: |
| 289 | 289 | model_name: "Qwen/Qwen3-Reranker-0.6B" |
| 290 | 290 | engine: "vllm" |
| 291 | - max_model_len: 256 | |
| 291 | + max_model_len: 160 | |
| 292 | 292 | tensor_parallel_size: 1 |
| 293 | 293 | gpu_memory_utilization: 0.36 |
| 294 | 294 | dtype: "float16" |
| 295 | 295 | enable_prefix_caching: true |
| 296 | 296 | enforce_eager: false |
| 297 | - infer_batch_size: 64 | |
| 297 | + infer_batch_size: 100 | |
| 298 | 298 | sort_by_doc_length: true |
| 299 | 299 | length_sort_mode: "char" # char | token |
| 300 | 300 | instruction: "rank products by given query" | ... | ... |
docs/TODO.txt
| 1 | 1 | |
| 2 | 2 | |
| 3 | -增加意图识别模块 | |
| 3 | +@reranker/backends/qwen3_vllm.py 单次 generate 前有进程内锁,同一进程里不会并行多路 vLLM 推理,这个锁有必要吗?是否会影响性能?是否能够打开,使得性能更好?比如这个场景,我一次请求 400 条,分成每64个一个batch,基于我现在的gpu配置,可以再提高并发度吗? | |
| 4 | +测试了,让每个批次都并发地进行,耗时没有变化 | |
| 5 | + | |
| 6 | +增加款式意图识别模块 | |
| 7 | + | |
| 8 | +意图类型: 颜色,尺寸(目前只需要支持这两种) | |
| 9 | + | |
| 10 | +意图召回层: | |
| 11 | +每种意图,有一个召回词集合 | |
| 12 | +对query(包括原始query、各种翻译query 都做匹配) | |
| 13 | + | |
| 14 | +意图识别层: | |
| 15 | +如果召回 判断有款式需求, | |
| 16 | + | |
| 17 | + | |
| 4 | 18 | 是否有: |
| 5 | 19 | 颜色需求 |
| 6 | 20 | 尺码需求 |
| 7 | - | |
| 8 | 21 | 如果有: 先做sku筛选,然后把最优的拼接到名称中,参与reranker。 |
| 9 | 22 | |
| 10 | 23 | ... | ... |
docs/suggestion索引构建.md
| ... | ... | @@ -96,14 +96,15 @@ |
| 96 | 96 | "lang": { "type": "keyword" }, |
| 97 | 97 | "text": { "type": "keyword" }, // 显示给用户的原始文案 |
| 98 | 98 | "text_norm": { "type": "keyword" }, // 归一化后文本:小写+空白规整 |
| 99 | - "sources": { "type": "keyword" }, // 来源集合:["title", "qanchor", "query_log"] | |
| 99 | + "sources": { "type": "keyword" }, // 来源集合:["title", "qanchor", "tag", "query_log"] | |
| 100 | 100 | "title_doc_count": { "type": "integer" }, |
| 101 | 101 | "qanchor_doc_count": { "type": "integer" }, |
| 102 | + "tag_doc_count": { "type": "integer" }, | |
| 102 | 103 | "query_count_7d": { "type": "integer" }, |
| 103 | 104 | "query_count_30d": { "type": "integer" }, |
| 104 | 105 | "rank_score": { "type": "float" }, // 排序打分 |
| 105 | 106 | "lang_confidence": { "type": "float" }, |
| 106 | - "lang_source": { "type": "keyword" }, // 语言来源:log_field / request_params / script / default | |
| 107 | + "lang_source": { "type": "keyword" }, // 语言来源:log_field / request_params / detector / fallback / default | |
| 107 | 108 | "lang_conflict": { "type": "boolean" }, // 是否存在多来源语言冲突 |
| 108 | 109 | "status": { "type": "byte" }, // 1 = 有效 |
| 109 | 110 | "updated_at": { "type": "date" }, |
| ... | ... | @@ -166,9 +167,9 @@ |
| 166 | 167 | |
| 167 | 168 | #### 4. 构建候选词 |
| 168 | 169 | |
| 169 | -##### 4.1 从商品索引收集 title / qanchors(Step 1) | |
| 170 | +##### 4.1 从商品索引收集 title / qanchors / tags(Step 1) | |
| 170 | 171 | |
| 171 | - - 遍历店铺的所有商品:获取每个商品的 `"spu_id"`, `"title"`, `"qanchors"` 3个字段(按`spu_id`升序) | |
| 172 | + - 遍历店铺的所有商品:获取每个商品的 `"spu_id"`, `"title"`, `"qanchors"`, `"tags"`(按 `spu_id`、`id.keyword` 升序,便于 `search_after` 稳定分页) | |
| 172 | 173 | |
| 173 | 174 | - 对每个商品文档: |
| 174 | 175 | |
| ... | ... | @@ -216,6 +217,11 @@ |
| 216 | 217 | - `text_norm = _normalize_text(q_text)`,再用 `_looks_noise` 过滤 |
| 217 | 218 | - 同样按 `(lang, text_norm)` 合并为 `SuggestionCandidate`,调用 `add_product("qanchor", spu_id=product_id)`。 |
| 218 | 219 | |
| 220 | + 4. **tags 处理**(与 `index_languages` 循环并列,每个商品只做一次): | |
| 221 | + - `tags` 可为字符串数组,或逗号等分隔的单个字符串;经 `_iter_product_tags` 展开为若干条。 | |
| 222 | + - 每条 tag **无语言字段**:使用 `query.query_parser.detect_text_language_for_suggestions`(与 `QueryParser` 相同的 `LanguageDetector`)判定语言,并约束在租户的 `index_languages` 内。 | |
| 223 | + - 通过 `_looks_noise` 后按 `(detected_lang, text_norm)` 合并,调用 `add_product("tag", spu_id=product_id)`。 | |
| 224 | + | |
| 219 | 225 | ##### 4.2 从查询日志收集用户 query(Step 2) |
| 220 | 226 | |
| 221 | 227 | 对应 `_iter_query_log_rows` 与 `_build_full_candidates` 的后半段。 |
| ... | ... | @@ -284,6 +290,7 @@ |
| 284 | 290 | 1.8 \cdot \log(1 + query\_count\_{30d}) + |
| 285 | 291 | 1.2 \cdot \log(1 + query\_count\_{7d}) + |
| 286 | 292 | 1.0 \cdot \log(1 + qanchor\_doc\_count) + |
| 293 | + 0.85 \cdot \log(1 + tag\_doc\_count) + | |
| 287 | 294 | 0.6 \cdot \log(1 + title\_doc\_count) |
| 288 | 295 | \] |
| 289 | 296 | |
| ... | ... | @@ -423,6 +430,7 @@ |
| 423 | 430 | - `sources = ["query_log"]` |
| 424 | 431 | - `title_doc_count = 0` |
| 425 | 432 | - `qanchor_doc_count = 0` |
| 433 | + - `tag_doc_count = 0` | |
| 426 | 434 | - `completion.<lang>.input = [text]` |
| 427 | 435 | - `completion.<lang>.weight = int(max(rank_score, 1.0) * 100)` |
| 428 | 436 | - `sat.<lang> = text` |
| ... | ... | @@ -446,7 +454,7 @@ |
| 446 | 454 | - 将 `"query_log"` 加入 `sources` |
| 447 | 455 | - `lang_conflict` 与 `params.lang_conflict` 取或 |
| 448 | 456 | - 若 `params.lang_confidence > ctx._source.lang_confidence` 则更新 `lang_confidence` 和 `lang_source` |
| 449 | - - 基于更新后的 `query_count_7d/30d` + `qanchor_doc_count` + `title_doc_count` 重新计算 `rank_score` | |
| 457 | + - 基于更新后的 `query_count_7d/30d` + `qanchor_doc_count` + `tag_doc_count` + `title_doc_count` 重新计算 `rank_score` | |
| 450 | 458 | - `status = 1` |
| 451 | 459 | - `updated_at = params.now_iso` |
| 452 | 460 | - 同步更新 `text / lang / text_norm` |
| ... | ... | @@ -502,3 +510,9 @@ |
| 502 | 510 | - 若是 `"zh_tw"` / `"pt_br"` → 保留全量 |
| 503 | 511 | - 其他 → 取 `_` 前缀(例如 `"en_US"` → `"en"`) |
| 504 | 512 | |
| 513 | +#### 4. 查询日志 / tag 的语言回退 `_resolve_query_language` 与 `detect_text_language_for_suggestions` | |
| 514 | + | |
| 515 | +- 日志语言优先级不变:`language` 字段 → `request_params.language` → **语言检测**。 | |
| 516 | +- 检测实现为 `query.query_parser.detect_text_language_for_suggestions`:内部使用与 `QueryParser` 相同的 `LanguageDetector`(`query/language_detector.py`),并将结果约束到租户 `index_languages`(含 `zh_tw` 等与检测码的 base 匹配)。 | |
| 517 | +- 在线联想:`SuggestionService` 在合并 completion 与 SAT 结果后,按 `ES_score × (1 / sqrt(词元数))` 排序(词元算法与 `simple_tokenize_query` 一致),再以 `rank_score` 作次要键,减轻长标题/长短语相对短词根的压制不足问题。 | |
| 518 | + | ... | ... |
mappings/search_suggestions.json
| ... | ... | @@ -27,6 +27,7 @@ |
| 27 | 27 | "sources": { "type": "keyword" }, |
| 28 | 28 | "title_doc_count": { "type": "integer" }, |
| 29 | 29 | "qanchor_doc_count": { "type": "integer" }, |
| 30 | + "tag_doc_count": { "type": "integer" }, | |
| 30 | 31 | "query_count_7d": { "type": "integer" }, |
| 31 | 32 | "query_count_30d": { "type": "integer" }, |
| 32 | 33 | "rank_score": { "type": "float" }, | ... | ... |
query/query_parser.py
| ... | ... | @@ -4,7 +4,7 @@ Query parser - main module for query processing. |
| 4 | 4 | Handles query rewriting, translation, and embedding generation. |
| 5 | 5 | """ |
| 6 | 6 | |
| 7 | -from typing import Dict, List, Optional, Any, Union | |
| 7 | +from typing import Dict, List, Optional, Any, Union, Tuple | |
| 8 | 8 | import numpy as np |
| 9 | 9 | import logging |
| 10 | 10 | import re |
| ... | ... | @@ -23,6 +23,20 @@ try: |
| 23 | 23 | except Exception: # pragma: no cover |
| 24 | 24 | hanlp = None |
| 25 | 25 | |
| 26 | + | |
| 27 | +def simple_tokenize_query(text: str) -> List[str]: | |
| 28 | + """ | |
| 29 | + Lightweight tokenizer for suggestion length / analysis (aligned with QueryParser fallback). | |
| 30 | + | |
| 31 | + - Consecutive CJK characters form one token | |
| 32 | + - Latin / digit runs (with internal hyphens) form tokens | |
| 33 | + """ | |
| 34 | + if not text: | |
| 35 | + return [] | |
| 36 | + pattern = re.compile(r"[\u4e00-\u9fff]+|[A-Za-z0-9_]+(?:-[A-Za-z0-9_]+)*") | |
| 37 | + return pattern.findall(text) | |
| 38 | + | |
| 39 | + | |
| 26 | 40 | class ParsedQuery: |
| 27 | 41 | """Container for parsed query results.""" |
| 28 | 42 | |
| ... | ... | @@ -173,16 +187,7 @@ class QueryParser: |
| 173 | 187 | return config.query_config.default_translation_model |
| 174 | 188 | |
| 175 | 189 | def _simple_tokenize(self, text: str) -> List[str]: |
| 176 | - """ | |
| 177 | - Lightweight tokenizer fallback. | |
| 178 | - | |
| 179 | - - Groups consecutive CJK chars as a token | |
| 180 | - - Groups consecutive latin/digits/underscore/dash as a token | |
| 181 | - """ | |
| 182 | - if not text: | |
| 183 | - return [] | |
| 184 | - pattern = re.compile(r"[\u4e00-\u9fff]+|[A-Za-z0-9_]+(?:-[A-Za-z0-9_]+)*") | |
| 185 | - return pattern.findall(text) | |
| 190 | + return simple_tokenize_query(text) | |
| 186 | 191 | |
| 187 | 192 | def _extract_keywords(self, query: str) -> str: |
| 188 | 193 | """Extract keywords (nouns with length > 1) from query.""" |
| ... | ... | @@ -636,3 +641,63 @@ class QueryParser: |
| 636 | 641 | queries.append(translation) |
| 637 | 642 | |
| 638 | 643 | return queries |
| 644 | + | |
| 645 | + | |
| 646 | +def detect_text_language_for_suggestions( | |
| 647 | + text: str, | |
| 648 | + *, | |
| 649 | + index_languages: Optional[List[str]] = None, | |
| 650 | + primary_language: str = "en", | |
| 651 | +) -> Tuple[str, float, str]: | |
| 652 | + """ | |
| 653 | + Language detection for short strings (mixed-language tags, query-log fallback). | |
| 654 | + | |
| 655 | + Uses the same ``LanguageDetector`` as :class:`QueryParser`. Returns a language | |
| 656 | + code present in ``index_languages`` when possible, otherwise the tenant primary. | |
| 657 | + | |
| 658 | + Returns: | |
| 659 | + (lang, confidence, source) where source is ``detector``, ``fallback``, or ``default``. | |
| 660 | + """ | |
| 661 | + langs_list = [x for x in (index_languages or []) if x] | |
| 662 | + langs_set = set(langs_list) | |
| 663 | + | |
| 664 | + def _norm_lang(raw: Optional[str]) -> Optional[str]: | |
| 665 | + if not raw: | |
| 666 | + return None | |
| 667 | + token = str(raw).strip().lower().replace("-", "_") | |
| 668 | + if not token: | |
| 669 | + return None | |
| 670 | + if token in {"zh_tw", "pt_br"}: | |
| 671 | + return token | |
| 672 | + return token.split("_")[0] | |
| 673 | + | |
| 674 | + primary = _norm_lang(primary_language) or "en" | |
| 675 | + if primary not in langs_set and langs_list: | |
| 676 | + primary = _norm_lang(langs_list[0]) or langs_list[0] | |
| 677 | + | |
| 678 | + if not text or not str(text).strip(): | |
| 679 | + return primary, 0.0, "default" | |
| 680 | + | |
| 681 | + raw_code = LanguageDetector().detect(str(text).strip()) | |
| 682 | + if not raw_code or raw_code == "unknown": | |
| 683 | + return primary, 0.35, "default" | |
| 684 | + | |
| 685 | + def _index_lang_base(cand: str) -> str: | |
| 686 | + t = str(cand).strip().lower().replace("-", "_") | |
| 687 | + return t.split("_")[0] if t else "" | |
| 688 | + | |
| 689 | + def _resolve_index_lang(code: str) -> Optional[str]: | |
| 690 | + if code in langs_set: | |
| 691 | + return code | |
| 692 | + for cand in langs_list: | |
| 693 | + if _index_lang_base(cand) == code: | |
| 694 | + return cand | |
| 695 | + return None | |
| 696 | + | |
| 697 | + if langs_list: | |
| 698 | + resolved = _resolve_index_lang(raw_code) | |
| 699 | + if resolved is None: | |
| 700 | + return primary, 0.5, "fallback" | |
| 701 | + return resolved, 0.92, "detector" | |
| 702 | + | |
| 703 | + return raw_code, 0.92, "detector" | ... | ... |
reranker/bge_reranker.py
| ... | ... | @@ -15,7 +15,7 @@ import time |
| 15 | 15 | from typing import Any, Dict, List, Optional, Tuple |
| 16 | 16 | |
| 17 | 17 | import torch |
| 18 | -from modelscope import AutoModelForSequenceClassification, AutoTokenizer | |
| 18 | +from transformers import AutoModelForSequenceClassification, AutoTokenizer | |
| 19 | 19 | |
| 20 | 20 | logger = logging.getLogger("reranker.core") |
| 21 | 21 | ... | ... |
| ... | ... | @@ -0,0 +1,237 @@ |
| 1 | +#!/usr/bin/env python3 | |
| 2 | +""" | |
| 3 | +Single-request rerank latency probe using real title lines (e.g. 1.8w export). | |
| 4 | + | |
| 5 | +Randomly samples N titles from a text file (one title per line), POSTs to the | |
| 6 | +rerank HTTP API, prints wall-clock latency. | |
| 7 | + | |
| 8 | +Supports multiple N values (comma-separated) and multiple repeats per N. | |
| 9 | + | |
| 10 | +Example: | |
| 11 | + source activate.sh | |
| 12 | + python scripts/benchmark_reranker_random_titles.py 386 | |
| 13 | + python scripts/benchmark_reranker_random_titles.py 40,80,100 | |
| 14 | + python scripts/benchmark_reranker_random_titles.py 40,80,100 --repeat 3 --seed 42 | |
| 15 | + RERANK_BASE=http://127.0.0.1:6007 python scripts/benchmark_reranker_random_titles.py 200 | |
| 16 | +""" | |
| 17 | + | |
| 18 | +from __future__ import annotations | |
| 19 | + | |
| 20 | +import argparse | |
| 21 | +import json | |
| 22 | +import os | |
| 23 | +import random | |
| 24 | +import statistics | |
| 25 | +import sys | |
| 26 | +import time | |
| 27 | +from pathlib import Path | |
| 28 | +from typing import List, Optional, Tuple | |
| 29 | + | |
| 30 | +import httpx | |
| 31 | + | |
| 32 | + | |
| 33 | +def _load_titles(path: Path) -> List[str]: | |
| 34 | + lines: List[str] = [] | |
| 35 | + with path.open(encoding="utf-8", errors="replace") as f: | |
| 36 | + for line in f: | |
| 37 | + s = line.strip() | |
| 38 | + if s: | |
| 39 | + lines.append(s) | |
| 40 | + return lines | |
| 41 | + | |
| 42 | + | |
| 43 | +def _parse_doc_counts(s: str) -> List[int]: | |
| 44 | + parts = [p.strip() for p in s.split(",") if p.strip()] | |
| 45 | + if not parts: | |
| 46 | + raise ValueError("empty doc-count list") | |
| 47 | + out: List[int] = [] | |
| 48 | + for p in parts: | |
| 49 | + v = int(p, 10) | |
| 50 | + if v <= 0: | |
| 51 | + raise ValueError(f"doc count must be positive, got {v}") | |
| 52 | + out.append(v) | |
| 53 | + return out | |
| 54 | + | |
| 55 | + | |
| 56 | +def _do_rerank( | |
| 57 | + client: httpx.Client, | |
| 58 | + url: str, | |
| 59 | + query: str, | |
| 60 | + docs: List[str], | |
| 61 | + *, | |
| 62 | + top_n: int, | |
| 63 | + normalize: bool, | |
| 64 | +) -> Tuple[bool, int, float, Optional[int], str]: | |
| 65 | + payload: dict = {"query": query, "docs": docs, "normalize": normalize} | |
| 66 | + if top_n > 0: | |
| 67 | + payload["top_n"] = top_n | |
| 68 | + body = json.dumps(payload, ensure_ascii=False) | |
| 69 | + headers = {"Content-Type": "application/json"} | |
| 70 | + t0 = time.perf_counter() | |
| 71 | + try: | |
| 72 | + resp = client.post(url, content=body.encode("utf-8"), headers=headers) | |
| 73 | + except httpx.HTTPError: | |
| 74 | + raise | |
| 75 | + elapsed_ms = (time.perf_counter() - t0) * 1000.0 | |
| 76 | + text = resp.text or "" | |
| 77 | + ok = resp.status_code == 200 | |
| 78 | + scores_len: Optional[int] = None | |
| 79 | + if ok: | |
| 80 | + try: | |
| 81 | + data = resp.json() | |
| 82 | + sc = data.get("scores") | |
| 83 | + if isinstance(sc, list): | |
| 84 | + scores_len = len(sc) | |
| 85 | + except json.JSONDecodeError: | |
| 86 | + scores_len = None | |
| 87 | + return ok, resp.status_code, elapsed_ms, scores_len, text | |
| 88 | + | |
| 89 | + | |
| 90 | +def main() -> int: | |
| 91 | + parser = argparse.ArgumentParser( | |
| 92 | + description="POST /rerank with N random titles from a file and print latency." | |
| 93 | + ) | |
| 94 | + parser.add_argument( | |
| 95 | + "n", | |
| 96 | + type=str, | |
| 97 | + metavar="N[,N,...]", | |
| 98 | + help="Doc counts: one integer or comma-separated list, e.g. 40,80,100.", | |
| 99 | + ) | |
| 100 | + parser.add_argument( | |
| 101 | + "--repeat", | |
| 102 | + type=int, | |
| 103 | + default=3, | |
| 104 | + help="Number of runs per doc count (default: 3).", | |
| 105 | + ) | |
| 106 | + parser.add_argument( | |
| 107 | + "--titles-file", | |
| 108 | + type=Path, | |
| 109 | + default=Path(os.environ.get("RERANK_TITLE_FILE", "/home/ubuntu/rerank_test/titles.1.8w")), | |
| 110 | + help="Path to newline-separated titles (default: %(default)s or env RERANK_TITLE_FILE).", | |
| 111 | + ) | |
| 112 | + parser.add_argument( | |
| 113 | + "--url", | |
| 114 | + type=str, | |
| 115 | + default=os.environ.get("RERANK_BASE", "http://127.0.0.1:6007").rstrip("/") + "/rerank", | |
| 116 | + help="Full rerank URL (default: $RERANK_BASE/rerank or http://127.0.0.1:6007/rerank).", | |
| 117 | + ) | |
| 118 | + parser.add_argument( | |
| 119 | + "--query", | |
| 120 | + type=str, | |
| 121 | + default="健身女生T恤短袖", | |
| 122 | + help="Rerank query string.", | |
| 123 | + ) | |
| 124 | + parser.add_argument( | |
| 125 | + "--seed", | |
| 126 | + type=int, | |
| 127 | + default=None, | |
| 128 | + help="RNG base seed; each (n, run) uses a derived seed when set (optional).", | |
| 129 | + ) | |
| 130 | + parser.add_argument( | |
| 131 | + "--top-n", | |
| 132 | + type=int, | |
| 133 | + default=0, | |
| 134 | + help="If > 0, include top_n in JSON body (omit field when 0).", | |
| 135 | + ) | |
| 136 | + parser.add_argument( | |
| 137 | + "--no-normalize", | |
| 138 | + action="store_true", | |
| 139 | + help="Send normalize=false (default: normalize=true).", | |
| 140 | + ) | |
| 141 | + parser.add_argument( | |
| 142 | + "--timeout", | |
| 143 | + type=float, | |
| 144 | + default=float(os.environ.get("RERANK_TIMEOUT_SEC", "240")), | |
| 145 | + help="HTTP timeout seconds.", | |
| 146 | + ) | |
| 147 | + parser.add_argument( | |
| 148 | + "--print-body-preview", | |
| 149 | + action="store_true", | |
| 150 | + help="Print first ~500 chars of response body on success (last run only).", | |
| 151 | + ) | |
| 152 | + args = parser.parse_args() | |
| 153 | + | |
| 154 | + try: | |
| 155 | + doc_counts = _parse_doc_counts(args.n) | |
| 156 | + except ValueError as exc: | |
| 157 | + print(f"error: invalid N list {args.n!r}: {exc}", file=sys.stderr) | |
| 158 | + return 2 | |
| 159 | + | |
| 160 | + repeat = int(args.repeat) | |
| 161 | + if repeat <= 0: | |
| 162 | + print("error: --repeat must be positive", file=sys.stderr) | |
| 163 | + return 2 | |
| 164 | + | |
| 165 | + if not args.titles_file.is_file(): | |
| 166 | + print(f"error: titles file not found: {args.titles_file}", file=sys.stderr) | |
| 167 | + return 2 | |
| 168 | + | |
| 169 | + titles = _load_titles(args.titles_file) | |
| 170 | + max_n = max(doc_counts) | |
| 171 | + if len(titles) < max_n: | |
| 172 | + print( | |
| 173 | + f"error: file has only {len(titles)} non-empty lines, need at least {max_n}", | |
| 174 | + file=sys.stderr, | |
| 175 | + ) | |
| 176 | + return 2 | |
| 177 | + | |
| 178 | + top_n = int(args.top_n) | |
| 179 | + normalize = not args.no_normalize | |
| 180 | + any_fail = False | |
| 181 | + summary: dict[int, List[float]] = {n: [] for n in doc_counts} | |
| 182 | + | |
| 183 | + with httpx.Client(timeout=args.timeout) as client: | |
| 184 | + for n in doc_counts: | |
| 185 | + for run_idx in range(repeat): | |
| 186 | + if args.seed is not None: | |
| 187 | + random.seed(args.seed + n * 10_000 + run_idx) | |
| 188 | + docs = random.sample(titles, n) | |
| 189 | + try: | |
| 190 | + ok, status, elapsed_ms, scores_len, text = _do_rerank( | |
| 191 | + client, | |
| 192 | + args.url, | |
| 193 | + args.query, | |
| 194 | + docs, | |
| 195 | + top_n=top_n, | |
| 196 | + normalize=normalize, | |
| 197 | + ) | |
| 198 | + except httpx.HTTPError as exc: | |
| 199 | + print( | |
| 200 | + f"n={n} run={run_idx + 1}/{repeat} error: request failed: {exc}", | |
| 201 | + file=sys.stderr, | |
| 202 | + ) | |
| 203 | + any_fail = True | |
| 204 | + continue | |
| 205 | + | |
| 206 | + if ok: | |
| 207 | + summary[n].append(elapsed_ms) | |
| 208 | + else: | |
| 209 | + any_fail = True | |
| 210 | + | |
| 211 | + print( | |
| 212 | + f"n={n} run={run_idx + 1}/{repeat} status={status} " | |
| 213 | + f"latency_ms={elapsed_ms:.2f} scores={scores_len if scores_len is not None else 'n/a'}" | |
| 214 | + ) | |
| 215 | + if args.print_body_preview and text and run_idx == repeat - 1 and n == doc_counts[-1]: | |
| 216 | + preview = text[:500] + ("…" if len(text) > 500 else "") | |
| 217 | + print(preview) | |
| 218 | + | |
| 219 | + for n in doc_counts: | |
| 220 | + lat = summary[n] | |
| 221 | + if not lat: | |
| 222 | + print(f"summary n={n} runs=0 (all failed)") | |
| 223 | + continue | |
| 224 | + avg = statistics.mean(lat) | |
| 225 | + lo, hi = min(lat), max(lat) | |
| 226 | + extra = "" | |
| 227 | + if len(lat) >= 2: | |
| 228 | + extra = f" stdev_ms={statistics.stdev(lat):.2f}" | |
| 229 | + print( | |
| 230 | + f"summary n={n} runs={len(lat)} min_ms={lo:.2f} max_ms={hi:.2f} avg_ms={avg:.2f}{extra}" | |
| 231 | + ) | |
| 232 | + | |
| 233 | + return 1 if any_fail else 0 | |
| 234 | + | |
| 235 | + | |
| 236 | +if __name__ == "__main__": | |
| 237 | + raise SystemExit(main()) | ... | ... |
suggestion/builder.py
| ... | ... | @@ -20,6 +20,7 @@ from sqlalchemy import text |
| 20 | 20 | |
| 21 | 21 | from config.loader import get_app_config |
| 22 | 22 | from config.tenant_config_loader import get_tenant_config_loader |
| 23 | +from query.query_parser import detect_text_language_for_suggestions | |
| 23 | 24 | from suggestion.mapping import build_suggestion_mapping |
| 24 | 25 | from utils.es_client import ESClient |
| 25 | 26 | |
| ... | ... | @@ -57,6 +58,7 @@ class SuggestionCandidate: |
| 57 | 58 | sources: set = field(default_factory=set) |
| 58 | 59 | title_spu_ids: set = field(default_factory=set) |
| 59 | 60 | qanchor_spu_ids: set = field(default_factory=set) |
| 61 | + tag_spu_ids: set = field(default_factory=set) | |
| 60 | 62 | query_count_7d: int = 0 |
| 61 | 63 | query_count_30d: int = 0 |
| 62 | 64 | lang_confidence: float = 1.0 |
| ... | ... | @@ -69,6 +71,8 @@ class SuggestionCandidate: |
| 69 | 71 | self.title_spu_ids.add(spu_id) |
| 70 | 72 | elif source == "qanchor": |
| 71 | 73 | self.qanchor_spu_ids.add(spu_id) |
| 74 | + elif source == "tag": | |
| 75 | + self.tag_spu_ids.add(spu_id) | |
| 72 | 76 | |
| 73 | 77 | def add_query_log(self, is_7d: bool) -> None: |
| 74 | 78 | self.sources.add("query_log") |
| ... | ... | @@ -150,6 +154,19 @@ class SuggestionIndexBuilder: |
| 150 | 154 | return out |
| 151 | 155 | |
| 152 | 156 | @staticmethod |
| 157 | + def _iter_product_tags(raw: Any) -> List[str]: | |
| 158 | + if raw is None: | |
| 159 | + return [] | |
| 160 | + if isinstance(raw, list): | |
| 161 | + return [str(x).strip() for x in raw if str(x).strip()] | |
| 162 | + s = str(raw).strip() | |
| 163 | + if not s: | |
| 164 | + return [] | |
| 165 | + parts = re.split(r"[,;|/\n\t]+", s) | |
| 166 | + out = [p.strip() for p in parts if p and p.strip()] | |
| 167 | + return out if out else [s] | |
| 168 | + | |
| 169 | + @staticmethod | |
| 153 | 170 | def _looks_noise(text_value: str) -> bool: |
| 154 | 171 | if not text_value: |
| 155 | 172 | return True |
| ... | ... | @@ -187,20 +204,6 @@ class SuggestionIndexBuilder: |
| 187 | 204 | return None |
| 188 | 205 | return None |
| 189 | 206 | |
| 190 | - @staticmethod | |
| 191 | - def _detect_script_language(query: str) -> Tuple[Optional[str], float, str]: | |
| 192 | - if re.search(r"[\u4e00-\u9fff]", query): | |
| 193 | - return "zh", 0.98, "script" | |
| 194 | - if re.search(r"[\u0600-\u06FF]", query): | |
| 195 | - return "ar", 0.98, "script" | |
| 196 | - if re.search(r"[\u0400-\u04FF]", query): | |
| 197 | - return "ru", 0.95, "script" | |
| 198 | - if re.search(r"[\u0370-\u03FF]", query): | |
| 199 | - return "el", 0.95, "script" | |
| 200 | - if re.search(r"[a-zA-Z]", query): | |
| 201 | - return "en", 0.55, "model" | |
| 202 | - return None, 0.0, "default" | |
| 203 | - | |
| 204 | 207 | def _resolve_query_language( |
| 205 | 208 | self, |
| 206 | 209 | query: str, |
| ... | ... | @@ -225,18 +228,29 @@ class SuggestionIndexBuilder: |
| 225 | 228 | if req_lang and (not langs_set or req_lang in langs_set): |
| 226 | 229 | return req_lang, 1.0, "request_params", conflict |
| 227 | 230 | |
| 228 | - detected_lang, conf, source = self._detect_script_language(query) | |
| 229 | - if detected_lang and (not langs_set or detected_lang in langs_set): | |
| 230 | - return detected_lang, conf, source, conflict | |
| 231 | + det_lang, conf, det_source = detect_text_language_for_suggestions( | |
| 232 | + query, | |
| 233 | + index_languages=index_languages, | |
| 234 | + primary_language=primary, | |
| 235 | + ) | |
| 236 | + if det_lang and (not langs_set or det_lang in langs_set): | |
| 237 | + return det_lang, conf, det_source, conflict | |
| 231 | 238 | |
| 232 | 239 | return primary, 0.3, "default", conflict |
| 233 | 240 | |
| 234 | 241 | @staticmethod |
| 235 | - def _compute_rank_score(query_count_30d: int, query_count_7d: int, qanchor_doc_count: int, title_doc_count: int) -> float: | |
| 242 | + def _compute_rank_score( | |
| 243 | + query_count_30d: int, | |
| 244 | + query_count_7d: int, | |
| 245 | + qanchor_doc_count: int, | |
| 246 | + title_doc_count: int, | |
| 247 | + tag_doc_count: int = 0, | |
| 248 | + ) -> float: | |
| 236 | 249 | return ( |
| 237 | 250 | 1.8 * math.log1p(max(query_count_30d, 0)) |
| 238 | 251 | + 1.2 * math.log1p(max(query_count_7d, 0)) |
| 239 | 252 | + 1.0 * math.log1p(max(qanchor_doc_count, 0)) |
| 253 | + + 0.85 * math.log1p(max(tag_doc_count, 0)) | |
| 240 | 254 | + 0.6 * math.log1p(max(title_doc_count, 0)) |
| 241 | 255 | ) |
| 242 | 256 | |
| ... | ... | @@ -247,6 +261,7 @@ class SuggestionIndexBuilder: |
| 247 | 261 | query_count_7d=c.query_count_7d, |
| 248 | 262 | qanchor_doc_count=len(c.qanchor_spu_ids), |
| 249 | 263 | title_doc_count=len(c.title_spu_ids), |
| 264 | + tag_doc_count=len(c.tag_spu_ids), | |
| 250 | 265 | ) |
| 251 | 266 | |
| 252 | 267 | def _iter_products(self, tenant_id: str, batch_size: int = 500) -> Iterator[Dict[str, Any]]: |
| ... | ... | @@ -259,9 +274,10 @@ class SuggestionIndexBuilder: |
| 259 | 274 | while True: |
| 260 | 275 | body: Dict[str, Any] = { |
| 261 | 276 | "size": batch_size, |
| 262 | - "_source": ["id", "spu_id", "title", "qanchors"], | |
| 277 | + "_source": ["id", "spu_id", "title", "qanchors", "tags"], | |
| 263 | 278 | "sort": [ |
| 264 | 279 | {"spu_id": {"order": "asc", "missing": "_last"}}, |
| 280 | + {"id.keyword": {"order": "asc", "missing": "_last"}}, | |
| 265 | 281 | ], |
| 266 | 282 | "query": {"match_all": {}}, |
| 267 | 283 | } |
| ... | ... | @@ -471,6 +487,22 @@ class SuggestionIndexBuilder: |
| 471 | 487 | key_to_candidate[key] = c |
| 472 | 488 | c.add_product("qanchor", spu_id=product_id) |
| 473 | 489 | |
| 490 | + for tag in self._iter_product_tags(src.get("tags")): | |
| 491 | + tag_lang, _, _ = detect_text_language_for_suggestions( | |
| 492 | + tag, | |
| 493 | + index_languages=index_languages, | |
| 494 | + primary_language=primary_language, | |
| 495 | + ) | |
| 496 | + text_norm = self._normalize_text(tag) | |
| 497 | + if self._looks_noise(text_norm): | |
| 498 | + continue | |
| 499 | + key = (tag_lang, text_norm) | |
| 500 | + c = key_to_candidate.get(key) | |
| 501 | + if c is None: | |
| 502 | + c = SuggestionCandidate(text=tag, text_norm=text_norm, lang=tag_lang) | |
| 503 | + key_to_candidate[key] = c | |
| 504 | + c.add_product("tag", spu_id=product_id) | |
| 505 | + | |
| 474 | 506 | # Step 2: query logs |
| 475 | 507 | now = datetime.now(timezone.utc) |
| 476 | 508 | since = now - timedelta(days=days) |
| ... | ... | @@ -521,6 +553,7 @@ class SuggestionIndexBuilder: |
| 521 | 553 | "sources": sorted(c.sources), |
| 522 | 554 | "title_doc_count": len(c.title_spu_ids), |
| 523 | 555 | "qanchor_doc_count": len(c.qanchor_spu_ids), |
| 556 | + "tag_doc_count": len(c.tag_spu_ids), | |
| 524 | 557 | "query_count_7d": c.query_count_7d, |
| 525 | 558 | "query_count_30d": c.query_count_30d, |
| 526 | 559 | "rank_score": float(rank_score), |
| ... | ... | @@ -672,6 +705,7 @@ class SuggestionIndexBuilder: |
| 672 | 705 | query_count_7d=delta.delta_7d, |
| 673 | 706 | qanchor_doc_count=0, |
| 674 | 707 | title_doc_count=0, |
| 708 | + tag_doc_count=0, | |
| 675 | 709 | ) |
| 676 | 710 | return { |
| 677 | 711 | "tenant_id": delta.tenant_id, |
| ... | ... | @@ -681,6 +715,7 @@ class SuggestionIndexBuilder: |
| 681 | 715 | "sources": ["query_log"], |
| 682 | 716 | "title_doc_count": 0, |
| 683 | 717 | "qanchor_doc_count": 0, |
| 718 | + "tag_doc_count": 0, | |
| 684 | 719 | "query_count_7d": delta.delta_7d, |
| 685 | 720 | "query_count_30d": delta.delta_30d, |
| 686 | 721 | "rank_score": float(rank_score), |
| ... | ... | @@ -710,6 +745,7 @@ class SuggestionIndexBuilder: |
| 710 | 745 | if (ctx._source.query_count_7d == null) { ctx._source.query_count_7d = 0; } |
| 711 | 746 | if (ctx._source.qanchor_doc_count == null) { ctx._source.qanchor_doc_count = 0; } |
| 712 | 747 | if (ctx._source.title_doc_count == null) { ctx._source.title_doc_count = 0; } |
| 748 | + if (ctx._source.tag_doc_count == null) { ctx._source.tag_doc_count = 0; } | |
| 713 | 749 | |
| 714 | 750 | ctx._source.query_count_30d += params.delta_30d; |
| 715 | 751 | ctx._source.query_count_7d += params.delta_7d; |
| ... | ... | @@ -729,10 +765,12 @@ class SuggestionIndexBuilder: |
| 729 | 765 | int q7 = ctx._source.query_count_7d; |
| 730 | 766 | int qa = ctx._source.qanchor_doc_count; |
| 731 | 767 | int td = ctx._source.title_doc_count; |
| 768 | + int tg = ctx._source.tag_doc_count; | |
| 732 | 769 | |
| 733 | 770 | double score = 1.8 * Math.log(1 + q30) |
| 734 | 771 | + 1.2 * Math.log(1 + q7) |
| 735 | 772 | + 1.0 * Math.log(1 + qa) |
| 773 | + + 0.85 * Math.log(1 + tg) | |
| 736 | 774 | + 0.6 * Math.log(1 + td); |
| 737 | 775 | ctx._source.rank_score = score; |
| 738 | 776 | ctx._source.status = 1; | ... | ... |
suggestion/mapping.py
| ... | ... | @@ -96,6 +96,7 @@ def build_suggestion_mapping(index_languages: List[str]) -> Dict[str, Any]: |
| 96 | 96 | "sources": {"type": "keyword"}, |
| 97 | 97 | "title_doc_count": {"type": "integer"}, |
| 98 | 98 | "qanchor_doc_count": {"type": "integer"}, |
| 99 | + "tag_doc_count": {"type": "integer"}, | |
| 99 | 100 | "query_count_7d": {"type": "integer"}, |
| 100 | 101 | "query_count_30d": {"type": "integer"}, |
| 101 | 102 | "rank_score": {"type": "float"}, | ... | ... |
suggestion/service.py
| ... | ... | @@ -7,12 +7,24 @@ import time |
| 7 | 7 | from typing import Any, Dict, List, Optional |
| 8 | 8 | |
| 9 | 9 | from config.tenant_config_loader import get_tenant_config_loader |
| 10 | +from query.query_parser import simple_tokenize_query | |
| 10 | 11 | from suggestion.builder import get_suggestion_alias_name |
| 11 | 12 | from utils.es_client import ESClient |
| 12 | 13 | |
| 13 | 14 | logger = logging.getLogger(__name__) |
| 14 | 15 | |
| 15 | 16 | |
| 17 | +def _suggestion_length_factor(text: str) -> float: | |
| 18 | + """Down-weight longer strings at query time: factor 1 / sqrt(token_len).""" | |
| 19 | + n = max(len(simple_tokenize_query(str(text or ""))), 1) | |
| 20 | + return 1.0 / (n ** 0.5) | |
| 21 | + | |
| 22 | + | |
| 23 | +def _score_with_token_length_penalty(item: Dict[str, Any]) -> float: | |
| 24 | + base = float(item.get("score") or 0.0) | |
| 25 | + return base * _suggestion_length_factor(str(item.get("text") or "")) | |
| 26 | + | |
| 27 | + | |
| 16 | 28 | class SuggestionService: |
| 17 | 29 | def __init__(self, es_client: ESClient): |
| 18 | 30 | self.es_client = es_client |
| ... | ... | @@ -150,6 +162,17 @@ class SuggestionService: |
| 150 | 162 | seen_text_norm.add(norm) |
| 151 | 163 | suggestions.append(dict(item)) |
| 152 | 164 | |
| 165 | + def _finalize_suggestion_list(items: List[Dict[str, Any]], limit: int) -> List[Dict[str, Any]]: | |
| 166 | + out = list(items) | |
| 167 | + out.sort( | |
| 168 | + key=lambda x: ( | |
| 169 | + _score_with_token_length_penalty(x), | |
| 170 | + float(x.get("rank_score") or 0.0), | |
| 171 | + ), | |
| 172 | + reverse=True, | |
| 173 | + ) | |
| 174 | + return out[:limit] | |
| 175 | + | |
| 153 | 176 | _append_items(completion_items) |
| 154 | 177 | |
| 155 | 178 | # Fast path: avoid a second ES query for short prefixes or when completion already full. |
| ... | ... | @@ -168,7 +191,7 @@ class SuggestionService: |
| 168 | 191 | "query": query, |
| 169 | 192 | "language": language, |
| 170 | 193 | "resolved_language": resolved_lang, |
| 171 | - "suggestions": suggestions[:size], | |
| 194 | + "suggestions": _finalize_suggestion_list(suggestions, size), | |
| 172 | 195 | "took_ms": took_ms, |
| 173 | 196 | } |
| 174 | 197 | |
| ... | ... | @@ -260,6 +283,6 @@ class SuggestionService: |
| 260 | 283 | "query": query, |
| 261 | 284 | "language": language, |
| 262 | 285 | "resolved_language": resolved_lang, |
| 263 | - "suggestions": suggestions[:size], | |
| 286 | + "suggestions": _finalize_suggestion_list(suggestions, size), | |
| 264 | 287 | "took_ms": took_ms, |
| 265 | 288 | } | ... | ... |
tests/test_suggestions.py
| ... | ... | @@ -388,6 +388,53 @@ def test_build_full_candidates_fallback_to_id_when_spu_id_missing(monkeypatch): |
| 388 | 388 | |
| 389 | 389 | |
| 390 | 390 | @pytest.mark.unit |
| 391 | +def test_build_full_candidates_tags_and_qanchor_phrases(monkeypatch): | |
| 392 | + fake_es = FakeESClient() | |
| 393 | + builder = SuggestionIndexBuilder(es_client=fake_es, db_engine=None) | |
| 394 | + | |
| 395 | + monkeypatch.setattr( | |
| 396 | + builder, | |
| 397 | + "_iter_products", | |
| 398 | + lambda tenant_id, batch_size=500: iter( | |
| 399 | + [ | |
| 400 | + { | |
| 401 | + "_id": "900", | |
| 402 | + "_source": { | |
| 403 | + "spu_id": "900", | |
| 404 | + "title": {"en": "Tee", "zh": "T恤"}, | |
| 405 | + "qanchors": { | |
| 406 | + "en": "slim fit, sporty casual", | |
| 407 | + "zh": "修身, 显瘦", | |
| 408 | + }, | |
| 409 | + "tags": ["Classic", "辣妹风", "ribbed neckline"], | |
| 410 | + }, | |
| 411 | + } | |
| 412 | + ] | |
| 413 | + ), | |
| 414 | + ) | |
| 415 | + monkeypatch.setattr(builder, "_iter_query_log_rows", lambda **kwargs: iter([])) | |
| 416 | + | |
| 417 | + key_to_candidate = builder._build_full_candidates( | |
| 418 | + tenant_id="162", | |
| 419 | + index_languages=["en", "zh"], | |
| 420 | + primary_language="en", | |
| 421 | + days=365, | |
| 422 | + batch_size=100, | |
| 423 | + min_query_len=1, | |
| 424 | + ) | |
| 425 | + | |
| 426 | + assert ("en", "slim fit") in key_to_candidate | |
| 427 | + assert ("en", "sporty casual") in key_to_candidate | |
| 428 | + assert ("zh", "修身") in key_to_candidate | |
| 429 | + assert ("zh", "显瘦") in key_to_candidate | |
| 430 | + assert ("en", "classic") in key_to_candidate | |
| 431 | + assert key_to_candidate[("en", "classic")].tag_spu_ids == {"900"} | |
| 432 | + assert ("zh", "辣妹风") in key_to_candidate | |
| 433 | + assert key_to_candidate[("zh", "辣妹风")].tag_spu_ids == {"900"} | |
| 434 | + assert ("en", "ribbed neckline") in key_to_candidate | |
| 435 | + | |
| 436 | + | |
| 437 | +@pytest.mark.unit | |
| 391 | 438 | def test_build_full_candidates_splits_long_title_for_suggest(monkeypatch): |
| 392 | 439 | fake_es = FakeESClient() |
| 393 | 440 | builder = SuggestionIndexBuilder(es_client=fake_es, db_engine=None) | ... | ... |