Files
JARVIS/development-doc/plan/forum-update/phase-f-4-ai-integration.md

653 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase F.4AI 集成
日期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 核心服务
```python
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 服务实现
```python
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 工具
```python
# 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 配置
```python
# 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 自动回复任务
```python
# 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 配置
```python
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 相关端点
```python
@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 端点正常工作
- [ ] 单元测试覆盖核心逻辑