mirror of
https://github.com/xszyou/Fay.git
synced 2026-09-01 15:12:25 +08:00
feat: 添加大小模型交互路由功能
实现小模型路由 + 大模型深度处理的分层架构: - 新增 llm/model_router.py 路由模块,小模型负责意图分类和复杂度评估 - 简单任务(闲聊、问候等)由小模型直接处理,复杂任务升级到大模型 - config_util.py 新增 small_model_api_key/base_url/engine 等配置项 - nlp_cognitive_stream.py 集成路由决策逻辑,支持按路由结果选择模型 - flask_server.py 新增 /api/model-router-status 接口查询路由状态 https://claude.ai/code/session_01DSLxgwX1HFtgRrkeoj7uYL
This commit is contained in:
@@ -863,6 +863,15 @@ def api_get_system_status():
|
||||
except Exception as e:
|
||||
return jsonify({'server': False, 'digital_human': False, 'remote_audio': False, 'error': str(e)}), 500
|
||||
|
||||
@__app.route('/api/model-router-status', methods=['GET'])
|
||||
def api_model_router_status():
|
||||
"""获取大小模型交互路由的配置状态"""
|
||||
try:
|
||||
from llm.model_router import get_route_info
|
||||
return jsonify(get_route_info())
|
||||
except Exception as e:
|
||||
return jsonify({'enabled': False, 'error': str(e)}), 500
|
||||
|
||||
@__app.route('/api/get-audio-config', methods=['GET'])
|
||||
def api_get_audio_config():
|
||||
"""获取麦克风和扬声器的配置状态"""
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
大小模型交互路由模块
|
||||
|
||||
设计思路:
|
||||
- 小模型(轻量/低成本):处理简单问答、闲聊、意图识别等低复杂度任务
|
||||
- 大模型(强推理):处理复杂推理、工具编排、多步规划、长文生成等高复杂度任务
|
||||
- 路由器:基于小模型的意图分类结果,决定由哪个模型处理当前请求
|
||||
|
||||
路由策略:
|
||||
1. 小模型先对用户输入进行意图分类和复杂度评估
|
||||
2. 简单任务(闲聊、问候、简单事实问答)由小模型直接回复
|
||||
3. 复杂任务(推理、工具调用、代码、分析)升级到大模型处理
|
||||
4. 小模型置信度不足时主动升级到大模型
|
||||
"""
|
||||
|
||||
import json
|
||||
import threading
|
||||
from typing import Optional, Tuple, Literal
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
from utils import util
|
||||
import utils.config_util as cfg
|
||||
|
||||
# 路由决策类型
|
||||
RouteDecision = Literal["small", "large"]
|
||||
|
||||
# 模块级锁,保护 LLM 实例的懒加载
|
||||
_init_lock = threading.Lock()
|
||||
|
||||
# 懒加载的模型实例
|
||||
_small_llm: Optional[ChatOpenAI] = None
|
||||
_large_llm: Optional[ChatOpenAI] = None
|
||||
|
||||
# 上次初始化时使用的配置指纹,用于检测配置变更后重建实例
|
||||
_small_llm_fingerprint: Optional[str] = None
|
||||
_large_llm_fingerprint: Optional[str] = None
|
||||
|
||||
|
||||
ROUTE_CLASSIFY_PROMPT = """\
|
||||
你是一个任务复杂度分类器。根据用户的输入,判断该任务应该由"小模型"还是"大模型"处理。
|
||||
|
||||
## 分类标准
|
||||
|
||||
**小模型处理(输出 "small")**:
|
||||
- 日常问候、闲聊、打招呼
|
||||
- 简单事实性问答(天气、时间、基础常识)
|
||||
- 简短的情感回应、安慰、鼓励
|
||||
- 简单的信息查询或确认
|
||||
- 上下文不复杂的简短对话
|
||||
|
||||
**大模型处理(输出 "large")**:
|
||||
- 需要多步推理或逻辑分析的问题
|
||||
- 代码编写、调试、技术问题
|
||||
- 需要调用工具/搜索/计算的任务
|
||||
- 长文创作、文案撰写、翻译
|
||||
- 复杂的数据分析或总结
|
||||
- 涉及专业领域知识的深度问答
|
||||
- 用户明确要求"详细分析"、"深入讲解"等
|
||||
- 多轮追问、需要综合上下文的复杂对话
|
||||
|
||||
## 输出格式
|
||||
|
||||
严格按照以下JSON格式输出,不要输出任何其他内容:
|
||||
{"route": "small"或"large", "confidence": 0.0到1.0之间的数字, "reason": "简短的分类理由"}
|
||||
"""
|
||||
|
||||
|
||||
def _build_fingerprint(api_key: Optional[str], base_url: Optional[str], model: Optional[str]) -> str:
|
||||
"""构建配置指纹,用于检测配置变更。"""
|
||||
return f"{api_key}|{base_url}|{model}"
|
||||
|
||||
|
||||
def _ensure_small_llm() -> Optional[ChatOpenAI]:
|
||||
"""懒加载并返回小模型实例,配置变更时自动重建。"""
|
||||
global _small_llm, _small_llm_fingerprint
|
||||
|
||||
api_key = cfg.small_model_api_key
|
||||
base_url = cfg.small_model_base_url
|
||||
model = cfg.small_model_engine
|
||||
|
||||
if not all([api_key, base_url, model]):
|
||||
return None
|
||||
|
||||
fp = _build_fingerprint(api_key, base_url, model)
|
||||
if _small_llm is not None and _small_llm_fingerprint == fp:
|
||||
return _small_llm
|
||||
|
||||
with _init_lock:
|
||||
# double-check
|
||||
if _small_llm is not None and _small_llm_fingerprint == fp:
|
||||
return _small_llm
|
||||
try:
|
||||
_small_llm = ChatOpenAI(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
streaming=True,
|
||||
)
|
||||
_small_llm_fingerprint = fp
|
||||
util.log(1, f"小模型已初始化: {model} @ {base_url}")
|
||||
except Exception as exc:
|
||||
util.log(1, f"小模型初始化失败: {exc}")
|
||||
_small_llm = None
|
||||
return _small_llm
|
||||
|
||||
|
||||
def _ensure_large_llm() -> Optional[ChatOpenAI]:
|
||||
"""懒加载并返回大模型实例(即原有的主模型),配置变更时自动重建。"""
|
||||
global _large_llm, _large_llm_fingerprint
|
||||
|
||||
api_key = cfg.key_gpt_api_key
|
||||
base_url = cfg.gpt_base_url
|
||||
model = cfg.gpt_model_engine
|
||||
|
||||
if not all([api_key, base_url, model]):
|
||||
return None
|
||||
|
||||
fp = _build_fingerprint(api_key, base_url, model)
|
||||
if _large_llm is not None and _large_llm_fingerprint == fp:
|
||||
return _large_llm
|
||||
|
||||
with _init_lock:
|
||||
if _large_llm is not None and _large_llm_fingerprint == fp:
|
||||
return _large_llm
|
||||
try:
|
||||
_large_llm = ChatOpenAI(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
streaming=True,
|
||||
)
|
||||
_large_llm_fingerprint = fp
|
||||
util.log(1, f"大模型已初始化: {model} @ {base_url}")
|
||||
except Exception as exc:
|
||||
util.log(1, f"大模型初始化失败: {exc}")
|
||||
_large_llm = None
|
||||
return _large_llm
|
||||
|
||||
|
||||
def is_enabled() -> bool:
|
||||
"""检查大小模型交互功能是否已启用且配置完整。"""
|
||||
cfg.load_config()
|
||||
if not cfg.model_interaction_enabled:
|
||||
return False
|
||||
# 小模型必须有独立配置
|
||||
if not all([cfg.small_model_api_key, cfg.small_model_base_url, cfg.small_model_engine]):
|
||||
return False
|
||||
# 大模型(主模型)必须有配置
|
||||
if not all([cfg.key_gpt_api_key, cfg.gpt_base_url, cfg.gpt_model_engine]):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def classify_request(content: str, has_tools: bool = False) -> Tuple[RouteDecision, float, str]:
|
||||
"""
|
||||
使用小模型对用户请求进行意图分类,决定路由方向。
|
||||
|
||||
Args:
|
||||
content: 用户输入文本
|
||||
has_tools: 当前是否有可用的 MCP 工具
|
||||
|
||||
Returns:
|
||||
(route_decision, confidence, reason)
|
||||
- route_decision: "small" 或 "large"
|
||||
- confidence: 0.0~1.0 置信度
|
||||
- reason: 分类理由
|
||||
"""
|
||||
# 如果有工具可用,直接路由到大模型(工具编排需要强推理能力)
|
||||
if has_tools:
|
||||
return "large", 1.0, "检测到可用工具,需要大模型进行工具编排"
|
||||
|
||||
small_llm = _ensure_small_llm()
|
||||
if small_llm is None:
|
||||
return "large", 1.0, "小模型不可用,回退到大模型"
|
||||
|
||||
try:
|
||||
messages = [
|
||||
SystemMessage(content=ROUTE_CLASSIFY_PROMPT),
|
||||
HumanMessage(content=content),
|
||||
]
|
||||
response = small_llm.invoke(messages)
|
||||
result_text = response.content.strip()
|
||||
|
||||
# 尝试解析 JSON(兼容模型可能输出的 markdown code block)
|
||||
if result_text.startswith("```"):
|
||||
# 去掉 ```json ... ``` 包裹
|
||||
lines = result_text.split("\n")
|
||||
result_text = "\n".join(
|
||||
line for line in lines if not line.strip().startswith("```")
|
||||
)
|
||||
|
||||
result = json.loads(result_text)
|
||||
route = result.get("route", "large")
|
||||
confidence = float(result.get("confidence", 0.5))
|
||||
reason = result.get("reason", "")
|
||||
|
||||
# 置信度不足时升级到大模型
|
||||
if route == "small" and confidence < 0.7:
|
||||
util.log(1, f"[路由] 小模型置信度不足({confidence:.2f}),升级到大模型: {reason}")
|
||||
return "large", confidence, f"置信度不足,升级: {reason}"
|
||||
|
||||
util.log(1, f"[路由] 决策={route}, 置信度={confidence:.2f}, 理由={reason}")
|
||||
return route, confidence, reason
|
||||
|
||||
except json.JSONDecodeError as exc:
|
||||
util.log(1, f"[路由] 分类结果解析失败: {exc}, 原始输出: {result_text}")
|
||||
return "large", 0.5, "分类结果解析失败,回退到大模型"
|
||||
except Exception as exc:
|
||||
util.log(1, f"[路由] 分类请求失败: {exc}")
|
||||
return "large", 0.5, f"分类异常,回退到大模型: {exc}"
|
||||
|
||||
|
||||
def get_llm_for_route(route: RouteDecision) -> ChatOpenAI:
|
||||
"""
|
||||
根据路由决策返回对应的 LLM 实例。
|
||||
|
||||
Args:
|
||||
route: "small" 或 "large"
|
||||
|
||||
Returns:
|
||||
ChatOpenAI 实例
|
||||
"""
|
||||
if route == "small":
|
||||
llm = _ensure_small_llm()
|
||||
if llm is not None:
|
||||
return llm
|
||||
util.log(1, "[路由] 小模型不可用,回退到大模型")
|
||||
|
||||
llm = _ensure_large_llm()
|
||||
if llm is not None:
|
||||
return llm
|
||||
|
||||
# 最终兜底:使用模块级的全局 llm(原有逻辑)
|
||||
util.log(1, "[路由] 大小模型均不可用,使用全局 llm 实例")
|
||||
from llm.nlp_cognitive_stream import llm as global_llm
|
||||
return global_llm
|
||||
|
||||
|
||||
def get_route_info() -> dict:
|
||||
"""返回当前路由配置状态信息,供 API / 调试使用。"""
|
||||
cfg.load_config()
|
||||
return {
|
||||
"enabled": is_enabled(),
|
||||
"model_interaction_enabled": bool(cfg.model_interaction_enabled),
|
||||
"small_model": {
|
||||
"engine": cfg.small_model_engine,
|
||||
"base_url": cfg.small_model_base_url,
|
||||
"configured": bool(cfg.small_model_engine and cfg.small_model_base_url and cfg.small_model_api_key),
|
||||
},
|
||||
"large_model": {
|
||||
"engine": cfg.gpt_model_engine,
|
||||
"base_url": cfg.gpt_base_url,
|
||||
"configured": bool(cfg.gpt_model_engine and cfg.gpt_base_url and cfg.key_gpt_api_key),
|
||||
},
|
||||
}
|
||||
@@ -53,6 +53,7 @@ from core import content_db
|
||||
from core import stream_manager
|
||||
from core import member_db
|
||||
from faymcp import runtime_bridge as mcp_runtime
|
||||
from llm import model_router
|
||||
|
||||
# 加载配置
|
||||
cfg.load_config()
|
||||
@@ -2500,8 +2501,9 @@ def question(content, username, observation=None):
|
||||
|
||||
return final_stream_done
|
||||
|
||||
def run_direct_llm() -> bool:
|
||||
def run_direct_llm(target_llm=None) -> bool:
|
||||
nonlocal full_response_text, accumulated_text, is_first_sentence, messages_buffer
|
||||
use_llm = target_llm if target_llm is not None else llm
|
||||
try:
|
||||
summary_state: AgentState = {
|
||||
"request": content,
|
||||
@@ -2518,7 +2520,7 @@ def question(content, username, observation=None):
|
||||
}
|
||||
|
||||
final_messages = _build_final_messages(summary_state)
|
||||
stream_response_chunks(llm.stream(final_messages))
|
||||
stream_response_chunks(use_llm.stream(final_messages))
|
||||
return True
|
||||
except Exception as exc:
|
||||
util.log(1, f"请求失败: {type(exc).__name__}: {exc}")
|
||||
@@ -2533,12 +2535,32 @@ def question(content, username, observation=None):
|
||||
if not sm.should_stop_generation(username, conversation_id=conversation_id):
|
||||
send_prestart_content()
|
||||
|
||||
workflow_success = False
|
||||
if tool_registry:
|
||||
workflow_success = run_workflow(tool_registry)
|
||||
# === 大小模型交互路由 ===
|
||||
route_decision = None
|
||||
routed_llm = None
|
||||
if model_router.is_enabled():
|
||||
try:
|
||||
route_decision, confidence, reason = model_router.classify_request(
|
||||
content, has_tools=bool(tool_registry)
|
||||
)
|
||||
routed_llm = model_router.get_llm_for_route(route_decision)
|
||||
util.log(1, f"[大小模型路由] 决策={route_decision}, 置信度={confidence:.2f}, 理由={reason}")
|
||||
except Exception as exc:
|
||||
util.log(1, f"[大小模型路由] 路由异常,回退默认流程: {exc}")
|
||||
route_decision = None
|
||||
routed_llm = None
|
||||
|
||||
if (not tool_registry or not workflow_success) and not sm.should_stop_generation(username, conversation_id=conversation_id):
|
||||
run_direct_llm()
|
||||
workflow_success = False
|
||||
if route_decision == "small":
|
||||
# 小模型直接回复,跳过工具工作流
|
||||
run_direct_llm(target_llm=routed_llm)
|
||||
else:
|
||||
# 大模型路径:先尝试工具工作流,失败则走直接 LLM
|
||||
if tool_registry:
|
||||
workflow_success = run_workflow(tool_registry)
|
||||
|
||||
if (not tool_registry or not workflow_success) and not sm.should_stop_generation(username, conversation_id=conversation_id):
|
||||
run_direct_llm(target_llm=routed_llm)
|
||||
|
||||
if not sm.should_stop_generation(username, conversation_id=conversation_id):
|
||||
finalize_stream(force_end=True)
|
||||
|
||||
+243
-220
@@ -82,11 +82,17 @@ config_json_path = None
|
||||
use_bionic_memory = None
|
||||
|
||||
# Embedding API 配置全局变量
|
||||
embedding_api_model = None
|
||||
embedding_api_base_url = None
|
||||
embedding_api_key = None
|
||||
|
||||
SYSTEM_CONFIG_ENV_KEY = 'FAY_SYSTEM_CONF_JSON'
|
||||
embedding_api_model = None
|
||||
embedding_api_base_url = None
|
||||
embedding_api_key = None
|
||||
|
||||
# 小模型配置全局变量(大小模型交互)
|
||||
small_model_api_key = None
|
||||
small_model_base_url = None
|
||||
small_model_engine = None
|
||||
model_interaction_enabled = None
|
||||
|
||||
SYSTEM_CONFIG_ENV_KEY = 'FAY_SYSTEM_CONF_JSON'
|
||||
|
||||
# 避免重复加载配置中心导致日志刷屏
|
||||
_last_loaded_project_id = None
|
||||
@@ -129,76 +135,76 @@ def _refresh_config_center():
|
||||
if env_project_id:
|
||||
CONFIG_SERVER['PROJECT_ID'] = env_project_id
|
||||
|
||||
_refresh_config_center()
|
||||
|
||||
|
||||
def _config_parser_to_dict(parser):
|
||||
data = {}
|
||||
if parser is None:
|
||||
return data
|
||||
for section in parser.sections():
|
||||
data[section] = {}
|
||||
for key, value in parser.items(section):
|
||||
data[section][key] = value
|
||||
return data
|
||||
|
||||
|
||||
def _dict_to_config_parser(data):
|
||||
parser = ConfigParser()
|
||||
if not isinstance(data, dict):
|
||||
return parser
|
||||
for section, items in data.items():
|
||||
if not isinstance(items, dict):
|
||||
continue
|
||||
if not parser.has_section(section):
|
||||
parser.add_section(section)
|
||||
for key, value in items.items():
|
||||
parser.set(section, str(key), '' if value is None else str(value))
|
||||
return parser
|
||||
|
||||
|
||||
def _save_system_config_to_env(parser, project_id=None, source='local'):
|
||||
if parser is None:
|
||||
return
|
||||
payload = {
|
||||
'meta': {
|
||||
'project_id': project_id,
|
||||
'source': source,
|
||||
},
|
||||
'sections': _config_parser_to_dict(parser),
|
||||
}
|
||||
try:
|
||||
os.environ[SYSTEM_CONFIG_ENV_KEY] = json.dumps(payload, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
util.log(2, f"save system.conf to env failed: {str(e)}")
|
||||
|
||||
|
||||
def _load_system_config_from_env(expected_project_id=None):
|
||||
raw_value = os.getenv(SYSTEM_CONFIG_ENV_KEY)
|
||||
if not raw_value:
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = json.loads(raw_value)
|
||||
meta = {}
|
||||
sections = payload
|
||||
if isinstance(payload, dict) and isinstance(payload.get('sections'), dict):
|
||||
meta = payload.get('meta') or {}
|
||||
sections = payload.get('sections') or {}
|
||||
|
||||
env_project_id = meta.get('project_id')
|
||||
if expected_project_id and env_project_id != expected_project_id:
|
||||
return None
|
||||
|
||||
parser = _dict_to_config_parser(sections)
|
||||
if parser.sections():
|
||||
return parser
|
||||
except Exception as e:
|
||||
util.log(2, f"load system.conf from env failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def load_config_from_api(project_id=None):
|
||||
_refresh_config_center()
|
||||
|
||||
|
||||
def _config_parser_to_dict(parser):
|
||||
data = {}
|
||||
if parser is None:
|
||||
return data
|
||||
for section in parser.sections():
|
||||
data[section] = {}
|
||||
for key, value in parser.items(section):
|
||||
data[section][key] = value
|
||||
return data
|
||||
|
||||
|
||||
def _dict_to_config_parser(data):
|
||||
parser = ConfigParser()
|
||||
if not isinstance(data, dict):
|
||||
return parser
|
||||
for section, items in data.items():
|
||||
if not isinstance(items, dict):
|
||||
continue
|
||||
if not parser.has_section(section):
|
||||
parser.add_section(section)
|
||||
for key, value in items.items():
|
||||
parser.set(section, str(key), '' if value is None else str(value))
|
||||
return parser
|
||||
|
||||
|
||||
def _save_system_config_to_env(parser, project_id=None, source='local'):
|
||||
if parser is None:
|
||||
return
|
||||
payload = {
|
||||
'meta': {
|
||||
'project_id': project_id,
|
||||
'source': source,
|
||||
},
|
||||
'sections': _config_parser_to_dict(parser),
|
||||
}
|
||||
try:
|
||||
os.environ[SYSTEM_CONFIG_ENV_KEY] = json.dumps(payload, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
util.log(2, f"save system.conf to env failed: {str(e)}")
|
||||
|
||||
|
||||
def _load_system_config_from_env(expected_project_id=None):
|
||||
raw_value = os.getenv(SYSTEM_CONFIG_ENV_KEY)
|
||||
if not raw_value:
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = json.loads(raw_value)
|
||||
meta = {}
|
||||
sections = payload
|
||||
if isinstance(payload, dict) and isinstance(payload.get('sections'), dict):
|
||||
meta = payload.get('meta') or {}
|
||||
sections = payload.get('sections') or {}
|
||||
|
||||
env_project_id = meta.get('project_id')
|
||||
if expected_project_id and env_project_id != expected_project_id:
|
||||
return None
|
||||
|
||||
parser = _dict_to_config_parser(sections)
|
||||
if parser.sections():
|
||||
return parser
|
||||
except Exception as e:
|
||||
util.log(2, f"load system.conf from env failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def load_config_from_api(project_id=None):
|
||||
global CONFIG_SERVER
|
||||
|
||||
"""
|
||||
@@ -317,6 +323,11 @@ def load_config(force_reload=False):
|
||||
global embedding_api_base_url
|
||||
global embedding_api_key
|
||||
|
||||
global small_model_api_key
|
||||
global small_model_base_url
|
||||
global small_model_engine
|
||||
global model_interaction_enabled
|
||||
|
||||
global CONFIG_SERVER
|
||||
global system_conf_path
|
||||
global config_json_path
|
||||
@@ -327,34 +338,34 @@ def load_config(force_reload=False):
|
||||
|
||||
_refresh_config_center()
|
||||
|
||||
env_project_id = os.getenv('FAY_CONFIG_CENTER_ID')
|
||||
explicit_config_center = bool(env_project_id)
|
||||
using_config_center = explicit_config_center
|
||||
env_system_config = None if force_reload else _load_system_config_from_env(
|
||||
expected_project_id=env_project_id if explicit_config_center else None
|
||||
)
|
||||
if (
|
||||
env_project_id
|
||||
and not force_reload
|
||||
env_project_id = os.getenv('FAY_CONFIG_CENTER_ID')
|
||||
explicit_config_center = bool(env_project_id)
|
||||
using_config_center = explicit_config_center
|
||||
env_system_config = None if force_reload else _load_system_config_from_env(
|
||||
expected_project_id=env_project_id if explicit_config_center else None
|
||||
)
|
||||
if (
|
||||
env_project_id
|
||||
and not force_reload
|
||||
and _last_loaded_config is not None
|
||||
and _last_loaded_project_id == env_project_id
|
||||
and _last_loaded_from_api
|
||||
):
|
||||
return _last_loaded_config
|
||||
|
||||
default_system_conf_path = os.path.join(os.getcwd(), 'system.conf')
|
||||
default_config_json_path = os.path.join(os.getcwd(), 'config.json')
|
||||
cache_system_conf_path = os.path.join(os.getcwd(), 'cache_data', 'system.conf')
|
||||
cache_config_json_path = os.path.join(os.getcwd(), 'cache_data', 'config.json')
|
||||
root_system_conf_exists = env_system_config is not None or os.path.exists(default_system_conf_path)
|
||||
root_config_json_exists = os.path.exists(default_config_json_path)
|
||||
root_config_complete = root_system_conf_exists and root_config_json_exists
|
||||
default_system_conf_path = os.path.join(os.getcwd(), 'system.conf')
|
||||
default_config_json_path = os.path.join(os.getcwd(), 'config.json')
|
||||
cache_system_conf_path = os.path.join(os.getcwd(), 'cache_data', 'system.conf')
|
||||
cache_config_json_path = os.path.join(os.getcwd(), 'cache_data', 'config.json')
|
||||
root_system_conf_exists = env_system_config is not None or os.path.exists(default_system_conf_path)
|
||||
root_config_json_exists = os.path.exists(default_config_json_path)
|
||||
root_config_complete = root_system_conf_exists and root_config_json_exists
|
||||
|
||||
# 构建system.conf和config.json相关路径.
|
||||
config_center_fallback = False
|
||||
if using_config_center:
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
# 构建system.conf和config.json相关路径.
|
||||
config_center_fallback = False
|
||||
if using_config_center:
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
else:
|
||||
if (
|
||||
system_conf_path is None
|
||||
@@ -365,11 +376,11 @@ def load_config(force_reload=False):
|
||||
system_conf_path = default_system_conf_path
|
||||
config_json_path = default_config_json_path
|
||||
|
||||
if not root_config_complete:
|
||||
cache_ready = (env_system_config is not None or os.path.exists(cache_system_conf_path)) and os.path.exists(cache_config_json_path)
|
||||
if (not _bootstrap_loaded_from_api) or (not cache_ready):
|
||||
using_config_center = True
|
||||
config_center_fallback = True
|
||||
if not root_config_complete:
|
||||
cache_ready = (env_system_config is not None or os.path.exists(cache_system_conf_path)) and os.path.exists(cache_config_json_path)
|
||||
if (not _bootstrap_loaded_from_api) or (not cache_ready):
|
||||
using_config_center = True
|
||||
config_center_fallback = True
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
else:
|
||||
@@ -380,132 +391,132 @@ def load_config(force_reload=False):
|
||||
loaded_from_api = False
|
||||
api_attempted = False
|
||||
if using_config_center:
|
||||
if explicit_config_center:
|
||||
util.log(1, f"检测到配置中心参数,优先加载项目配置: {CONFIG_SERVER['PROJECT_ID']}")
|
||||
else:
|
||||
util.log(1, f"未检测到本地system.conf或config.json,尝试从配置中心加载配置: {CONFIG_SERVER['PROJECT_ID']}")
|
||||
api_config = load_config_from_api(CONFIG_SERVER['PROJECT_ID'])
|
||||
if explicit_config_center:
|
||||
util.log(1, f"检测到配置中心参数,优先加载项目配置: {CONFIG_SERVER['PROJECT_ID']}")
|
||||
else:
|
||||
util.log(1, f"未检测到本地system.conf或config.json,尝试从配置中心加载配置: {CONFIG_SERVER['PROJECT_ID']}")
|
||||
api_config = load_config_from_api(CONFIG_SERVER['PROJECT_ID'])
|
||||
api_attempted = True
|
||||
if api_config:
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
loaded_from_api = True
|
||||
if config_center_fallback:
|
||||
_bootstrap_loaded_from_api = True
|
||||
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
forced_loaded = True
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
forced_loaded = True
|
||||
|
||||
_warn_public_config_once()
|
||||
else:
|
||||
util.log(2, "配置中心加载失败,尝试使用缓存配置")
|
||||
util.log(2, "配置中心加载失败,尝试使用缓存配置")
|
||||
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
|
||||
# 如果任一本地文件不存在,直接尝试从API加载
|
||||
if (not sys_conf_exists or not config_json_exists) and not forced_loaded:
|
||||
if using_config_center:
|
||||
if not api_attempted:
|
||||
util.log(1, "配置中心缓存缺失,尝试从配置中心加载配置...")
|
||||
util.log(1, "配置中心缓存缺失,尝试从配置中心加载配置...")
|
||||
api_config = load_config_from_api(CONFIG_SERVER['PROJECT_ID'])
|
||||
api_attempted = True
|
||||
if api_config:
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
loaded_from_api = True
|
||||
if config_center_fallback:
|
||||
_bootstrap_loaded_from_api = True
|
||||
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
|
||||
_warn_public_config_once()
|
||||
else:
|
||||
# 使用项目配置或全局项目配置作为回退来源.
|
||||
util.log(1, f"本地配置文件不完整,尝试从API加载配置...")
|
||||
api_config = load_config_from_api(CONFIG_SERVER['PROJECT_ID'])
|
||||
# 使用项目配置或全局项目配置作为回退来源.
|
||||
util.log(1, f"本地配置文件不完整,尝试从API加载配置...")
|
||||
api_config = load_config_from_api(CONFIG_SERVER['PROJECT_ID'])
|
||||
|
||||
if api_config:
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
util.log(1, "成功从配置中心加载配置")
|
||||
system_config = api_config['system_config']
|
||||
env_system_config = system_config
|
||||
_save_system_config_to_env(system_config, project_id=CONFIG_SERVER['PROJECT_ID'], source='api')
|
||||
config = api_config['config']
|
||||
loaded_from_api = True
|
||||
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
# 将配置中心配置缓存到本地文件.
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
save_api_config_to_local(
|
||||
api_config,
|
||||
system_conf_path,
|
||||
config_json_path,
|
||||
save_config_json=not os.path.exists(config_json_path),
|
||||
save_system_conf=False
|
||||
)
|
||||
|
||||
_warn_public_config_once()
|
||||
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
if using_config_center and (not sys_conf_exists or not config_json_exists):
|
||||
if _last_loaded_config is not None and _last_loaded_from_api:
|
||||
util.log(2, "配置中心缓存不可用,继续使用内存中的配置")
|
||||
util.log(2, "配置中心缓存不可用,继续使用内存中的配置")
|
||||
return _last_loaded_config
|
||||
if config_center_fallback and using_config_center and (not sys_conf_exists or not config_json_exists):
|
||||
cache_ready = (env_system_config is not None or os.path.exists(cache_system_conf_path)) and os.path.exists(cache_config_json_path)
|
||||
cache_ready = (env_system_config is not None or os.path.exists(cache_system_conf_path)) and os.path.exists(cache_config_json_path)
|
||||
if cache_ready:
|
||||
util.log(2, "配置中心不可用,回退使用缓存配置")
|
||||
util.log(2, "配置中心不可用,回退使用缓存配置")
|
||||
using_config_center = False
|
||||
system_conf_path = cache_system_conf_path
|
||||
config_json_path = cache_config_json_path
|
||||
else:
|
||||
util.log(2, "配置中心不可用,回退使用本地配置文件")
|
||||
util.log(2, "配置中心不可用,回退使用本地配置文件")
|
||||
using_config_center = False
|
||||
system_conf_path = default_system_conf_path
|
||||
config_json_path = default_config_json_path
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
sys_conf_exists = env_system_config is not None or os.path.exists(system_conf_path)
|
||||
config_json_exists = os.path.exists(config_json_path)
|
||||
# 如果本地文件存在,从本地文件加载
|
||||
# 加载system.conf
|
||||
if env_system_config is not None:
|
||||
system_config = env_system_config
|
||||
else:
|
||||
system_config = ConfigParser()
|
||||
if os.path.exists(system_conf_path):
|
||||
system_config.read(system_conf_path, encoding='UTF-8')
|
||||
_save_system_config_to_env(
|
||||
system_config,
|
||||
project_id=CONFIG_SERVER['PROJECT_ID'] if using_config_center else None,
|
||||
source='api' if using_config_center else 'local'
|
||||
)
|
||||
|
||||
if not system_config.has_section('key'):
|
||||
system_config.add_section('key')
|
||||
if env_system_config is not None:
|
||||
system_config = env_system_config
|
||||
else:
|
||||
system_config = ConfigParser()
|
||||
if os.path.exists(system_conf_path):
|
||||
system_config.read(system_conf_path, encoding='UTF-8')
|
||||
_save_system_config_to_env(
|
||||
system_config,
|
||||
project_id=CONFIG_SERVER['PROJECT_ID'] if using_config_center else None,
|
||||
source='api' if using_config_center else 'local'
|
||||
)
|
||||
|
||||
if not system_config.has_section('key'):
|
||||
system_config.add_section('key')
|
||||
|
||||
# 从system.conf中读取所有配置项
|
||||
key_ali_nls_key_id = system_config.get('key', 'ali_nls_key_id', fallback=None)
|
||||
@@ -538,6 +549,12 @@ def load_config(force_reload=False):
|
||||
embedding_api_base_url = gpt_base_url # 复用 LLM base_url
|
||||
embedding_api_key = key_gpt_api_key # 复用 LLM api_key
|
||||
|
||||
# 读取小模型配置(大小模型交互),未配置时复用大模型设置
|
||||
small_model_api_key = system_config.get('key', 'small_model_api_key', fallback=None)
|
||||
small_model_base_url = system_config.get('key', 'small_model_base_url', fallback=None)
|
||||
small_model_engine = system_config.get('key', 'small_model_engine', fallback=None)
|
||||
model_interaction_enabled = system_config.get('key', 'model_interaction_enabled', fallback='false').lower() == 'true'
|
||||
|
||||
start_mode = system_config.get('key', 'start_mode', fallback=None)
|
||||
fay_url = system_config.get('key', 'fay_url', fallback=None)
|
||||
# 如果fay_url为空或None,则动态获取本机IP地址
|
||||
@@ -548,12 +565,12 @@ def load_config(force_reload=False):
|
||||
# 更新system_config中的值,但不写入文件
|
||||
if not system_config.has_section('key'):
|
||||
system_config.add_section('key')
|
||||
system_config.set('key', 'fay_url', fay_url)
|
||||
_save_system_config_to_env(
|
||||
system_config,
|
||||
project_id=CONFIG_SERVER['PROJECT_ID'] if using_config_center else None,
|
||||
source='api' if using_config_center else 'local'
|
||||
)
|
||||
system_config.set('key', 'fay_url', fay_url)
|
||||
_save_system_config_to_env(
|
||||
system_config,
|
||||
project_id=CONFIG_SERVER['PROJECT_ID'] if using_config_center else None,
|
||||
source='api' if using_config_center else 'local'
|
||||
)
|
||||
|
||||
# 读取用户配置
|
||||
with codecs.open(config_json_path, encoding='utf-8') as f:
|
||||
@@ -601,6 +618,12 @@ def load_config(force_reload=False):
|
||||
'embedding_api_base_url': embedding_api_base_url,
|
||||
'embedding_api_key': embedding_api_key,
|
||||
|
||||
# 小模型配置(大小模型交互)
|
||||
'small_model_api_key': small_model_api_key,
|
||||
'small_model_base_url': small_model_base_url,
|
||||
'small_model_engine': small_model_engine,
|
||||
'model_interaction_enabled': model_interaction_enabled,
|
||||
|
||||
'source': 'api' if using_config_center else 'local' # 标记配置来源
|
||||
}
|
||||
|
||||
@@ -610,42 +633,42 @@ def load_config(force_reload=False):
|
||||
|
||||
return config_dict
|
||||
|
||||
def save_api_config_to_local(api_config, system_conf_path, config_json_path, save_config_json=True, save_system_conf=False):
|
||||
"""
|
||||
Persist API config to local files.
|
||||
def save_api_config_to_local(api_config, system_conf_path, config_json_path, save_config_json=True, save_system_conf=False):
|
||||
"""
|
||||
Persist API config to local files.
|
||||
|
||||
Args:
|
||||
api_config: API response dict.
|
||||
system_conf_path: Path to system.conf.
|
||||
config_json_path: Path to config.json.
|
||||
save_config_json: Whether to write config.json.
|
||||
Args:
|
||||
api_config: API response dict.
|
||||
system_conf_path: Path to system.conf.
|
||||
config_json_path: Path to config.json.
|
||||
save_config_json: Whether to write config.json.
|
||||
"""
|
||||
try:
|
||||
# 确保目录存在.
|
||||
if save_system_conf and system_conf_path:
|
||||
os.makedirs(os.path.dirname(system_conf_path), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(config_json_path), exist_ok=True)
|
||||
|
||||
# 始终刷新 system.conf.
|
||||
if save_system_conf and system_conf_path:
|
||||
with open(system_conf_path, 'w', encoding='utf-8') as f:
|
||||
api_config['system_config'].write(f)
|
||||
|
||||
# 默认只在首次下载时保存config.json.
|
||||
if save_config_json:
|
||||
with codecs.open(config_json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(api_config['config'], f, ensure_ascii=False, indent=4)
|
||||
if save_system_conf and system_conf_path:
|
||||
util.log(1, f"cached config center files to local: {system_conf_path}, {config_json_path}")
|
||||
return
|
||||
if save_config_json:
|
||||
util.log(1, f"cached config center config.json to local: {config_json_path}")
|
||||
return
|
||||
return
|
||||
|
||||
util.log(1, f"已将配置中心配置缓存到本地文件: {system_conf_path} 和 {config_json_path}")
|
||||
except Exception as e:
|
||||
util.log(2, f"保存配置中心配置到本地文件时出错: {str(e)}")
|
||||
# 确保目录存在.
|
||||
if save_system_conf and system_conf_path:
|
||||
os.makedirs(os.path.dirname(system_conf_path), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(config_json_path), exist_ok=True)
|
||||
|
||||
# 始终刷新 system.conf.
|
||||
if save_system_conf and system_conf_path:
|
||||
with open(system_conf_path, 'w', encoding='utf-8') as f:
|
||||
api_config['system_config'].write(f)
|
||||
|
||||
# 默认只在首次下载时保存config.json.
|
||||
if save_config_json:
|
||||
with codecs.open(config_json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(api_config['config'], f, ensure_ascii=False, indent=4)
|
||||
if save_system_conf and system_conf_path:
|
||||
util.log(1, f"cached config center files to local: {system_conf_path}, {config_json_path}")
|
||||
return
|
||||
if save_config_json:
|
||||
util.log(1, f"cached config center config.json to local: {config_json_path}")
|
||||
return
|
||||
return
|
||||
|
||||
util.log(1, f"已将配置中心配置缓存到本地文件: {system_conf_path} 和 {config_json_path}")
|
||||
except Exception as e:
|
||||
util.log(2, f"保存配置中心配置到本地文件时出错: {str(e)}")
|
||||
|
||||
@synchronized
|
||||
def save_config(config_data):
|
||||
|
||||
Reference in New Issue
Block a user