PMFlow/backend/app/services/signal_capture_service/mixin_capture_entry.py

174 lines
7.0 KiB
Python

from __future__ import annotations
from .shared import *
class SignalCaptureEntryMixin:
def __init__(self, db: AsyncSession):
self.db = db
self.llm = LLMService()
async def capture_from_feishu(
self,
group_chat_id: str,
message_content: str,
sender: str,
tenant_id: int = 1,
proposer_open_id: Optional[str] = None,
captured_at: Optional[datetime] = None,
mentioned_bot_names: Optional[List[str]] = None,
feishu_message_id: Optional[str] = None,
chat_type: Optional[str] = None,
) -> Dict[str, Any]:
"""
从飞书群聊捕获信号
触发条件:
- 提到产品、竞品、技术关键词
- 讨论架构、技术选型
- 分享技术文章
"""
trace_id = f"feishu-{uuid4().hex[:12]}"
captured_dt = captured_at or now_cn_aware()
if captured_dt.tzinfo is None:
captured_dt = captured_dt.replace(tzinfo=CN_TZ)
proposer_name = self._normalize_single_line_text(sender or "未知用户", max_len=80) or "未知用户"
proposer_open_id = self._normalize_single_line_text(proposer_open_id or "", max_len=128) or None
self._emit_ai_think(
"capture_start",
trace_id=trace_id,
tenant_id=tenant_id,
group_chat_id=group_chat_id,
sender=proposer_name,
)
cleaned_message = self._sanitize_feishu_message(message_content)
if not cleaned_message:
self._emit_ai_think("capture_skip_empty", trace_id=trace_id)
return {"status": "ignored", "reason": "empty_or_emoji_only"}
if self._is_non_requirement_message(cleaned_message) and not self._is_question_like(cleaned_message):
self._emit_ai_think(
"capture_skip_noise",
trace_id=trace_id,
message_preview=cleaned_message[:120],
)
return {"status": "ignored", "reason": "non_requirement_noise"}
kernel = AIPMOKernelService(self.db, tenant_id=tenant_id)
try:
pipeline_result = await kernel.process_feishu_message(
group_chat_id=group_chat_id,
message_content=cleaned_message,
sender=proposer_name,
source_message_id=feishu_message_id,
source_sender_open_id=proposer_open_id,
captured_at=captured_dt,
chat_type=chat_type,
)
if not bool(pipeline_result.get("handled")):
self._emit_ai_think(
"capture_kernel_failed",
trace_id=trace_id,
reason=pipeline_result.get("reason"),
error=pipeline_result.get("error"),
)
return {
"status": "error",
"reason": str(pipeline_result.get("reason") or "kernel_not_handled"),
"classification": "error",
"project_scope": "error",
"linked_project_ids": [],
"signal_id": None,
"created_project_id": None,
"auto_tasks": [],
"ai_trace_id": trace_id,
"direct_reply_required": True,
"direct_reply_text": "系统处理中出现异常,本次未执行入库。请稍后重试,或补充更明确的目标/对象/动作。",
}
self._emit_ai_think(
"capture_done_ai_pmo",
trace_id=trace_id,
classification=pipeline_result.get("classification"),
linked_project_ids=pipeline_result.get("linked_project_ids", []),
work_item_id=pipeline_result.get("work_item_id"),
)
pipeline_result = await self._apply_pipeline_post_actions(
trace_id=trace_id,
tenant_id=tenant_id,
cleaned_message=cleaned_message,
proposer_name=proposer_name,
captured_at=captured_dt,
pipeline_result=pipeline_result,
)
try:
await self._sync_feishu_recent_cache(
tenant_id=tenant_id,
group_chat_id=group_chat_id,
sender=proposer_name,
sender_open_id=proposer_open_id,
message=cleaned_message,
pipeline_result=pipeline_result,
)
except Exception as cache_exc:
self._emit_ai_think(
"capture_recent_cache_sync_failed",
trace_id=trace_id,
error=str(cache_exc),
)
try:
asyncio.create_task(
AdaptiveRAGService.ingest_feishu_message_async(
tenant_id=tenant_id,
content=cleaned_message,
sender=proposer_name,
chat_id=group_chat_id,
message_id=feishu_message_id,
captured_at=captured_dt,
project_id=(
int(pipeline_result["linked_project_ids"][0])
if isinstance(pipeline_result.get("linked_project_ids"), list)
and pipeline_result.get("linked_project_ids")
else None
),
)
)
except Exception:
pass
return pipeline_result
except Exception as exc:
self._emit_ai_think("capture_kernel_failed", trace_id=trace_id, error=str(exc))
try:
await self.db.rollback()
except Exception as rollback_exc:
self._emit_ai_think(
"capture_kernel_failed_rollback_failed",
trace_id=trace_id,
error=str(rollback_exc),
)
return {
"status": "error",
"reason": "kernel_failed",
"classification": "error",
"project_scope": "error",
"linked_project_ids": [],
"signal_id": None,
"created_project_id": None,
"auto_tasks": [],
"ai_trace_id": trace_id,
"direct_reply_required": True,
"direct_reply_text": "系统处理中出现异常,本次未执行入库。请稍后重试,或补充更明确的目标/对象/动作。",
}
return await self._capture_from_feishu_legacy_flow(
trace_id=trace_id,
group_chat_id=group_chat_id,
message_content=message_content,
cleaned_message=cleaned_message,
proposer_name=proposer_name,
proposer_open_id=proposer_open_id,
mentioned_bot_names=mentioned_bot_names,
tenant_id=tenant_id,
captured_dt=captured_dt,
)