117 lines
4.5 KiB
Python
117 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
"""
|
|
Proactive Signal Capture Service - 主动式信号捕获服务
|
|
|
|
核心功能:
|
|
1. 飞书群聊监控 - 自动捕获群内讨论的产品/技术相关内容
|
|
2. 浏览器标签监控 - 检测用户访问的技术文档、竞品页面
|
|
3. GitHub 仓库监控 - 跟踪上游仓库更新、竞品开源项目
|
|
4. HackerNews 自动抓取 - 定时抓取技术前沿信息
|
|
|
|
每个信号都会自动:
|
|
- 评估战略重要性
|
|
- 提取关键技术栈
|
|
- 生成任务建议
|
|
- 推送给相关角色
|
|
"""
|
|
import asyncio
|
|
import inspect
|
|
import json
|
|
import re
|
|
from uuid import uuid4
|
|
from datetime import datetime, timedelta, timezone
|
|
from typing import Any, Dict, List, Optional
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.future import select
|
|
from sqlalchemy import and_, text
|
|
|
|
from ....core.logging import get_logger
|
|
from ....core.database import AsyncSessionLocal
|
|
from ....models import (
|
|
Signal, Project, ProjectDomain, User,
|
|
ProjectSignalLink, Task, TruthNode,
|
|
SkillTag, UserProject, UserWorkload, UserSkillBinding,
|
|
)
|
|
from ....config.ai_prompts import (
|
|
PROMPT_FEISHU_PROJECT_SIGNAL_CLASSIFICATION,
|
|
PROMPT_FEISHU_TASK_ORCHESTRATION,
|
|
)
|
|
from ...llm_service import LLMService
|
|
from ...notification_service import NotificationService
|
|
from ... import workload as workload_service
|
|
from ...adaptive_rag_service import AdaptiveRAGService
|
|
from ...ai_pmo.workflow_semantic_planner.constants import (
|
|
NON_REQUIREMENT_CONTAINS,
|
|
NON_REQUIREMENT_EXACT,
|
|
REQUIREMENT_HINTS,
|
|
)
|
|
from ...ai_orchestration_run import AIOrchestrationRunService
|
|
from ...context_cache_service import context_cache_service
|
|
from ..signal_source_orchestration import SignalSourceOrchestrationService
|
|
from ...ai_pmo.ai_pmo_kernel import AIPMOKernelService
|
|
from ....utils.project_members import ensure_project_default_members
|
|
from ....utils.sequence import ensure_project_id_sequence, ensure_signal_id_sequence
|
|
from app.utils.thread_context import build_feishu_thread_key
|
|
|
|
|
|
from ....utils.timezone import CN_TZ, now_cn_aware
|
|
EMOJI_UNICODE_RE = re.compile(
|
|
r"[\U0001F300-\U0001F6FF\U0001F700-\U0001F77F\U0001F780-\U0001F7FF"
|
|
r"\U0001F800-\U0001F8FF\U0001F900-\U0001F9FF\U0001FA00-\U0001FAFF"
|
|
r"\U00002600-\U000026FF\U00002700-\U000027BF]+"
|
|
)
|
|
ONLY_PUNCT_OR_SPACE_RE = re.compile(r"^[\s\W_]+$", re.UNICODE)
|
|
MENTION_TAG_RE = re.compile(r"</?at[^>]*>", re.IGNORECASE)
|
|
CONTROL_CHAR_RE = re.compile(r"[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+")
|
|
|
|
logger = get_logger(__name__)
|
|
AI_THINK_STAGE_ZH = {
|
|
"capture_start": "开始处理飞书消息",
|
|
"capture_skip_empty": "消息为空,忽略",
|
|
"capture_skip_noise": "识别为噪音消息,忽略",
|
|
"capture_done_ai_pmo": "AI PMO kernel 已完成决策闭环",
|
|
"capture_kernel_failed": "AI PMO kernel 执行异常",
|
|
"capture_kernel_failed_rollback_failed": "kernel 异常后回滚失败",
|
|
"classification_done": "语义分类完成",
|
|
"classification_parse_failed": "语义分类结果解析失败",
|
|
"capture_skip_not_requirement": "非需求消息,忽略",
|
|
"capture_skip_non_requirement": "判定为非需求,忽略",
|
|
"signal_created": "信号已创建",
|
|
"project_link_done": "项目关联完成",
|
|
"project_link_failed": "项目关联失败",
|
|
"task_orchestrate_failed": "任务编排失败",
|
|
"capture_done": "飞书消息处理完成",
|
|
"pipeline_post_orchestrate_failed": "分流后置任务编排失败",
|
|
"pipeline_post_update_signal_failed": "分流后置信号结果回写失败",
|
|
"pipeline_post_source_onboard_failed": "分流后置信号源接入编排失败",
|
|
"task_plan_llm_done": "任务编排 LLM 返回成功",
|
|
"task_plan_llm_failed": "任务编排 LLM 调用失败",
|
|
"task_created": "任务已创建",
|
|
"charter_auto_created": "自动创建宪章节点",
|
|
"hn_story_captured": "HackerNews 信号已捕获",
|
|
"hn_monitor_error": "HackerNews 监控异常",
|
|
"new_project_created_from_signal": "已根据信号自动创建项目",
|
|
"draft_new_project_brief_failed": "新项目摘要生成失败",
|
|
}
|
|
|
|
|
|
def _build_feishu_rag_conversation_key(
|
|
*,
|
|
source_chat_id: Optional[str],
|
|
source_sender_open_id: Optional[str],
|
|
sender: Optional[str],
|
|
source_message_id: Optional[str] = None,
|
|
thread_context: Optional[dict[str, Any]] = None,
|
|
) -> str:
|
|
return build_feishu_thread_key(
|
|
source_chat_id=source_chat_id,
|
|
source_sender_open_id=source_sender_open_id,
|
|
sender=sender,
|
|
source_message_id=source_message_id,
|
|
thread_context=thread_context,
|
|
)
|
|
|
|
|
|
__all__ = [name for name in globals() if not name.startswith("__")]
|