李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 每日心情
  • Now
  • 系列文章
  • 论坛
  • 左邻右舍
    • 左邻右舍
    • 博友圈
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于我
    • 网站地图
    • 网站统计
    • 另一个网站
    • 我的导航站
    • 赞助
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. AI
  3. 正文

深度实战:基于 LangGraph + MCP 的智能音乐推荐 Agent 全解析(从搭建到落地)

2025年11月11日 约 5,161 字18 分钟 54 1 0
本文最后更新于 2025年11月11日,距今已 280 天,其中的信息可能已经发生变化,请注意甄别。

在 AI 与推荐系统结合的浪潮中,传统音乐推荐往往局限于“基于协同过滤”或“关键词匹配”,无法理解用户自然语言中的模糊需求(如“心情低落想听治愈系民谣”“运动时需要燃脂电子乐”)。而基于 LangGraph + MCP 构建的智能音乐推荐 Agent,恰好解决了这一痛点——它能听懂自然语言意图,通过标准化工具调用实现个性化推荐,还能输出可解释的推荐理由。

一、项目核心价值与技术选型深度解析

1. 项目定位与核心优势

  • 自然语言意图理解:支持多维度需求(心情、场景、流派、相似歌曲、艺术家),无需用户手动筛选;
  • 可解释推荐:每首歌附带推荐理由(如“节奏明快适合运动”“旋律治愈匹配低落心情”),提升用户信任度;
  • 轻量化可扩展:基于 Streamlit 快速搭建 UI,支持本地数据与外部 API 切换,MCP 协议适配多工具;
  • 技术栈灵活:LangGraph 编排工作流,支持替换 LLM(DeepSeek、Qwen、GPT 等),适配不同预算与场景。

2. 技术选型深层原因

技术组件 核心作用 选型深层原因
LangGraph 工作流编排与状态管理 支持复杂分支逻辑(多意图路由)、状态持久化,适合 Agent 任务拆解
LangChain LLM 工具链与提示词管理 封装 LLM 调用、工具适配,降低与 MCP/外部 API 的集成成本
硅基流动(SiliconFlow) LLM 服务提供 兼容 OpenAI API 格式,支持多模型切换,无需修改业务代码
Streamlit 轻量 Web UI 开发 快速迭代原型,支持组件化布局,无需前端框架(Vue/React)经验
MCP 工具调用标准化 统一对接音乐平台 API、本地工具,降低跨平台适配成本
Pydantic 数据校验与模型定义 确保 LLM 输出格式规范,避免推荐结果解析失败

3. 适用场景扩展

  • 个人使用:本地部署打造专属音乐助手,支持自定义音乐库;
  • 创业原型:快速验证 AI 推荐产品可行性,对接 Spotify/网易云 API 即可商业化;
  • 企业内部工具:适配办公场景(如“会议室背景音乐推荐”“员工健身音乐合集”);
  • 教学/研究:学习 LangGraph 工作流编排、MCP 协议应用、意图识别技术。

二、快速上手:5 分钟跑通 + 常见问题速解

1. 环境准备

  • 操作系统:Windows 10+/macOS 12+/Linux(Ubuntu 20.04+);
  • Python 版本:3.9-3.11(推荐 3.10,避免版本兼容问题);
  • 核心依赖版本锁定(避免依赖冲突):
    langgraph==0.1.14
    langchain==0.1.10
    streamlit==1.32.2
    openai==1.13.3
    pydantic==2.6.1
    aiohttp==3.9.3
    python-dotenv==1.0.1

2. 完整部署步骤

步骤 1:克隆项目与安装依赖

# 克隆仓库(替换为实际仓库地址)
git clone https://github.com/imagist13/Muisc-Research.git
cd Muisc-Research

# 创建虚拟环境(推荐,避免依赖污染)
python -m venv venv
# 激活虚拟环境(Windows)
venv\Scripts\activate
# 激活虚拟环境(macOS/Linux)
source venv/bin/activate

# 安装依赖(指定版本避免冲突)
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

步骤 2:配置文件详解(setting.json 全字段说明)

