第 2 章 · 02 .env 配置全解 本节摘要:本节逐项解读 AutoHedge 的环境变量配置。项目根目录的 是唯一权威样例,但只有 5 行——真正读懂它需要对照源码:谁在用、用什么名字、缺了会不会崩。本节先逐行拆 (JUPITERAPIKEY、OPENAIAPIKEY、ANTHROPICAPIKEY、WORKSPACEDIR、WALLETPRIVATEKEY),再回到源码里把「真正被读取的变量」补全(包括 漏写的 EXAAPIKEY,以及 用的 与样例的 不一致的坑),最后讲清 如何从当前目录向上递归找 。读完本节,你能配出一份真正能让项目跑通的 。 内容来源:原项目源码 、 、 、 ,精读并套用体系化模板。 ⚠️ 风险提示:涉及私钥。
本节摘要:本节逐项解读 AutoHedge 的环境变量配置。项目根目录的
.env.example是唯一权威样例,但只有 5 行——真正读懂它需要对照源码:谁在用、用什么名字、缺了会不会崩。本节先逐行拆.env.example(JUPITER_API_KEY、OPENAI_API_KEY、ANTHROPIC_API_KEY、WORKSPACE_DIR、WALLET_PRIVATE_KEY),再回到源码里把「真正被读取的变量」补全(包括.env.example漏写的 EXA_API_KEY,以及ultra_tools.py用的SOLANA_PRIVATE_KEY与样例的WALLET_PRIVATE_KEY不一致的坑),最后讲清env_loader.py如何从当前目录向上递归找.env。读完本节,你能配出一份真正能让项目跑通的.env。
内容来源:原项目源码
.env.example、autohedge/env_loader.py、autohedge/tools/exa_search_tool.py、autohedge/tools/ultra_tools.py,精读并套用体系化模板。
⚠️ 风险提示:涉及私钥。务必用测试钱包,切勿填主资产钱包私钥——任何泄露即资产清零。
.env已被.gitignore排除,确认别误提交。
阅读完本节,你应当能够:
.env.example,说出每项变量的用途与必填性。.env.example 漏写与不一致的变量(EXA_API_KEY、SOLANA_PRIVATE_KEY)。.env 查找逻辑。override=False 为何不会覆盖已存在的环境变量。.env。项目根目录的 .env.example(共 12 行,5 个变量):
# Jupiter API (token price & search tools) # Get a key at https://portal.jup.ag JUPITER_API_KEY= # OpenAI (experimental agents) OPENAI_API_KEY= ANTHROPIC_API_KEY= WORKSPACE_DIR="agent_workspace" # Trading WALLET_PRIVATE_KEY=""
逐行解读:
| 变量 | 必填? | 用途 | 谁在读 |
|---|---|---|---|
JUPITER_API_KEY |
链上交易时必填 | Jupiter Ultra API 的鉴权 key(token 报价、广播) | ultra_tools.py 的 _headers() |
OPENAI_API_KEY |
必须 | 调 GPT-4.1 / gpt-4o-mini(整个 Agent 系统的命脉) | swarms 框架间接读取;cli.py 启动时校验 |
ANTHROPIC_API_KEY |
可选(当前未用) | 预留 Claude 模型接入,但 model_name 全是 OpenAI |
当前代码无任何引用 |
WORKSPACE_DIR |
可选 | Agent 工作目录(swarms 用) | swarms 框架内部 |
WALLET_PRIVATE_KEY |
链上交易时必填 | 样例写的是这个名字,但真实代码读的是另一个——见第三节 | (样例声明,代码不读这个名字) |
💡 核心心法:
.env.example是「作者声明我以为我用了什么」,源码是「作者实际用了什么」。两者对不上是这个项目反复出现的毛病。永远以源码为准。
把源码里所有 os.getenv(...) 找出来,才是真实的变量清单:
OPENAI_API_KEY——唯一启动门槛cli.py 第 17-21 行,启动时主动校验:
load_env() if not require_openai_key(): Console().print( "[yellow]Warning: OPENAI_API_KEY not set. Set it in .env or export it.[/]" )
require_openai_key() 的实现(env_loader.py 第 30-32 行):
def require_openai_key() -> bool: """Return True if OPENAI_API_KEY is set (required for swarms gpt-4.x).""" return bool(os.getenv("OPENAI_API_KEY"))
注意:它只是警告,不阻断启动——但真到 Agent 调用时,swarms 会因为没 key 报 401。所以OPENAI_API_KEY 是跑通任何任务的硬门槛。
EXA_API_KEY——样例漏写情绪 Agent 的工具 exa_search(exa_search_tool.py 第 44-49 行):
api_key = os.getenv("EXA_API_KEY") if not api_key: raise ValueError( "EXA_API_KEY environment variable is not set" )
.env.example 完全没提 EXA_API_KEY。但只要 Director 把任务 handoff 给情绪 Agent,它就会调 exa_search,进而抛 ValueError。要跑通情绪分析,必须自己补一行:
EXA_API_KEY=你的_exa_key # 去 https://exa.ai 申请
JUPITER_API_KEY——链上鉴权ultra_tools.py 的 _headers() 读它(给 Jupiter Ultra API 请求加 x-api-key 头)。只做链上分析(get_order/get_holdings)或签名广播(execute_trade)时需要。
SOLANA_PRIVATE_KEY——样例名字不一致这是本节最大的坑。样例写的是 WALLET_PRIVATE_KEY,但真正签名的 ultra_tools.py 读的是另一个名字。看 _get_keypair()(ultra_tools.py 第 57-66 行):
raw = os.getenv("SOLANA_PRIVATE_KEY") if not raw or not raw.strip(): raise ValueError("SOLANA_PRIVATE_KEY is required in .env to sign transactions")
即:你照 .env.example 填 WALLET_PRIVATE_KEY=xxx,签名时代码却去找 SOLANA_PRIVATE_KEY,结果就是「明明填了却报缺 key」。正确做法是两个名字都填上(或只填代码真正读的那个):
# 代码真正读的名字(签名必需) SOLANA_PRIVATE_KEY=你的_base58_私钥 # 样例声明的名字(保留以防别的脚本读) WALLET_PRIVATE_KEY=你的_base58_私钥
⚠️ 现实澄清:变量名不一致是典型的「样例与代码脱节」。这种 bug 在生产里会让人调试到崩溃。学这套代码,养成「先 grep
os.getenv再说」的习惯。
光配 .env 不够,还得让 Python 找得到它。env_loader.py 做的就是这件事——全文仅 33 行,设计却很巧:
import os from pathlib import Path from dotenv import load_dotenv def find_project_env() -> Path | None: """Find .env by walking up from cwd (so CLI/scripts work from any subdir).""" cwd = Path(os.getcwd()).resolve() for parent in [cwd, *cwd.parents]: env_file = parent / ".env" if env_file.is_file(): return env_file return None def load_env() -> None: """Load .env from project root; fall back to cwd. Does not override existing env.""" env_path = find_project_env() if env_path: load_dotenv(env_path, override=False) else: load_dotenv()
逐段拆解:
Path(os.getcwd()).resolve() 拿到当前工作目录的绝对路径。[cwd, *cwd.parents] 是一个列表:先看 cwd 自己,再看它的每一级父目录,直到磁盘根。.env 文件是否存在(is_file()),存在就返回它的路径。None。意图:让你无论从项目的哪个子目录跑代码(比如 cd autohedge/tools && python xxx.py),都能自动找到项目根的 .env,而不是要求你必须 cd 到根目录。
load_dotenv(env_path, override=False)
关键是 override=False:如果某个环境变量已经在系统环境里存在(比如你提前 set OPENAI_API_KEY=xxx),.env 里的值不会覆盖它。这在 CI/CD 或容器里很有用——可以用环境变量优先级覆盖文件配置。
💡 核心心法:
override=False体现的优先级是 系统环境变量 > .env 文件。调试时如果想强制用.env的值,要么改True,要么先清掉系统变量。
光有 load_env() 函数不够,得有人调它。AutoHedge 在两个入口都调了,形成双保险:
入口 1:autohedge/__init__.py 第 1-3 行——只要 import autohedge,load_env() 就执行:
from autohedge.env_loader import load_env load_env() from autohedge.main import AutoHedge
入口 2:autohedge/cli.py 第 17 行——REPL 启动时再调一次:
from autohedge.env_loader import load_env, require_openai_key load_env()
所以无论你用 example.py(里面 from autohedge import AutoHedge)还是 autohedge 命令,.env 都会被加载。重复调用 load_env() 无副作用(因为 override=False,且 load_dotenv 内部会去重)。
综合以上,推荐你把 .env 配成这样(把 .env.example 复制为 .env 后修改):
# === LLM(必填,否则任何任务都跑不动)=== OPENAI_API_KEY=sk-你的_openai_key # === 联网搜索(情绪 Agent 必需,样例漏写)=== EXA_API_KEY=你的_exa_key # === Jupiter(做链上分析/交易时填)=== JUPITER_API_KEY=你的_jupiter_key # === Solana 私钥(只在第 8 章签名时需要;务必用测试钱包)=== # 注意:代码真正读的是 SOLANA_PRIVATE_KEY,不是样例的 WALLET_PRIVATE_KEY SOLANA_PRIVATE_KEY=你的_base58_测试钱包私钥 WALLET_PRIVATE_KEY=你的_base58_测试钱包私钥 # === 可选 === WORKSPACE_DIR="agent_workspace" # ANTHROPIC_API_KEY 当前代码未用,可不填
⚠️ 风险提示:私钥那两行,只在学第 8 章链上签名时再填,且必须用测试钱包(新建一个,转少量测试 SOL 进去)。学第 3~7 章的多 Agent 编排,完全不需要私钥,这两行留空即可。
WALLET_PRIVATE_KEY,代码读 SOLANA_PRIVATE_KEY——两个都填最稳;务必用测试钱包。find_project_env 从 cwd 向上逐级找 .env,让你在任何子目录都能跑;load_dotenv(..., override=False) 保证系统环境变量优先于文件。__init__.py 和 cli.py 两处都调 load_env(),import autohedge 即触发。下一节,我们用
example.py跑通第一个分析任务,看 AutoHedge 初始化与run()的完整调用链。