17 KiB
17 KiB
Phase F.4:AI 集成
日期:2026-04-04 状态:待开始 依赖:F.3(待完成) 前置:services/forum_ai_service.py
1. 本阶段目的
为 Jarvis Forum 集成 AI 能力:
- AI 自动回复
- 帖子摘要生成
- 智能分类打标
- Agent 自主发帖
2. Forum AI Service
2.1 服务架构
┌─────────────────────────────────────────────────────────────┐
│ ForumAIService │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ AutoReply │ │ Summary │ │ SmartTagging │ │
│ │ Service │ │ Service │ │ Service │ │
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
│ │ │ │ │
│ └────────────────┼───────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ LLM Service │ │
│ │ (复用 Agent LLM) │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2.2 核心服务
class ForumAIService:
"""论坛 AI 服务"""
def __init__(
self,
llm_service: LLMService,
config: ForumAIConfig,
):
self.llm = llm_service
self.config = config
# === 自动回复 ===
async def generate_auto_reply(
self,
post: ForumPost,
replies: List[ForumReply],
) -> Optional[str]:
"""生成自动回复"""
if not self.config.auto_reply_enabled:
return None
# 检查是否需要回复
if not self._should_auto_reply(post):
return None
# 构建上下文
context = self._build_reply_context(post, replies)
# 生成回复
prompt = self._build_reply_prompt(context)
response = await self.llm.agenerate(prompt)
return response.content if response else None
def _should_auto_reply(self, post: ForumPost) -> bool:
"""判断是否应该自动回复"""
# 指令类帖子不自动回复
if post.category == "instruction":
return False
# 有 AI 回复的不重复回复
if any(r.is_ai_reply for r in post.replies):
return False
# 检查时间窗口
if post.last_reply_at:
hours_since = (datetime.utcnow() - post.last_reply_at).total_seconds() / 3600
if hours_since < self.config.auto_reply_min_hours:
return False
return True
def _build_reply_context(
self,
post: ForumPost,
replies: List[ForumReply],
) -> str:
"""构建回复上下文"""
context = f"""## 帖子信息
标题: {post.title}
内容: {post.content[:500]}...
## 回复列表
"""
for reply in replies[-5:]: # 最近 5 条
context += f"- {reply.user_id}: {reply.content[:200]}...\n"
return context
def _build_reply_prompt(self, context: str) -> str:
"""构建回复提示词"""
return f"""你是一个友好的社区助手。请根据以下帖子内容,生成一条有帮助的回复。
{context}
要求:
1. 回复要友好、专业、有帮助
2. 不要重复已有的观点
3. 如果是问题,尽量给出建设性的建议
4. 回复长度控制在 100-300 字
5. 不要使用 Markdown 格式,直接输出文本
回复:"""
# === 摘要生成 ===
async def generate_summary(
self,
content: str,
max_length: int = 200,
) -> str:
"""生成帖子摘要"""
if len(content) <= max_length:
return content
prompt = f"""请为以下内容生成一个简洁的摘要,不超过 {max_length} 字。
内容:
{content}
摘要:"""
response = await self.llm.agenerate(prompt)
return response.content if response else content[:max_length]
# === 智能打标 ===
async def suggest_tags(
self,
title: str,
content: str,
existing_tags: List[str],
) -> List[str]:
"""智能推荐标签"""
prompt = f"""请根据以下帖子内容,推荐 3-5 个最合适的标签。
标题: {title}
内容: {content[:1000]}...
已有标签: {', '.join(existing_tags) if existing_tags else '无'}
要求:
1. 标签要简洁,2-4 个字
2. 选择最相关的标签
3. 如果已有标签合适可以保留
4. 只输出标签,用逗号分隔,不要其他内容
标签:"""
response = await self.llm.agenerate(prompt)
if not response:
return existing_tags
# 解析标签
tags = [t.strip() for t in response.content.split(",")]
return tags[:5] # 最多 5 个
# === 智能分类 ===
async def classify_category(
self,
title: str,
content: str,
) -> str:
"""智能分类"""
categories = ["instruction", "discussion", "question", "praise", "bug", "other"]
prompt = f"""请为以下帖子选择一个最合适的分类。
标题: {title}
内容: {content[:500]}...
可选分类:
- instruction: 指令/任务请求
- discussion: 讨论/分享
- question: 问题/求助
- praise: 表扬/感谢
- bug: Bug 反馈
- other: 其他
请直接输出分类名称,不要其他内容。
分类:"""
response = await self.llm.agenerate(prompt)
if not response:
return "other"
category = response.content.strip().lower()
if category not in categories:
return "other"
return category
3. Summary Service
3.1 服务实现
class SummaryService:
"""帖子摘要服务"""
def __init__(self, cache: ForumCache):
self.cache = cache
async def get_post_summary(
self,
post_id: str,
post: ForumPost,
) -> str:
"""获取帖子摘要(带缓存)"""
cache_key = f"summary:{post_id}"
# 尝试从缓存获取
cached = await self.cache.get(cache_key)
if cached:
return cached
# 生成摘要
summary = await self._generate_summary(post)
# 存入缓存(1 小时)
await self.cache.set(cache_key, summary, ttl=3600)
return summary
async def get_thread_summary(
self,
post: ForumPost,
replies: List[ForumReply],
) -> str:
"""获取帖子串摘要(主帖+回复摘要)"""
summaries = [await self.get_post_summary(post.id, post)]
# 汇总回复要点
if len(replies) > 0:
summary_prompt = f"""请总结以下回复的核心观点,用 50 字以内概括。
回复列表:
{self._format_replies(replies[:10])}
总结:"""
response = await self.llm.agenerate(summary_prompt)
if response:
summaries.append(f"回复要点: {response.content}")
return "\n\n".join(summaries)
async def invalidate_summary(self, post_id: str) -> None:
"""清除摘要缓存"""
await self.cache.invalidate(f"summary:{post_id}")
4. Agent 自主发帖
4.1 Agent Forum 工具
# agents/tools/forum_tools.py
from typing import List, Optional
class ForumTools:
"""Forum Agent 工具集"""
def __init__(self, forum_service: ForumService, ai_service: ForumAIService):
self.forum = forum_service
self.ai = ai_service
@tool
async def create_forum_post(
title: str,
content: str,
board_id: Optional[str] = None,
category: Optional[str] = None,
tags: Optional[List[str]] = None,
) -> dict:
"""创建论坛帖子
参数:
- title: 帖子标题
- content: 帖子内容
- board_id: 板块 ID(可选)
- category: 分类(可选)
- tags: 标签列表(可选)
"""
data = ForumPostCreate(
title=title,
content=content,
board_id=board_id,
category=category,
tags=tags or [],
)
post = await self.forum.create_post(agent_id=AGENT_ID, data=data)
return {"post_id": post.id, "title": post.title}
@tool
async def reply_to_post(
post_id: str,
content: str,
) -> dict:
"""回复帖子
参数:
- post_id: 帖子 ID
- content: 回复内容
"""
data = ForumReplyCreate(content=content)
reply = await self.forum.create_reply(
post_id=post_id,
agent_id=AGENT_ID,
data=data,
)
return {"reply_id": reply.id, "floor": reply.floor}
@tool
async def search_forum_posts(
query: str,
board_id: Optional[str] = None,
category: Optional[str] = None,
limit: int = 10,
) -> List[dict]:
"""搜索论坛帖子
参数:
- query: 搜索关键词
- board_id: 限定板块(可选)
- category: 限定分类(可选)
- limit: 返回数量(默认 10)
"""
posts = await self.forum.search_posts(
query=query,
board_id=board_id,
category=category,
limit=limit,
)
return [
{"id": p.id, "title": p.title, "summary": p.content[:100]}
for p in posts
]
@tool
async def get_forum_trending(
board_id: Optional[str] = None,
limit: int = 5,
) -> List[dict]:
"""获取热门帖子
参数:
- board_id: 板块 ID(可选)
- limit: 返回数量(默认 5)
"""
posts = await self.forum.get_trending(
board_id=board_id,
limit=limit,
)
return [
{
"id": p.id,
"title": p.title,
"reply_count": p.reply_count,
"view_count": p.view_count,
}
for p in posts
]
4.2 Agent 配置
# agents/prompts/forum_agent.py
FORUM_AGENT_PROMPT = """你是一个活跃的社区成员,可以帮助用户解决问题和参与讨论。
## 你的能力
1. 在论坛发帖分享信息或见解
2. 回复其他用户的帖子
3. 搜索论坛内容
4. 查看热门帖子
## 行为规则
1. 只在有帮助时才发帖/回复,不要刷屏
2. 回复要专业、有建设性
3. 如果用户的问题已经解决,不要重复回答
4. 遇到 Bug 或问题可以主动发帖提醒
5. 定期查看论坛,如果有重要帖子可以参与讨论
## 当前时间
{current_time}
## 用户信息
用户名: {username}
积分: {forum_score}
"""
# Agent 可用的 Forum 工具
FORUM_TOOLS = [
create_forum_post,
reply_to_post,
search_forum_posts,
get_forum_trending,
]
5. 定时任务
5.1 自动回复任务
# tasks/forum_auto_reply.py
from apscheduler.schedulers.asyncio import AsyncIOScheduler
async def auto_reply_task():
"""自动回复任务"""
# 获取需要回复的帖子
posts = await forum_service.get_pending_posts(
min_age_hours=settings.auto_reply_min_hours,
limit=10,
)
for post in posts:
replies = await forum_service.get_replies(post.id)
# 生成回复
response = await ai_service.generate_auto_reply(post, replies)
if response:
# 发布回复
await forum_service.create_reply(
post_id=post.id,
agent_id=AGENT_ID,
data=ForumReplyCreate(content=response),
)
# 更新回复计数
await forum_service.increment_reply_count(post.id)
def setup_forum_scheduler(scheduler: AsyncIOScheduler):
"""配置论坛定时任务"""
# 每小时检查一次自动回复
scheduler.add_job(
auto_reply_task,
"interval",
hours=1,
id="forum_auto_reply",
)
6. 配置项
6.1 AI 配置
class ForumAIConfig:
"""Forum AI 配置"""
# 自动回复
auto_reply_enabled: bool = True
auto_reply_min_hours: int = 24 # 帖子发布 N 小时后才自动回复
auto_reply_max_per_day: int = 10 # 每天最多自动回复 N 条
# 摘要生成
summary_enabled: bool = True
summary_max_length: int = 200
summary_cache_ttl: int = 3600 # 缓存 1 小时
# 智能打标
smart_tagging_enabled: bool = True
smart_tagging_max_tags: int = 5
# 智能分类
smart_classification_enabled: bool = True
# settings.py
class Settings(BaseSettings):
# Forum AI
forum_ai_auto_reply: bool = True
forum_ai_summary: bool = True
forum_ai_smart_tagging: bool = True
7. API 端点
7.1 AI 相关端点
@router.post("/posts/{post_id}/generate-summary")
async def generate_post_summary(
post_id: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""生成帖子摘要"""
result = await db.execute(
select(ForumPost).where(ForumPost.id == post_id)
)
post = result.scalar_one_or_none()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
ai_service = ForumAIService(llm_service, config)
summary = await ai_service.generate_summary(post.content)
return {"summary": summary}
@router.post("/posts/suggest-tags")
async def suggest_post_tags(
title: str,
content: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""推荐帖子标签"""
ai_service = ForumAIService(llm_service, config)
tags = await ai_service.suggest_tags(title, content, [])
return {"tags": tags}
@router.post("/posts/classify")
async def classify_post(
title: str,
content: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""分类帖子"""
ai_service = ForumAIService(llm_service, config)
category = await ai_service.classify_category(title, content)
return {"category": category}
@router.get("/posts/{post_id}/ai-status")
async def get_ai_status(
post_id: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""获取帖子 AI 状态"""
result = await db.execute(
select(ForumPost).where(ForumPost.id == post_id)
)
post = result.scalar_one_or_none()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
# 检查 AI 回复状态
has_ai_reply = any(r.is_ai_reply for r in post.replies)
summary_cached = await cache.get(f"summary:{post_id}")
return {
"has_ai_reply": has_ai_reply,
"summary_available": bool(summary_cached),
}
8. 实现步骤
| 步骤 | 任务 | 优先级 |
|---|---|---|
| 1 | 创建 ForumAIService | 🟢 高 |
| 2 | 实现自动回复 | 🟢 高 |
| 3 | 实现摘要生成 | 🟡 中 |
| 4 | 实现智能打标 | 🟡 中 |
| 5 | 实现智能分类 | 🟡 中 |
| 6 | 创建 ForumTools | 🟢 高 |
| 7 | 配置定时任务 | 🟡 中 |
| 8 | 扩展 API 端点 | 🟡 中 |
| 9 | 单元测试 | 🟡 中 |
9. 核心文件变更
| 文件 | 变更 |
|---|---|
services/forum_ai_service.py |
新增 |
services/summary_service.py |
新增 |
agents/tools/forum_tools.py |
新增 |
agents/prompts/forum_agent.py |
新增 |
tasks/forum_auto_reply.py |
新增 |
routers/forum.py |
扩展 AI 端点 |
10. 工作量估算
| 任务 | 工作量 |
|---|---|
| ForumAIService | 1 天 |
| 自动回复 | 1 天 |
| 摘要/打标/分类 | 1 天 |
| ForumTools | 1 天 |
| 定时任务 | 0.5 天 |
| API 端点 | 0.5 天 |
| 单元测试 | 0.5 天 |
| 总计 | 5.5 天 |
11. 验收标准
- ForumAIService 可正常调用 LLM
- 自动回复功能正常工作
- 摘要生成功能正常
- 智能打标推荐准确
- 智能分类推荐准确
- ForumTools 可被 Agent 调用
- 定时任务正常执行
- API 端点正常工作
- 单元测试覆盖核心逻辑