{
  "SILICONFLOW_API_KEY": "你的硅基流动 API Key", // 从硅基流动控制台获取
  "SILICONFLOW_BASE_URL": "https://api.siliconflow.cn/v1", // 固定地址
  "SILICONFLOW_CHAT_MODEL": "Qwen/Qwen2.5-72B-Instruct", // 可选:DeepSeek/DeepSeek-MoE-16B-Instruct
  "SILICONFLOW_TEMPERATURE": 0.3, // 推荐 0.2-0.5,值越低推荐越稳定
  "TAILYAPI_API_KEY": "你的 Tavily 搜索 API Key", // 用于补充实时音乐数据(可选)
  "TAILYAPI_BASE_URL": "https://api.tavily.com",
  "APP_NAME": "智能音乐推荐助手", // 自定义应用名称
  "SPOTIFY_CLIENT_ID": "你的 Spotify Client ID", // 对接 Spotify 时必填
  "SPOTIFY_CLIENT_SECRET": "你的 Spotify Client Secret",
  "LOCAL_MUSIC_DB_PATH": "data/music_database.json", // 本地音乐数据库路径
  "CACHE_EXPIRE_SECONDS": 3600, // 推荐结果缓存时间(1小时)
  "MAX_RECOMMEND_NUM": 10, // 单次最大推荐歌曲数
  "INTENT_RECOGNITION_PROMPT_PATH": "prompts/intent_recognition.txt" // 意图识别提示词路径
}

步骤 3:启动应用与验证

# 启动 Streamlit 应用
python run_music_app.py
# 或直接启动(跳过检查脚本)
streamlit run music_app.py --server.port 8501
  • 访问地址:http://localhost:8501
  • 验证场景:
    1. 输入“心情很好,推荐开心的流行乐”,查看推荐结果与理由;
    2. 点击侧边栏“运动”快捷按钮,验证场景化推荐;
    3. 输入“类似《晴天》的歌曲”,验证相似歌曲推荐。

3. 快速上手常见问题速解

问题现象 解决方案
启动报错“缺少 SILICONFLOW_API_KEY” 1. 硅基流动官网注册获取 API Key;2. 填入 setting.json
Streamlit 启动后无法访问 检查端口是否被占用,更换端口:--server.port 8502
推荐结果为空 1. 检查本地音乐数据库(data/music_database.json)是否有数据;2. 确保 LLM 模型配置正确
意图识别错误(如“运动”识别为“聊天”) 优化 prompts/intent_recognition.txt 中的提示词,增加意图示例

三、系统架构深度解析:模块交互与数据流转

1. 整体架构图(补充交互关系)

用户 → Streamlit UI(输入/展示) → MusicAgent 主入口 → LangGraph 工作流
                                      ↓
                          ┌─────────┬─────────┬─────────┬─────────┐
                          │  LLM 层  │ 工具层  │ 配置层  │ 数据层  │
                          │(意图识别)│(MCP/API)│(参数管理)│(本地/外部)│
                          └─────────┴─────────┴─────────┴─────────┘
                                      ↓
                          MCP Server → 外部工具(Spotify API/音乐搜索)

2. 核心模块职责与交互细节

(1)配置层(config/):统一参数管理

  • 核心文件:settings_loader.py
  • 核心逻辑:
    1. 优先级:setting.json > 环境变量 > 默认值;
    2. 支持动态加载:修改配置后无需重启应用,下次请求自动生效;
    3. 关键功能:参数校验(如 API Key 格式、模型名称合法性)。
  • 代码解析:

    def load_settings():
      # 加载 JSON 配置
      with open("setting.json", "r", encoding="utf-8") as f:
          json_config = json.load(f)
      # 合并环境变量(覆盖 JSON 配置)
      env_config = {
          key: os.getenv(key) for key in json_config if os.getenv(key) is not None
      }
      final_config = {**json_config, **env_config}
      # 参数校验
      validate_settings(final_config)
      return final_config

(2)LLM 层(llms/):模型抽象与适配

  • 核心文件:siliconflow_llm.py
  • 设计思路:
    1. 兼容 OpenAI API 格式,替换模型时无需修改业务代码;
    2. 封装重试逻辑(API 超时、限流时自动重试);
    3. 支持模型调优参数(temperature、max_tokens、top_p)。
  • 关键代码:

    class SiliconFlowLLM:
      def __init__(self, api_key, base_url, model_name, temperature=0.3):
          self.client = OpenAI(
              api_key=api_key,
              base_url=base_url
          )
          self.model_name = model_name
          self.temperature = temperature
    
      def chat_completion(self, messages):
          # 重试逻辑(最多 3 次)
          for _ in range(3):
              try:
                  response = self.client.chat.completions.create(
                      model=self.model_name,
                      messages=messages,
                      temperature=self.temperature,
                      response_format={"type": "json_object"}  # 强制 JSON 输出
                  )
                  return json.loads(response.choices[0].message.content)
              except Exception as e:
                  log.error(f"LLM 调用失败:{e}")
                  time.sleep(1)
          raise Exception("LLM 调用多次失败,请检查 API Key 或网络")

