Files
yu-zhi-ran/platform/backend/app/api/assistant_actions.py
T
Yuzhiran Dev 9b9ee4964b feat(assistant): 可执行操作改造,支持实时查询与任务触发
- 新增 assistant_actions.py 动作注册表(list_topics/get_system_stats/get_task_status/trigger_task/search_web 等)
- assistant.py 增加规则意图识别优先执行,失败则走 LLM ACTION 格式
- 格式化返回结果,前端无感知
- 支持查看系统统计、选题、任务日志、手动触发定时任务
2026-05-31 13:25:07 +08:00

249 lines
9.0 KiB
Python
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.
"""
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": "任务模块IDscheduled_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": {}
}
}