Tutorial 9: 多平台发布 Agent

项目概述

本教程将构建一个多平台发布 Agent,它能够:

  1. 将内容自动适配不同平台的格式

  2. 管理多平台账号和发布配置

  3. 自动化发布流程

  4. 追踪发布状态和数据

系统架构

┌─────────────────────────────────────────────────────────────┐
│                Multi-Platform Publishing Agent              │
│                                                              │
│  ┌────────────────────────────────────────────────────────┐ │
│  │                   Content Adapter                       │ │
│  │  原始内容 ──► 微信版本 / 知乎版本 / 小红书版本 / ...    │ │
│  └────────────────────────────────────────────────────────┘ │
│                            │                                 │
│  ┌────────────────────────┴───────────────────────────────┐ │
│  │                   Platform Connectors                   │ │
│  │  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐     │ │
│  │  │微信  │  │知乎  │  │小红书│  │头条  │  │微博  │     │ │
│  │  └──┬───┘  └──┬───┘  └──┬───┘  └──┬───┘  └──┬───┘     │ │
│  └─────┼─────────┼─────────┼─────────┼─────────┼─────────┘ │
│        │         │         │         │         │            │
│  ┌─────┴─────────┴─────────┴─────────┴─────────┴─────────┐ │
│  │                   Status Tracker                       │ │
│  │          发布状态追踪 / 数据统计 / 错误处理             │ │
│  └────────────────────────────────────────────────────────┘ │
│                                                              │
└─────────────────────────────────────────────────────────────┘

内容适配器

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from dataclasses import dataclass
from typing import Dict, List, Optional
from enum import Enum
import json

class Platform(str, Enum):
    WECHAT = "微信公众号"
    ZHIHU = "知乎"
    XIAOHONGSHU = "小红书"
    TOUTIAO = "今日头条"
    WEIBO = "微博"

@dataclass
class AdaptedContent:
    platform: Platform
    title: str
    content: str
    summary: str
    tags: List[str]
    cover_suggestion: str
    publish_time_suggestion: str

class ContentAdapter:
    """内容适配器:将原始内容适配到不同平台"""

    PLATFORM_PROMPTS = {
        Platform.WECHAT: """
将以下内容改写为微信公众号风格:
- 标题:有深度,可以稍长,引发思考
- 开头:要有钩子,3秒抓住读者
- 正文:分段清晰,每段不超过3行
- 使用小标题分隔章节
- 结尾:引导关注和互动
- 字数:1500-3000字
""",
        Platform.ZHIHU: """
将以下内容改写为知乎风格:
- 标题:专业、直接,可以是问题形式
- 开头:直接给出核心观点
- 正文:逻辑严密,数据支撑
- 适当引用来源增加可信度
- 结尾:总结观点,欢迎讨论
- 字数:不限,但要言之有物
""",
        Platform.XIAOHONGSHU: """
将以下内容改写为小红书风格:
- 标题:简短有力,带emoji 📚
- 开头:亲切口语化,像朋友聊天
- 正文:分点列出,多用emoji
- 加入个人体验和感受
- 结尾:互动提问
- 字数:500-1000字
""",
        Platform.TOUTIAO: """
将以下内容改写为今日头条风格:
- 标题:吸引眼球,信息量大
- 开头:直入主题,抓住注意力
- 正文:通俗易懂,案例丰富
- 适合快速阅读
- 结尾:引导评论
- 字数:1000-2000字
""",
        Platform.WEIBO: """
将以下内容改写为微博风格:
- 标题:简短有力
- 正文:精炼,140字左右核心观点
- 可以是长微博(1000字以内)
- 话题标签:#话题#格式
- 适合传播和讨论
"""
    }

    def __init__(self):
        self.llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)

    def adapt(self, original_content: str, original_title: str,
              target_platform: Platform) -> AdaptedContent:
        """将内容适配到目标平台"""

        prompt = ChatPromptTemplate.from_template("""
{platform_instruction}

原始标题:{original_title}

原始内容:
{original_content}

请输出适配后的内容,格式如下:
【标题】
(适配后的标题)

【正文】
(适配后的正文)

【摘要】
(50字以内的摘要)

【标签】
(5个相关标签,逗号分隔)

【封面建议】
(封面图片创意描述)

【发布时间建议】
(最佳发布时间)
""")

        chain = prompt | self.llm | StrOutputParser()

        result = chain.invoke({
            "platform_instruction": self.PLATFORM_PROMPTS[target_platform],
            "original_title": original_title,
            "original_content": original_content
        })

        # 解析结果
        return self._parse_result(result, target_platform)

    def _parse_result(self, result: str, platform: Platform) -> AdaptedContent:
        """解析LLM输出"""
        sections = {}
        current_section = None
        current_content = []

        for line in result.split('\n'):
            if line.startswith('【') and line.endswith('】'):
                if current_section:
                    sections[current_section] = '\n'.join(current_content).strip()
                current_section = line[1:-1]
                current_content = []
            else:
                current_content.append(line)

        if current_section:
            sections[current_section] = '\n'.join(current_content).strip()

        return AdaptedContent(
            platform=platform,
            title=sections.get('标题', ''),
            content=sections.get('正文', ''),
            summary=sections.get('摘要', ''),
            tags=[t.strip() for t in sections.get('标签', '').split(',')],
            cover_suggestion=sections.get('封面建议', ''),
            publish_time_suggestion=sections.get('发布时间建议', '')
        )

    def adapt_to_all(self, original_content: str, original_title: str,
                     platforms: List[Platform] = None) -> Dict[Platform, AdaptedContent]:
        """适配到多个平台"""
        if platforms is None:
            platforms = list(Platform)

        results = {}
        for platform in platforms:
            print(f"正在适配到 {platform.value}...")
            results[platform] = self.adapt(original_content, original_title, platform)

        return results

