Standardize LLM clients and expose configurable base URLs.

This commit is contained in:
666ghj
2025-10-09 02:23:08 +08:00
parent 960608cee9
commit 49d4893780
22 changed files with 189 additions and 62 deletions
+6 -3
View File
@@ -12,7 +12,7 @@ import re
# 添加项目根目录到Python路径以导入config # 添加项目根目录到Python路径以导入config
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config import GUIJI_QWEN3_API_KEY from config import GUIJI_QWEN3_API_KEY, GUIJI_QWEN3_BASE_URL
# 添加utils目录到Python路径 # 添加utils目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -30,21 +30,24 @@ class ForumHost:
使用Qwen3-235B模型作为智能主持人 使用Qwen3-235B模型作为智能主持人
""" """
def __init__(self, api_key: str = None): def __init__(self, api_key: str = None, base_url: Optional[str] = None):
""" """
初始化论坛主持人 初始化论坛主持人
Args: Args:
api_key: 硅基流动API密钥,如果不提供则从配置文件读取 api_key: 硅基流动API密钥,如果不提供则从配置文件读取
base_url: 接口基础地址,默认使用配置文件提供的SiliconFlow地址
""" """
self.api_key = api_key or GUIJI_QWEN3_API_KEY self.api_key = api_key or GUIJI_QWEN3_API_KEY
if not self.api_key: if not self.api_key:
raise ValueError("未找到硅基流动API密钥,请在config.py中设置GUIJI_QWEN3_API_KEY") raise ValueError("未找到硅基流动API密钥,请在config.py中设置GUIJI_QWEN3_API_KEY")
self.base_url = base_url or GUIJI_QWEN3_BASE_URL
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.siliconflow.cn/v1" base_url=self.base_url
) )
self.model = "Qwen/Qwen3-235B-A22B-Instruct-2507" # Use larger model variant self.model = "Qwen/Qwen3-235B-A22B-Instruct-2507" # Use larger model variant
+4 -2
View File
@@ -72,7 +72,8 @@ class DeepSearchAgent:
if self.config.default_llm_provider == "deepseek": if self.config.default_llm_provider == "deepseek":
return DeepSeekLLM( return DeepSeekLLM(
api_key=self.config.deepseek_api_key, api_key=self.config.deepseek_api_key,
model_name=self.config.deepseek_model model_name=self.config.deepseek_model,
base_url=self.config.deepseek_base_url
) )
elif self.config.default_llm_provider == "openai": elif self.config.default_llm_provider == "openai":
return OpenAILLM( return OpenAILLM(
@@ -82,7 +83,8 @@ class DeepSearchAgent:
elif self.config.default_llm_provider == "kimi": elif self.config.default_llm_provider == "kimi":
return KimiLLM( return KimiLLM(
api_key=self.config.kimi_api_key, api_key=self.config.kimi_api_key,
model_name=self.config.kimi_model model_name=self.config.kimi_model,
base_url=self.config.kimi_base_url
) )
else: else:
raise ValueError(f"不支持的LLM提供商: {self.config.default_llm_provider}") raise ValueError(f"不支持的LLM提供商: {self.config.default_llm_provider}")
+8 -3
View File
@@ -9,6 +9,8 @@ from openai import OpenAI
from .base import BaseLLM from .base import BaseLLM
import sys import sys
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
# 添加utils目录到Python路径 # 添加utils目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(os.path.dirname(current_dir)) root_dir = os.path.dirname(os.path.dirname(current_dir))
@@ -30,13 +32,14 @@ except ImportError:
class DeepSeekLLM(BaseLLM): class DeepSeekLLM(BaseLLM):
"""DeepSeek LLM实现类""" """DeepSeek LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None):
""" """
初始化DeepSeek客户端 初始化DeepSeek客户端
Args: Args:
api_key: DeepSeek API密钥,如果不提供则从环境变量读取 api_key: DeepSeek API密钥,如果不提供则从环境变量读取
model_name: 模型名称,默认使用deepseek-chat model_name: 模型名称,默认使用deepseek-chat
base_url: DeepSeek API基础地址
""" """
if api_key is None: if api_key is None:
api_key = os.getenv("DEEPSEEK_API_KEY") api_key = os.getenv("DEEPSEEK_API_KEY")
@@ -45,10 +48,12 @@ class DeepSeekLLM(BaseLLM):
super().__init__(api_key, model_name) super().__init__(api_key, model_name)
self.base_url = base_url or os.getenv("DEEPSEEK_BASE_URL") or DEFAULT_DEEPSEEK_BASE_URL
# 初始化OpenAI客户端,使用DeepSeek的endpoint # 初始化OpenAI客户端,使用DeepSeek的endpoint
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.deepseek.com" base_url=self.base_url
) )
self.default_model = model_name or self.get_default_model() self.default_model = model_name or self.get_default_model()
@@ -110,5 +115,5 @@ class DeepSeekLLM(BaseLLM):
return { return {
"provider": "DeepSeek", "provider": "DeepSeek",
"model": self.default_model, "model": self.default_model,
"api_base": "https://api.deepseek.com" "api_base": self.base_url
} }
+10 -5
View File
@@ -10,6 +10,8 @@ from openai import OpenAI
# 假设 .base 模块和 BaseLLM 类已存在 # 假设 .base 模块和 BaseLLM 类已存在
from .base import BaseLLM from .base import BaseLLM
DEFAULT_KIMI_BASE_URL = "https://api.moonshot.cn/v1"
# 添加utils目录到Python路径并导入重试模块 # 添加utils目录到Python路径并导入重试模块
try: try:
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -30,13 +32,14 @@ except ImportError:
class KimiLLM(BaseLLM): class KimiLLM(BaseLLM):
"""Kimi LLM实现类""" """Kimi LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None):
""" """
初始化Kimi客户端 初始化Kimi客户端
Args: Args:
api_key: Kimi API密钥,如果不提供则从环境变量读取 api_key: Kimi API密钥,如果不提供则从环境变量读取
model_name: 模型名称,默认使用kimi-k2-0711-preview model_name: 模型名称,默认使用kimi-k2-0711-preview
base_url: Kimi API基础地址
""" """
if api_key is None: if api_key is None:
api_key = os.getenv("KIMI_API_KEY") api_key = os.getenv("KIMI_API_KEY")
@@ -44,11 +47,13 @@ class KimiLLM(BaseLLM):
raise ValueError("Kimi API Key未找到!请设置KIMI_API_KEY环境变量或在初始化时提供") raise ValueError("Kimi API Key未找到!请设置KIMI_API_KEY环境变量或在初始化时提供")
super().__init__(api_key, model_name) super().__init__(api_key, model_name)
self.base_url = base_url or os.getenv("KIMI_BASE_URL") or DEFAULT_KIMI_BASE_URL
# 初始化OpenAI客户端,使用Kimi的endpoint # 初始化OpenAI客户端,使用Kimi的endpoint
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.moonshot.cn/v1" base_url=self.base_url
) )
self.default_model = model_name or self.get_default_model() self.default_model = model_name or self.get_default_model()
@@ -136,7 +141,7 @@ class KimiLLM(BaseLLM):
return { return {
"provider": "Kimi", "provider": "Kimi",
"model": self.default_model, "model": self.default_model,
"api_base": "https://api.moonshot.cn/v1", "api_base": self.base_url,
"max_context_length": "长文本支持(200K+ tokens" "max_context_length": "长文本支持(200K+ tokens"
} }
@@ -159,4 +164,4 @@ class KimiLLM(BaseLLM):
kwargs.setdefault("max_tokens", 16384) kwargs.setdefault("max_tokens", 16384)
# 直接调用核心的invoke方法,将所有参数(包括预设的默认值)传递给它。 # 直接调用核心的invoke方法,将所有参数(包括预设的默认值)传递给它。
return self.invoke(system_prompt, user_prompt, **kwargs) return self.invoke(system_prompt, user_prompt, **kwargs)
+6 -3
View File
@@ -12,7 +12,7 @@ from dataclasses import dataclass
# 添加项目根目录到Python路径以导入config # 添加项目根目录到Python路径以导入config
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from config import GUIJI_QWEN3_API_KEY from config import GUIJI_QWEN3_API_KEY, GUIJI_QWEN3_BASE_URL
# 添加utils目录到Python路径 # 添加utils目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -38,21 +38,24 @@ class KeywordOptimizer:
使用硅基流动的Qwen3模型将Agent生成的搜索词优化为更贴近真实舆情的关键词 使用硅基流动的Qwen3模型将Agent生成的搜索词优化为更贴近真实舆情的关键词
""" """
def __init__(self, api_key: str = None): def __init__(self, api_key: str = None, base_url: str = None):
""" """
初始化关键词优化器 初始化关键词优化器
Args: Args:
api_key: 硅基流动API密钥,如果不提供则从配置文件读取 api_key: 硅基流动API密钥,如果不提供则从配置文件读取
base_url: 接口基础地址,默认使用配置文件提供的SiliconFlow地址
""" """
self.api_key = api_key or GUIJI_QWEN3_API_KEY self.api_key = api_key or GUIJI_QWEN3_API_KEY
if not self.api_key: if not self.api_key:
raise ValueError("未找到硅基流动API密钥,请在config.py中设置GUIJI_QWEN3_API_KEY") raise ValueError("未找到硅基流动API密钥,请在config.py中设置GUIJI_QWEN3_API_KEY")
self.base_url = base_url or GUIJI_QWEN3_BASE_URL
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.siliconflow.cn/v1" base_url=self.base_url
) )
self.model = "Qwen/Qwen3-30B-A3B-Instruct-2507" self.model = "Qwen/Qwen3-30B-A3B-Instruct-2507"
+10
View File
@@ -15,6 +15,9 @@ class Config:
deepseek_api_key: Optional[str] = None deepseek_api_key: Optional[str] = None
openai_api_key: Optional[str] = None openai_api_key: Optional[str] = None
kimi_api_key: Optional[str] = None kimi_api_key: Optional[str] = None
deepseek_base_url: str = "https://api.deepseek.com"
openai_base_url: Optional[str] = None
kimi_base_url: str = "https://api.moonshot.cn/v1"
# 数据库配置 # 数据库配置
db_host: Optional[str] = None db_host: Optional[str] = None
@@ -85,6 +88,10 @@ class Config:
return cls( return cls(
deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None), deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None),
openai_api_key=getattr(config_module, "OPENAI_API_KEY", None), openai_api_key=getattr(config_module, "OPENAI_API_KEY", None),
kimi_api_key=getattr(config_module, "KIMI_API_KEY", None),
deepseek_base_url=getattr(config_module, "DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=getattr(config_module, "OPENAI_BASE_URL", None),
kimi_base_url=getattr(config_module, "KIMI_BASE_URL", "https://api.moonshot.cn/v1"),
db_host=getattr(config_module, "DB_HOST", None), db_host=getattr(config_module, "DB_HOST", None),
db_user=getattr(config_module, "DB_USER", None), db_user=getattr(config_module, "DB_USER", None),
@@ -131,6 +138,9 @@ class Config:
deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"), deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"),
openai_api_key=config_dict.get("OPENAI_API_KEY"), openai_api_key=config_dict.get("OPENAI_API_KEY"),
kimi_api_key=config_dict.get("KIMI_API_KEY"), kimi_api_key=config_dict.get("KIMI_API_KEY"),
deepseek_base_url=config_dict.get("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=config_dict.get("OPENAI_BASE_URL"),
kimi_base_url=config_dict.get("KIMI_BASE_URL", "https://api.moonshot.cn/v1"),
db_host=config_dict.get("DB_HOST"), db_host=config_dict.get("DB_HOST"),
db_user=config_dict.get("DB_USER"), db_user=config_dict.get("DB_USER"),
+4 -2
View File
@@ -60,7 +60,8 @@ class DeepSearchAgent:
if self.config.default_llm_provider == "deepseek": if self.config.default_llm_provider == "deepseek":
return DeepSeekLLM( return DeepSeekLLM(
api_key=self.config.deepseek_api_key, api_key=self.config.deepseek_api_key,
model_name=self.config.deepseek_model model_name=self.config.deepseek_model,
base_url=self.config.deepseek_base_url
) )
elif self.config.default_llm_provider == "openai": elif self.config.default_llm_provider == "openai":
return OpenAILLM( return OpenAILLM(
@@ -70,7 +71,8 @@ class DeepSearchAgent:
elif self.config.default_llm_provider == "gemini": elif self.config.default_llm_provider == "gemini":
return GeminiLLM( return GeminiLLM(
api_key=self.config.gemini_api_key, api_key=self.config.gemini_api_key,
model_name=self.config.gemini_model model_name=self.config.gemini_model,
base_url=self.config.gemini_base_url
) )
else: else:
raise ValueError(f"不支持的LLM提供商: {self.config.default_llm_provider}") raise ValueError(f"不支持的LLM提供商: {self.config.default_llm_provider}")
+8 -3
View File
@@ -9,6 +9,8 @@ from typing import Optional, Dict, Any
from openai import OpenAI from openai import OpenAI
from .base import BaseLLM from .base import BaseLLM
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
# 添加utils目录到Python路径并导入重试模块 # 添加utils目录到Python路径并导入重试模块
try: try:
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -29,13 +31,14 @@ except ImportError:
class DeepSeekLLM(BaseLLM): class DeepSeekLLM(BaseLLM):
"""DeepSeek LLM实现类""" """DeepSeek LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None):
""" """
初始化DeepSeek客户端 初始化DeepSeek客户端
Args: Args:
api_key: DeepSeek API密钥,如果不提供则从环境变量读取 api_key: DeepSeek API密钥,如果不提供则从环境变量读取
model_name: 模型名称,默认使用deepseek-chat model_name: 模型名称,默认使用deepseek-chat
base_url: DeepSeek API基础地址
""" """
if api_key is None: if api_key is None:
api_key = os.getenv("DEEPSEEK_API_KEY") api_key = os.getenv("DEEPSEEK_API_KEY")
@@ -44,10 +47,12 @@ class DeepSeekLLM(BaseLLM):
super().__init__(api_key, model_name) super().__init__(api_key, model_name)
self.base_url = base_url or os.getenv("DEEPSEEK_BASE_URL") or DEFAULT_DEEPSEEK_BASE_URL
# 初始化OpenAI客户端,使用DeepSeek的endpoint # 初始化OpenAI客户端,使用DeepSeek的endpoint
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.deepseek.com" base_url=self.base_url
) )
self.default_model = model_name or self.get_default_model() self.default_model = model_name or self.get_default_model()
@@ -109,5 +114,5 @@ class DeepSeekLLM(BaseLLM):
return { return {
"provider": "DeepSeek", "provider": "DeepSeek",
"model": self.default_model, "model": self.default_model,
"api_base": "https://api.deepseek.com" "api_base": self.base_url
} }
+8 -3
View File
@@ -9,6 +9,8 @@ from typing import Optional, Dict, Any
from openai import OpenAI from openai import OpenAI
from .base import BaseLLM from .base import BaseLLM
DEFAULT_GEMINI_BASE_URL = "https://www.chataiapi.com/v1"
# 添加utils目录到Python路径并导入重试模块 # 添加utils目录到Python路径并导入重试模块
try: try:
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -29,13 +31,14 @@ except ImportError:
class GeminiLLM(BaseLLM): class GeminiLLM(BaseLLM):
"""Gemini LLM实现类""" """Gemini LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None):
""" """
初始化Gemini客户端 初始化Gemini客户端
Args: Args:
api_key: Gemini API密钥,如果不提供则从环境变量读取 api_key: Gemini API密钥,如果不提供则从环境变量读取
model_name: 模型名称,默认使用gemini-2.5-pro model_name: 模型名称,默认使用gemini-2.5-pro
base_url: Gemini API基础地址
""" """
if api_key is None: if api_key is None:
api_key = os.getenv("GEMINI_API_KEY") api_key = os.getenv("GEMINI_API_KEY")
@@ -44,10 +47,12 @@ class GeminiLLM(BaseLLM):
super().__init__(api_key, model_name) super().__init__(api_key, model_name)
self.base_url = base_url or os.getenv("GEMINI_BASE_URL") or DEFAULT_GEMINI_BASE_URL
# 初始化OpenAI客户端,使用Gemini的中转endpoint # 初始化OpenAI客户端,使用Gemini的中转endpoint
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://www.chataiapi.com/v1" base_url=self.base_url
) )
self.default_model = model_name or self.get_default_model() self.default_model = model_name or self.get_default_model()
@@ -109,5 +114,5 @@ class GeminiLLM(BaseLLM):
return { return {
"provider": "Gemini", "provider": "Gemini",
"model": self.default_model, "model": self.default_model,
"api_base": "https://www.chataiapi.com/v1" "api_base": self.base_url
} }
+9
View File
@@ -16,6 +16,9 @@ class Config:
openai_api_key: Optional[str] = None openai_api_key: Optional[str] = None
gemini_api_key: Optional[str] = None gemini_api_key: Optional[str] = None
bocha_api_key: Optional[str] = None bocha_api_key: Optional[str] = None
deepseek_base_url: str = "https://api.deepseek.com"
openai_base_url: Optional[str] = None
gemini_base_url: str = "https://www.chataiapi.com/v1"
# 模型配置 # 模型配置
default_llm_provider: str = "deepseek" # deepseek、openai 或 gemini default_llm_provider: str = "deepseek" # deepseek、openai 或 gemini
@@ -72,6 +75,9 @@ class Config:
deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None), deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None),
openai_api_key=getattr(config_module, "OPENAI_API_KEY", None), openai_api_key=getattr(config_module, "OPENAI_API_KEY", None),
gemini_api_key=getattr(config_module, "GEMINI_API_KEY", None), gemini_api_key=getattr(config_module, "GEMINI_API_KEY", None),
deepseek_base_url=getattr(config_module, "DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=getattr(config_module, "OPENAI_BASE_URL", None),
gemini_base_url=getattr(config_module, "GEMINI_BASE_URL", "https://www.chataiapi.com/v1"),
bocha_api_key=getattr(config_module, "BOCHA_API_KEY", None), bocha_api_key=getattr(config_module, "BOCHA_API_KEY", None),
default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"), default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"),
deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"), deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"),
@@ -100,6 +106,9 @@ class Config:
deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"), deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"),
openai_api_key=config_dict.get("OPENAI_API_KEY"), openai_api_key=config_dict.get("OPENAI_API_KEY"),
gemini_api_key=config_dict.get("GEMINI_API_KEY"), gemini_api_key=config_dict.get("GEMINI_API_KEY"),
deepseek_base_url=config_dict.get("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=config_dict.get("OPENAI_BASE_URL"),
gemini_base_url=config_dict.get("GEMINI_BASE_URL", "https://www.chataiapi.com/v1"),
bocha_api_key=config_dict.get("BOCHA_API_KEY"), bocha_api_key=config_dict.get("BOCHA_API_KEY"),
default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"), default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"),
deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"), deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"),
+2 -1
View File
@@ -60,7 +60,8 @@ class DeepSearchAgent:
if self.config.default_llm_provider == "deepseek": if self.config.default_llm_provider == "deepseek":
return DeepSeekLLM( return DeepSeekLLM(
api_key=self.config.deepseek_api_key, api_key=self.config.deepseek_api_key,
model_name=self.config.deepseek_model model_name=self.config.deepseek_model,
base_url=self.config.deepseek_base_url
) )
elif self.config.default_llm_provider == "openai": elif self.config.default_llm_provider == "openai":
return OpenAILLM( return OpenAILLM(
+8 -3
View File
@@ -9,6 +9,8 @@ from typing import Optional, Dict, Any
from openai import OpenAI from openai import OpenAI
from .base import BaseLLM from .base import BaseLLM
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
# 添加utils目录到Python路径并导入重试模块 # 添加utils目录到Python路径并导入重试模块
try: try:
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -29,13 +31,14 @@ except ImportError:
class DeepSeekLLM(BaseLLM): class DeepSeekLLM(BaseLLM):
"""DeepSeek LLM实现类""" """DeepSeek LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None):
""" """
初始化DeepSeek客户端 初始化DeepSeek客户端
Args: Args:
api_key: DeepSeek API密钥,如果不提供则从环境变量读取 api_key: DeepSeek API密钥,如果不提供则从环境变量读取
model_name: 模型名称,默认使用deepseek-chat model_name: 模型名称,默认使用deepseek-chat
base_url: DeepSeek API基础地址
""" """
if api_key is None: if api_key is None:
api_key = os.getenv("DEEPSEEK_API_KEY") api_key = os.getenv("DEEPSEEK_API_KEY")
@@ -44,10 +47,12 @@ class DeepSeekLLM(BaseLLM):
super().__init__(api_key, model_name) super().__init__(api_key, model_name)
self.base_url = base_url or os.getenv("DEEPSEEK_BASE_URL") or DEFAULT_DEEPSEEK_BASE_URL
# 初始化OpenAI客户端,使用DeepSeek的endpoint # 初始化OpenAI客户端,使用DeepSeek的endpoint
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://api.deepseek.com" base_url=self.base_url
) )
self.default_model = model_name or self.get_default_model() self.default_model = model_name or self.get_default_model()
@@ -109,5 +114,5 @@ class DeepSeekLLM(BaseLLM):
return { return {
"provider": "DeepSeek", "provider": "DeepSeek",
"model": self.default_model, "model": self.default_model,
"api_base": "https://api.deepseek.com" "api_base": self.base_url
} }
+6
View File
@@ -15,6 +15,8 @@ class Config:
deepseek_api_key: Optional[str] = None deepseek_api_key: Optional[str] = None
openai_api_key: Optional[str] = None openai_api_key: Optional[str] = None
tavily_api_key: Optional[str] = None tavily_api_key: Optional[str] = None
deepseek_base_url: str = "https://api.deepseek.com"
openai_base_url: Optional[str] = None
# 模型配置 # 模型配置
default_llm_provider: str = "deepseek" # deepseek 或 openai default_llm_provider: str = "deepseek" # deepseek 或 openai
@@ -66,6 +68,8 @@ class Config:
deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None), deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None),
openai_api_key=getattr(config_module, "OPENAI_API_KEY", None), openai_api_key=getattr(config_module, "OPENAI_API_KEY", None),
tavily_api_key=getattr(config_module, "TAVILY_API_KEY", None), tavily_api_key=getattr(config_module, "TAVILY_API_KEY", None),
deepseek_base_url=getattr(config_module, "DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=getattr(config_module, "OPENAI_BASE_URL", None),
default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"), default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"),
deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"), deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"),
openai_model=getattr(config_module, "OPENAI_MODEL", "gpt-4o-mini"), openai_model=getattr(config_module, "OPENAI_MODEL", "gpt-4o-mini"),
@@ -93,6 +97,8 @@ class Config:
deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"), deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"),
openai_api_key=config_dict.get("OPENAI_API_KEY"), openai_api_key=config_dict.get("OPENAI_API_KEY"),
tavily_api_key=config_dict.get("TAVILY_API_KEY"), tavily_api_key=config_dict.get("TAVILY_API_KEY"),
deepseek_base_url=config_dict.get("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
openai_base_url=config_dict.get("OPENAI_BASE_URL"),
default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"), default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"),
deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"), deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"),
openai_model=config_dict.get("OPENAI_MODEL", "gpt-4o-mini"), openai_model=config_dict.get("OPENAI_MODEL", "gpt-4o-mini"),
+14 -3
View File
@@ -1,12 +1,21 @@
<div align="center"> <div align="center">
<img src="static/image/logo_compressed.png" alt="Weibo Public Opinion Analysis System Logo" width="800"> <img src="static/image/logo_compressed.png" alt="Weibo Public Opinion Analysis System Logo" width="700">
<a href="https://trendshift.io/repositories/12461" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12461" alt="666ghj%2FWeibo_PublicOpinion_AnalysisSystem | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://leaflow.net/" target="_blank"><img src="static/image/Leaflow_logo.png" alt="666ghj%2FWeibo_PublicOpinion_AnalysisSystem | Leaflow" style="width: 150px;" width="150"/></a>
[![GitHub Stars](https://img.shields.io/github/stars/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/stargazers) [![GitHub Stars](https://img.shields.io/github/stars/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/stargazers)
[![GitHub Watchers](https://img.shields.io/github/watchers/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/watchers) [![GitHub Watchers](https://img.shields.io/github/watchers/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/watchers)
[![GitHub Forks](https://img.shields.io/github/forks/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/network) [![GitHub Forks](https://img.shields.io/github/forks/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/network)
[![GitHub Issues](https://img.shields.io/github/issues/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/issues) [![GitHub Issues](https://img.shields.io/github/issues/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/pulls)
[![GitHub License](https://img.shields.io/github/license/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/blob/main/LICENSE) [![GitHub License](https://img.shields.io/github/license/666ghj/Weibo_PublicOpinion_AnalysisSystem?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem/blob/main/LICENSE)
[![Version](https://img.shields.io/badge/version-v1.0.0-green.svg?style=flat-square)](https://github.com/666ghj/Weibo_PublicOpinion_AnalysisSystem)
[![Docker](https://img.shields.io/badge/Docker-Build-2496ED?style=flat-square&logo=docker&logoColor=white)](https://hub.docker.com/)
[English](./README-EN.md) | [中文文档](./README.md) [English](./README-EN.md) | [中文文档](./README.md)
@@ -221,7 +230,7 @@ playwright install chromium
#### 4.1 Configure API Keys #### 4.1 Configure API Keys
Edit the `config.py` file and fill in your API keys (you can also choose your own models and search proxies): Edit the `config.py` file and fill in your API keys (you can also choose your own models and search proxies; please see the config file for details):
```python ```python
# MySQL Database Configuration # MySQL Database Configuration
@@ -283,7 +292,9 @@ conda activate your_conda_name
python app.py python app.py
``` ```
> Note: Data crawling requires separate operation, see section 5.3 for guidance > Note 1: After a run is terminated, the Streamlit app might not shut down correctly and may still be occupying the port. If this occurs, find the process that is holding the port and kill it.
> Note 2: Data scraping needs to be performed as a separate operation. Please refer to the instructions in section 5.3.
Visit http://localhost:5000 to use the complete system Visit http://localhost:5000 to use the complete system
+4 -2
View File
@@ -230,7 +230,7 @@ playwright install chromium
#### 4.1 配置API密钥 #### 4.1 配置API密钥
编辑 `config.py` 文件,填入您的API密钥(您也可以选择自己的模型、搜索代理): 编辑 `config.py` 文件,填入您的API密钥(您也可以选择自己的模型、搜索代理,详情见config文件内):
```python ```python
# MySQL数据库配置 # MySQL数据库配置
@@ -292,7 +292,9 @@ conda activate your_conda_name
python app.py python app.py
``` ```
> :数据爬取需要单独操作,见5.3指引 > 1:一次运行终止后,streamlit app可能结束异常仍然占用端口,此时搜索占用端口的进程kill掉即可
> 注2:数据爬取需要单独操作,见5.3指引
访问 http://localhost:5000 即可使用完整系统 访问 http://localhost:5000 即可使用完整系统
+1
View File
@@ -192,6 +192,7 @@ class ReportAgent:
return GeminiLLM( return GeminiLLM(
api_key=self.config.gemini_api_key, api_key=self.config.gemini_api_key,
model_name=self.config.gemini_model, model_name=self.config.gemini_model,
base_url=self.config.gemini_base_url,
config=self.config # 传入配置对象以支持动态超时设置 config=self.config # 传入配置对象以支持动态超时设置
) )
else: else:
+15 -5
View File
@@ -9,6 +9,8 @@ from typing import Optional, Dict, Any
from openai import OpenAI from openai import OpenAI
from .base import BaseLLM from .base import BaseLLM
DEFAULT_GEMINI_BASE_URL = "https://www.chataiapi.com/v1"
# 导入根目录的config # 导入根目录的config
try: try:
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -50,13 +52,14 @@ except ImportError:
class GeminiLLM(BaseLLM): class GeminiLLM(BaseLLM):
"""Report Engine Gemini LLM实现类""" """Report Engine Gemini LLM实现类"""
def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, config=None): def __init__(self, api_key: Optional[str] = None, model_name: Optional[str] = None, base_url: Optional[str] = None, config=None):
""" """
初始化Gemini客户端 初始化Gemini客户端
Args: Args:
api_key: Gemini API密钥如果不提供则从config或环境变量读取 api_key: Gemini API密钥如果不提供则从config或环境变量读取
model_name: 模型名称默认使用gemini-2.5-pro model_name: 模型名称默认使用gemini-2.5-pro
base_url: Gemini API基础地址
config: 配置对象用于获取超时设置 config: 配置对象用于获取超时设置
""" """
if api_key is None: if api_key is None:
@@ -77,15 +80,22 @@ class GeminiLLM(BaseLLM):
# 从配置获取超时时间,默认15分钟(适应7分钟平均生成时间) # 从配置获取超时时间,默认15分钟(适应7分钟平均生成时间)
timeout = config.api_timeout if config and hasattr(config, 'api_timeout') else 900.0 timeout = config.api_timeout if config and hasattr(config, 'api_timeout') else 900.0
self.base_url = (
base_url
or (getattr(self.config, 'gemini_base_url', None) if self.config else None)
or os.getenv('GEMINI_BASE_URL')
or DEFAULT_GEMINI_BASE_URL
)
# 创建针对此实例的重试配置 # 创建针对此实例的重试配置
self.retry_config = create_report_retry_config(config) self.retry_config = create_report_retry_config(config)
# 初始化OpenAI客户端,使用Gemini的中转endpoint # 初始化OpenAI客户端,使用Gemini的中转endpoint
# 专门为报告生成设置长超时(15分钟),适应7分钟平均生成时间 # 专门为报告生成设置长超时(15分钟),适应7分钟平均生成时间
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
base_url="https://www.chataiapi.com/v1", base_url=self.base_url,
timeout=timeout timeout=timeout
) )
@@ -188,6 +198,6 @@ class GeminiLLM(BaseLLM):
return { return {
"provider": "Gemini", "provider": "Gemini",
"model": self.default_model, "model": self.default_model,
"api_base": "https://www.chataiapi.com/v1", "api_base": self.base_url,
"purpose": "Report Generation" "purpose": "Report Generation"
} }
+3
View File
@@ -13,6 +13,7 @@ class Config:
"""Report Engine配置类""" """Report Engine配置类"""
# API密钥 # API密钥
gemini_api_key: Optional[str] = None gemini_api_key: Optional[str] = None
gemini_base_url: str = "https://www.chataiapi.com/v1"
# 模型配置 # 模型配置
default_llm_provider: str = "gemini" default_llm_provider: str = "gemini"
@@ -56,6 +57,7 @@ class Config:
return cls( return cls(
gemini_api_key=getattr(config_module, "GEMINI_API_KEY", None), gemini_api_key=getattr(config_module, "GEMINI_API_KEY", None),
gemini_base_url=getattr(config_module, "GEMINI_BASE_URL", "https://www.chataiapi.com/v1"),
default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "gemini"), default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "gemini"),
gemini_model=getattr(config_module, "GEMINI_MODEL", "gemini-2.5-pro"), gemini_model=getattr(config_module, "GEMINI_MODEL", "gemini-2.5-pro"),
max_content_length=getattr(config_module, "MAX_CONTENT_LENGTH", 200000), max_content_length=getattr(config_module, "MAX_CONTENT_LENGTH", 200000),
@@ -82,6 +84,7 @@ class Config:
return cls( return cls(
gemini_api_key=config_dict.get("GEMINI_API_KEY"), gemini_api_key=config_dict.get("GEMINI_API_KEY"),
gemini_base_url=config_dict.get("GEMINI_BASE_URL", "https://www.chataiapi.com/v1"),
default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "gemini"), default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "gemini"),
gemini_model=config_dict.get("GEMINI_MODEL", "gemini-2.5-pro"), gemini_model=config_dict.get("GEMINI_MODEL", "gemini-2.5-pro"),
max_content_length=int(config_dict.get("MAX_CONTENT_LENGTH", "200000")), max_content_length=int(config_dict.get("MAX_CONTENT_LENGTH", "200000")),
@@ -27,7 +27,18 @@ except locale.Error:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from InsightEngine import DeepSearchAgent, Config from InsightEngine import DeepSearchAgent, Config
from config import DEEPSEEK_API_KEY, KIMI_API_KEY, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, DB_CHARSET from config import (
DEEPSEEK_API_KEY,
DEEPSEEK_BASE_URL,
KIMI_API_KEY,
KIMI_BASE_URL,
DB_HOST,
DB_USER,
DB_PASSWORD,
DB_NAME,
DB_PORT,
DB_CHARSET,
)
def main(): def main():
@@ -111,6 +122,8 @@ def main():
deepseek_api_key=None, deepseek_api_key=None,
openai_api_key=None, openai_api_key=None,
kimi_api_key=KIMI_API_KEY, # 强制使用配置文件中的Kimi Key kimi_api_key=KIMI_API_KEY, # 强制使用配置文件中的Kimi Key
deepseek_base_url=DEEPSEEK_BASE_URL,
kimi_base_url=KIMI_BASE_URL,
db_host=db_host, db_host=db_host,
db_user=db_user, db_user=db_user,
db_password=db_password, db_password=db_password,
@@ -225,4 +238,4 @@ def display_results(agent: DeepSearchAgent, final_report: str):
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+10 -2
View File
@@ -27,7 +27,13 @@ except locale.Error:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from MediaEngine import DeepSearchAgent, Config from MediaEngine import DeepSearchAgent, Config
from config import DEEPSEEK_API_KEY, BOCHA_Web_Search_API_KEY, GEMINI_API_KEY from config import (
DEEPSEEK_API_KEY,
DEEPSEEK_BASE_URL,
BOCHA_Web_Search_API_KEY,
GEMINI_API_KEY,
GEMINI_BASE_URL,
)
def main(): def main():
@@ -112,6 +118,8 @@ def main():
openai_api_key=None, openai_api_key=None,
gemini_api_key=gemini_key, gemini_api_key=gemini_key,
bocha_api_key=bocha_key, bocha_api_key=bocha_key,
deepseek_base_url=DEEPSEEK_BASE_URL,
gemini_base_url=GEMINI_BASE_URL,
default_llm_provider=llm_provider, default_llm_provider=llm_provider,
deepseek_model="deepseek-chat", # 保留默认值以兼容 deepseek_model="deepseek-chat", # 保留默认值以兼容
openai_model="gpt-4o-mini", # 保留默认值以兼容 openai_model="gpt-4o-mini", # 保留默认值以兼容
@@ -220,4 +228,4 @@ def display_results(agent: DeepSearchAgent, final_report: str):
if __name__ == "__main__": if __name__ == "__main__":
main() main()
@@ -27,7 +27,7 @@ except locale.Error:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from QueryEngine import DeepSearchAgent, Config from QueryEngine import DeepSearchAgent, Config
from config import DEEPSEEK_API_KEY, TAVILY_API_KEY from config import DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, TAVILY_API_KEY
def main(): def main():
@@ -110,6 +110,7 @@ def main():
deepseek_api_key=deepseek_key, deepseek_api_key=deepseek_key,
openai_api_key=None, openai_api_key=None,
tavily_api_key=tavily_key, tavily_api_key=tavily_key,
deepseek_base_url=DEEPSEEK_BASE_URL,
default_llm_provider=llm_provider, default_llm_provider=llm_provider,
deepseek_model=model_name, deepseek_model=model_name,
openai_model="gpt-4o-mini", # 保留默认值以兼容 openai_model="gpt-4o-mini", # 保留默认值以兼容
@@ -217,4 +218,4 @@ def display_results(agent: DeepSearchAgent, final_report: str):
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+35 -18
View File
@@ -4,35 +4,52 @@ Intelligence Public Opinion Analysis Platform Configuration File
Stores database connection information and API keys Stores database connection information and API keys
""" """
# ============================== 数据库配置 ==============================
# MySQL Database Configuration # MySQL Database Configuration
DB_HOST = "your_database_host" # e.g., "localhost" or "127.0.0.1" DB_HOST = "your_database_host" # e.g., "localhost" or "127.0.0.1"
DB_PORT = 3306 DB_PORT = 3306 # e.g., 3306
DB_USER = "your_database_user" DB_USER = "your_database_user"
DB_PASSWORD = "your_database_password" DB_PASSWORD = "your_database_password"
DB_NAME = "your_database_name" DB_NAME = "your_database_name"
DB_CHARSET = "utf8mb4" DB_CHARSET = "utf8mb4"
# 我们也提供云数据库资源便捷配置,日均10w+数据,目前推广阶段可免费申请,联系我们:670939375@qq.com # 我们也提供云数据库资源便捷配置,日均10w+数据,学术研究可免费申请,联系我们:670939375@qq.com
# DeepSeek API Key
# ============================== LLM配置 ==============================
# 重要提醒:推荐第一次先按照默认模型安排配置,成功跑通后再更改自己的模型!
# DeepSeek API Key (openai调用格式)
# 用于Query Agent
# 申请地址https://www.deepseek.com/ # 申请地址https://www.deepseek.com/
DEEPSEEK_API_KEY = "your_deepseek_api_key" DEEPSEEK_API_KEY = "sk-xxxxxxxxxxxxxxxxx"
DEEPSEEK_BASE_URL = "https://api.deepseek.com"
# Kimi API Key (openai调用格式)
# 用于Insight Agent
# 申请地址https://platform.moonshot.cn/
KIMI_API_KEY = "sk-xxxxxxxxxxxxxxxxx"
KIMI_BASE_URL = "https://api.moonshot.cn/v1"
# Gemini API Key (openai调用格式)
# 用于Media Agent与Report Agent
# 这里我用了一个中转api来接入Gemini,申请地址https://api.chataiapi.com/,你也可以使用其他
GEMINI_API_KEY = "sk-xxxxxxxxxxxxxxxxx"
GEMINI_BASE_URL = "https://www.chataiapi.com/v1"
# Siliconflow API Key (openai调用格式)
# 用于Forum Host与keyword Optimizer
# 申请地址https://siliconflow.cn/
GUIJI_QWEN3_API_KEY = "sk-xxxxxxxxxxxxxxxxx"
GUIJI_QWEN3_BASE_URL = "https://api.siliconflow.cn/v1"
# 调试阶段出于成本考虑,没有使用ChatGPT与Claude,您也可以接入自己的模型,只要符合openai调用格式即可
# ============================== Web工具配置 ==============================
# Tavily Search API Key # Tavily Search API Key
# 申请地址https://www.tavily.com/ # 申请地址https://www.tavily.com/
TAVILY_API_KEY = "your_tavily_api_key" TAVILY_API_KEY = "tvly-xxxxxxxxxxxxxxxxx"
# Kimi API Key
# 申请地址https://www.kimi.com/
KIMI_API_KEY = "your_kimi_api_key"
# Gemini API Key (via OpenAI format proxy)
# 这里我用了一个中转api来接入Gemini,申请地址https://api.chataiapi.com/,你也可以使用其他
GEMINI_API_KEY = "your_gemini_api_key"
# Bocha Search API Key # Bocha Search API Key
# 申请地址https://open.bochaai.com/ # 申请地址https://open.bochaai.com/
BOCHA_Web_Search_API_KEY = "your_bocha_web_search_api_key" BOCHA_Web_Search_API_KEY = "sk-xxxxxxxxxxxxxxxxx"
# Guiji Flow API Key
# 申请地址https://siliconflow.cn/
GUIJI_QWEN3_API_KEY = "your_guiji_qwen3_api_key"