基于 Ollama 的商品描述生成服务:从产品属性到营销文案的 Prompt 工程化

发布时间:2026/7/25 1:37:25

基于 Ollama 的商品描述生成服务:从产品属性到营销文案的 Prompt 工程化 基于 Ollama 的商品描述生成服务从产品属性到营销文案的 Prompt 工程化一、商品描述生成的工程痛点电商平台每天上架数千个 SKU运营团队需要为每个商品撰写描述。手工编写效率低、风格不一致导致搜索结果相关性差。AI 生成商品描述是明确的提效场景——但直接将产品属性拼接后丢给 LLM生成的文案往往缺乏营销感或者在关键参数上出现事实性错误幻觉。工程化的核心在于 Prompt 的结构化设计。不是写一个商品描述而是将产品属性以结构化格式输入约束输出格式注入品牌语气参数。这类似于编译器的前端——将非结构化的需求翻译为 LLM 可精确理解的指令。Ollama 作为本地推理引擎其稳定性优于公有 API无网络延迟波动、无 API 配额限制适合批量生成场景。但默认 Prompt 模板针对对话优化商品描述需要定制化的 System Prompt 和 Few-Shot 示例。二、Prompt 工程化的技术框架Prompt 的工程化设计包含四个组件System Prompt定义模型的角色行为——你是电商平台的商品文案专家。你的任务是生成精确、有营销力、无幻觉的商品描述。结构化输入将产品属性以 JSON 或 YAML 格式注入——{name: ..., material: ..., features: [...], target_audience: ...}。结构化比自然语言拼接更易被模型精确解析。Few-Shot 示例提供 2~3 个高质量的输入-输出对锚定模型的输出风格。示例覆盖不同的营销语气专业型、情感型、功能型让模型学会根据产品类型自适应调整。输出约束在 Prompt 末尾明确格式要求以 3 个短句描述产品每句不超过 20 字。禁止使用敏感词。如果是服装必须包含材质和适用场景。三、Rust 实现的 Prompt 构建引擎use serde::{Deserialize, Serialize}; use std::collections::HashMap; use anyhow::Result; /// 产品属性——结构化输入 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProductAttributes { pub name: String, pub category: String, pub brand: OptionString, pub material: OptionString, pub features: VecString, pub target_audience: OptionString, pub price_range: OptionString, } /// 营销语气枚举 #[derive(Debug, Clone, Copy)] pub enum MarketingTone { /// 专业描述——适合 3C 电子产品 Professional, /// 情感共鸣——适合礼品、家居 Emotional, /// 功能导向——适合工具、厨具 Functional, } /// Prompt 模板管理器 /// 设计原因按品类分类模板使模型在特定领域内 /// 生成质量更高——领域特化的 System Prompt 优于通用 Prompt pub struct PromptTemplateEngine { /// 品类 → System Prompt system_prompts: HashMapString, String, /// 品类 → Few-Shot 示例 few_shot_examples: HashMapString, Vec(String, String), /// 输出约束模板 constraints: VecString, } impl PromptTemplateEngine { pub fn new() - Self { let mut system_prompts HashMap::new(); // 服装品类的特化 System Prompt system_prompts.insert(服装.to_string(), 你是专业服装文案。你的描述应包含材质触感、版型特点、适用场景、搭配建议。\ 禁止编造材质——仅使用给定的产品属性。风格简洁每句不超过25字。.to_string() ); system_prompts.insert(电子产品.to_string(), 你是专业3C产品文案。你的描述应包含核心技术参数、差异化卖点、适用人群。\ 参数必须精确——仅使用给定的产品属性。避免营销夸张词。.to_string() ); let mut few_shot HashMap::new(); few_shot.insert(服装.to_string(), vec![ ( // 输入示例 r#{name:经典羊绒衫,material:100%羊绒,features:[亲肤,保暖,不起球]}#.to_string(), // 输出示例 100%山羊绒织造轻柔如云。圆领设计修饰脸型适合日常通勤与周末休闲。冷水手洗养护。.to_string(), ), ]); let constraints vec![ 每条描述3-5句总字数50-120字.to_string(), 必须包含至少一个产品特征.to_string(), 禁止使用的词顶级、完美、绝对.to_string(), 如果缺少某项属性跳过——不要编造.to_string(), ]; Self { system_prompts, few_shot_examples: few_shot, constraints, } } /// 构建完整的 Prompt /// 设计原因System Prompt Few-Shot 结构化输入 约束 /// 四段式结构使模型在有限的 Context Window 内获得最大信息密度 pub fn build_prompt( self, product: ProductAttributes, tone: MarketingTone, ) - ResultString { let sys_prompt self.system_prompts .get(product.category) .cloned() .unwrap_or_else(|| { 你是电商商品文案专家。基于产品属性生成精确、有吸引力的描述。.to_string() }); let mut prompt format!(|system|\n{}\n, sys_prompt); // 注入 Few-Shot 示例 if let Some(examples) self.few_shot_examples.get(product.category) { for (input, output) in examples { prompt.push_str(format!( \n|user|\n商品属性{}\n|assistant|\n{}\n, input, output )); } } // 注入当前产品属性结构化 JSON let attributes_json serde_json::to_string_pretty(product)?; prompt.push_str(format!(\n|user|\n商品属性\n{}\n, attributes_json)); // 注入语气参数 let tone_instruction match tone { MarketingTone::Professional 使用专业、客观的语气。, MarketingTone::Emotional 使用温暖、感性的语气唤起情感共鸣。, MarketingTone::Functional 使用务实、功能的语气强调实际用途。, }; prompt.push_str(format!(\n语气要求{}\n, tone_instruction)); // 注入输出约束 prompt.push_str(\n输出约束\n); for (i, constraint) in self.constraints.iter().enumerate() { prompt.push_str(format!({}. {}\n, i 1, constraint)); } prompt.push_str(\n|assistant|\n); Ok(prompt) } /// 路由选择——根据产品属性自动选择合适的语气 pub fn auto_select_tone(self, product: ProductAttributes) - MarketingTone { match product.category.as_str() { 电子产品 | 家电 MarketingTone::Professional, 礼品 | 家居 | 饰品 MarketingTone::Emotional, 工具 | 厨具 | 运动户外 MarketingTone::Functional, _ MarketingTone::Professional, } } } /// Ollama 推理服务封装 pub struct OllamaDescriptionService { base_url: String, model: String, template_engine: PromptTemplateEngine, } impl OllamaDescriptionService { pub fn new(base_url: str, model: str) - Self { Self { base_url: base_url.to_string(), model: model.to_string(), template_engine: PromptTemplateEngine::new(), } } /// 生成商品描述 pub async fn generate_description( self, product: ProductAttributes, ) - ResultString { let tone self.template_engine.auto_select_tone(product); let prompt self.template_engine.build_prompt(product, tone)?; let client reqwest::Client::new(); let response client .post(format!({}/api/generate, self.base_url)) .json(serde_json::json!({ model: self.model, prompt: prompt, stream: false, options: { temperature: 0.7, // 商品描述需要一定创造性 top_p: 0.9, num_predict: 256, // 限制生成长度 }, })) .send() .await?; let body: serde_json::Value response.json().await?; let description body[response] .as_str() .unwrap_or() .trim() .to_string(); Ok(description) } }Prompt 构建引擎的四段式结构——System Prompt → Few-Shot → 结构化输入 → 约束——是经验验证的高效模式。每段的信息密度逐步聚焦角色定义 → 风格范例 → 具体数据 → 格式规范。温度参数temperature: 0.7是商品描述的平衡点——过低导致所有描述趋同过高产生幻觉。num_predict: 256限制输出 Token 数——商品描述不应超过 200 Token约 150 中文字。四、方案边界与适用场景分析适用场景电商平台的批量商品上架描述生成SKU 类型集中如服装系列Few-Shot 示例复用价值高本地部署 Ollama 的批量生成场景——避免公有 API 的高延迟抖动。不适用场景描述风格极度多样化的平台如艺术品——Few-Shot 示例覆盖率低需要实时上下文感知的交互式生成——用户修改后立即刷新而非批量生成Token 预算极度紧张 512的场景——Prompt 模板本身占用 200~300 Token。Trade-offsPrompt 工程化需要为每个品类维护模板和示例——维护成本随品类增长。Few-Shot 示例的质量直接影响输出——需要领域专家参与撰写。输出校验模块的召回率影响整体通过率——过于严格导致大量重试过于宽松导致低质描述上线。五、总结Prompt 的结构化设计角色 范例 输入 约束是 LLM 文本生成质量的工程保证按品类路由的特化 System Prompt 优于通用 Prompt——领域聚焦提升生成精度Few-Shot 示例锚定输出风格——每个品类 2~3 个示例即可显著提升一致性输出约束的明确表达格式、长度、禁用词减少后处理的人工校验成本Ollama 本地推理的延迟稳定性优于公有 API适合批量生成长时任务

相关新闻