from __future__ import annotations from xhs_bridge.skill_catalog import SkillRecipe, compose_prompt_brief _ANALYSIS_INSTRUCTION = """ 请根据上方爆款技能配方,对下方笔记做拆解分析。输出使用 Markdown,建议包含: 1. 爆款公式匹配(情绪/实用/视觉/社交) 2. 标题与开头钩子 3. 正文结构与信息密度 4. 互动数据解读 5. 评论区亮点(如有) 6. 可借鉴的 3~5 条 actionable 建议 直接输出分析正文,不要 JSON,不要重复粘贴整篇原文。 """ def extract_feed_image_urls(payload: dict, *, max_images: int = 6) -> list[str]: """从笔记详情中提取配图 URL(供图文模型使用)。""" note = payload.get("note") or {} images = note.get("images") or [] urls: list[str] = [] for img in images: if not isinstance(img, dict): continue url = str(img.get("url") or "").strip() if url.startswith("http"): urls.append(url) if len(urls) >= max_images: break return urls def format_feed_for_analysis(payload: dict, *, max_comments: int = 20) -> str: """将详情数据整理为 AI 可读文本。""" note = payload.get("note") or {} user = note.get("user") or {} interact = note.get("interact") or {} lines = [ "【笔记信息】", f"标题:{note.get('title') or '(无)'}", f"作者:{user.get('nickname') or '—'}", f"发布时间:{note.get('time') or '—'}", f"位置:{note.get('ip_location') or '—'}", f"类型:{'视频' if note.get('is_video') else '图文'}", "", "【互动数据】", f"点赞 {interact.get('liked_count', '0')} · " f"收藏 {interact.get('collected_count', '0')} · " f"评论 {interact.get('comment_count', '0')} · " f"分享 {interact.get('shared_count', '0')}", "", "【正文】", note.get("desc") or "(无正文)", ] comments = payload.get("comments") or [] if comments: lines.append("") lines.append(f"【评论摘录(最多 {max_comments} 条)】") for i, c in enumerate(comments[:max_comments]): cu = c.get("user") or {} lines.append( f"- {cu.get('nickname', '匿名')}:{c.get('content', '')}" + (f"(赞 {c['like_count']})" if c.get("like_count") else "") ) return "\n".join(lines) def build_analysis_prompt(recipe: SkillRecipe, feed_payload: dict) -> str: return ( compose_prompt_brief(recipe) + "\n" + _ANALYSIS_INSTRUCTION + "\n\n" + format_feed_for_analysis(feed_payload) ) _COMMENT_INSTRUCTION = """ 请根据笔记内容与上方技能配方,生成 1 条适合发布在小红书评论区的中文短评。 要求:口语自然、不超过 80 字、不要 hashtag、不要引号包裹、不要解释分析过程。 只输出评论正文一行。 """ def build_comment_prompt(recipe: SkillRecipe, feed_payload: dict) -> str: return ( compose_prompt_brief(recipe) + "\n" + _COMMENT_INSTRUCTION + "\n\n" + format_feed_for_analysis(feed_payload, max_comments=8) )