(3)工作流层(graphs/):LangGraph 状态与路由

  • 核心概念:
    • 状态(State):存储用户输入、意图类型、推荐结果、错误信息;
    • 节点(Node):封装具体逻辑(意图识别、搜索、推荐、结果生成);
    • 路由(Route):根据意图类型分发到对应节点。
  • 状态定义代码:

    from langgraph.graph import StateGraph, MessagesState
    from pydantic import BaseModel
    
    # 自定义状态(扩展 MessagesState)
    class MusicAgentState(MessagesState):
      intent: str = None  # 意图类型(recommend_by_mood/activity/genre 等)
      recommendations: list = []  # 推荐结果列表
      error: str = None  # 错误信息
  • 核心节点实现:

    def analyze_intent(state: MusicAgentState) -> MusicAgentState:
      """意图识别节点:从用户输入中提取意图"""
      user_input = state.messages[-1].content
      # 调用 LLM 识别意图
      llm = SiliconFlowLLM(**settings)
      prompt = load_prompt("prompts/intent_recognition.txt")
      messages = [
          {"role": "system", "content": prompt},
          {"role": "user", "content": user_input}
      ]
      result = llm.chat_completion(messages)
      state.intent = result["intent"]
      state.error = result.get("error")
      return state
    
    def route_by_intent(state: MusicAgentState) -> str:
      """路由节点:根据意图分发到对应处理节点"""
      if state.error:
          return "handle_error"
      match state.intent:
          case "recommend_by_mood":
              return "recommend_by_mood"
          case "recommend_by_activity":
              return "recommend_by_activity"
          case "search_music":
              return "search_music"
          case "general_chat":
              return "general_chat"
          case _:
              return "handle_unknown_intent"
  • 工作流构建:

    def build_music_graph() -> StateGraph:
      graph = StateGraph(MusicAgentState)
      # 添加节点
      graph.add_node("analyze_intent", analyze_intent)
      graph.add_node("recommend_by_mood", recommend_by_mood)
      graph.add_node("recommend_by_activity", recommend_by_activity)
      graph.add_node("search_music", search_music)
      graph.add_node("generate_result", generate_result)
      graph.add_node("handle_error", handle_error)
      # 定义边(路由逻辑)
      graph.set_entry_point("analyze_intent")
      graph.add_conditional_edges("analyze_intent", route_by_intent)
      # 所有处理节点最终指向结果生成
      graph.add_edge("recommend_by_mood", "generate_result")
      graph.add_edge("recommend_by_activity", "generate_result")
      graph.add_edge("search_music", "generate_result")
      graph.add_edge("general_chat", "generate_result")
      graph.add_edge("handle_error", "generate_result")
      graph.add_edge("handle_unknown_intent", "generate_result")
      return graph.compile()

(4)工具层(tools/):MCP 适配与外部调用