平台连接器

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional
from datetime import datetime
import json

@dataclass
class PublishResult:
    success: bool
    platform: str
    post_id: Optional[str] = None
    post_url: Optional[str] = None
    error_message: Optional[str] = None
    publish_time: Optional[datetime] = None

class PlatformConnector(ABC):
    """平台连接器基类"""

    @abstractmethod
    def authenticate(self, credentials: dict) -> bool:
        """认证"""
        pass

    @abstractmethod
    def publish(self, content: AdaptedContent) -> PublishResult:
        """发布内容"""
        pass

    @abstractmethod
    def get_post_stats(self, post_id: str) -> dict:
        """获取文章数据"""
        pass

class WeChatConnector(PlatformConnector):
    """微信公众号连接器"""

    def __init__(self):
        self.access_token = None
        self.app_id = None

    def authenticate(self, credentials: dict) -> bool:
        """微信公众号认证

        实际应用需要:
        1. 使用 app_id 和 app_secret 获取 access_token
        2. access_token 有效期2小时,需要刷新
        """
        self.app_id = credentials.get('app_id')
        app_secret = credentials.get('app_secret')

        # 模拟认证
        # 实际代码:
        # url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={self.app_id}&secret={app_secret}"
        # response = requests.get(url)
        # self.access_token = response.json()['access_token']

        self.access_token = "mock_access_token"
        return True

    def publish(self, content: AdaptedContent) -> PublishResult:
        """发布到微信公众号

        实际流程:
        1. 上传图片素材获取 media_id
        2. 创建图文消息
        3. 发布或群发
        """
        try:
            # 模拟发布
            # 实际代码需要调用微信API
            print(f"[微信公众号] 发布文章: {content.title}")

            return PublishResult(
                success=True,
                platform="微信公众号",
                post_id="wechat_" + datetime.now().strftime("%Y%m%d%H%M%S"),
                post_url="https://mp.weixin.qq.com/s/xxxxx",
                publish_time=datetime.now()
            )
        except Exception as e:
            return PublishResult(
                success=False,
                platform="微信公众号",
                error_message=str(e)
            )

    def get_post_stats(self, post_id: str) -> dict:
        """获取文章数据"""
        # 模拟数据
        return {
            "read_count": 1234,
            "like_count": 56,
            "share_count": 23,
            "comment_count": 12
        }

