diff --git a/config/fastgpt.example.json b/config/fastgpt.example.json index e616962..ca88edb 100644 --- a/config/fastgpt.example.json +++ b/config/fastgpt.example.json @@ -45,9 +45,7 @@ "user_speech_timeout_sec": 0.2 }, "agent": { - "system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.", - "greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", - "greeting_mode": "fixed" + "greeting_mode": "fastgpt_opener" }, "services": { "stt": { @@ -72,8 +70,7 @@ "chat_id": null, "variables": {}, "detail": false, - "timeout_sec": 60.0, - "send_system_prompt": false + "timeout_sec": 60.0 }, "tts": { "provider": "xfyun", diff --git a/config/fastgpt.state.xfyun.json b/config/fastgpt.state.xfyun.json index 33c8206..6d62bcf 100644 --- a/config/fastgpt.state.xfyun.json +++ b/config/fastgpt.state.xfyun.json @@ -48,9 +48,7 @@ "idle_prompt_text": "你好,请问还在吗?" }, "agent": { - "system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.", - "greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", - "greeting_mode": "fixed", + "greeting_mode": "fastgpt_opener", "response_state": { "enabled": true, "tag": "state", @@ -81,8 +79,7 @@ "chat_id": null, "variables": {}, "detail": false, - "timeout_sec": 60.0, - "send_system_prompt": false + "timeout_sec": 60.0 }, "tts": { "provider": "xfyun_super", diff --git a/config/fastgpt.xfyun.dfn.json b/config/fastgpt.xfyun.dfn.json index 0cc63b5..a40a3b4 100644 --- a/config/fastgpt.xfyun.dfn.json +++ b/config/fastgpt.xfyun.dfn.json @@ -55,9 +55,7 @@ "user_speech_timeout_sec": 0.2 }, "agent": { - "system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.", - "greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", - "greeting_mode": "fixed", + "greeting_mode": "fastgpt_opener", "response_state": { "enabled": false, "tag": "state", @@ -88,8 +86,7 @@ "chat_id": null, "variables": {}, "detail": false, - "timeout_sec": 60.0, - "send_system_prompt": false + "timeout_sec": 60.0 }, "tts": { "provider": "xfyun", diff --git a/config/fastgpt.xfyun.json b/config/fastgpt.xfyun.json index d23727d..b91ee6d 100644 --- a/config/fastgpt.xfyun.json +++ b/config/fastgpt.xfyun.json @@ -45,9 +45,7 @@ "user_speech_timeout_sec": 0.2 }, "agent": { - "system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.", - "greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", - "greeting_mode": "fixed", + "greeting_mode": "fastgpt_opener", "response_state": { "enabled": false, "tag": "state", @@ -78,8 +76,7 @@ "chat_id": null, "variables": {}, "detail": false, - "timeout_sec": 60.0, - "send_system_prompt": false + "timeout_sec": 60.0 }, "tts": { "provider": "xfyun", diff --git a/config/fastgpt.xfyun.supertts.json b/config/fastgpt.xfyun.supertts.json index 3477052..b454295 100644 --- a/config/fastgpt.xfyun.supertts.json +++ b/config/fastgpt.xfyun.supertts.json @@ -48,9 +48,7 @@ "idle_prompt_text": "你好,请问还在吗?" }, "agent": { - "system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.", - "greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", - "greeting_mode": "fixed", + "greeting_mode": "fastgpt_opener", "response_state": { "enabled": false, "tag": "state", @@ -81,8 +79,7 @@ "chat_id": null, "variables": {}, "detail": false, - "timeout_sec": 60.0, - "send_system_prompt": false + "timeout_sec": 60.0 }, "tts": { "provider": "xfyun_super", diff --git a/engine/config.py b/engine/config.py index 42bf3d8..60a31a7 100644 --- a/engine/config.py +++ b/engine/config.py @@ -147,7 +147,6 @@ class LLMConfig: variables: dict[str, str] = field(default_factory=dict) detail: bool = False timeout_sec: float = 60.0 - send_system_prompt: bool = False @property def is_fastgpt(self) -> bool: @@ -160,7 +159,7 @@ class LLMConfig: @property def uses_local_context_history(self) -> bool: """Whether the pipeline should seed and maintain local LLM context history.""" - return not self.is_fastgpt or self.send_system_prompt + return not self.is_fastgpt @dataclass(frozen=True) @@ -233,9 +232,11 @@ def config_from_dict(data: dict) -> EngineConfig: agent = _dict(data.get("agent")) if agent.get("greeting") == "": agent["greeting"] = None - if agent.get("greeting_mode") not in (None, "generated", "fixed", "off"): - raise ValueError("agent.greeting_mode must be one of: generated, fixed, off") - response_state = ResponseStateConfig(**_dict(agent.pop("response_state"))) + if agent.get("greeting_mode") not in (None, "generated", "fixed", "off", "fastgpt_opener"): + raise ValueError( + "agent.greeting_mode must be one of: generated, fixed, off, fastgpt_opener" + ) + response_state = ResponseStateConfig(**_dict(agent.pop("response_state", None))) if response_state.max_prefix_chars < 1: raise ValueError("agent.response_state.max_prefix_chars must be greater than 0") if not response_state.tag: @@ -255,6 +256,10 @@ def config_from_dict(data: dict) -> EngineConfig: llm["app_id"] = None if not isinstance(llm.get("variables"), dict): llm["variables"] = {} + if agent.get("greeting_mode") == "fastgpt_opener" and llm["provider"] != "fastgpt": + raise ValueError( + "agent.greeting_mode='fastgpt_opener' requires services.llm.provider='fastgpt'" + ) turn = _dict(data.get("turn")) vad = _dict(turn.get("vad")) diff --git a/engine/fastgpt_llm.py b/engine/fastgpt_llm.py index d3c0466..d3fb01b 100644 --- a/engine/fastgpt_llm.py +++ b/engine/fastgpt_llm.py @@ -170,7 +170,6 @@ class FastGPTLLMService(LLMService): base_url: str, chat_id: str | None = None, app_id: str | None = None, - send_system_prompt: bool = False, greeting_prompt: str | None = None, timeout: float = 60.0, settings: FastGPTLLMSettings | None = None, @@ -183,7 +182,6 @@ class FastGPTLLMService(LLMService): self._chat_id = chat_id or f"voice_{uuid.uuid4().hex[:16]}" self._app_id = (app_id or "").strip() - self._send_system_prompt = send_system_prompt self._greeting_prompt = (greeting_prompt or "你好").strip() or "你好" self._client = AsyncChatClient( api_key=api_key, @@ -246,6 +244,8 @@ class FastGPTLLMService(LLMService): return _first_nonempty_text( chat_config.get("welcomeText"), app_payload.get("welcomeText"), + app_payload.get("opener"), + app_payload.get("intro"), ) async def fetch_welcome_text(self) -> str | None: @@ -261,7 +261,7 @@ class FastGPTLLMService(LLMService): response.raise_for_status() text = self._welcome_text_from_init_payload(response.json()) if text: - logger.info(f"FastGPT welcomeText loaded for appId={self._app_id}") + logger.info(f"FastGPT app opener loaded for appId={self._app_id}") return text or None except FastGPTError as exc: logger.warning(f"FastGPT chat init failed: {exc}") @@ -279,26 +279,15 @@ class FastGPTLLMService(LLMService): def _build_fastgpt_messages(self, context: LLMContext) -> list[dict[str, str]]: raw_messages = context.get_messages() - messages: list[dict[str, str]] = [] - - if self._send_system_prompt: - for message in raw_messages: - if not isinstance(message, dict) or message.get("role") != "system": - continue - text = _message_text(message) - if text: - messages.append({"role": "system", "content": text}) for message in reversed(raw_messages): if not isinstance(message, dict) or message.get("role") != "user": continue text = _message_text(message) if text: - messages.append({"role": "user", "content": text}) - return messages + return [{"role": "user", "content": text}] - messages.append({"role": "user", "content": self._greeting_prompt}) - return messages + return [{"role": "user", "content": self._greeting_prompt}] async def _process_context(self, context: LLMContext) -> None: messages = self._build_fastgpt_messages(context) diff --git a/engine/pipeline.py b/engine/pipeline.py index 6dd88eb..cb178a8 100644 --- a/engine/pipeline.py +++ b/engine/pipeline.py @@ -196,15 +196,17 @@ async def run_pipeline_with_serializer( logger.info(f"{client_label} websocket client connected") if config.agent.greeting_mode == "fixed" and config.agent.greeting: await task.queue_frames([TTSSpeakFrame(config.agent.greeting)]) - elif config.agent.greeting_mode == "generated": + elif config.agent.greeting_mode == "fastgpt_opener": if isinstance(llm, FastGPTLLMService): welcome = await llm.fetch_welcome_text() if welcome: await task.queue_frames([TTSSpeakFrame(welcome)]) else: - await task.queue_frames([LLMRunFrame()]) + logger.warning("FastGPT opener requested but no opener text was returned") else: - await task.queue_frames([LLMRunFrame()]) + raise RuntimeError("agent.greeting_mode='fastgpt_opener' requires FastGPT LLM service") + elif config.agent.greeting_mode == "generated": + await task.queue_frames([LLMRunFrame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(_transport, _client): diff --git a/engine/services.py b/engine/services.py index 1446e30..20778bc 100644 --- a/engine/services.py +++ b/engine/services.py @@ -63,7 +63,6 @@ def create_llm_service( base_url=config.base_url or "http://localhost:3000", chat_id=chat_id or config.chat_id, app_id=config.app_id, - send_system_prompt=config.send_system_prompt, greeting_prompt=greeting_prompt, timeout=config.timeout_sec, settings=FastGPTLLMSettings(