工具层是连接 Agent 与外部服务的核心:

  • 工具分类:
    • 本地工具:本地音乐搜索、相似歌曲匹配(基于 TF-IDF 或余弦相似度);
    • MCP 工具:对接外部音乐 API(Spotify、网易云)、音乐评分查询。
  • MCP Server 开发(音乐搜索工具示例):

    # mcp/music_search_server.py
    from mcp.server import Server, Tool
    from mcp.types import InputSchema, OutputSchema
    from pydantic import BaseModel
    
    # 输入/输出模型
    class MusicSearchInput(BaseModel):
      keyword: str  # 搜索关键词(歌曲名/艺术家)
      genre: str = None  # 流派筛选(可选)
    
    class MusicSearchOutput(BaseModel):
      songs: list[dict]  # 搜索结果:[{"title": "", "artist": "", "album": ""}]
    
    # 工具实现
    def search_music(input_data: MusicSearchInput) -> MusicSearchOutput:
      # 对接本地数据库或外部 API
      with open(settings["LOCAL_MUSIC_DB_PATH"], "r", encoding="utf-8") as f:
          music_db = json.load(f)
      # 筛选逻辑
      results = [
          song for song in music_db
          if input_data.keyword.lower() in song["title"].lower() 
          and (input_data.genre is None or song["genre"] == input_data.genre)
      ]
      return MusicSearchOutput(songs=results[:10])
    
    # 注册 MCP Server
    server = Server()
    search_tool = Tool(
      name="music_search",
      description="根据关键词和流派搜索音乐",
      input_schema=InputSchema(model=MusicSearchInput),
      output_schema=OutputSchema(model=MusicSearchOutput),
      handler=search_music
    )
    server.register_tool(search_tool)
    
    # 启动 MCP Server
    if __name__ == "__main__":
      import uvicorn
      uvicorn.run(server.fastapi_app, host="0.0.0.0", port=8000)
  • Agent 调用 MCP 工具代码:

    def search_music(state: MusicAgentState) -> MusicAgentState:
      """搜索节点:调用 MCP 工具搜索音乐"""
      user_input = state.messages[-1].content
      # 提取搜索关键词(通过 LLM 解析)
      llm = SiliconFlowLLM(**settings)
      prompt = "提取用户输入中的音乐搜索关键词和流派(如无则为 None),返回 JSON:{\"keyword\": \"\", \"genre\": \"\"}"
      messages = [{"role": "user", "content": f"{prompt}\n用户输入:{user_input}"}]
      search_params = llm.chat_completion(messages)
      # 调用 MCP 工具
      mcp_client = Client(base_url="http://localhost:8000")
      try:
          response = mcp_client.call_tool("music_search", search_params)
          state.recommendations = response.content[0].text["songs"]
      except Exception as e:
          state.error = f"搜索失败:{str(e)}"
      return state

(5)前端层(music_app.py):交互体验优化

  • 核心组件:
    1. 侧边栏:快捷按钮(心情/场景/流派)、配置状态显示;
    2. 主区域:用户输入框、推荐结果卡片(含歌曲信息、推荐理由)、历史记录;
    3. 结果卡片:使用 Streamlit 的 card 组件,支持点击播放链接。
  • 关键代码:

    def render_recommendation_cards(recommendations):
      """渲染推荐结果卡片"""
      for idx, song in enumerate(recommendations, 1):
          with st.card(f"推荐歌曲 #{idx}"):
              col1, col2 = st.columns([1, 2])
              with col1:
                  st.image(song.get("cover_url", "default_cover.png"), use_column_width=True)
              with col2:
                  st.subheader(song["title"])
                  st.text(f"艺术家:{song['artist']}")
                  st.text(f"流派:{song['genre']}")
                  st.text(f"推荐理由:{song['reason']}")
                  # 播放链接(对接 Spotify 时生效)
                  if song.get("spotify_url"):
                      st.link_button("Spotify 播放", song["spotify_url"])

四、核心流程:从意图识别到推荐结果的全链路拆解

1. 核心时序图(用户输入“运动时需要燃脂电子乐”)

用户 → 输入需求 → Streamlit UI → 提交请求 → MusicAgent
                          ↓
MusicAgent → 调用 LangGraph 工作流 → 分析意图节点(analyze_intent)
                          ↓
LLM → 识别意图为“recommend_by_activity” → 路由节点(route_by_intent)
                          ↓
推荐节点(recommend_by_activity) → 调用 MCP 工具搜索“燃脂电子乐”
                          ↓
外部 API/本地数据库 → 返回符合条件的歌曲列表 → 结果生成节点(generate_result)
                          ↓
LLM → 为每首歌生成推荐理由 → Streamlit UI → 渲染结果卡片

2. 关键环节详解

(1)意图识别:提示词优化技巧

意图识别的准确性直接影响推荐效果,推荐使用“示例引导+格式约束”的提示词:

# prompts/intent_recognition.txt
你是音乐推荐 Agent 的意图识别助手,需从用户输入中提取意图类型,返回 JSON 格式(必须包含 intent 字段)。
支持的意图类型:
1. recommend_by_mood:用户提到心情(如开心、悲伤、治愈);
2. recommend_by_activity:用户提到场景/活动(如运动、工作、睡觉);
3. recommend_by_genre:用户提到音乐流派(如民谣、电子乐、摇滚);
4. recommend_by_artist:用户提到艺术家/歌手(如周杰伦、Taylor Swift);
5. recommend_by_similar:用户提到“类似/像XX歌曲”;
6. search_music:用户明确搜索歌曲/专辑;
7. general_chat:不涉及音乐推荐的闲聊。

