feat(config): expose sampling temperature and document reproducibility

Adds a cross-provider temperature config (and TRADINGAGENTS_TEMPERATURE),
forwarded to every LLM client when set, so runs can be made less variable
on models that honor it. Adds a README "Reproducibility" section that
separates the sources of run-to-run variation, what users can control
(temperature, non-reasoning model, pinned date), and what is inherent to
LLM-driven analysis, and notes that the identity and verified-data fixes
already removed the "different companies / fabricated prices" variance.

#178 #168
This commit is contained in:
Yijia-Xiao
2026-05-31 03:51:50 +00:00
parent 47cbb321fe
commit 8a22594607
9 changed files with 123 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ _ENV_OVERRIDES = {
"TRADINGAGENTS_MAX_RISK_ROUNDS": "max_risk_discuss_rounds",
"TRADINGAGENTS_CHECKPOINT_ENABLED": "checkpoint_enabled",
"TRADINGAGENTS_BENCHMARK_TICKER": "benchmark_ticker",
"TRADINGAGENTS_TEMPERATURE": "temperature",
}
@@ -64,6 +65,11 @@ DEFAULT_CONFIG = _apply_env_overrides({
"google_thinking_level": None, # "high", "minimal", etc.
"openai_reasoning_effort": None, # "medium", "high", "low"
"anthropic_effort": None, # "high", "medium", "low"
# Sampling temperature, forwarded to every provider when set. None leaves
# each provider at its own default. Lower values reduce run-to-run
# variation on models that honor it; reasoning models largely ignore it
# and no setting makes LLM output bit-identical across runs (see README).
"temperature": None,
# Checkpoint/resume: when True, LangGraph saves state after each node
# so a crashed run can resume from the last successful step.
"checkpoint_enabled": False,

View File

@@ -155,6 +155,13 @@ class TradingAgentsGraph:
if effort:
kwargs["effort"] = effort
# Sampling temperature is cross-provider: forward it whenever set.
# float() here so a value coming from a TRADINGAGENTS_TEMPERATURE env
# string ("0.2") works the same as a programmatic float.
temperature = self.config.get("temperature")
if temperature is not None and temperature != "":
kwargs["temperature"] = float(temperature)
return kwargs
def _create_tool_nodes(self) -> Dict[str, ToolNode]:

View File

@@ -7,7 +7,7 @@ from .base_client import BaseLLMClient, normalize_content
from .validators import validate_model
_PASSTHROUGH_KWARGS = (
"timeout", "max_retries", "api_key", "max_tokens",
"timeout", "max_retries", "api_key", "max_tokens", "temperature",
"callbacks", "http_client", "http_async_client", "effort",
)

View File

@@ -7,7 +7,7 @@ from .base_client import BaseLLMClient, normalize_content
from .validators import validate_model
_PASSTHROUGH_KWARGS = (
"timeout", "max_retries", "api_key", "reasoning_effort",
"timeout", "max_retries", "api_key", "reasoning_effort", "temperature",
"callbacks", "http_client", "http_async_client",
)

View File

@@ -31,7 +31,7 @@ class GoogleClient(BaseLLMClient):
if self.base_url:
llm_kwargs["base_url"] = self.base_url
for key in ("timeout", "max_retries", "callbacks", "http_client", "http_async_client"):
for key in ("timeout", "max_retries", "temperature", "callbacks", "http_client", "http_async_client"):
if key in self.kwargs:
llm_kwargs[key] = self.kwargs[key]

View File

@@ -138,7 +138,7 @@ class MinimaxChatOpenAI(NormalizedChatOpenAI):
# Kwargs forwarded from user config to ChatOpenAI
_PASSTHROUGH_KWARGS = (
"timeout", "max_retries", "reasoning_effort",
"timeout", "max_retries", "reasoning_effort", "temperature",
"api_key", "callbacks", "http_client", "http_async_client",
)