【保姆级教程】零基础搭建你的专属 AI 金融分析师 (TradingAgents-CN) - 第一篇:部署实战

前言: 想要拥有一个像华尔街专业分析师一样的 AI 助手吗?它能帮你全天候盯盘、分析财报、评估风险,甚至直接给出操作建议。 哪怕你没有任何编程基础,跟着这篇教程走,30 分钟内你也能拥有一套完全属于自己的 AI 投研系统!

🚀 为什么选择 TradingAgents-CN?

市面上的 AI 很多,但能深度整合金融数据的很少。TradingAgents-CN 是专为中国市场优化的版本,不仅能接入 GPT-5.2、Claude Opus 4.5 等顶尖大脑,还能直接读取 A 股、美股的实时行情和财报数据进行分析。

最重要的是:数据掌握在自己手里,安全、私密、可定制。

🛠️ 第一阶段:准备工作

1. 准备一台服务器 (VPS)

对于新手,我们推荐使用 Linux 服务器。由于我们要使用 LLM Hub 解决网络问题,所以你从阿里云、腾讯云购买的国内服务器完全可以使用!

  • 推荐配置
    • CPU: 2核 及以上
    • 内存: 4GB 及以上 (推荐 8GB,运行更流畅)
    • 系统: Ubuntu 22.04 LTS (最稳、坑最少)

带宽: 3M 及以上

2. 准备 AI“大脑”密钥 (LLM Hub)

想要使用 GPT-5.2、Claude Opus 4.5、Gemini 3 Pro 这些顶尖模型,通常需要去各模型官网分别注册账号,不仅繁琐,还面临网络访问和封号难题。

本教程使用 LLM Hub 聚合平台来解决这些问题。它最大的优势是:

  • 一站式接入:一个账号就能同时使用 OpenAI、Claude、Gemini 以及国内 Qwen、DeepSeek 等主流大模型。
  • 免去繁琐注册:不需要你去申请 OpenAI 账号或绑国外信用卡,直接使用 LLM Hub 提供的密钥即可。
  • 稳定高速:专为国内开发者优化,无需魔法上网,支持支付宝支付。
  1. 访问 LLM Hub 官网 注册账号。

在后台创建令牌 (Token),复制 sk- 开头的密钥。

3. 安装服务器管家 (1Panel)

登录服务器终端(SSH),执行以下命令安装 1Panel:

# 1. 更新系统软件包 sudo apt update && sudo apt upgrade -y # 2. 安装 1Panel 面板 bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)"

安装完成后,登录 1Panel 面板,你就会拥有一个可视化的管理后台。

🏗️ 第二阶段:安装基础软件 (数据库)

为了让系统更稳定,我们通过 1Panel 的应用商店来独立部署数据库。

1. 安装 Redis

  1. 进入 1Panel 应用商店,搜索 Redis
  2. 点击安装。
  3. 关键设置
  • 版本:选最新的 8.x。
  • 容器名称:1panel-redis,稍后要用。

密码:1Panel 自动生成密码,务必记下来

2. 安装 MongoDB

  1. 应用商店 搜索 MongoDB
  2. 点击安装。
  3. 关键设置
  • 版本:选最新的 8.x。
  • 容器名称:1panel-mongodb,稍后要用。

Root 用户名与密码:1Panel 自动生成用户名和密码,务必记下来

⚡️ 第三阶段:部署 TradingAgents-CN

1. 创建项目目录

在 1Panel 的【主机】->【文件】管理中,逐级进入目录:/opt/1panel/docker/compose。 【创建文件夹】,命名为 tradingagents-cn,并进入该目录。

2. 创建核心配置文件

tradingagents-cn 目录下,我们需要手动创建两个子文件夹和几个文件。