示例:
用户输入:“心情低落想听治愈系民谣” → {"intent": "recommend_by_mood", "mood": "治愈"}
用户输入:“运动时需要燃脂的电子乐” → {"intent": "recommend_by_activity", "activity": "运动", "genre": "电子乐"}
用户输入:“类似《晴天》的歌曲” → {"intent": "recommend_by_similar", "song": "晴天"}

注意:
- 若无法识别意图,intent 设为 "unknown";
- 额外提取相关参数(如 mood、activity、genre),无则留空;
- 严格返回 JSON,不添加额外描述。

(2)推荐逻辑:结合用户需求与音乐特征

推荐节点的核心是“筛选符合条件的歌曲 + 个性化排序”,示例代码:

def recommend_by_activity(state: MusicAgentState) -> MusicAgentState:
    """根据活动场景推荐音乐"""
    # 提取活动类型和额外参数
    user_input = state.messages[-1].content
    llm = SiliconFlowLLM(**settings)
    prompt = "提取用户输入中的活动类型和音乐偏好(如流派、节奏),返回 JSON:{\"activity\": \"\", \"genre\": \"\", \"tempo\": \"\"}"
    params = llm.chat_completion([{"role": "user", "content": f"{prompt}\n{user_input}"}])
    activity = params["activity"]
    genre = params.get("genre")
    tempo = params.get("tempo", "fast")  # 默认快节奏(运动场景)

    # 从本地数据库/外部 API 筛选歌曲
    with open(settings["LOCAL_MUSIC_DB_PATH"], "r", encoding="utf-8") as f:
        music_db = json.load(f)
    # 筛选逻辑:活动匹配 + 流派匹配 + 节奏匹配
    filtered_songs = [
        song for song in music_db
        if song["activity"] == activity 
        and (genre is None or song["genre"] == genre)
        and song["tempo"] == tempo
    ]
    # 排序:按流行度降序
    filtered_songs.sort(key=lambda x: x["popularity"], reverse=True)
    state.recommendations = filtered_songs[:settings["MAX_RECOMMEND_NUM"]]
    return state

(3)结果生成:可解释性优化

推荐结果需包含“歌曲信息 + 推荐理由”,让用户理解“为什么推荐这首歌”:

def generate_result(state: MusicAgentState) -> MusicAgentState:
    """生成可解释的推荐结果"""
    if state.error:
        result = f"推荐失败:{state.error}"
    elif not state.recommendations:
        result = "未找到符合条件的歌曲,可尝试调整需求描述~"
    else:
        # 生成推荐理由
        llm = SiliconFlowLLM(**settings)
        prompt = load_prompt("prompts/generate_result.txt")
        songs_str = json.dumps([{"title": s["title"], "artist": s["artist"], "genre": s["genre"]} for s in state.recommendations])
        messages = [
            {"role": "system", "content": prompt},
            {"role": "user", "content": f"用户需求:{state.messages[-1].content}\n推荐歌曲:{songs_str}"}
        ]
        result = llm.chat_completion(messages)
        # 合并歌曲信息与推荐理由
        for song, reason in zip(state.recommendations, result["reasons"]):
            song["reason"] = reason

    # 添加到状态,用于前端展示
    state.messages.append({"role": "assistant", "content": json.dumps(state.recommendations)})
    return state

五、进阶实战:MCP + Spotify API 对接全流程

1. Spotify 开发者账号准备

步骤 1:创建应用

  1. 访问 Spotify for Developers,注册账号并创建应用;
  2. 记录 Client ID 和 Client Secret(填入 setting.json);
  3. 在“Redirect URIs”中添加 http://localhost:8501/callback(用于授权)。

步骤 2:获取访问令牌(Authorization Code Flow)

Spotify API 需授权才能调用,核心流程:

  1. 前端引导用户授权,获取 Authorization Code;
  2. 后端用 Code 兑换 Access Token 和 Refresh Token;
  3. 用 Access Token 调用 API,过期后用 Refresh Token 刷新。

2. MCP Server 对接 Spotify API

