feat(agents): Phase 8.4-10.5 built-in plugins, bundled skills, coordinator
This commit is contained in:
652
development-doc/plan/forum-update/phase-f-4-ai-integration.md
Normal file
652
development-doc/plan/forum-update/phase-f-4-ai-integration.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# 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 核心服务
|
||||
|
||||
```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 端点正常工作
|
||||
- [ ] 单元测试覆盖核心逻辑
|
||||
Reference in New Issue
Block a user