feat(server): 完善 LLM 集成基础设施与规则配置
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
# ============================================
|
||||||
|
# JC Video Recognize - 服务端配置模板
|
||||||
|
# ============================================
|
||||||
|
# 使用方法: 复制本文件为 .env 并填入真实配置
|
||||||
|
# cp .env.example .env
|
||||||
|
# ⚠️ 严禁将真实 API Key / 密码 提交到 Git!
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# --- 服务器 ---
|
||||||
|
API_HOST=0.0.0.0
|
||||||
|
API_PORT=8000
|
||||||
|
|
||||||
|
# --- 检测 ---
|
||||||
|
DETECTION_DEFAULT_CONFIDENCE=0.5
|
||||||
|
DETECTION_MIN_CONFIDENCE=0.3
|
||||||
|
|
||||||
|
# --- LLM 二次判断 (MVP-3) ---
|
||||||
|
# 支持的 provider: openai (兼容协议) / mock (离线测试)
|
||||||
|
# 火山方舟接入示例:
|
||||||
|
# LLM_API_BASE=https://ark.cn-beijing.volces.com/api/v3
|
||||||
|
# LLM_MODEL=<你在火山方舟控制台开通的 Endpoint ID>
|
||||||
|
LLM_ENABLED=false
|
||||||
|
LLM_PROVIDER=mock
|
||||||
|
LLM_API_BASE=https://ark.cn-beijing.volces.com/api/v3
|
||||||
|
LLM_API_KEY=your-api-key-here
|
||||||
|
LLM_MODEL=doubao-vision-pro-32k
|
||||||
|
LLM_TIMEOUT=20.0
|
||||||
|
LLM_MAX_RETRIES=2
|
||||||
|
LLM_MAX_TOKENS=512
|
||||||
|
LLM_TEMPERATURE=0.0
|
||||||
|
LLM_MAX_CONCURRENCY=2
|
||||||
|
LLM_IMAGE_MAX_SIDE=768
|
||||||
|
|
||||||
|
# --- LLM 触发器 (D26-D28) ---
|
||||||
|
# [生产模式] 推荐: HITS=3, CONFIDENCE=0.55, COOLDOWN=20.0
|
||||||
|
# [测试模式] 调低阈值: HITS=1, CONFIDENCE=0.1, COOLDOWN=2.0
|
||||||
|
LLM_TRIGGER_ENABLED=true
|
||||||
|
LLM_TRIGGER_WINDOW_SECONDS=3.0
|
||||||
|
LLM_TRIGGER_MIN_CONSECUTIVE_HITS=3
|
||||||
|
LLM_TRIGGER_MIN_AVG_CONFIDENCE=0.55
|
||||||
|
LLM_TRIGGER_COOLDOWN_SECONDS=20.0
|
||||||
|
|
||||||
|
# --- 融合策略 (D30) ---
|
||||||
|
# strategy: weighted / conservative / llm_priority
|
||||||
|
FUSION_STRATEGY=weighted
|
||||||
|
FUSION_YOLO_WEIGHT=0.4
|
||||||
|
FUSION_LLM_WEIGHT=0.6
|
||||||
|
FUSION_SUPPRESS_ON_LLM_NEGATIVE=true
|
||||||
|
FUSION_FALLBACK_TO_YOLO=true
|
||||||
|
|
||||||
|
# --- 成本追踪 / 降级 (D34) ---
|
||||||
|
# daily_budget_usd=0.0 表示不限制预算
|
||||||
|
LLM_COST_DAILY_BUDGET_USD=0.0
|
||||||
|
LLM_COST_ERROR_RATE_THRESHOLD=0.5
|
||||||
|
LLM_COST_COOLDOWN_SECONDS=60.0
|
||||||
|
|
||||||
|
# --- 事件引擎 ---
|
||||||
|
EVENT_DEDUP_WINDOW_SECONDS=30.0
|
||||||
|
EVENT_RULES_DIR=config/rules
|
||||||
|
EVENT_MAX_ACTIVE_EVENTS=1000
|
||||||
|
|
||||||
|
# --- MQTT (可选) ---
|
||||||
|
MQTT_ENABLED=false
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
# 打架检测预警规则 (MVP-1)
|
# 打架检测预警规则 (MVP-1)
|
||||||
|
# [测试模式] min_confidence 已调低到 0.3 用于触发测试
|
||||||
|
# [生产模式] 推荐: high=0.55, critical=0.75
|
||||||
rules:
|
rules:
|
||||||
- name: fight_high
|
- name: fight_high
|
||||||
event_type: fight
|
event_type: fight
|
||||||
enabled: true
|
enabled: true
|
||||||
min_confidence: 0.55
|
min_confidence: 0.3
|
||||||
severity: high
|
severity: high
|
||||||
min_bbox_area: 800
|
min_bbox_area: 100
|
||||||
description: 检测到打架/暴力行为,触发高级预警
|
description: 检测到打架/暴力行为,触发高级预警
|
||||||
|
|
||||||
- name: fight_critical_continuous
|
- name: fight_critical_continuous
|
||||||
event_type: fight
|
event_type: fight
|
||||||
enabled: true
|
enabled: true
|
||||||
min_confidence: 0.75
|
min_confidence: 0.5
|
||||||
severity: critical
|
severity: critical
|
||||||
min_bbox_area: 1200
|
min_bbox_area: 100
|
||||||
description: 检测到高置信度打架行为,立即触发最高级别预警
|
description: 检测到高置信度打架行为,立即触发最高级别预警
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ import logging
|
|||||||
from api import detection, models
|
from api import detection, models
|
||||||
from api.alerts import get_broadcaster
|
from api.alerts import get_broadcaster
|
||||||
from api.rtsp import init_stream_manager
|
from api.rtsp import init_stream_manager
|
||||||
|
from api.llm import init_llm_api, router as llm_router
|
||||||
|
from api.rules import init_rules_api, router as rules_router
|
||||||
|
from core.settings import get_settings
|
||||||
from services.model_service import ModelService
|
from services.model_service import ModelService
|
||||||
from services.camera_service import CameraService
|
from services.camera_service import CameraService
|
||||||
|
from services.llm_analysis_service import create_llm_service_from_settings
|
||||||
|
from services.llm_cost_tracker import init_global_tracker
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -74,6 +79,29 @@ async def lifespan(app: FastAPI):
|
|||||||
app.include_router(rtsp_router, prefix="/api")
|
app.include_router(rtsp_router, prefix="/api")
|
||||||
app.include_router(alerts_router, prefix="") # WebSocket 路径已带 /ws 前缀
|
app.include_router(alerts_router, prefix="") # WebSocket 路径已带 /ws 前缀
|
||||||
|
|
||||||
|
# 初始化 LLM 集成 (MVP-3)
|
||||||
|
settings = get_settings()
|
||||||
|
llm_service = create_llm_service_from_settings(settings)
|
||||||
|
cost_tracker = init_global_tracker(
|
||||||
|
daily_budget_usd=settings.llm_cost.daily_budget_usd,
|
||||||
|
history_days=settings.llm_cost.history_days,
|
||||||
|
recent_window=settings.llm_cost.recent_window,
|
||||||
|
error_rate_threshold=settings.llm_cost.error_rate_threshold,
|
||||||
|
cooldown_seconds=settings.llm_cost.cooldown_seconds,
|
||||||
|
)
|
||||||
|
init_llm_api(llm_service, cost_tracker)
|
||||||
|
app.include_router(llm_router, prefix="/api")
|
||||||
|
logger.info(
|
||||||
|
"LLM 集成已加载: enabled=%s provider=%s",
|
||||||
|
settings.llm.enabled,
|
||||||
|
settings.llm.provider,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 初始化规则配置 API (MVP-3 / D33),与 DetectionService 共享同一规则引擎
|
||||||
|
detection_service = detection.get_detection_service(model_service)
|
||||||
|
init_rules_api(lambda: detection_service.rule_engine)
|
||||||
|
app.include_router(rules_router, prefix="/api")
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# 关闭时清理资源
|
# 关闭时清理资源
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
"""事件引擎子包 (MVP-1 / P1-P2 / P5)。
|
"""事件引擎子包 (MVP-1 / P1-P2 / P5 + MVP-3 / D26-D28)。
|
||||||
|
|
||||||
模块结构::
|
模块结构::
|
||||||
|
|
||||||
decision_engine.py 决策引擎 (置信度评估 + 事件类型映射)
|
decision_engine.py 决策引擎 (置信度评估 + 事件类型映射)
|
||||||
rule_engine.py 规则引擎 (YAML 驱动)
|
rule_engine.py 规则引擎 (YAML 驱动)
|
||||||
aggregator.py 事件聚合器 (时间窗口去重)
|
aggregator.py 事件聚合器 (时间窗口去重)
|
||||||
|
frame_accumulator.py 多帧累积分析器 (MVP-3)
|
||||||
|
llm_trigger.py LLM 触发决策器 (MVP-3)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .decision_engine import EventDecisionEngine
|
|
||||||
from .rule_engine import AlertRuleEngine
|
|
||||||
from .aggregator import EventAggregator
|
from .aggregator import EventAggregator
|
||||||
|
from .decision_engine import EventDecisionEngine
|
||||||
|
from .frame_accumulator import AccumulationEntry, MultiFrameAccumulator
|
||||||
|
from .llm_trigger import LLMTrigger, TriggerDecision
|
||||||
|
from .rule_engine import AlertRuleEngine
|
||||||
|
|
||||||
__all__ = ["EventDecisionEngine", "AlertRuleEngine", "EventAggregator"]
|
__all__ = [
|
||||||
|
"EventDecisionEngine",
|
||||||
|
"AlertRuleEngine",
|
||||||
|
"EventAggregator",
|
||||||
|
"MultiFrameAccumulator",
|
||||||
|
"AccumulationEntry",
|
||||||
|
"LLMTrigger",
|
||||||
|
"TriggerDecision",
|
||||||
|
]
|
||||||
|
|||||||
@@ -68,3 +68,97 @@ export const detectionApi = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// MVP-2 / MVP-3 扩展 API: 摄像头管理 / 规则配置 / LLM 状态
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摄像头 (RTSP 流) 管理 API (MVP-2 / D11-D14)
|
||||||
|
*/
|
||||||
|
export const cameraApi = {
|
||||||
|
list() {
|
||||||
|
return api.get('/rtsp/streams')
|
||||||
|
},
|
||||||
|
get(streamId) {
|
||||||
|
return api.get(`/rtsp/streams/${encodeURIComponent(streamId)}`)
|
||||||
|
},
|
||||||
|
add(payload) {
|
||||||
|
return api.post('/rtsp/streams', payload)
|
||||||
|
},
|
||||||
|
remove(streamId) {
|
||||||
|
return api.delete(`/rtsp/streams/${encodeURIComponent(streamId)}`)
|
||||||
|
},
|
||||||
|
start(streamId) {
|
||||||
|
return api.post(`/rtsp/streams/${encodeURIComponent(streamId)}/start`)
|
||||||
|
},
|
||||||
|
stop(streamId) {
|
||||||
|
return api.post(`/rtsp/streams/${encodeURIComponent(streamId)}/stop`)
|
||||||
|
},
|
||||||
|
updateConfig(streamId, payload) {
|
||||||
|
return api.put(`/rtsp/streams/${encodeURIComponent(streamId)}/config`, payload)
|
||||||
|
},
|
||||||
|
startAll() {
|
||||||
|
return api.post('/rtsp/start-all')
|
||||||
|
},
|
||||||
|
stopAll() {
|
||||||
|
return api.post('/rtsp/stop-all')
|
||||||
|
},
|
||||||
|
health() {
|
||||||
|
return api.get('/rtsp/health')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则配置 API (MVP-3 / D33)
|
||||||
|
*/
|
||||||
|
export const ruleApi = {
|
||||||
|
list() {
|
||||||
|
return api.get('/rules')
|
||||||
|
},
|
||||||
|
get(name) {
|
||||||
|
return api.get(`/rules/${encodeURIComponent(name)}`)
|
||||||
|
},
|
||||||
|
create(payload) {
|
||||||
|
return api.post('/rules', payload)
|
||||||
|
},
|
||||||
|
update(name, payload) {
|
||||||
|
return api.put(`/rules/${encodeURIComponent(name)}`, payload)
|
||||||
|
},
|
||||||
|
remove(name) {
|
||||||
|
return api.delete(`/rules/${encodeURIComponent(name)}`)
|
||||||
|
},
|
||||||
|
reload() {
|
||||||
|
return api.post('/rules/reload')
|
||||||
|
},
|
||||||
|
stats() {
|
||||||
|
return api.get('/rules/_stats')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLM 状态 / 成本 / 控制 API (MVP-3 / D29-D34)
|
||||||
|
*/
|
||||||
|
export const llmApi = {
|
||||||
|
status() {
|
||||||
|
return api.get('/llm/status')
|
||||||
|
},
|
||||||
|
cost() {
|
||||||
|
return api.get('/llm/cost')
|
||||||
|
},
|
||||||
|
costHistory(days = 7) {
|
||||||
|
return api.get('/llm/cost/history', { params: { days } })
|
||||||
|
},
|
||||||
|
costRecords(limit = 20) {
|
||||||
|
return api.get('/llm/cost/records', { params: { limit } })
|
||||||
|
},
|
||||||
|
enable() {
|
||||||
|
return api.post('/llm/enable')
|
||||||
|
},
|
||||||
|
disable() {
|
||||||
|
return api.post('/llm/disable')
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
return api.post('/llm/reset')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user