feat(assistant): 可执行操作改造,支持实时查询与任务触发
- 新增 assistant_actions.py 动作注册表(list_topics/get_system_stats/get_task_status/trigger_task/search_web 等) - assistant.py 增加规则意图识别优先执行,失败则走 LLM ACTION 格式 - 格式化返回结果,前端无感知 - 支持查看系统统计、选题、任务日志、手动触发定时任务
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
"""
|
||||
AI Assistant 可执行操作注册表
|
||||
"""
|
||||
import json
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
from ..database import SessionLocal
|
||||
from ..models import Topic, ContentTask, TaskLog, SystemConfig, TopicField, Case
|
||||
from ..core.scheduler import scheduler as bg_scheduler
|
||||
|
||||
# -------------------- 工具函数 --------------------
|
||||
|
||||
def list_topics(
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
status: Optional[str] = None,
|
||||
field: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""获取选题列表,支持分页和筛选"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(Topic)
|
||||
if status:
|
||||
query = query.filter(Topic.status == status)
|
||||
if field:
|
||||
query = query.filter(Topic.field_name == field)
|
||||
total = query.count()
|
||||
items = query.order_by(Topic.priority.desc(), Topic.created_at.desc()) \
|
||||
.offset((page - 1) * page_size) \
|
||||
.limit(page_size) \
|
||||
.all()
|
||||
result_items = []
|
||||
for t in items:
|
||||
result_items.append({
|
||||
"id": t.id,
|
||||
"title": t.title,
|
||||
"field": t.field_name,
|
||||
"status": t.status,
|
||||
"priority": t.priority,
|
||||
"priority_score": t.priority_score,
|
||||
"created_at": t.created_at.isoformat() if t.created_at else None
|
||||
})
|
||||
return {
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"items": result_items
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_topic(topic_id: str) -> Dict[str, Any]:
|
||||
"""获取单个选题详情"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
t = db.query(Topic).filter(Topic.id == topic_id).first()
|
||||
if not t:
|
||||
return {"error": f"选题 {topic_id} 不存在"}
|
||||
return {
|
||||
"id": t.id,
|
||||
"title": t.title,
|
||||
"field": t.field_name,
|
||||
"status": t.status,
|
||||
"priority": t.priority,
|
||||
"priority_score": t.priority_score,
|
||||
"core_concept": t.core_concept,
|
||||
"audience_pain": t.audience_pain,
|
||||
"unique_angle": t.unique_angle,
|
||||
"cases": t.cases or [],
|
||||
"tags": t.tags or [],
|
||||
"platform_urls": t.platform_urls or {},
|
||||
"created_at": t.created_at.isoformat() if t.created_at else None
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_recent_task_logs(limit: int = 10) -> List[Dict[str, Any]]:
|
||||
"""获取最近的任务执行日志"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
logs = db.query(TaskLog).order_by(TaskLog.started_at.desc()).limit(limit).all()
|
||||
return [{
|
||||
"id": l.id,
|
||||
"module_id": l.module_id,
|
||||
"status": l.status,
|
||||
"message": l.message,
|
||||
"started_at": l.started_at.isoformat() if l.started_at else None,
|
||||
"finished_at": l.finished_at.isoformat() if l.finished_at else None,
|
||||
"triggered_by": l.triggered_by
|
||||
} for l in logs]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_system_stats() -> Dict[str, Any]:
|
||||
"""获取系统概览统计数据"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
total_topics = db.query(Topic).count()
|
||||
pending = db.query(Topic).filter(Topic.status == "pending").count()
|
||||
review = db.query(Topic).filter(Topic.status == "review").count()
|
||||
draft = db.query(Topic).filter(Topic.status == "draft").count()
|
||||
ready = db.query(Topic).filter(Topic.status == "ready").count()
|
||||
published = db.query(Topic).filter(Topic.status == "published").count()
|
||||
total_cases = db.query(Case).count()
|
||||
week_ago = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=7)
|
||||
new_this_week = db.query(Topic).filter(Topic.created_at >= week_ago).count()
|
||||
return {
|
||||
"topics": {
|
||||
"total": total_topics,
|
||||
"pending": pending,
|
||||
"review": review,
|
||||
"draft": draft,
|
||||
"ready": ready,
|
||||
"published": published,
|
||||
"new_this_week": new_this_week
|
||||
},
|
||||
"cases": total_cases,
|
||||
"date": datetime.now(timezone.utc).isoformat()
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_task_status(module_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
"""查询任务日志(如 collector、generator、optimizer 等)"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(TaskLog)
|
||||
if module_id:
|
||||
query = query.filter(TaskLog.module_id == module_id)
|
||||
logs = query.order_by(TaskLog.started_at.desc()).limit(10).all()
|
||||
return [{
|
||||
"id": l.id,
|
||||
"module_id": l.module_id,
|
||||
"status": l.status,
|
||||
"message": l.message,
|
||||
"started_at": l.started_at.isoformat() if l.started_at else None,
|
||||
"finished_at": l.finished_at.isoformat() if l.finished_at else None,
|
||||
"triggered_by": l.triggered_by
|
||||
} for l in logs]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def trigger_task(module_id: str) -> Dict[str, Any]:
|
||||
"""手动触发定时任务(如 collector、generator、optimizer、metrics_sync 等)"""
|
||||
# 只允许触发特定任务
|
||||
allowed = ["scheduled_collect", "scheduled_generate", "scheduled_optimize",
|
||||
"scheduled_optimize_sources", "scheduled_metrics_sync", "scheduled_trends"]
|
||||
if module_id not in allowed:
|
||||
return {"error": f"不允许手动触发该任务: {module_id}"}
|
||||
job_id = module_id if module_id.startswith("scheduled_") else f"scheduled_{module_id}"
|
||||
# 检查任务是否存在
|
||||
job = bg_scheduler.get_job(job_id)
|
||||
if not job:
|
||||
return {"error": f"任务不存在: {job_id}"}
|
||||
try:
|
||||
# 如果任务正在运行,返回提示
|
||||
# APScheduler 的 run_job 会立即执行
|
||||
bg_scheduler.run_job(job_id)
|
||||
return {"success": True, "message": f"任务 {job_id} 已触发执行"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def search_web(query: str, max_results: int = 5) -> List[Dict[str, Any]]:
|
||||
"""联网搜索(通过本项目的 search_utils)"""
|
||||
try:
|
||||
from ...scripts.search_utils import search as web_search
|
||||
results = web_search(query, max_results)
|
||||
return {"success": True, "results": results, "count": len(results)}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def list_system_configs() -> List[Dict[str, Any]]:
|
||||
"""列出系统配置项"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
configs = db.query(SystemConfig).all()
|
||||
return [{"key": c.key, "value": c.value, "description": c.description} for c in configs]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# Action registry
|
||||
ACTION_REGISTRY = {
|
||||
"list_topics": {
|
||||
"func": list_topics,
|
||||
"description": "获取选题列表,支持分页(page, page_size)和筛选(status, field)",
|
||||
"params_schema": {
|
||||
"page": {"type": "integer", "default": 1, "desc": "页码"},
|
||||
"page_size": {"type": "integer", "default": 20, "desc": "每页条数"},
|
||||
"status": {"type": "string", "enum": ["pending", "review", "draft", "ready", "published"], "desc": "按状态筛选"},
|
||||
"field": {"type": "string", "desc": "按领域筛选"}
|
||||
}
|
||||
},
|
||||
"get_topic": {
|
||||
"func": get_topic,
|
||||
"description": "获取单个选题详情",
|
||||
"params_schema": {
|
||||
"topic_id": {"type": "string", "desc": "选题ID,如 A01 或 LIVING-001-26"}
|
||||
}
|
||||
},
|
||||
"get_recent_task_logs": {
|
||||
"func": get_recent_task_logs,
|
||||
"description": "获取最近的任务执行日志",
|
||||
"params_schema": {
|
||||
"limit": {"type": "integer", "default": 10, "desc": "返回条数"}
|
||||
}
|
||||
},
|
||||
"get_system_stats": {
|
||||
"func": get_system_stats,
|
||||
"description": "获取系统概览统计(选题总数、各状态数量、案例数等)",
|
||||
"params_schema": {}
|
||||
},
|
||||
"get_task_status": {
|
||||
"func": get_task_status,
|
||||
"description": "查询任务日志,可按模块ID筛选",
|
||||
"params_schema": {
|
||||
"module_id": {"type": "string", "desc": "可选:筛选特定模块ID(如 scheduled_collect)"}
|
||||
}
|
||||
},
|
||||
"trigger_task": {
|
||||
"func": trigger_task,
|
||||
"description": "手动触发定时任务(采集、生成、优化、指标同步等)",
|
||||
"params_schema": {
|
||||
"module_id": {"type": "string", "desc": "任务模块ID:scheduled_collect/scheduled_generate/scheduled_optimize/scheduled_optimize_sources/scheduled_metrics_sync/scheduled_trends"}
|
||||
}
|
||||
},
|
||||
"search_web": {
|
||||
"func": search_web,
|
||||
"description": "执行网络搜索,返回结构化结果",
|
||||
"params_schema": {
|
||||
"query": {"type": "string", "desc": "搜索关键词"},
|
||||
"max_results": {"type": "integer", "default": 5, "desc": "返回结果数量"}
|
||||
}
|
||||
},
|
||||
"list_system_configs": {
|
||||
"func": list_system_configs,
|
||||
"description": "列出所有系统配置项(key/value)",
|
||||
"params_schema": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user