步骤 1:开发 Spotify 音乐搜索 MCP Server

# mcp/spotify_search_server.py
from mcp.server import Server, Tool
from mcp.types import InputSchema, OutputSchema
from pydantic import BaseModel
import spotipy
from spotipy.oauth2 import SpotifyOAuth

# 输入/输出模型
class SpotifySearchInput(BaseModel):
    keyword: str
    genre: str = None
    limit: int = 10

class SpotifySearchOutput(BaseModel):
    songs: list[dict] = []

# Spotify 客户端初始化
def init_spotify_client():
    return spotipy.Spotify(
        auth_manager=SpotifyOAuth(
            client_id=settings["SPOTIFY_CLIENT_ID"],
            client_secret=settings["SPOTIFY_CLIENT_SECRET"],
            redirect_uri="http://localhost:8501/callback",
            scope="user-read-private user-read-email playlist-modify-private"
        )
    )

# 工具实现
def spotify_search(input_data: SpotifySearchInput) -> SpotifySearchOutput:
    try:
        sp = init_spotify_client()
        # 搜索歌曲
        query = f"{input_data.keyword} {'genre:' + input_data.genre if input_data.genre else ''}"
        results = sp.search(q=query, type="track", limit=input_data.limit)
        # 解析结果
        songs = []
        for track in results["tracks"]["items"]:
            songs.append({
                "title": track["name"],
                "artist": ", ".join([a["name"] for a in track["artists"]]),
                "album": track["album"]["name"],
                "cover_url": track["album"]["images"][0]["url"],
                "spotify_url": track["external_urls"]["spotify"],
                "popularity": track["popularity"],
                "tempo": "fast" if track["tempo"] > 120 else "slow"
            })
        return SpotifySearchOutput(songs=songs)
    except Exception as e:
        log.error(f"Spotify 搜索失败:{e}")
        raise Exception(f"Spotify API 调用失败:{str(e)}")

# 注册工具
server = Server()
spotify_tool = Tool(
    name="spotify_search",
    description="通过 Spotify API 搜索音乐,支持关键词和流派筛选",
    input_schema=InputSchema(model=SpotifySearchInput),
    output_schema=OutputSchema(model=SpotifySearchOutput),
    handler=spotify_search
)
server.register_tool(spotify_tool)

# 启动 MCP Server
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(server.fastapi_app, host="0.0.0.0", port=8001)

步骤 2:Agent 调用 Spotify MCP 工具

修改 tools/music_tools.py,添加 MCP 客户端调用:

def search_music(state: MusicAgentState) -> MusicAgentState:
    """调用 Spotify MCP 工具搜索音乐"""
    # 解析搜索参数
    user_input = state.messages[-1].content
    llm = SiliconFlowLLM(**settings)
    params = llm.chat_completion([{"role": "user", "content": f"提取搜索关键词和流派:{user_input}"}])
    # 调用 MCP Server
    mcp_client = Client(base_url="http://localhost:8001")
    try:
        response = mcp_client.call_tool("spotify_search", {
            "keyword": params["keyword"],
            "genre": params.get("genre"),
            "limit": settings["MAX_RECOMMEND_NUM"]
        })
        state.recommendations = response.content[0].text["songs"]
    except Exception as e:
        state.error = f"搜索失败:{str(e)}"
    return state

步骤 3:前端添加 Spotify 播放功能

修改 music_app.py,支持点击播放和歌单创建:

def render_spotify_features(song):
    """渲染 Spotify 专属功能"""
    col1, col2 = st.columns(2)
    with col1:
        st.link_button("立即播放", song["spotify_url"], type="primary")
    with col2:
        if st.button("添加到歌单"):
            # 调用 Spotify API 创建歌单
            try:
                sp = init_spotify_client()
                playlist = sp.user_playlist_create(
                    user=sp.current_user()["id"],
                    name="AI 推荐歌单",
                    public=False
                )
                sp.playlist_add_items(playlist["id"], [song["spotify_url"].split("/")[-1]])
                st.success("已添加到歌单!")
            except Exception as e:
                st.error(f"添加失败:{str(e)}")

六、性能优化与生产级部署

1. 性能优化技巧

(1)缓存策略:减少重复调用

对相同意图和参数的请求,缓存推荐结果(使用 Redis 或本地缓存):

