博客Caster
一个基于CLI的代理,通过RSS提要从基于Hugo的博客平台获取博客帖子,使用LLM将其格式化为特定于平台的社交媒体帖子,并将其发布到LinkedIn、X(Twitter)和Facebook。
代理是平台驱动的——每个博客平台都在JSON注册表中注册,代理可以在每个平台上独立运行。添加新的博客平台只需要在注册表文件中添加一个新条目,无需更改代码。
运作原理
RSS Feed ──> Record Check ──> Post Selection ──> LLM Formatting ──> LinkedIn + X + Facebook Post ──> Record Save五台FastMCP服务器处理核心操作,由中央协调器协调:
| MCP服务器 | 角色 |
|---|---|
| rss提取器 | 获取并解析Hugo RSS提要和个人博客帖子 |
| 记录着 | 以JSON格式跟踪已发布的帖子,防止重复 |
| 领英海报 | 将格式化内容发布到LinkedIn个人资料 |
| x海报 | 通过tweetpy将格式化的推文发布到X(推特) |
| 脸书海报 | 通过Graph API将格式化内容发布到Facebook页面 |
两种操作模式
手动模式
提供一个特定的博客帖子URL。代理会自动检测平台,获取内容,格式化内容,将其发布到LinkedIn、X和Facebook,并记录结果。还用于为以前发布的URL重试失败的平台。
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post"自动模式
指定平台ID。代理获取RSS提要,选择最新未发布的帖子,对其进行格式化,将其发布到LinkedIn、X和Facebook,并记录结果。
python -m agent_engine.social_agent.main --auto --platform aspose
python -m agent_engine.social_agent.main --auto --platform groupdocs
python -m agent_engine.social_agent.main --auto --platform conholdate先决条件
- Python 3.10+
- 自托管GPT-OSS LLM终结点(与OpenAI兼容的API)
- LinkedIn OAuth 2.0访问令牌
w_member_social范围 - X(Twitter)OAuth 1.0a凭据,具有读写权限
- Facebook页面访问令牌
pages_read_engagement和pages_manage_posts权限
设置
1.克隆并导航到项目
cd socialAgent2.安装依赖项
pip install mcp feedparser httpx beautifulsoup4 aiofiles pydantic-settings openai tweepy3.配置环境变量
复制示例文件并填写您的凭据:
cp .env.example .env编辑 .env 与你的价值观:
# LLM Configuration (GPT-OSS)
PROFESSIONALIZE_BASE_URL=https://your-gpt-oss-endpoint.com/v1
PROFESSIONALIZE_API_KEY_2=your-api-key
PROFESSIONALIZE_LLM_MODEL=your-model-name
# LinkedIn Configuration
LINKEDIN_ACCESS_TOKEN=your-linkedin-oauth-token
# X (Twitter) Configuration
X_API_KEY=your-x-api-key
X_API_SECRET=your-x-api-secret
X_ACCESS_TOKEN=your-x-access-token
X_ACCESS_TOKEN_SECRET=your-x-access-token-secret
# Facebook Page Configuration (default — used by aspose-cloud)
FACEBOOK_PAGE_ID=your-facebook-page-id
FACEBOOK_PAGE_ACCESS_TOKEN=your-facebook-page-access-token
# Facebook Page Configuration (platform-specific overrides)
FACEBOOK_GROUPDOCS_PAGE_ID=your-groupdocs-facebook-page-id
FACEBOOK_GROUPDOCS_PAGE_ACCESS_TOKEN=your-groupdocs-facebook-page-access-token
FACEBOOK_CONHOLDATE_PAGE_ID=your-conholdate-facebook-page-id
FACEBOOK_CONHOLDATE_PAGE_ACCESS_TOKEN=your-conholdate-facebook-page-access-token| 变量 | 描述 |
|---|---|
PROFESSIONALIZE_BASE_URL | OpenAI兼容LLM端点的基本URL |
PROFESSIONALIZE_API_KEY_2 | LLM终结点的API密钥 |
PROFESSIONALIZE_LLM_MODEL | 用于文本生成的模型名称 |
LINKEDIN_ACCESS_TOKEN | 预先生成的LinkedIn OAuth 2.0访问令牌 |
X_API_KEY | X(Twitter)API消费者密钥 |
X_API_SECRET | X(Twitter)API消费者秘密 |
X_ACCESS_TOKEN | X(推特)OAuth 1.0a访问令牌 |
X_ACCESS_TOKEN_SECRET | X(推特)OAuth 1.0a访问令牌密钥 |
FACEBOOK_PAGE_ID | Facebook页面数字ID(默认,由aspose cloud使用) |
FACEBOOK_PAGE_ACCESS_TOKEN | Facebook页面访问令牌(默认,由aspose云使用) |
FACEBOOK_{BRAND}_PAGE_ID | 特定于平台的Facebook页面ID(例如。 FACEBOOK_GROUPDOCS_PAGE_ID) |
FACEBOOK_{BRAND}_PAGE_ACCESS_TOKEN | 特定平台的Facebook页面访问令牌 |
Facebook页面设置
Facebook要求 页面访问令牌 (不是用户访问令牌)发布到页面。请按照以下步骤操作:
第一步:创建Facebook应用
- 首选 Facebook开发者 并创建应用程序(类型:Business)
- 将“Facebook商业登录”产品添加到您的应用程序中
步骤2:获取用户访问令牌
- 去 图形API资源管理器
- 从下拉列表中选择您的应用程序
- 点击“生成访问令牌”
- 授予以下权限:
pages_read_engagement,pages_manage_posts
步骤3:交换页面访问令牌
用户令牌允许您访问您的页面,但您需要 Page自己的令牌 张贴。运行:
curl "https://graph.facebook.com/v21.0/me/accounts?access_token=YOUR_USER_TOKEN"这将返回您管理的页面列表:
{
"data": [
{
"access_token": "EAAxxxxx_PAGE_TOKEN_xxxxx",
"name": "Your Page Name",
"id": "123456789012345"
}
]
}复制 access_token 和 id 从页面条目。
步骤4:验证页面令牌
curl "https://graph.facebook.com/v21.0/me?access_token=YOUR_PAGE_TOKEN"这应该会返回 佩奇 姓名和ID(不是您的个人姓名)。如果它显示了页面名称,则令牌是正确的。
步骤5:更新 .env
FACEBOOK_PAGE_ID=123456789012345
FACEBOOK_PAGE_ACCESS_TOKEN=EAAxxxxx_PAGE_TOKEN_xxxxx第六步:测试
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post" --target facebook常见错误:使用用户令牌而不是页面令牌
如果您看到此错误:
Insufficient permissions to post: (#200) If posting to a group, requires app being
installed in the group...您正在使用 用户访问令牌 而不是a 页面访问令牌.返回步骤3并使用 me/accounts endpoint获取页面自己的令牌。
代币到期和长期代币
图形API资源管理器中的令牌将在约1小时后过期。用于生产,换成 长期代币 (约60天):
1.将短期用户令牌兑换为长期用户令牌:
curl -G "https://graph.facebook.com/v19.0/oauth/access_token" \
--data-urlencode "grant_type=fb_exchange_token" \
--data-urlencode "client_id=YOUR_APP_ID" \
--data-urlencode "client_secret=YOUR_APP_SECRET" \
--data-urlencode "fb_exchange_token=YOUR_SHORT_LIVED_USER_TOKEN"答复:
{
"access_token": "LONG_LIVED_USER_TOKEN",
"token_type": "bearer",
"expires_in": 5183423
}expires_in 以秒为单位(约60天)。
2.将长期用户令牌兑换为页面令牌:
curl "https://graph.facebook.com/v19.0/me/accounts?access_token=LONG_LIVED_USER_TOKEN"从长期用户令牌返回的页面访问令牌 不会过期.复制 access_token 和 id 从您的页面条目和更新 .env /GitHub操作秘密。
3.验证您的令牌:
curl "https://graph.facebook.com/v19.0/debug_token?input_token=YOUR_PAGE_TOKEN&access_token=YOUR_APP_ID|YOUR_APP_SECRET"检查 expires_at --a值 0 意味着令牌永远不会过期。
安全注意事项:
- 永远不要暴露
client_secret前端代码 - 将令牌存储在环境变量或GitHub Actions secrets中
- 如果暴露,请立即旋转凭据
用法
所有命令都必须从项目根目录运行。
发布特定博客URL(手动模式)
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post"从平台发布最新未发布的博客(自动模式)
python -m agent_engine.social_agent.main --auto --platform aspose针对特定的社交媒体平台
# Post to Facebook only
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post" --target facebook
# Post to LinkedIn only
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post" --target linkedin
# Post to X only
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post" --target x
# Post to all platforms (default)
python -m agent_engine.social_agent.main --url "https://blog.aspose.com/some-post" --target all列出可用平台
python -m agent_engine.social_agent.main --auto输出:
Error: --platform is required with --auto
Available platforms:
- aspose (Aspose Blog)
- groupdocs (GroupDocs Blog)
- conholdate (Conholdate Blog)显示使用帮助
python -m agent_engine.social_agent.main添加新的博客平台
添加新品牌需要 无代码更改 --仅配置:
1.添加到平台注册表
编辑 registry/platforms_registry.json 并添加新条目:
{
"id": "conholdate",
"name": "Conholdate Blog",
"url": "https://blog.conholdate.com",
"rss_feed": "https://blog.conholdate.com/index.xml",
"active": true
}2.添加特定于平台的Facebook凭据
Facebook凭据根据平台名称动态解析。命名约定:
FACEBOOK_{BRAND}_PAGE_ID
FACEBOOK_{BRAND}_PAGE_ACCESS_TOKEN哪里 {BRAND} 是平台ID的第一部分(之前 -),大写。
| 平台ID | Facebook环境变量 |
|---|---|
aspose-cloud | FACEBOOK_PAGE_ID + FACEBOOK_PAGE_ACCESS_TOKEN (默认) |
groupdocs-cloud | FACEBOOK_GROUPDOCS_PAGE_ID + FACEBOOK_GROUPDOCS_PAGE_ACCESS_TOKEN |
conholdate-cloud | FACEBOOK_CONHOLDATE_PAGE_ID + FACEBOOK_CONHOLDATE_PAGE_ACCESS_TOKEN |
将这些添加到您的 .env 本地文件和GitHub Actions机密。
如果找不到特定于平台的凭据,则默认 FACEBOOK_PAGE_ID / FACEBOOK_PAGE_ACCESS_TOKEN 使用。
LinkedIn和X证书在所有品牌中共享。
3.创建GitHub操作工作流
复制现有工作流文件(例如。 .github/workflows/blogcaster-groupdocs-cloud.yml)并更改:
name:BlogCaster — Your Brandschedule:选择不与现有工作流程重叠的日期platform default:您的平台ID(例如。conholdate-cloud)- 脸书环境变量:使用你品牌特有的秘密名称
当前时间表:
| 工作流程 | 天数 | Cron |
|---|---|---|
| BlogCaster-Aspose Cloud | 周一和周五 | 0 5 * * 1,5 |
| BlogCaster-GroupDocs Cloud | 周二和周四 | 0 5 * * 2,4 |
4.本地测试
python -m agent_engine.social_agent.main --auto --platform conholdate-cloud --target facebook5.在GitHub操作上测试
通过手动触发 workflow_dispatch 在“操作”选项卡中,然后确认计划在预期日期运行。
项目结构
socialAgent/
├── agent_engine/
│ └── social_agent/
│ ├── main.py # CLI entry point
│ ├── config.py # Pydantic BaseSettings config
│ ├── agent_logic/
│ │ └── orchestrator.py # Core workflow (manual + auto modes)
│ ├── tools/
│ │ └── mcp_tools.py # MCP server wrappers
│ └── utils/
│ ├── helpers.py # Logger setup + registry helpers
│ ├── llm_service.py # LLM formatting via OpenAI SDK
│ └── prompts.py # Platform-specific prompt templates
├── mcp-servers/
│ ├── rss-fetcher/server.py # RSS feed parsing (feedparser + httpx + bs4)
│ ├── linkedin-poster/server.py # LinkedIn API posting (httpx)
│ ├── x-poster/server.py # X (Twitter) API posting (tweepy)
│ ├── facebook-poster/server.py # Facebook Page posting (httpx + Graph API)
│ └── record-keeper/server.py # JSON-based record storage (aiofiles)
├── registry/
│ └── platforms_registry.json # Blog platform registry
├── content/
│ └── records/
│ └── published_record.json # Published post records
├── logs/
│ └── logs.txt # Application logs
├── .env # Credentials (not committed)
├── .env.example # Credential template
└── .gitignore日志
所有操作都记录到 logs/logs.txt 带有时间戳。控制台输出显示INFO级别的消息,日志文件捕获DEBUG级别的详细信息。
2026-03-17 03:28:49 | INFO | social_agent | Starting Auto Mode for platform: aspose
2026-03-17 03:28:50 | INFO | social_agent | Fetched 10 posts from RSS feed
2026-03-17 03:28:51 | INFO | social_agent | Selected unpublished post: "Convert OneNote..." — https://...
2026-03-17 03:28:58 | INFO | social_agent | LLM formatted post (102 words)
2026-03-17 03:29:01 | INFO | social_agent | LinkedIn post successful: post_id=urn:li:share:...
2026-03-17 03:29:01 | INFO | social_agent | Record saved for https://...防止重复和按平台重试
代理跟踪每个平台的发布结果。在 自动模式,任何有现有记录的URL都会被跳过——它总是选择一个新的帖子。在 手动模式 (--url),如果URL之前已发布到某些平台,但不是全部平台,则代理仅重试失败的平台。
URL被规范化(小写、删除尾随斜线、删除查询参数)以捕获以下变化:
https://blog.aspose.com/post/vshttps://blog.aspose.com/posthttps://blog.aspose.com/post?utm=123vshttps://blog.aspose.com/post
只有当至少一个平台成功时,才会保存记录。如果所有平台都失败,则不会写入任何记录,因此可以在下次运行时重试URL。
文章格式
领英
- 专业语气,150-200字
- 吸引注意力的钩线
- 3-5个相关标签
- 末尾的博客URL
X(推特)
- 一个有力的句子+2-3个相关标签
- 自动附加博客URL
- 推文总长度不超过280个字符
脸书
- 以社区为中心,友好的对话语气,150-200字
- 好奇心驱动的钩线
- 明确行动号召(阅读文章、分享想法、标记某人)
- 3-5个相关标签
- 博客URL作为链接附加
故障排除
| 问题 | 解决方案 |
|---|---|
PROFESSIONALIZE_BASE_URL is not set | 创建 .env 文件来自 .env.example 并填写法学硕士证书 |
LinkedIn token is expired or invalid (401) | 生成新的LinkedIn OAuth令牌并更新 .env |
Insufficient permissions (403) | 确保您的LinkedIn令牌具有 w_member_social 范围 |
All recent posts have already been shared | 所有RSS提要帖子都已在记录中——等待新的博客帖子 |
X credentials have insufficient permissions (403) | 启用读写权限后重新生成X Access令牌和密钥 |
X API rate limit exceeded (429) | 请稍候几分钟,然后重试 |
Facebook token is expired or invalid | 获取新的页面访问令牌(请参阅上面的Facebook页面设置) |
Insufficient permissions to post: (#200) | 您正在使用用户令牌而不是页面令牌--use me/accounts 获取页面令牌 |
Facebook API rate limit exceeded | 请稍候几分钟,然后重试 |
Platform 'xxx' not found | 检查 registry/platforms_registry.json 对于可用的平台ID |
未来发展
其他社交媒体平台
- 中等/发展到 --全文转载(不仅仅是社交片段)以获得更强的SEO反向链接
- 博客/WordPress --将整篇文章交叉发布到博客平台,以扩大覆盖面和反向链接注入
性能改进
- 并行LLM调用 --快跑
format_for_linkedin(),format_for_x(),以及format_for_facebook()同时通过asyncio.gather()随着更多平台的添加,减少了总格式化时间,而不是按顺序 - 内容质量验证 --在发送到LLM之前预先验证抓取的内容质量,防止在低质量或垃圾页面上浪费LLM调用
架构增强
- 基于YAML的提示配置 --将平台提示从代码移动到YAML配置文件,使非开发人员能够在不更改代码的情况下调整音调、长度和风格
- REST API包装 --通过FastAPI将编排器作为HTTP端点公开,实现与web界面和工作流自动化工具的集成
- 轻质连接器图案 --随着平台数量超过5+,评估基于插件的连接器架构,作为每个平台启动一个MCP子流程的替代方案
可扩展性(无代码更改)
| 目标 | 需要更改 |
|---|---|
| 添加新博客平台 | 在中添加新条目 registry/platforms_registry.json |
| 添加web UI | 封装现有MCP工具的FastAPI层 |
| 切换到数据库 | 替换 record-keeper/server.py 内部构件(相同的MCP接口) |