class ZhihuConnector(PlatformConnector):
    """知乎连接器"""

    def __init__(self):
        self.cookies = None

    def authenticate(self, credentials: dict) -> bool:
        """知乎认证(通常使用cookies)"""
        self.cookies = credentials.get('cookies')
        return self.cookies is not None

    def publish(self, content: AdaptedContent) -> PublishResult:
        """发布到知乎"""
        try:
            print(f"[知乎] 发布文章: {content.title}")

            return PublishResult(
                success=True,
                platform="知乎",
                post_id="zhihu_" + datetime.now().strftime("%Y%m%d%H%M%S"),
                post_url="https://zhuanlan.zhihu.com/p/xxxxx",
                publish_time=datetime.now()
            )
        except Exception as e:
            return PublishResult(
                success=False,
                platform="知乎",
                error_message=str(e)
            )

    def get_post_stats(self, post_id: str) -> dict:
        return {
            "view_count": 5678,
            "upvote_count": 234,
            "comment_count": 45,
            "collect_count": 67
        }

class XiaohongshuConnector(PlatformConnector):
    """小红书连接器"""

    def authenticate(self, credentials: dict) -> bool:
        return True

    def publish(self, content: AdaptedContent) -> PublishResult:
        try:
            print(f"[小红书] 发布笔记: {content.title}")

            return PublishResult(
                success=True,
                platform="小红书",
                post_id="xhs_" + datetime.now().strftime("%Y%m%d%H%M%S"),
                post_url="https://www.xiaohongshu.com/explore/xxxxx",
                publish_time=datetime.now()
            )
        except Exception as e:
            return PublishResult(
                success=False,
                platform="小红书",
                error_message=str(e)
            )

    def get_post_stats(self, post_id: str) -> dict:
        return {
            "like_count": 890,
            "collect_count": 123,
            "comment_count": 34,
            "share_count": 12
        }

发布管理器

from typing import Dict, List
from datetime import datetime
import json

class PublishManager:
    """发布管理器:协调多平台发布"""

    def __init__(self):
        self.connectors: Dict[Platform, PlatformConnector] = {
            Platform.WECHAT: WeChatConnector(),
            Platform.ZHIHU: ZhihuConnector(),
            Platform.XIAOHONGSHU: XiaohongshuConnector(),
        }
        self.publish_history: List[PublishResult] = []

    def setup_credentials(self, platform: Platform, credentials: dict) -> bool:
        """配置平台凭证"""
        if platform in self.connectors:
            return self.connectors[platform].authenticate(credentials)
        return False

    def publish_to_platform(self, content: AdaptedContent) -> PublishResult:
        """发布到单个平台"""
        connector = self.connectors.get(content.platform)
        if not connector:
            return PublishResult(
                success=False,
                platform=content.platform.value,
                error_message=f"不支持的平台: {content.platform.value}"
            )

        result = connector.publish(content)
        self.publish_history.append(result)
        return result

    def publish_to_all(self, adapted_contents: Dict[Platform, AdaptedContent]) -> List[PublishResult]:
        """发布到所有已适配的平台"""
        results = []
        for platform, content in adapted_contents.items():
            print(f"\n正在发布到 {platform.value}...")
            result = self.publish_to_platform(content)
            results.append(result)

            if result.success:
                print(f"✅ 发布成功: {result.post_url}")
            else:
                print(f"❌ 发布失败: {result.error_message}")

        return results

    def get_publish_report(self) -> str:
        """生成发布报告"""
        report = []
        report.append("=" * 50)
        report.append("发布报告")
        report.append("=" * 50)

        success_count = sum(1 for r in self.publish_history if r.success)
        fail_count = len(self.publish_history) - success_count

        report.append(f"总计: {len(self.publish_history)} 篇")
        report.append(f"成功: {success_count} 篇")
        report.append(f"失败: {fail_count} 篇")
        report.append("")

        for result in self.publish_history:
            status = "✅" if result.success else "❌"
            report.append(f"{status} [{result.platform}]")
            if result.success:
                report.append(f"   链接: {result.post_url}")
                report.append(f"   时间: {result.publish_time}")
            else:
                report.append(f"   错误: {result.error_message}")

        return "\n".join(report)

完整的发布 Agent

from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

