Update project and configurations
This commit is contained in:
323
intelligent_cabin/app/core/bootstrap.py
Normal file
323
intelligent_cabin/app/core/bootstrap.py
Normal file
@@ -0,0 +1,323 @@
|
||||
from app.core.config import settings
|
||||
from app.plugins.base import PluginRegistry
|
||||
from app.plugins.mock import MockPluginExecutor
|
||||
from app.services.agent_service import AgentService
|
||||
from app.services.classifier import (
|
||||
BertIntentClassifier,
|
||||
IntentClassifier,
|
||||
JointBertIntentClassifier,
|
||||
MockIntentClassifier,
|
||||
RemoteIntentClassifier,
|
||||
)
|
||||
from app.services.config_loader import ConfigLoader
|
||||
from app.services.intent_registry import IntentRegistry
|
||||
from app.services.joint_nlu import JointBertNLU
|
||||
from app.services.knowledge_llm import DashScopeKnowledgeLLM
|
||||
from app.services.knowledge_store import KnowledgeStore
|
||||
from app.services.multi_intent_detector import (
|
||||
BertMultiIntentDetector,
|
||||
JointBertMultiIntentDetector,
|
||||
MultiIntentDetector,
|
||||
)
|
||||
from app.services.planner import (
|
||||
CompositeWorkflowPlanner,
|
||||
DashScopeWorkflowPlanner,
|
||||
HeuristicWorkflowPlanner,
|
||||
TemplateWorkflowPlanner,
|
||||
WorkflowPlanner,
|
||||
)
|
||||
from app.services.response_policy import ResponsePolicy
|
||||
from app.services.rewrite_engine import ContextRewriteEngine
|
||||
from app.services.router import (
|
||||
HeuristicSlotExtractor,
|
||||
JointBertSlotExtractor,
|
||||
IntentRouter,
|
||||
Router,
|
||||
build_matcher_pipeline,
|
||||
)
|
||||
from app.services.session_store import InMemorySessionStore, RedisSessionStore, SessionStore
|
||||
from app.services.social import DashScopeSocialResponder, SocialResponder, SocialRouter
|
||||
|
||||
|
||||
def build_session_store(session_backend: str | None = None) -> SessionStore:
|
||||
backend = session_backend or settings.session_backend
|
||||
if backend == "memory":
|
||||
return InMemorySessionStore()
|
||||
if backend == "redis":
|
||||
return RedisSessionStore(
|
||||
redis_url=settings.redis_url,
|
||||
key_prefix=settings.redis_key_prefix,
|
||||
ttl_seconds=settings.session_ttl_seconds,
|
||||
)
|
||||
raise ValueError(f"Unsupported session backend: {backend}")
|
||||
|
||||
|
||||
def build_router(
|
||||
intent_registry: IntentRegistry,
|
||||
matcher_pipeline: str | None = None,
|
||||
classifier_backend: str | None = None,
|
||||
classifier: IntentClassifier | None = None,
|
||||
joint_nlu: JointBertNLU | None = None,
|
||||
) -> Router:
|
||||
active_pipeline = matcher_pipeline or settings.matcher_pipeline
|
||||
matcher_stages = [stage.strip() for stage in active_pipeline.split(",") if stage.strip()]
|
||||
if not matcher_stages:
|
||||
matcher_stages = ["classifier"]
|
||||
if matcher_stages != ["classifier"]:
|
||||
raise ValueError("Only classifier matcher pipeline is supported in bert-first mode")
|
||||
if settings.slot_extractor_backend not in {"heuristic", "joint_bert"}:
|
||||
raise ValueError(f"Unsupported slot extractor backend: {settings.slot_extractor_backend}")
|
||||
classifier = classifier or build_classifier(
|
||||
matcher_pipeline=active_pipeline,
|
||||
classifier_backend=classifier_backend,
|
||||
joint_nlu=joint_nlu,
|
||||
)
|
||||
if settings.slot_extractor_backend == "heuristic":
|
||||
slot_extractor = HeuristicSlotExtractor()
|
||||
else:
|
||||
if joint_nlu is None:
|
||||
raise ValueError("slot_extractor_backend=joint_bert requires a Joint NLU runtime")
|
||||
slot_extractor = JointBertSlotExtractor(joint_nlu)
|
||||
return IntentRouter(
|
||||
matcher=build_matcher_pipeline(
|
||||
intent_registry,
|
||||
matcher_stages,
|
||||
classifier=classifier,
|
||||
route_to_cloud_threshold=settings.local_route_to_cloud_threshold,
|
||||
clarify_margin_threshold=settings.local_clarify_margin_threshold,
|
||||
classifier_execute_score_threshold=settings.local_classifier_execute_score_threshold,
|
||||
classifier_execute_margin_threshold=settings.local_classifier_execute_margin_threshold,
|
||||
),
|
||||
slot_extractor=slot_extractor,
|
||||
)
|
||||
|
||||
|
||||
def build_classifier(
|
||||
matcher_pipeline: str | None = None,
|
||||
classifier_backend: str | None = None,
|
||||
joint_nlu: JointBertNLU | None = None,
|
||||
) -> IntentClassifier | None:
|
||||
active_pipeline = matcher_pipeline or settings.matcher_pipeline
|
||||
active_backend = classifier_backend or settings.classifier_backend
|
||||
if "classifier" not in active_pipeline:
|
||||
return None
|
||||
fallback = MockIntentClassifier(
|
||||
threshold=settings.classifier_threshold,
|
||||
top_k=settings.classifier_top_k,
|
||||
)
|
||||
if active_backend == "mock":
|
||||
return fallback
|
||||
if active_backend == "bert":
|
||||
classifier = BertIntentClassifier(
|
||||
model_path=settings.classifier_model_path,
|
||||
threshold=settings.classifier_bert_threshold,
|
||||
label_map_path=settings.classifier_label_map_path or None,
|
||||
fallback=fallback,
|
||||
top_k=settings.classifier_top_k,
|
||||
)
|
||||
if settings.classifier_warmup_enabled:
|
||||
classifier.warmup(settings.classifier_warmup_text)
|
||||
return classifier
|
||||
if active_backend == "joint_bert":
|
||||
runtime = joint_nlu or build_joint_nlu()
|
||||
classifier = JointBertIntentClassifier(
|
||||
nlu=runtime,
|
||||
threshold=settings.joint_nlu_intent_threshold if settings.joint_nlu_intent_threshold > 0 else 0.0,
|
||||
top_k=settings.joint_nlu_top_k,
|
||||
)
|
||||
if settings.classifier_warmup_enabled:
|
||||
classifier.warmup(settings.classifier_warmup_text)
|
||||
return classifier
|
||||
if active_backend == "remote":
|
||||
return RemoteIntentClassifier(
|
||||
endpoint=settings.classifier_remote_url,
|
||||
timeout_seconds=settings.classifier_remote_timeout_seconds,
|
||||
threshold=settings.classifier_threshold,
|
||||
fallback=fallback,
|
||||
label_map_path=settings.classifier_label_map_path or None,
|
||||
top_k=settings.classifier_top_k,
|
||||
)
|
||||
raise ValueError(f"Unsupported classifier backend: {active_backend}")
|
||||
|
||||
|
||||
def build_agent_service() -> AgentService:
|
||||
return build_agent_service_with_runtime()
|
||||
|
||||
|
||||
def build_agent_service_with_runtime(
|
||||
matcher_pipeline: str | None = None,
|
||||
classifier_backend: str | None = None,
|
||||
session_backend: str | None = None,
|
||||
) -> AgentService:
|
||||
runtime_bundle = load_runtime_bundle()
|
||||
intent_registry = runtime_bundle.intent_registry
|
||||
active_classifier_backend = classifier_backend or settings.classifier_backend
|
||||
needs_joint_nlu = active_classifier_backend == "joint_bert" or settings.slot_extractor_backend == "joint_bert"
|
||||
joint_nlu = build_joint_nlu() if needs_joint_nlu else None
|
||||
classifier = build_classifier(
|
||||
matcher_pipeline=matcher_pipeline or settings.matcher_pipeline,
|
||||
classifier_backend=active_classifier_backend,
|
||||
joint_nlu=joint_nlu,
|
||||
)
|
||||
planner_clause_classifier = (
|
||||
classifier
|
||||
if settings.planner_clause_classifier_enabled and active_classifier_backend in {"bert", "remote", "joint_bert"}
|
||||
else None
|
||||
)
|
||||
multi_intent_detector = build_multi_intent_detector(
|
||||
classifier_backend=classifier_backend,
|
||||
joint_nlu=joint_nlu,
|
||||
)
|
||||
plugin_registry = PluginRegistry()
|
||||
MockPluginExecutor().register(plugin_registry)
|
||||
return AgentService(
|
||||
intent_registry=intent_registry,
|
||||
router=build_router(
|
||||
intent_registry,
|
||||
matcher_pipeline=matcher_pipeline,
|
||||
classifier_backend=active_classifier_backend,
|
||||
classifier=classifier,
|
||||
joint_nlu=joint_nlu,
|
||||
),
|
||||
plugins=plugin_registry,
|
||||
session_store=build_session_store(session_backend=session_backend),
|
||||
rewrite_engine=runtime_bundle.rewrite_engine,
|
||||
response_policy=ResponsePolicy(
|
||||
templates=runtime_bundle.response_templates,
|
||||
intent_hints=runtime_bundle.intent_hints,
|
||||
),
|
||||
dialog_rules=runtime_bundle.dialog_rules,
|
||||
dialog_act_engine=runtime_bundle.dialog_act_engine,
|
||||
planner=build_planner(
|
||||
runtime_bundle.workflow_templates,
|
||||
clause_classifier=planner_clause_classifier,
|
||||
multi_intent_detector=multi_intent_detector,
|
||||
joint_nlu=joint_nlu,
|
||||
),
|
||||
social_router=SocialRouter(),
|
||||
social_responder=build_social_responder(),
|
||||
knowledge_llm=build_knowledge_llm(),
|
||||
)
|
||||
|
||||
|
||||
def build_intent_registry() -> IntentRegistry:
|
||||
return load_runtime_bundle().intent_registry
|
||||
|
||||
|
||||
def load_runtime_bundle():
|
||||
return ConfigLoader(
|
||||
domain_path=settings.domain_config_path,
|
||||
action_path=settings.action_config_path,
|
||||
response_path=settings.response_config_path,
|
||||
form_path=settings.form_config_path,
|
||||
rule_path=settings.rule_config_path,
|
||||
dialog_act_path=settings.dialog_act_config_path,
|
||||
workflow_path=settings.workflow_config_path,
|
||||
legacy_intent_path=settings.intent_config_path,
|
||||
context_rewrite_path=settings.context_rewrite_config_path,
|
||||
).load()
|
||||
|
||||
|
||||
def build_joint_nlu() -> JointBertNLU:
|
||||
runtime = JointBertNLU(
|
||||
model_path=settings.joint_nlu_model_path,
|
||||
intent_threshold=settings.joint_nlu_intent_threshold if settings.joint_nlu_intent_threshold > 0 else None,
|
||||
top_k=settings.joint_nlu_top_k,
|
||||
)
|
||||
if settings.classifier_warmup_enabled:
|
||||
runtime.warmup(settings.classifier_warmup_text)
|
||||
return runtime
|
||||
|
||||
|
||||
def build_multi_intent_detector(
|
||||
classifier_backend: str | None = None,
|
||||
joint_nlu: JointBertNLU | None = None,
|
||||
) -> MultiIntentDetector | None:
|
||||
active_backend = classifier_backend or settings.classifier_backend
|
||||
if not settings.planner_multi_intent_detector_enabled:
|
||||
return None
|
||||
if active_backend not in {"bert", "joint_bert"}:
|
||||
return None
|
||||
if active_backend == "joint_bert":
|
||||
runtime = joint_nlu or build_joint_nlu()
|
||||
detector = JointBertMultiIntentDetector(
|
||||
nlu=runtime,
|
||||
threshold=settings.planner_multi_intent_detector_threshold if settings.planner_multi_intent_detector_threshold > 0 else None,
|
||||
top_k=settings.planner_multi_intent_detector_top_k,
|
||||
max_labels=settings.planner_multi_intent_detector_max_labels,
|
||||
)
|
||||
if settings.classifier_warmup_enabled:
|
||||
detector.warmup(settings.classifier_warmup_text)
|
||||
return detector
|
||||
detector_model_path = settings.planner_multi_intent_detector_model_path or settings.classifier_model_path
|
||||
detector = BertMultiIntentDetector(
|
||||
model_path=detector_model_path,
|
||||
threshold=settings.planner_multi_intent_detector_threshold,
|
||||
top_k=settings.planner_multi_intent_detector_top_k,
|
||||
max_labels=settings.planner_multi_intent_detector_max_labels,
|
||||
)
|
||||
if settings.classifier_warmup_enabled:
|
||||
detector.warmup(settings.classifier_warmup_text)
|
||||
return detector
|
||||
|
||||
|
||||
def build_planner(
|
||||
workflow_templates=None,
|
||||
clause_classifier: IntentClassifier | None = None,
|
||||
multi_intent_detector: MultiIntentDetector | None = None,
|
||||
joint_nlu: JointBertNLU | None = None,
|
||||
) -> WorkflowPlanner:
|
||||
template_planner = TemplateWorkflowPlanner(
|
||||
workflow_templates,
|
||||
clause_classifier=clause_classifier,
|
||||
multi_intent_detector=multi_intent_detector,
|
||||
joint_nlu=joint_nlu,
|
||||
classifier_weight=settings.planner_clause_classifier_weight,
|
||||
model_only_threshold=settings.planner_clause_model_only_threshold,
|
||||
)
|
||||
local_first = CompositeWorkflowPlanner(
|
||||
[
|
||||
template_planner,
|
||||
HeuristicWorkflowPlanner(
|
||||
clause_classifier=clause_classifier,
|
||||
multi_intent_detector=multi_intent_detector,
|
||||
joint_nlu=joint_nlu,
|
||||
classifier_weight=settings.planner_clause_classifier_weight,
|
||||
model_only_threshold=settings.planner_clause_model_only_threshold,
|
||||
),
|
||||
]
|
||||
)
|
||||
if settings.planner_backend == "heuristic":
|
||||
return local_first
|
||||
if settings.planner_backend == "dashscope":
|
||||
cloud_planner = DashScopeWorkflowPlanner(
|
||||
base_url=settings.planner_base_url,
|
||||
api_key=settings.planner_api_key,
|
||||
model_name=settings.planner_model_name,
|
||||
timeout_seconds=settings.planner_timeout_seconds,
|
||||
fallback=local_first,
|
||||
joint_nlu=joint_nlu,
|
||||
)
|
||||
return CompositeWorkflowPlanner([local_first, cloud_planner])
|
||||
raise ValueError(f"Unsupported planner backend: {settings.planner_backend}")
|
||||
|
||||
|
||||
def build_social_responder() -> SocialResponder:
|
||||
return DashScopeSocialResponder(
|
||||
base_url=settings.planner_base_url,
|
||||
api_key=settings.planner_api_key,
|
||||
model_name=settings.planner_model_name,
|
||||
timeout_seconds=settings.planner_timeout_seconds,
|
||||
)
|
||||
|
||||
|
||||
def build_knowledge_llm() -> DashScopeKnowledgeLLM:
|
||||
"""构建知识库 LLM 问答器(与 planner 共用 DashScope 配置)。"""
|
||||
store = KnowledgeStore(settings.knowledge_dir)
|
||||
return DashScopeKnowledgeLLM(
|
||||
base_url=settings.planner_base_url,
|
||||
api_key=settings.planner_api_key,
|
||||
model_name=settings.planner_model_name,
|
||||
knowledge_store=store,
|
||||
timeout_seconds=12.0,
|
||||
)
|
||||
Reference in New Issue
Block a user