外观
LLM 调用链路
LLM 调用链路由 LLMConfig、LLMManager 和 provider client 组成。业务层不直接拼接厂商请求,而是通过 RAG response generation、处方 generation 或 Chat use case 的 provider 切换入口间接访问当前 client。
Provider 配置
| Provider | Client | 模型配置 | 协议 |
|---|---|---|---|
moonshot | OpenAICompatClient | moonshot-v1-8k、kimi-k2.5 | OpenAI Chat Completions 兼容 HTTP API。 |
spark | SparkLLMClient | spark_max、spark_ultra | 讯飞星火 WebSocket API。 |
当前生产默认:
| 配置项 | 值 |
|---|---|
LLM_PROVIDER | moonshot |
LLM_MODEL | moonshot-v1-8k |
SPARK_MODEL | spark_max |
凭据从环境变量注入:MOONSHOT_API_KEY / KIMI_API_KEY、SPARK_APP_ID、SPARK_API_KEY、SPARK_API_SECRET。文档和日志不记录真实值。
管理器职责
LLMManager 是统一入口:
| 方法 | 行为 |
|---|---|
_init_default_provider() | 读取 LLMConfig.get_current_provider() 并加载对应 client。 |
_load_client(provider) | 通过 importlib 动态导入 client module 与 class。 |
switch_provider(provider, model) | 切换配置并重新加载 client;失败时尝试回滚到 spark。 |
get_client() | 返回当前 client,缺失时重新初始化。 |
get_current_info() | 返回 provider 和 model 的可展示元信息。 |
Chat API 的请求体可传 provider,SendChatMessageUseCase 会在本次执行开始时调用 llm_manager.switch_provider(...)。切换失败不阻断对话,会使用当前默认厂商。
OpenAI-compatible 路径
OpenAICompatClient 的调用流程:
- 从
LLMConfig读取当前 provider、model、base URL 和参数。 - 校验 API key 是否存在。
- 构造
AsyncOpenAI(api_key, base_url)。 chat_completion(...)合并 system prompt、conversation history 和当前 user message。- 消息超过 10 条时保留 system message 和最近 9 条。
- 调用
client.chat.completions.create(...)。 - 返回
content、reasoning_content、model、provider、usage、created。
kimi-k2.5 配置会通过 extra_body 关闭 thinking,并省略 temperature、top_p、seed、frequency_penalty 等参数。
Spark 路径
SparkLLMClient 的调用流程:
| 阶段 | 行为 |
|---|---|
| 鉴权 URL | 使用 app id、api key、api secret、host、date、request-line 生成 WebSocket 鉴权 URL。 |
| 请求体 | 构造 header、parameter.chat、payload.message.text。 |
| 非流式 | 外层 asyncio.wait_for 包住整个 WebSocket 生命周期。 |
| 流式收集 | 默认 chat_completion(stream=True),内部逐帧收集,对上游仍返回完整 dict。 |
| 历史截断 | 与 OpenAI-compatible 路径一致,保留 system message 和最近 9 条。 |
| 超时 | 会话总超时后抛出异常,由上游 fallback 处理。 |
Spark client 当前返回的 model 字段仍是兼容命名,实际模型由 SPARK_MODEL 和 SparkConfig 解析。
Chat 生成
Chat 中的 LLM 调用由 ResponseGenerationService 承接:
| 步骤 | 行为 |
|---|---|
| 患者画像 | render_patient_profile(...) 只基于已填写用户上下文生成摘要,不调用 LLM。 |
| System prompt | 明确动养AI身份、回复长度原则、非诊断边界、紧急症状就医提示。 |
| User message | 拼接用户问题、专业知识上下文和前 3 个来源。 |
| LLM 调用 | 调用 llm_client.chat_completion(...)。 |
| 失败 fallback | LLM 异常时,从已检索知识上下文整理 1-3 条建议,并保留安全边界提示。 |
| 无证据 fallback | 通过 generate_fallback_response(...) 基于一般健康管理原则回答,并标记一般常识来源。 |
Chat use case 会根据 model_info.fallback 调整响应置信度:正常模型输出为较高置信,fallback 输出为较低置信。
Prompt 结构、患者画像拼接、JSON 输出约束和 fallback 文案在 Prompt 契约 中单独说明。
处方生成
处方生成由 PrescriptionGenerationService 承接:
| 步骤 | 行为 |
|---|---|
| Prompt | 基于年龄、性别、BMI、血压、脉搏、疾病史、手术史、评估上下文、器械、偏好构造系统提示词。 |
| User message | 要求模型输出结构化运动处方 JSON。 |
| 解析 | 从 LLM 文本中解析处方 JSON,并补齐必要字段。 |
| 安全检查 | 使用 SafetyFilter.check_prescription_safety(...) 检查强度、禁忌、年龄和疾病冲突。 |
| 成功返回 | rag_used=false、knowledge_sources=[]、information_source=基于循证医学通识。 |
| 失败 fallback | 生成默认安全处方;再次失败时返回应急处方形状。 |
处方生成的 LLM 输出必须被视为待校验结构化草案,不能绕过后端安全检查直接写入展示。
处方 prompt 的角色、用户摘要、约束注入、JSON 形状和后处理在 Prompt 契约 中单独说明。
失败与降级
| 失败点 | 处理 |
|---|---|
| provider 配置缺失 | client 初始化抛错;manager 初始化记录 warning。 |
| API key 缺失 | provider client 校验失败,不输出真实 key。 |
| provider 切换失败 | Chat 记录 warning,并使用默认厂商继续。 |
| LLM 调用失败且已有 RAG 上下文 | 用 optimize_knowledge_response(...) 整理知识摘要。 |
| LLM 调用失败且处方生成失败 | 返回默认安全处方或应急处方。 |
| Spark WebSocket timeout | 抛出超时异常,由上游 fallback。 |
来源锚点
- Config:
apps/backend_service/app/core/llm_config.py、apps/backend_service/app/core/config.py - Manager:
apps/backend_service/app/external/llm_manager.py - OpenAI-compatible client:
apps/backend_service/app/external/openai_compat_client.py - Spark client:
apps/backend_service/app/external/spark_client.py - Chat generation:
apps/backend_service/app/core/rag/response_generation.py - Prescription generation:
apps/backend_service/app/core/rag/prescription_generation.py - Chat use case:
apps/backend_service/app/use_cases/chat/send_message.py