从语义到推荐:大语言模型(LLM)如何驱动智能选车系统?

发布于:2025-06-22 ⋅ 阅读:(23) ⋅ 点赞:(0)

近年来,随着大语言模型(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

📌 使用方式

  1. 输入自然语言购车需求,如:

    我想买一辆适合城市代步的纯电车,预算15万以内,续航不要低于400公里
  2. 选择展示推荐的数量(Top-N)

  3. (可选)展开“高级设置”自定义评分权重(如:更注重动力类型、续航等)

  4. 点击“开始智能选车”按钮

  5. 查看推荐表格,并可导出 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 请求,将用户自然语言转换为结构化需求 JSON
    completion = 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):  # 权重归一化,确保总和为 1
    total = sum(weights.values())
    return {k: v / total for k, v in weights.items()} if total else weights

def range_score(value, target, tolerance, weight):  # 根据偏差计算得分,允许在一定容差范围内递减
    delta = abs(value - target)
    return weight * (1 - delta / tolerance) if delta <= tolerance else 0

def score_car(car, spec, w):  # 对单辆车进行打分,按需求与权重进行匹配度评估
    score = 0.0
    w = 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")  # 读取车辆数据库为 DataFrame
    df["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 Key
    custom_key = st.text_input("Moonshot API Key", type="password")  # 输入框(隐藏密码)
    if custom_key:  # 如果用户提供了 key
        os.environ["MOONSHOT_API_KEY"] = custom_key  # 设置为当前环境变量,覆盖默认 key

if 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"
  }