# 工具定义
@tool
def adapt_content_for_platform(content: str, title: str, platform: str) -> str:
    """将内容适配到指定平台

    Args:
        content: 原始内容
        title: 原始标题
        platform: 目标平台(微信公众号/知乎/小红书)
    """
    adapter = ContentAdapter()
    try:
        p = Platform(platform)
        result = adapter.adapt(content, title, p)
        return json.dumps({
            "platform": platform,
            "title": result.title,
            "content_preview": result.content[:200] + "...",
            "summary": result.summary,
            "tags": result.tags,
            "cover_suggestion": result.cover_suggestion,
            "publish_time_suggestion": result.publish_time_suggestion
        }, ensure_ascii=False)
    except ValueError:
        return json.dumps({"error": f"不支持的平台: {platform}"}, ensure_ascii=False)

@tool
def publish_to_platform(platform: str, title: str, content: str) -> str:
    """发布内容到指定平台

    Args:
        platform: 目标平台
        title: 文章标题
        content: 文章内容
    """
    manager = PublishManager()

    try:
        p = Platform(platform)
        adapted = AdaptedContent(
            platform=p,
            title=title,
            content=content,
            summary="",
            tags=[],
            cover_suggestion="",
            publish_time_suggestion=""
        )
        result = manager.publish_to_platform(adapted)

        return json.dumps({
            "success": result.success,
            "platform": result.platform,
            "post_url": result.post_url,
            "error": result.error_message
        }, ensure_ascii=False)
    except Exception as e:
        return json.dumps({"error": str(e)}, ensure_ascii=False)

@tool
def get_best_publish_time(platform: str) -> str:
    """获取平台最佳发布时间

    Args:
        platform: 平台名称
    """
    times = {
        "微信公众号": {
            "best_times": ["早8:00-9:00", "中午12:00-13:00", "晚20:00-22:00"],
            "best_days": ["周二", "周三", "周四"],
            "avoid": "周末下午"
        },
        "知乎": {
            "best_times": ["上午10:00-11:00", "晚21:00-23:00"],
            "best_days": ["工作日"],
            "avoid": "凌晨时段"
        },
        "小红书": {
            "best_times": ["中午12:00-14:00", "晚19:00-22:00"],
            "best_days": ["周末"],
            "avoid": "工作日上午"
        }
    }

    return json.dumps(times.get(platform, {"error": "未知平台"}), ensure_ascii=False)

class PublishingAgent:
    """多平台发布 Agent"""

    def __init__(self):
        self.llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
        self.tools = [
            adapt_content_for_platform,
            publish_to_platform,
            get_best_publish_time
        ]

        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """你是专业的自媒体运营助手,负责多平台内容发布。

你的职责:
1. 将内容适配到不同平台的风格
2. 选择最佳发布时间
3. 执行发布操作
4. 追踪发布状态

工作原则:
- 确保内容适配平台特点
- 选择最佳发布时间
- 发布前确认内容质量
- 记录发布结果"""),
            MessagesPlaceholder(variable_name="chat_history", optional=True),
            ("human", "{input}"),
            MessagesPlaceholder(variable_name="agent_scratchpad")
        ])

        self.agent = create_tool_calling_agent(self.llm, self.tools, self.prompt)
        self.executor = AgentExecutor(
            agent=self.agent,
            tools=self.tools,
            verbose=True
        )

    def publish(self, content: str, title: str, platforms: List[str]) -> str:
        """发布内容到多个平台"""
        platforms_str = "、".join(platforms)

        return self.executor.invoke({
            "input": f"""
            请将以下内容发布到这些平台: {platforms_str}

            标题: {title}

            内容: {content}

            请执行以下步骤:
            1. 为每个平台适配内容
            2. 查询每个平台的最佳发布时间
            3. 执行发布
            4. 汇报发布结果
            """
        })["output"]

# 使用示例
if __name__ == "__main__":
    agent = PublishingAgent()

    result = agent.publish(
        content="""
        AI编程正在改变软件开发的方式。通过使用AI辅助工具,
        开发者可以更快地编写代码、发现bug、优化性能。
        本文将介绍几个实用的AI编程技巧...
        """,
        title="AI编程入门指南",
        platforms=["微信公众号", "知乎", "小红书"]
    )

    print(result)

下一步

在下一个教程中,我们将整合所有组件,构建完整的自媒体工作流系统。

Tutorial 10: 完整自媒体工作流