def add_cache_middleware(func):
    """缓存装饰器"""
    cache = {}
    @wraps(func)
    def wrapper(state: MusicAgentState):
        # 生成缓存键(用户输入 + 意图)
        cache_key = f"{state.messages[-1].content}_{state.intent}"
        if cache_key in cache and time.time() - cache[cache_key]["timestamp"] < settings["CACHE_EXPIRE_SECONDS"]:
            state.recommendations = cache[cache_key]["data"]
            return state
        # 无缓存则执行原函数
        result = func(state)
        # 存入缓存
        cache[cache_key] = {
            "data": state.recommendations,
            "timestamp": time.time()
        }
        return result
    return wrapper

# 为推荐节点添加缓存
@add_cache_middleware
def recommend_by_activity(state: MusicAgentState) -> MusicAgentState:
    # 原有逻辑不变
    pass

(2)模型调优:平衡速度与效果

  • 降低模型参数量:测试环境用 Qwen2.5-7B,生产环境根据预算选择 14B/72B;
  • 调整温度参数:推荐场景用 0.2-0.3,避免推荐结果波动;
  • 限制上下文长度:只保留最近 3 轮对话,减少 LLM 推理负担。

(3)异步处理:提升并发能力

使用 asyncio 异步调用 LLM 和 MCP 工具,支持多用户同时请求:

async def async_recommend_by_mood(state: MusicAgentState) -> MusicAgentState:
    """异步推荐节点"""
    loop = asyncio.get_event_loop()
    # 异步调用 LLM
    llm_task = loop.run_in_executor(None, llm.chat_completion, messages)
    # 异步调用 MCP 工具
    mcp_task = loop.run_in_executor(None, mcp_client.call_tool, "music_search", params)
    # 等待结果
    llm_result, mcp_result = await asyncio.gather(llm_task, mcp_task)
    # 处理结果
    state.recommendations = mcp_result.content[0].text["songs"]
    return state

2. 生产级部署方案

(1)Docker 容器化部署

创建 Dockerfile 和 docker-compose.yml,简化部署流程:

# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . .
EXPOSE 8501
CMD ["python", "run_music_app.py"]
# docker-compose.yml
version: "3"
services:
  music-agent:
    build: .
    ports:
      - "8501:8501"
    environment:
      - SILICONFLOW_API_KEY=your_key
      - SPOTIFY_CLIENT_ID=your_id
      - SPOTIFY_CLIENT_SECRET=your_secret
    volumes:
      - ./data:/app/data
    restart: always
  mcp-server:
    build: ./mcp
    ports:
      - "8001:8001"
    restart: always

(2)云服务部署(以阿里云为例)

  1. 创建 ECS 实例(推荐 2C4G 及以上);
  2. 安装 Docker 和 Docker Compose;
  3. 克隆项目,配置 setting.json;
  4. 执行 docker-compose up -d 启动服务;
  5. 配置安全组,开放 8501 端口;
  6. 访问 http://ECS公网IP:8501 即可使用。

(3)监控与日志

添加 Prometheus + Grafana 监控,或使用 Streamlit 的内置日志功能:

def setup_logging():
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        handlers=[
            logging.FileHandler("logs/music_agent.log"),
            logging.StreamHandler()
        ]
    )

七、扩展方向与未来规划

1. 功能扩展

  • 多模态推荐:结合歌词情感分析、封面图风格识别,提升推荐精准度;
  • 用户偏好学习:存储用户历史反馈(喜欢/不喜欢),用向量数据库(如 Pinecone)构建个性化模型;
  • 多平台对接:除 Spotify 外,支持网易云音乐、QQ 音乐 API;
  • 语音交互:集成 Whisper 实现语音输入,支持“语音说需求,语音听推荐”。

2. 技术升级

  • 多 Agent 协同:拆分意图识别、推荐、结果生成到不同 Agent,提升灵活性;
  • 模型微调:用音乐推荐数据集微调 LLM,提升意图识别和推荐理由生成的准确性;
  • 边缘部署:适配嵌入式设备(如智能音箱),支持离线推荐。

八、总结:智能推荐 Agent 的核心价值与落地建议

基于 LangGraph + MCP 的智能音乐推荐 Agent,核心价值在于“用自然语言理解打破推荐壁垒,用标准化工具调用实现生态扩展”。它不仅是一个 Demo 级项目,更是一套可复用的 Agent 构建框架——你可以将其适配到其他推荐场景(如电影、书籍、商品),只需替换工具层和数据层。

