从语义到推荐:大语言模型(LLM)如何驱动智能选车系统?
近年来,随着大语言模型(LLM, Large Language Model)在自然语言理解上的突破,传统推荐系统也迎来了一次全新的“语言驱动”升级。本文将以我开发的“AI 智能选车助手”为例,介绍 LLM 在智能决策系统中的角色,以及我们是如何将“自然语言需求”转化为“结构化推荐逻辑”的。
🎯 用户痛点:选车不想填表格,只想说人话
传统的购车推荐系统往往依赖用户去填写各种选项:
-
用途?
-
座位数?
-
动力类型?
-
预算区间?
这种方式虽然精确,但用户体验不够自然,也缺乏智能性。
然而现在,用户只需要说一句:
“我想买辆适合家庭出行的中型 SUV,预算二十万以内,续航最好超过 800 公里。”
我们就能把这句话转化为精准的推荐结果——这背后,靠的正是大语言模型。
🧠 模型能力:让 LLM 来理解你的购车需求
大语言模型的强大之处在于:
-
能理解非结构化语义;
-
能输出结构化数据;
-
能补全用户未明确提及的信息;
-
能学会领域专业术语(如车辆类型、动力形式等)。
🔢 实现方式:从语义结构到精准评分
我们为每辆车打分的方式如下:
属性维度 | 评分方式 |
---|---|
预算范围 | 区间匹配 + 容差惩罚 |
用途/类型 | 精准匹配打满分 |
动力匹配 | 完全匹配双倍得分,插混与油混兼容打1.5倍 |
能耗/电耗 | 按动力类型区分单位(L/100km vs kWh/100km) |
续航/座位数 | 模糊评分(越接近用户需求得分越高) |
评分函数样例:
def range_score(value, target, tolerance, weight):delta = abs(value - target)return weight * (1 - delta / tolerance) if delta <= tolerance else 0
最终的推荐结果是将得分排序,返回匹配度最高的 N 辆车。
💡 创新点:LLM + 评分引擎的组合价值
这个系统的亮点并不在于推荐算法本身,而在于LLM 所赋予的前端语义接口能力:
传统方法 | LLM 驱动的智能推荐 |
---|---|
用户需逐项填写参数 | 用户只需描述一句话 |
推荐逻辑死板 | 可调整权重灵活打分 |
无法理解模糊语言 | 可理解“长续航”、“适合家用”等词 |
不会补全默认值 | LLM 会自动填默认意图 |
这使得推荐系统从“选项列表”升级为“自然语言交互”系统,用户体验显著提升。
🏗️ 系统架构与技术栈
模块 | 技术 |
---|---|
前端界面 | Streamlit |
模型接口 | Moonshot / OpenAI GPT-4 LLM |
数据处理 | Pandas, JSON 数据库 |
可视化输出 | DataFrame + CSV 导出 |
能耗识别逻辑 | 动态单位判别(L vs kWh) |
编程语言 | Python 3.10+ |
🔄 核心功能实现
1. 自然语言解析模块(query_kimi)
-
接收用户输入的购车意图(自然语言)
-
调用 Moonshot/GPT 接口,输入自定义 System Prompt
-
模型输出标准化 JSON 结构,包含:
-
需求
字段:包含用途、预算、动力、续航等 -
权重
字段:各指标重要性,用于个性化评分
-
示例输出:
{"需求": {"用途": "家庭出行","预算区间": {"min": 0, "max": 200000},"动力类型": "油电混合","能耗上限": 7.0,"续航需求_km": 800},"权重": {"用途": 1,"预算区间": 3,"动力类型": 2,"能耗上限": 2,"续航需求_km": 2}
}
✨ 亮点:即使用户没有明确提及某个参数(如驱动方式),模型也会补全合理的默认值。
2. 车辆数据库与结构化评分模块(score_car)
系统加载一份包含 1000 款汽车的 JSON 数据集,字段包括:
-
名称、价格区间、动力类型、用途、续航、电耗/油耗、驱动方式、座位数等
评分逻辑包含:
维度 | 匹配方式说明 |
---|---|
用途/类型 | 精准匹配得满分 |
动力类型 | 完全匹配加倍得分,油电与插混视为兼容加权 |
价格/预算 | 区间匹配 + 容差评分 |
能耗评分 | 根据动力类型判断单位:L/100km 或 kWh/100km |
续航评分 | 容差 ±200km 内逐步减分 |
函数示例:
def range_score(value, target, tolerance, weight):delta = abs(value - target)return weight * (1 - delta / tolerance) if delta <= tolerance else 0
3. 推荐函数(recommend_car)
-
批量对所有车辆打分
-
按得分降序排列
-
保留预算内且座位数符合的车型
-
返回前 N 个最优推荐
4. Streamlit 图形界面(your_script.py)
-
用户输入自然语言
-
可手动设置权重(8个评分维度)
-
实时展示推荐结果
-
支持 CSV 文件导出
-
自动识别电车/油车并切换能耗单位显示(kWh or L)
🌟 亮点:权重调整后即时影响推荐排序,用户控制力强。
📁 项目结构
project/
├── ai_car_selector_kimi.py # 主推荐逻辑 + LLM接口
├── your_script.py # Streamlit界面逻辑
├── vehicle_db_1000_named.json # 汽车数据库
📥 依赖安装
使用 pip 安装依赖:
pip install -r requirements.txt
requirements.txt 示例内容:
openai
streamlit
pandas
numpy
🚀 运行系统
Step 1:准备 API Key
你需要一个 Moonshot 或 OpenAI 的 API 密钥。如果你使用 Moonshot,可以在界面输入:
sk-xxxxxxxxxxxxxxxxxxxx
也可以设置为环境变量:
export MOONSHOT_API_KEY=sk-xxxxxxx
Step 2:启动界面(Streamlit)
streamlit run your_script.py
然后浏览器会自动打开界面,或访问:
http://localhost:8501
📌 使用方式
-
输入自然语言购车需求,如:
我想买一辆适合城市代步的纯电车,预算15万以内,续航不要低于400公里
-
选择展示推荐的数量(Top-N)
-
(可选)展开“高级设置”自定义评分权重(如:更注重动力类型、续航等)
-
点击“开始智能选车”按钮
-
查看推荐表格,并可导出 CSV 文件
📁 代码说明
ai_car_selector_kimi.py
import os # 用于访问环境变量
import json # 用于解析和生成 JSON 数据
import pandas as pd # 用于处理结构化数据
from openai import OpenAI # 导入 OpenAI 接口客户端MOONSHOT_API_KEY = os.getenv("MOONSHOT_API_KEY", "sk-****************") # 从环境变量获取 API 密钥,或使用默认密钥,这里需要自己修改
client = OpenAI(api_key=MOONSHOT_API_KEY, base_url="https://api.moonshot.cn/v1") # 初始化 OpenAI 客户端,设置 base_url 为 moonshot 接口
MODEL_NAME = "moonshot-v1-8k" # 使用的模型名称
VEHICLE_DB = "vehicle_db_1000_named.json" # 本地车辆数据库文件名SYSTEM_PROMPT = """ # 系统提示词,定义 AI 应该如何将用户意图结构化为标准 JSON
你是一名资深汽车顾问,根据用户的自然语言购车需求,严格生成符合以下格式和要求的 JSON 数据。
【必填字段】以下字段必须提供,若用户未明确指出,请使用指定默认值:
- 用途(可选项:家庭出行、城市代步、商务接待、长途出行、运动旅行、越野探险;默认:"家庭出行")
- 车辆类型(可选项:中型SUV、小型掀背、大型轿车、旅行车、房车、小型两厢、Coupe、MPV、硬派SUV、皮卡;默认:"中型SUV")
- 预算区间(格式:{"min": 数字, "max": 数字},默认:{"min": 0, "max": 200000})
- 座位数(整数,默认:5)
- 动力类型(可选项:油电混合、纯电、3.0T汽油、1.6L汽油、插电混动、2.5L汽油、氢燃料电池;默认:"油电混合")
- 驱动方式(可选项:两驱、前驱、后驱、四驱;默认:"两驱")
- 续航需求_km(单位:公里,默认:800)
- 能耗上限(数字,单位根据动力类型自动判定:燃油车为 L/100km,电动车为 kWh/100km;默认:7.0)
【说明】
- 无需让用户输入单位,系统将自动判断。
- “能耗上限”代表用户希望的最大单位能耗,不论是油耗还是电耗。
【硬性筛选规则】
1. 车辆价格超出预算直接排除。
2. 车辆座位数不足直接排除。
【输出格式】
仅输出以下 JSON 格式,不要添加任何多余内容:
{"需求": {"用途": "...","车辆类型": "...","预算区间": { "min": ..., "max": ... },"座位数": ...,"动力类型": "...","驱动方式": "...","续航需求_km": ...,"能耗上限": ...},"权重": {"用途": 数字,"车辆类型": 数字,"预算区间": 数字,"座位数": 数字,"动力类型": 数字,"驱动方式": 数字,"续航需求_km": 数字,"能耗上限": 数字}
}
请严格遵守字段名、格式和选项要求,确保字段齐全,不可遗漏。
"""def query_kimi(user_text: str) -> dict: # 向 Moonshot 请求,将用户自然语言转换为结构化需求 JSONcompletion = client.chat.completions.create(model=MODEL_NAME,messages=[{"role": "system", "content": SYSTEM_PROMPT},{"role": "user", "content": user_text}],temperature=0.3,)return json.loads(completion.choices[0].message.content) # 解析模型返回内容为 dict 格式def normalize_weights(weights): # 权重归一化,确保总和为 1total = sum(weights.values())return {k: v / total for k, v in weights.items()} if total else weightsdef range_score(value, target, tolerance, weight): # 根据偏差计算得分,允许在一定容差范围内递减delta = abs(value - target)return weight * (1 - delta / tolerance) if delta <= tolerance else 0def score_car(car, spec, w): # 对单辆车进行打分,按需求与权重进行匹配度评估score = 0.0w = normalize_weights(w) # 首先归一化权重try:lo, hi = car["价格区间"].replace("万人民币", "").split("-") # 提取价格区间car_price = (int(lo) + int(hi)) * 5000 # 取中值并转为元单位bmin, bmax = spec["预算区间"]["min"], spec["预算区间"]["max"]if bmin <= car_price <= bmax:score += w["预算区间"] # 价格在预算内,加满分else:score += range_score(car_price, (bmin + bmax) / 2, 100000, w["预算区间"]) # 不在预算范围内但允许容差except: pass # 异常跳过价格评分seat_diff = abs(car["座位数"] - spec["座位数"]) # 计算座位差异score += w["座位数"] * {0:1.0,1:0.75,2:0.5}.get(seat_diff, -0.1) # 根据差异进行加权打分if car["用途"] == spec["用途"]: score += w["用途"] # 用途匹配加分if car["车辆类型"] == spec["车辆类型"]: score += w["车辆类型"] # 类型匹配加分if car["驱动方式"] == spec["驱动方式"]: score += w["驱动方式"] # 驱动方式匹配加分if car["动力类型"] == spec["动力类型"]:score += w["动力类型"] * 2 # 完全匹配动力类型,权重加倍elif (car["动力类型"], spec["动力类型"]) in [("油电混合", "插电混动"), ("插电混动", "油电混合")]:score += w["动力类型"] * 1.5 # 类似动力匹配,加权较高分try:energy_value = float(car["油耗/电耗"].split()[0]) # 提取能耗数值if car["动力类型"] in ["纯电", "氢燃料电池"]:score += range_score(energy_value, spec["能耗上限"], 5, w["能耗上限"]) # 电车能耗评分score += range_score(int(car["续航/续驶里程"].split()[0]), spec["续航需求_km"], 200, w["续航需求_km"]) # 电车续航评分else:score += range_score(energy_value, spec["能耗上限"], 2, w["能耗上限"]) # 油车能耗评分except: pass # 解析失败则跳过能耗评分return round(score, 2) # 最终得分保留两位小数def recommend_car(user_query: str, top_n: int = 3, custom_weights: dict = None, custom_spec: dict = None): # 推荐车辆主函数if custom_spec is None:ai_resp = query_kimi(user_query) # 若无自定义需求,调用 Kimi 获取结构化需求spec = ai_resp["需求"]else:spec = custom_spec # 使用传入的需求规范weights = custom_weights or query_kimi(user_query)["权重"] # 使用自定义或 AI 提供的权重df = pd.read_json(VEHICLE_DB, orient="records") # 读取车辆数据库为 DataFramedf["score"] = df.apply(lambda row: score_car(row, spec, weights), axis=1) # 逐行计算得分return df.sort_values(["score", "价格区间"], ascending=[False, True]).head(top_n)[ # 根据得分和价格排序取前 top_n["id", "名称", "价格区间", "用途", "车辆类型", "动力类型", "驱动方式", "座位数", "续航/续驶里程", "油耗/电耗", "score"]]
your_script.py
import os # 操作系统相关模块,用于设置环境变量等
import streamlit as st # 引入 Streamlit 库,用于构建 Web 应用界面
from ai_car_selector_kimi import recommend_car, query_kimi # 从自定义模块导入推荐函数和 Kimi 查询函数st.set_page_config(page_title="🚘 AI 智能选车助手", layout="centered") # 设置网页标题和布局
st.title("🚗 AI 智能选车助手") # 页面主标题st.markdown("**请输入你的购车需求**(如:我想买一辆适合家庭出行的中型SUV,预算20万以内,最好油电混合,续航超过800公里):") # 输入提示
user_query = st.text_area("购车需求", height=120) # 多行文本输入框用于填写用户需求
top_n = st.slider("展示前 N 个推荐结果", 1, 10, value=3) # 滑动条选择推荐结果数量# 自动判断默认单位(简化逻辑处理)
default_unit = "L/100km" # 默认是油耗单位
if any(x in user_query for x in ["纯电", "电动", "电车", "氢", "氢燃料"]): # 如果用户提到电车/氢能源default_unit = "kWh/100km" # 更换为电耗单位with st.expander("⚖️ 高级设置:自定义评分权重(可选)"): # 可展开的区域用于设置评分权重weight_inputs = {"用途": st.slider("用途 权重", 0, 5, 1), # 各评分项使用滑动条设置权重"车辆类型": st.slider("车辆类型 权重", 0, 5, 1),"预算区间": st.slider("预算区间 权重", 0, 5, 1),"座位数": st.slider("座位数 权重", 0, 5, 1),"动力类型": st.slider("动力类型 权重", 0, 5, 1),"驱动方式": st.slider("驱动方式 权重", 0, 5, 1),"续航需求_km": st.slider("续航需求_km 权重", 0, 5, 1),"能耗上限": st.slider(f"能耗上限(单位:{default_unit}) 权重", 0, 5, 1),}with st.expander("🔐 API Key 设置(当前会话内有效)"): # 可展开区域用于输入自定义 API Keycustom_key = st.text_input("Moonshot API Key", type="password") # 输入框(隐藏密码)if custom_key: # 如果用户提供了 keyos.environ["MOONSHOT_API_KEY"] = custom_key # 设置为当前环境变量,覆盖默认 keyif st.button("开始智能选车 🚀"): # 点击按钮开始推荐流程if not user_query.strip(): # 如果输入为空st.error("请输入购车需求") # 显示错误提示st.stop() # 停止运行with st.spinner("正在匹配推荐车型…"): # 显示加载状态提示try:ai_resp = query_kimi(user_query) # 调用 LLM 接口获取结构化需求与权重spec = ai_resp["需求"] # 提取需求字段if any(val > 0 for val in weight_inputs.values()): # 如果用户自定义了权重weights = weight_inputs # 使用用户定义权重st.info("✅ 使用手动设置的评分权重")else:weights = ai_resp["权重"] # 否则使用 AI 推荐的权重st.info("⚠️ 使用 AI 自动推荐权重")st.subheader("AI 分析出的购车需求:") # 显示需求st.json(spec) # 用 JSON 格式展示需求st.write("**使用的评分权重:**") # 展示权重标题st.json(weights) # 展示权重内容result_df = recommend_car( # 调用推荐函数user_query=user_query,top_n=top_n,custom_weights=weights,custom_spec=spec)except Exception as e: # 捕获异常st.error(f"调用失败:{e}") # 显示错误信息st.stop() # 停止运行if result_df.empty: # 如果结果为空st.warning("未找到符合条件的车型,请调整条件重试。") # 提示用户修改条件else:st.success("✅ 推荐完成,以下是匹配度最高的车型:") # 成功提示st.dataframe(result_df.rename(columns={ # 重命名列标题并显示为表格"id": "车型ID","名称": "名称","价格区间": "价格","用途": "用途","车辆类型": "类型","动力类型": "动力","驱动方式": "驱动","座位数": "座位","续航/续驶里程": "续航","油耗/电耗": "油耗","score": "得分"}), use_container_width=True)st.download_button( # 提供下载按钮"📥 下载推荐结果 CSV", # 按钮标题data=result_df.to_csv(index=False, encoding="utf-8-sig"), # 下载内容为 CSV 格式数据file_name="car_recommendations.csv", # 下载文件名mime="text/csv" # 文件类型)
vehicle_db_1000_named.json(部分如下)
{"id": "car0001","名称": "日产·宝马4系 Pro","用途": "越野探险","车辆类型": "Coupe","价格区间": "56-77万人民币","动力类型": "氢燃料电池","驱动方式": "两驱","座位数": 4,"续航/续驶里程": "651 km","油耗/电耗": "17.1 kWh/100km"},{"id": "car0002","名称": "法拉利·GL8 Max","用途": "商务接待","车辆类型": "MPV","价格区间": "245-257万人民币","动力类型": "油电混合","驱动方式": "前驱","座位数": 7,"续航/续驶里程": "334 km","油耗/电耗": "12.9 L/100km"},{"id": "car0003","名称": "现代·骐达 Max","用途": "商务接待","车辆类型": "小型两厢","价格区间": "28-51万人民币","动力类型": "2.5L汽油","驱动方式": "后驱","座位数": 5,"续航/续驶里程": "311 km","油耗/电耗": "7.8 L/100km"}