【保姆级教程】零基础搭建你的专属 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,即可打开项目。

Read more

Android陀螺仪实战:从基础到VR运动策略封装

1. 陀螺仪基础:从传感器数据到三维旋转 大家好,我是老张,在移动端和智能硬件领域摸爬滚打了十几年,今天想和大家聊聊 Android 陀螺仪。很多刚接触的朋友会觉得这东西很神秘,什么角速度、姿态解算,听起来就头大。其实没那么复杂,你可以把陀螺仪想象成一个特别灵敏的“旋转速度计”。当你拿着手机转动时,它就能立刻告诉你:“嘿,你现在正绕着X轴,以每秒0.5弧度的速度在转呢!” 在 Android 里,我们通过 SensorManager 这个“大管家”来和陀螺仪打交道。第一步永远是获取服务,这就像你去银行办事得先取号一样。拿到 SensorManager 后,我们就能查询设备上有没有陀螺仪(Sensor.TYPE_GYROSCOPE)。现在绝大多数手机都有,但稳妥起见,检查一下总是好的。接下来就是注册一个监听器,告诉系统:“我准备好接收旋转数据了,有新数据就赶紧通知我。” 这里有个关键参数叫采样延迟,比如 SENSOR_DELAY_

AI一镜到底效果炸裂 把教材插图变成VR全景视频(附提示词)

AI一镜到底效果炸裂 把教材插图变成VR全景视频(附提示词)

大家好,我是AI培训韩老师! 在电影的世界里,有一种拍摄手法总能引发观众惊叹——一镜到底。它让镜头像一双无形的眼睛,带领我们穿越战场、潜入犯罪现场、亲历角色内心世界,不间断地体验完整的故事时空。 于是很多人会问我,如何用AI实现一镜到底?简单来说就是不用剪辑一键生成,又简单有高级那种。下面通过这篇文章告诉你! 用AI生成具有电影感的“一镜到底”视频,关键在于清晰地告诉AI你想要的镜头运动轨迹和场景衔接方式。下面我为你梳理了从核心思路、具体方法到实用技巧的完整指南。 🎬 理解AI一镜到底的核心 在AI视频生成中,它通常通过两种方式实现: * 智能多帧创作:这是目前更主流高效的方法。你先准备一系列在内容上连贯的图片(相当于分镜图),然后AI会模拟镜头的连续运动,将这些画面无缝连接成一段长视频,营造出一镜到底的观感。 * 单一长提示词生成:直接用一个详细的长段文本描述整个镜头的运动路径和所有场景变化,由AI直接生成视频。这对提示词书写要求极高,且效果不确定性更大。 无论哪种方式,精准地描述镜头运动(运镜)都是成功的关键。 📷 掌握核心运镜技巧 你需要像导演一样思考,

电影知识图谱推荐问答系统 | Python Django系统 Neo4j MySQL Echarts 协同过滤 大数据 人工智能 毕业设计源码(建议收藏)✅

电影知识图谱推荐问答系统 | Python Django系统 Neo4j MySQL Echarts 协同过滤 大数据 人工智能 毕业设计源码(建议收藏)✅

博主介绍:✌全网粉丝10W+,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,选择我们,就是选择放心、选择安心毕业✌ > 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与我联系了。🍅 点击查看作者主页,了解更多项目! 🍅感兴趣的可以先收藏起来,点赞、关注不迷路,大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助同学们顺利毕业 。🍅 1、毕业设计:2026年计算机专业毕业设计选题汇总(建议收藏)✅ 2、最全计算机专业毕业设计选题大全(建议收藏)✅ 1、项目介绍 技术栈 以Python作为核心开发语言,基于Django框架搭建系统整体架构,采用Neo4j图形数据库与MySQL数据库存储数据,整合Echarts可视化工具、协同过滤推荐算法,结合HTML技术完成前端页面的搭建。 功能模块 * 电影知识图谱管理 * 电影问答交互 * 电影列表展示 * 个人信息查看 * 电影详情展示 * 用户注册登录 * 后

【数据库】国产数据库的新机遇:电科金仓以融合技术同步全球竞争

【数据库】国产数据库的新机遇:电科金仓以融合技术同步全球竞争

7月15日,国产数据库厂商中电科金仓(北京)科技股份有限公司(以下简称“电科金仓”)在北京举行了一场技术发布会,集中发布四款核心产品:AI时代的融合数据库KES V9 2025、企业级统一管控平台KEMCC、数据库一体机(云数据库AI版)以及企业级智能海量数据集成平台KFS Ultra,并同步举行了“金兰组织2.0”启动仪式。 如果放在过去几年,这场发布会可能被归入“信创替代”的常规范畴。但这一次,电科金仓试图讲述的不再是“我们也能做、我们可以兼容”,而是“我们能不能定义下一代数据库形态”。 整个发布会贯穿了三个关键词:“融合”“AI”“平台能力”。这背后的核心逻辑是清晰的:在“去IOE”与“兼容Oracle”的红利渐近尾声之际,国产数据库厂商开始面对一个更加复杂、也更具挑战性的市场命题——如何在大模型时代支撑非结构化数据、高维向量检索和复杂语义计算的新需求? 正如我国数据库学科带头人王珊教授所说,数据库内核与AI能力的深度结合,已成为释放数据核心价值的关键路径,正催生着更智能、更自适应、更能应对复杂挑战的新一代数据库形态。