步骤 2.1:创建 Nginx 配置

  1. 新建文件夹:nginx
  2. 进入 nginx 文件夹,新建文件:nginx.conf
  3. 填入以下内容:
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { proxy_pass http://frontend:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /api/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

步骤 2.2:创建初始化脚本

  1. 回到 tradingagents-cn 根目录。
  2. 新建文件夹:scripts
  3. 进入 scripts 文件夹,新建文件:import_config_and_create_user.py
  4. 填入以下内容:
#!/usr/bin/env python3 """ 导入配置数据并创建默认用户 功能: 1. 从导出的 JSON 文件导入配置数据到 MongoDB 2. 创建默认管理员用户(admin/admin123) 3. 支持选择性导入集合 4. 支持覆盖或跳过已存在的数据 使用方法: python scripts/import_config_and_create_user.py <export_file.json> python scripts/import_config_and_create_user.py <export_file.json> --overwrite python scripts/import_config_and_create_user.py <export_file.json> --collections system_configs users """ import json import sys import hashlib from datetime import datetime from pathlib import Path from typing import List, Dict, Any, Optional import argparse import os # 添加项目根目录到路径 project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) from pymongo import MongoClient from bson import ObjectId def load_env_config(script_dir: Path) -> dict: """从 .env 文件加载配置 Args: script_dir: 脚本所在目录 Returns: 配置字典,包含 mongodb_port 等 """ # 查找 .env 文件(在项目根目录) env_file = script_dir.parent / '.env' # 优先从系统环境变量获取(适配 Docker 环境) config = { 'mongodb_port': int(os.environ.get('MONGODB_PORT', 27017)), 'mongodb_host': os.environ.get('MONGODB_HOST', 'localhost'), 'mongodb_username': os.environ.get('MONGODB_USERNAME', 'admin'), 'mongodb_password': os.environ.get('MONGODB_PASSWORD', 'tradingagents123'), 'mongodb_database': os.environ.get('MONGODB_DATABASE', 'tradingagents') } # 如果系统环境变量中没有设置(例如本地开发),则尝试读取 .env 文件覆盖 # 注意:这里逻辑改为“只有当环境变量未设置时才从文件读”或者“文件作为补充” # 但为了简单且安全,我们保持“系统环境变量优先”。 # 如果脚本在容器外运行且没有设置环境变量,下面的 .env 读取逻辑会生效(覆盖默认值,但不覆盖已有的os.environ值) if env_file.exists(): try: file_config = {} with open(env_file, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if not line or line.startswith('#'): continue if '=' in line: key, value = line.split('=', 1) key = key.strip() value = value.strip() file_config[key] = value # 仅当 config 中的值为默认值(或空)时,才使用文件中的值 # 或者,更简单的逻辑:如果 os.environ 没取到(即使用了默认值),则尝试用文件值 # 但由于 os.environ.get 已经给了默认值,这里我们做个判断: if 'MONGODB_PORT' not in os.environ and 'MONGODB_PORT' in file_config: config['mongodb_port'] = int(file_config['MONGODB_PORT']) # 对其他字段同理,只有环境变量未设置时才采纳文件配置 if 'MONGODB_HOST' not in os.environ and 'MONGODB_HOST' in file_config: config['mongodb_host'] = file_config['MONGODB_HOST'] if 'MONGODB_USERNAME' not in os.environ and 'MONGODB_USERNAME' in file_config: config['mongodb_username'] = file_config['MONGODB_USERNAME'] if 'MONGODB_PASSWORD' not in os.environ and 'MONGODB_PASSWORD' in file_config: config['mongodb_password'] = file_config['MONGODB_PASSWORD'] except Exception as e: print(f"⚠️ 警告: 读取 .env 文件失败: {e}") else: # 仅在非Docker环境且无env文件时提示警告 if not os.environ.get("DOCKER_CONTAINER"): print(f"⚠️ 警告: .env 文件不存在: {env_file}") return config # MongoDB 连接配置 # Docker 内部运行时使用服务名 "mongodb" # 宿主机运行时使用 "localhost" DB_NAME = "tradingagents" # 默认管理员用户 DEFAULT_ADMIN = { "username": "admin", "password": "admin123", "email": "[email protected]" } # 配置集合列表 CONFIG_COLLECTIONS = [ "system_configs", "users", "llm_providers", "market_categories", "user_tags", "datasource_groupings", "platform_configs", "user_configs", "model_catalog" ] def hash_password(password: str) -> str: """使用 SHA256 哈希密码(与系统一致)""" return hashlib.sha256(password.encode()).hexdigest() def convert_to_bson(data: Any) -> Any: """将 JSON 数据转换为 BSON 兼容格式""" if isinstance(data, dict): result = {} for key, value in data.items(): # 处理 ObjectId if key == "_id" or key.endswith("_id"): if isinstance(value, str) and len(value) == 24: try: result[key] = ObjectId(value) continue except: pass # 处理日期时间 if key.endswith("_at") or key in ["created_at", "updated_at", "last_login", "added_at"]: if isinstance(value, str): try: result[key] = datetime.fromisoformat(value.replace('Z', '+00:00')) continue except: pass result[key] = convert_to_bson(value) return result elif isinstance(data, list): return [convert_to_bson(item) for item in data] else: return data def load_export_file(file_path: str) -> Dict[str, Any]: """加载导出的 JSON 文件""" print(f"\n📂 加载导出文件: {file_path}") try: with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) if "export_info" not in data or "data" not in data: print("❌ 错误: 文件格式不正确,缺少 export_info 或 data 字段") sys.exit(1) export_info = data["export_info"] print(f"✅ 文件加载成功") print(f" 导出时间: {export_info.get('created_at', 'Unknown')}") print(f" 导出格式: {export_info.get('format', 'Unknown')}") print(f" 集合数量: {len(export_info.get('collections', []))}") return data except FileNotFoundError: print(f"❌ 错误: 文件不存在: {file_path}") sys.exit(1) except json.JSONDecodeError as e: print(f"❌ 错误: JSON 解析失败: {e}") sys.exit(1) except Exception as e: print(f"❌ 错误: 加载文件失败: {e}") sys.exit(1) def connect_mongodb(use_docker: bool = True, config: dict = None) -> MongoClient: """连接到 MongoDB Args: use_docker: True=在 Docker 容器内运行(使用 mongodb 服务名) False=在宿主机运行(使用 localhost) config: 配置字典,包含端口等信息 """ if config is None: config = { 'mongodb_port': 27017, 'mongodb_host': 'localhost', 'mongodb_username': 'admin', 'mongodb_password': 'tradingagents123', 'mongodb_database': 'tradingagents' } # 构建 MongoDB URI host = 'mongodb' if use_docker else config['mongodb_host'] port = config['mongodb_port'] username = config['mongodb_username'] password = config['mongodb_password'] database = config['mongodb_database'] mongo_uri = f"mongodb://{username}:{password}@{host}:{port}/{database}?authSource=admin" env_name = "Docker 容器内" if use_docker else "宿主机" print(f"\n🔌 连接到 MongoDB ({env_name})...") print(f" URI: mongodb://{username}:***@{host}:{port}/{database}?authSource=admin") try: client = MongoClient(mongo_uri, serverSelectionTimeoutMS=5000) # 测试连接 client.admin.command('ping') print(f"✅ MongoDB 连接成功") return client except Exception as e: print(f"❌ 错误: MongoDB 连接失败: {e}") if use_docker: print(f" 请确保在 Docker 容器内运行,或使用 --host 参数在宿主机运行") print(f" 检查容器: docker ps | grep mongodb") else: print(f" 请确保 MongoDB 正在运行并监听端口 {port}") print(f" 检查端口: netstat -an | findstr {port}") sys.exit(1) def import_collection( db: Any, collection_name: str, documents: List[Dict[str, Any]], overwrite: bool = False ) -> Dict[str, int]: """导入单个集合""" collection = db[collection_name] # 转换文档格式 converted_docs = [convert_to_bson(doc) for doc in documents] if overwrite: # 覆盖模式:删除现有数据 result = collection.delete_many({}) deleted_count = result.deleted_count if converted_docs: result = collection.insert_many(converted_docs) inserted_count = len(result.inserted_ids) else: inserted_count = 0 return { "deleted": deleted_count, "inserted": inserted_count, "skipped": 0 } else: # 增量模式:跳过已存在的文档 inserted_count = 0 skipped_count = 0 for doc in converted_docs: # 检查是否已存在(根据 _id 或 username) query = {} if "_id" in doc: query["_id"] = doc["_id"] elif "username" in doc: query["username"] = doc["username"] elif "name" in doc: query["name"] = doc["name"] else: # 没有唯一标识,直接插入 collection.insert_one(doc) inserted_count += 1 continue existing = collection.find_one(query) if existing: skipped_count += 1 else: collection.insert_one(doc) inserted_count += 1 return { "deleted": 0, "inserted": inserted_count, "skipped": skipped_count } def create_default_admin(db: Any, overwrite: bool = False) -> bool: """创建默认管理员用户""" print(f"\n👤 创建默认管理员用户...") users_collection = db.users # 检查用户是否已存在 existing_user = users_collection.find_one({"username": DEFAULT_ADMIN["username"]}) if existing_user: if not overwrite: print(f"⚠️ 用户 '{DEFAULT_ADMIN['username']}' 已存在,跳过创建") return False else: print(f"⚠️ 用户 '{DEFAULT_ADMIN['username']}' 已存在,将覆盖") users_collection.delete_one({"username": DEFAULT_ADMIN["username"]}) # 创建用户文档 user_doc = { "username": DEFAULT_ADMIN["username"], "email": DEFAULT_ADMIN["email"], "hashed_password": hash_password(DEFAULT_ADMIN["password"]), "is_active": True, "is_verified": True, "is_admin": True, "created_at": datetime.utcnow(), "updated_at": datetime.utcnow(), "last_login": None, "preferences": { "default_market": "A股", "default_depth": "深度", "ui_theme": "light", "language": "zh-CN", "notifications_enabled": True, "email_notifications": False }, "daily_quota": 10000, "concurrent_limit": 10, "total_analyses": 0, "successful_analyses": 0, "failed_analyses": 0, "favorite_stocks": [] } users_collection.insert_one(user_doc) print(f"✅ 默认管理员用户创建成功") print(f" 用户名: {DEFAULT_ADMIN['username']}") print(f" 密码: {DEFAULT_ADMIN['password']}") print(f" 邮箱: {DEFAULT_ADMIN['email']}") print(f" 角色: 管理员") return True def ensure_new_providers(db: Any): """确保新加入的 Provider 存在于数据库中(兼容旧导出文件)""" print(f"\n✨ 检查并补充缺失的新 Provider...") providers_collection = db.llm_providers now = datetime.utcnow() # 新 Provider 定义列表 new_providers = [ { "name": "oneapi", "display_name": "LLM Hub", "description": "LLM Hub 一站式人工智能集成平台", "website": "https://www.llmhub.com.cn", "api_doc_url": "https://docs.llmhub.com.cn/", "default_base_url": "https://api.llmhub.com.cn/v1", "is_active": True, "supported_features": ["chat", "completion", "embedding", "image", "vision", "function_calling", "streaming"], "is_aggregator": True, "aggregator_type": "openai_compatible", "logo_url": "/assets/logos/oneapi.png" } ] added_count = 0 for provider in new_providers: # 检查是否存在 existing = providers_collection.find_one({"name": provider["name"]}) if not existing: # 补全时间字段 provider["created_at"] = now provider["updated_at"] = now provider["extra_config"] = {} # 插入 providers_collection.insert_one(provider) print(f" ➕ 已自动补全: {provider['display_name']} ({provider['name']})") added_count += 1 else: # 可选:如果存在但不是聚合类型,更新它 if not existing.get("is_aggregator"): providers_collection.update_one( {"_id": existing["_id"]}, {"$set": {"is_aggregator": True, "aggregator_type": "openai_compatible"}} ) print(f" 🔄 已更新以支持聚合模式: {provider['display_name']}") if added_count == 0: print(" ✅ 所有新 Provider 已存在,无需补充") else: print(f" 🎉 成功补全 {added_count} 个 Provider") def main(): """主函数""" parser = argparse.ArgumentParser( description="导入配置数据并创建默认用户", formatter_class=argparse.RawDescriptionHelpFormatter," 示例: # 在 Docker 容器内运行(默认) python scripts/import_config_and_create_user.py # 在宿主机运行(连接到 localhost:27017) python scripts/import_config_and_create_user.py --host # 从指定文件导入(默认覆盖模式) python scripts/import_config_and_create_user.py export.json # 增量模式:跳过已存在的数据 python scripts/import_config_and_create_user.py --incremental # 只导入指定的集合 python scripts/import_config_and_create_user.py --collections system_configs users # 只创建默认用户,不导入数据 python scripts/import_config_and_create_user.py --create-user-only """ ) parser.add_argument( "export_file", nargs="?", help="导出的 JSON 文件路径(默认:install/database_export_config_*.json)" ) parser.add_argument( "--host", action="store_true", help="在宿主机运行(连接 localhost:27017),默认在 Docker 容器内运行(连接 mongodb:27017)" ) parser.add_argument( "--overwrite", action="store_true", default=True, help="覆盖已存在的数据(默认:覆盖)" ) parser.add_argument( "--incremental", action="store_true", help="增量模式:跳过已存在的数据" ) parser.add_argument( "--collections", nargs="+", help="指定要导入的集合(默认:所有配置集合)" ) parser.add_argument( "--create-user-only", action="store_true", help="只创建默认用户,不导入数据" ) parser.add_argument( "--skip-user", action="store_true", help="跳过创建默认用户" ) parser.add_argument( "--mongodb-port", type=int, help="MongoDB 端口(覆盖 .env 配置)" ) parser.add_argument( "--mongodb-host", type=str, help="MongoDB 主机(覆盖 .env 配置)" ) args = parser.parse_args() # 处理 incremental 参数(如果指定了 --incremental,则 overwrite 为 False) if args.incremental: args.overwrite = False # 如果没有指定文件,尝试从 install 目录查找 if not args.create_user_only and not args.export_file: install_dir = project_root / "install" if install_dir.exists(): # 查找 database_export_config_*.json 文件 config_files = list(install_dir.glob("database_export_config_*.json")) if config_files: # 使用最新的文件 args.export_file = str(sorted(config_files)[-1]) print(f"💡 未指定文件,使用默认配置: {args.export_file}") else: parser.error("install 目录中未找到配置文件 (database_export_config_*.json)") else: parser.error("必须提供导出文件路径,或使用 --create-user-only") print("=" * 80) print("📦 导入配置数据并创建默认用户") print("=" * 80) # 加载 .env 配置 script_dir = Path(__file__).parent env_config = load_env_config(script_dir) # 命令行参数覆盖 .env 配置 if args.mongodb_port: env_config['mongodb_port'] = args.mongodb_port print(f"💡 使用命令行指定的 MongoDB 端口: {args.mongodb_port}") if args.mongodb_host: env_config['mongodb_host'] = args.mongodb_host print(f"💡 使用命令行指定的 MongoDB 主机: {args.mongodb_host}") # 连接数据库 use_docker = not args.host # 默认在 Docker 内运行,除非指定 --host client = connect_mongodb(use_docker=use_docker, config=env_config) db = client[DB_NAME] # 导入数据 if not args.create_user_only: # 加载导出文件 export_data = load_export_file(args.export_file) data = export_data["data"] # 确定要导入的集合 if args.collections: collections_to_import = args.collections else: collections_to_import = [c for c in CONFIG_COLLECTIONS if c in data] print(f"\n📋 准备导入 {len(collections_to_import)} 个集合:") for col in collections_to_import: doc_count = len(data.get(col, [])) print(f" - {col}: {doc_count} 个文档") # 导入集合 print(f"\n🚀 开始导入...") print(f" 模式: {'覆盖' if args.overwrite else '增量'}") total_stats = { "deleted": 0, "inserted": 0, "skipped": 0 } for collection_name in collections_to_import: if collection_name not in data: print(f"⚠️ 跳过 {collection_name}: 导出文件中不存在") continue documents = data[collection_name] print(f"\n 导入 {collection_name}...") try: stats = import_collection(db, collection_name, documents, args.overwrite) total_stats["deleted"] += stats["deleted"] total_stats["inserted"] += stats["inserted"] total_stats["skipped"] += stats["skipped"] if args.overwrite: print(f" ✅ 删除 {stats['deleted']} 个,插入 {stats['inserted']} 个") else: print(f" ✅ 插入 {stats['inserted']} 个,跳过 {stats['skipped']} 个") except Exception as e: print(f" ❌ 失败: {e}") print(f"\n📊 导入统计:") if args.overwrite: print(f" 删除: {total_stats['deleted']} 个文档") print(f" 插入: {total_stats['inserted']} 个文档") if not args.overwrite: print(f" 跳过: {total_stats['skipped']} 个文档") # 创建默认用户 if not args.skip_user: create_default_admin(db, args.overwrite) # 🟢 确保新加入的 Provider (OneAPI, NewAPI, 302.AI) 存在 # 这是为了兼容旧的导出文件,防止新功能缺失 ensure_new_providers(db) # 关闭连接 client.close() print("\n" + "=" * 80) print("✅ 操作完成!") print("=" * 80) if not args.skip_user: print(f"\n🔐 登录信息:") print(f" 用户名: {DEFAULT_ADMIN['username']}") print(f" 密码: {DEFAULT_ADMIN['password']}") print(f"\n📝 后续步骤:") print(f" 1. 重启后端服务: docker restart tradingagents-backend") print(f" 2. 访问前端并使用默认账号登录") print(f" 3. 检查系统配置是否正确加载") if __name__ == "__main__": main()

3. 配置连接 (最关键的一步!)

回到 tradingagents-cn 目录(即 /opt/1panel/docker/compose/tradingagents-cn),我们需要创建环境变量文件。

步骤 3.1:创建.env文件: 新建文件 .env,填入以下配置(请自行替换数据库信息):

# TradingAgents-CN 生产环境配置 (.env) # 专为 1Panel 集成设计 # ==================== 1. 核心连接配置 ==================== # ⚠️ 注意:必须填写正确的容器名称 (1Panel 创建的容器通常有随机后缀) # 请在 1Panel 容器列表或终端使用 `docker ps` 查看准确名称并填入下方 # MongoDB 配置 (外部) MONGODB_ENABLED=true # 填入 MongoDB 容器名称 (例如: 1panel-mongodb) MONGODB_HOST=1Panel-mongodb MONGODB_PORT=27017 MONGODB_USERNAME=mongo_zetJdx MONGODB_PASSWORD=mongo_WHdXh8 MONGODB_DATABASE=tradingagents MONGODB_AUTH_SOURCE=admin # 连接字符串 (格式: mongodb://用户:密码@容器名:端口/数据库?authSource=admin) # 请同步修改下方的容器名 MONGODB_CONNECTION_STRING=mongodb://mongo_zetJdx:mongo_WHdXh8@1Panel-mongodb:27017/tradingagents?authSource=admin # Redis 配置 (外部) REDIS_ENABLED=true # 填入 Redis 容器名称 (例如: 1panel-redis) REDIS_HOST=1Panel-redis REDIS_PORT=6379 REDIS_PASSWORD=redis_3wPzP3 # 连接字符串 (格式: redis://:密码@容器名:6379/0) REDIS_URL=redis://:redis_3wPzP3@1Panel-redis:6379/0 # ==================== 2. 应用端口 ==================== # 暴露给宿主机的端口,1Panel OpenResty 将反代此端口 APP_PORT=18000 # ==================== 3. 安全配置 (必须修改) ==================== # 生成新密钥: openssl rand -hex 32 JWT_SECRET=334a40eb30d222bee16de8ac34b6ad9af9f25cc3c7e1ed2df536401ea9f88446 CSRF_SECRET=a97e64c9678b6c3948dc0d18072753df55303592ca6aa65c6337dcc4d90638b1 ACCESS_TOKEN_EXPIRE_MINUTES=480 # ==================== 4. API 密钥 (按需填写) ==================== # DeepSeek (推荐) DEEPSEEK_API_KEY= DEEPSEEK_ENABLED=false # DashScope (阿里百炼) DASHSCOPE_API_KEY= DASHSCOPE_ENABLED=false # OpenAI OPENAI_API_KEY= OPENAI_ENABLED=false # ==================== 5. 聚合渠道配置 (LLM Hub) ==================== ONEAPI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ONEAPI_BASE_URL=https://api.llmhub.com.cn/v1 # ==================== 6. 数据源配置 ==================== TUSHARE_TOKEN= TUSHARE_ENABLED=false # ==================== 6. 系统参数 ==================== TZ=Asia/Shanghai TRADINGAGENTS_LOG_LEVEL=INFO # 生产环境通常不需要这些 DEBUG=false PYTHONDONTWRITEBYTECODE=1

步骤 3.2:创建.docker-compose.yml文件: 新建文件.docker-compose.yml,填入以下配置

services: # 后端服务 backend: image: hsliup/tradingagents-backend:v1.0.0-preview container_name: tradingagents-backend restart: always expose: - "8000" volumes: - ./logs:/app/logs - ./data:/app/data # 挂载修改后的初始化脚本覆盖镜像内的版本 - ./scripts/import_config_and_create_user.py:/app/scripts/import_config_and_create_user.py env_file: - .env networks: - 1panel-network deploy: resources: limits: memory: 2G healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"] interval: 30s timeout: 10s retries: 3 # 前端服务 frontend: image: hsliup/tradingagents-frontend:v1.0.0-preview container_name: tradingagents-frontend restart: always expose: - "80" environment: # 前端只需知道它被代理在 /api 下访问后端,通常不需要改 VITE_API_BASE_URL: "/api" networks: - 1panel-network healthcheck: test: [ "CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80", ] interval: 30s timeout: 10s retries: 3 # 应用网关 (Nginx) nginx: image: nginx:alpine container_name: tradingagents-nginx restart: always ports: # 映射到宿主机端口,供 1Panel OpenResty 反代 - "${APP_PORT:-18000}:80" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./logs/nginx:/var/log/nginx networks: - 1panel-network depends_on: - backend - frontend networks: # 引用外部 1Panel 网络 1panel-network: external: true

4. 启动应用

在终端执行:

# 进入 /opt/1panel/docker/compose/tradingagents-cn cd /opt/1panel/docker/compose/tradingagents-cn # 指定生产环境文件启动 docker compose --env-file .env up -d

(注意:容器会自动加入 1Panel 的内部网络,从而能连上刚才安装的 Redis 和 MongoDB)

等待日志显示 Done

5. 初始化数据

看到 ✅ 操作完成 即大功告成!

最后一步:防火墙放行端口

进入你的 VPS 防火墙设置放行 18000 端口。

访问地址为 http://111.222.333.444:18000,即可打开项目。

Could not load content