落地建议:

  1. 初期用本地数据验证流程,再对接外部 API;
  2. 优先优化意图识别和推荐逻辑,再扩展 MCP 工具和多平台集成;
  3. 小流量测试后,逐步添加缓存、监控和部署优化;
  4. 关注用户反馈,通过“反馈闭环”持续优化推荐效果。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/ren-gong-zhi-neng/4569

推荐阅读

  • 企业级自动化 Agent 架构深析:Prompt 演进驱动的智能工作流落地
  • LangChain 1.0 智能体实战:MCP 协议赋能工具标准化调用(从开发到落地)
  • 从技术本质到生态关联,一文吃透 AIGC、RAG、Agent、Function Call、MCP
  • AI Agent 扩展双雄:MCP 与 Agent Skill 深度拆解——从设计哲学到实战落地
  • 6款核心MCP协议工具让AI深度融入业务,告别“纸上谈兵”
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: Agent AI LangGraph MCP
最后更新:2025年11月11日
相关文章
  • 企业级自动化 Agent 架构深析:Prompt 演进驱动的智能工作流落地2025年12月1日
  • 企业级 RAG 系统进阶实战:基于 Qwen Agent 构建 GB 级智能知识库(从架构到落地)2025年11月20日
  • Agent 开发完全指南:从核心原理到实战落地(2025 进阶版)2025年11月19日
  • Claude Design介绍与使用2026年7月15日
  • MCP深度解析:从协议本质到实战落地,彻底搞懂 AI 工具交互的标准化方案2025年11月11日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复

文章评论

还没有评论,快来抢沙发吧~

Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma -- which is living with the results of other people's thinking. Don't let the noise of other's opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition. They somehow already know what you truly want to become. Everything else is secondary.

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
每日早报 · 2026年8月18日 写了一个订阅每日新闻的WP插件 每日早报 · 2026年8月17日 每日早报 · 2026年8月16日 每日早报 · 2026年8月15日 每日早报 · 2026年8月14日
给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能Kratos+ v1.1.16版本更新说明AI时代,个人技术博客的出路在哪里?增加了两套复古皮肤-牛皮纸、千禧网页这个域名注册整整十年了,十年时间,真快啊Kratos+ v1.1.14版本更新说明
CC知识共享协议各协议内容解释 深度解析多级缓存架构:从设计到落地,彻底解决数据一致性难题 从零开始入门 K8s | Kubernetes 网络概念及策略控制 AI原生数据库新标杆:seekdb深度解析,轻量架构与混合搜索的双重革命 结合Apollo配置中心实现日志级别动态配置 使用RocketMQ时,服务启动过程中,Consumer在服务未启动时消费消息问题处理
最近评论
李锋镝 发布于 3 小时前(08月18日) 给主题加了个功能,可以在RSS中排除指定分类,哈哈哈
李锋镝 发布于 4 小时前(08月18日) :8: 喜欢就好
不凡 发布于 14 小时前(08月17日) 哇,配色真好看,观感很舒服! :52:
李锋镝 发布于 17 小时前(08月17日) 哈哈哈,那个插件我之前也用过,那个内容都是固定的,我这个每天不同的时间拉取数据也不一样,等我研究研究...
Hary 发布于 17 小时前(08月17日) emmm大哥,这样每天输出到博客,然后别人订阅rss全是这个,有点不太好,之前很流行一段时间每天60...
标签聚合
Redis MQ JVM MySQL 多线程 ElasticSearch 日常 Claude IDEA AI 分布式 SpringBoot SQL AI编程 K8s 数据库 WordPress JAVA 架构 Spring
友情链接
  • 瓦匠个人小站
  • 若梦博客
  • 九仞之行
  • 老张博客
  • 知向前端
  • 哥斯拉
  • Honesty
  • 韩情脉脉
  • 皮皮社
  • Serendipity
  • Mr.Sun的博客
  • 韩小韩博客
  • 志文工作室
  • 懋和道人
  • 临窗旋墨
  • 搬砖日记
  • 林羽凡
  • 彬红茶日记
  • 蜗牛工作室
  • sssr7844的博客

COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

正在博友圈履约中

域名年龄

Theme Kratos+ By Dylan Li

津ICP备2024022503号-3

京公网安备11011502039375号