Commit 00c8ddb9b138aaaf9e2e9d40bd555ead00226efb

Authored by tangwang
1 parent e8443ea0

suggest rank optimize

b.sh 0 → 100644
... ... @@ -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 +
... ...
bb.sh 0 → 100644
... ... @@ -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  
... ...
scripts/benchmark_reranker_random_titles.py 0 → 100755
... ... @@ -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]) -&gt; 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)
... ...