Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 28 additions & 29 deletions experiments/comparison_agents/agents4sci_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def reason(self, scenario: str, domains: List[str]) -> ReasoningResult:
model_id=self.model_id,
expert_roles=domains,
rounds=3,
max_workers=8
)

# Create and run orchestrator
Expand Down Expand Up @@ -102,34 +101,34 @@ def reason(self, scenario: str, domains: List[str]) -> ReasoningResult:
## Conclusion
The Agents4Sci multi-agent approach provides comprehensive coverage across all relevant domains, identifies critical cross-domain interactions, and quantifies uncertainties systematically. This approach significantly improves the quality and reliability of counterfactual scenario analysis."""

# Extract insights from the multi-round process
insights = self._extract_agents4sci_insights(temp_path, final_report)
return ReasoningResult(
agent_type="Agents4Sci",
reasoning_steps=insights["reasoning_steps"],
conclusions=insights["conclusions"],
assumptions=insights["assumptions"],
uncertainties=insights["uncertainties"],
cross_domain_insights=insights["cross_domain_insights"],
confidence_score=insights["confidence_score"],
reasoning_depth=insights["reasoning_depth"],
raw_output=final_report,
metadata={
"domains_covered": domains,
"approach": "agents4sci_multi_agent",
"rounds_completed": metadata.get("rounds_completed", 0),
"experts_consulted": len(domains),
"conflicts_resolved": metadata.get("conflicts_resolved", 0),
"advantages": [
"Multi-agent collaboration",
"Conflict resolution",
"Iterative refinement",
"Domain specialization",
"Cross-domain synthesis"
]
}
)
# Extract insights from the multi-round process
insights = self._extract_agents4sci_insights(temp_path, final_report)

return ReasoningResult(
agent_type="Agents4Sci",
reasoning_steps=insights["reasoning_steps"],
conclusions=insights["conclusions"],
assumptions=insights["assumptions"],
uncertainties=insights["uncertainties"],
cross_domain_insights=insights["cross_domain_insights"],
confidence_score=insights["confidence_score"],
reasoning_depth=insights["reasoning_depth"],
raw_output=final_report,
metadata={
"domains_covered": domains,
"approach": "agents4sci_multi_agent",
"rounds_completed": metadata.get("rounds_completed", 0),
"experts_consulted": len(domains),
"conflicts_resolved": metadata.get("conflicts_resolved", 0),
"advantages": [
"Multi-agent collaboration",
"Conflict resolution",
"Iterative refinement",
"Domain specialization",
"Cross-domain synthesis"
]
}
)

def _extract_agents4sci_insights(self, output_dir: Path, final_report: str) -> Dict[str, Any]:
"""Extract structured insights from Agents4Sci outputs"""
Expand Down
16 changes: 16 additions & 0 deletions experiments/comparison_agents/mock_llm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def chat(
- Human health impacts from oxidative stress
- Economic disruption from increased fire incidents

## Catastrophic Risks:
- Catastrophic global wildfires leading to infrastructure collapse
- Potential extinction events for oxygen-sensitive species

## Assumptions:
- Current technology levels maintained
- Gradual increase over 50 years allows some adaptation
Expand Down Expand Up @@ -77,6 +81,10 @@ def chat(
- Higher biological mutation rates
- Human health risks from radiation

## Catastrophic Risks:
- Catastrophic failure of electrical grids from solar storms
- Widespread radiation-induced health crises

## Assumptions:
- Gradual decrease over 100 years
- Current technology levels maintained
Expand Down Expand Up @@ -109,6 +117,10 @@ def chat(
- Economic disruption in coastal regions
- Human health effects from malnutrition

## Catastrophic Risks:
- Catastrophic collapse of global fisheries
- Mass starvation events in vulnerable regions

## Assumptions:
- Rapid pH decrease over 30 years
- Current technology levels maintained
Expand Down Expand Up @@ -141,6 +153,10 @@ def chat(
- Need for adaptation and mitigation strategies
- Potential for both positive and negative outcomes

## Catastrophic Risks:
- Catastrophic cascading failures across critical systems
- Potential for global socio-economic collapse

## Assumptions:
- Current technology levels maintained
- Gradual change allows some adaptation
Expand Down
7 changes: 6 additions & 1 deletion src/a4s/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,16 @@ def run(self, expert_input: ExpertInput) -> ExpertOutput:
text = resp["choices"][0]["message"]["content"].strip()

# Non-strict parsing by headings
sections = {"reasoning": [], "conclusions": [], "assumptions": [], "uncertainties": [], "dependency": []}
sections = {"reasoning": [], "conclusions": [], "catastrophic": [], "assumptions": [], "uncertainties": [], "dependency": []}
current = None
for line in text.splitlines():
lower = line.lower()
if "reasoning" in lower and ":" in line:
current = "reasoning"; continue
if "conclusion" in lower and ":" in line:
current = "conclusions"; continue
if "catastrophic" in lower and ":" in line:
current = "catastrophic"; continue
if "assumption" in lower and ":" in line:
current = "assumptions"; continue
if "uncertaint" in lower and ":" in line:
Expand All @@ -89,6 +91,7 @@ def run(self, expert_input: ExpertInput) -> ExpertOutput:
role=self.role,
reasoning_steps=sections["reasoning"] or [text],
conclusions=sections["conclusions"] or [],
catastrophic_risks=sections["catastrophic"] or [],
assumptions=sections["assumptions"] or [],
uncertainties=sections["uncertainties"] or [],
dependency_notes=sections["dependency"] or [],
Expand All @@ -113,6 +116,8 @@ def reconcile(self, expert_outputs: List[ExpertOutput]) -> ConflictReport:
*out.reasoning_steps,
"Conclusions:",
*out.conclusions,
"Catastrophic Risks:",
*out.catastrophic_risks,
"Assumptions:",
*out.assumptions,
"Uncertainties:",
Expand Down
28 changes: 22 additions & 6 deletions src/a4s/llm_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import os
import sys
from pathlib import Path
from typing import List, Dict, Any, Optional

from openai import OpenAI
try:
from openai import OpenAI
except Exception: # pragma: no cover - library may be absent in testing
OpenAI = None # type: ignore


class LLMClient:
Expand All @@ -17,11 +22,19 @@ def __init__(
default_model: str = "doubao-seed-1-6-flash-250715",
) -> None:
api_key = os.environ.get(api_key_env)
if not api_key:
raise RuntimeError(
f"Missing API key in environment variable {api_key_env}."
)
self.client = OpenAI(base_url=base_url, api_key=api_key)
if not api_key or OpenAI is None:
# Fall back to mock client for offline testing
try:
from experiments.comparison_agents.mock_llm_client import MockLLMClient
except Exception:
# Ensure repository root on path for imports
sys.path.append(str(Path(__file__).resolve().parents[2]))
from experiments.comparison_agents.mock_llm_client import MockLLMClient
self.client = MockLLMClient(default_model=default_model)
self._use_mock = True
else:
self.client = OpenAI(base_url=base_url, api_key=api_key)
self._use_mock = False
self.default_model = default_model

def chat(
Expand All @@ -31,6 +44,9 @@ def chat(
temperature: float = 0.2,
max_tokens: Optional[int] = None,
) -> Dict[str, Any]:
if getattr(self, "_use_mock", False):
return self.client.chat(messages, model=model or self.default_model, temperature=temperature, max_tokens=max_tokens)

response = self.client.chat.completions.create(
model=model or self.default_model,
messages=messages,
Expand Down
3 changes: 3 additions & 0 deletions src/a4s/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ def _render_round_summary(round_index: int, outputs: List[ExpertOutput], conflic
if out.conclusions:
parts.append("Conclusions:")
parts.extend([f"- {c}" for c in out.conclusions])
if out.catastrophic_risks:
parts.append("Catastrophic Risks:")
parts.extend([f"- {r}" for r in out.catastrophic_risks])
if out.uncertainties:
parts.append("Uncertainties:")
parts.extend([f"- {u}" for u in out.uncertainties])
Expand Down
3 changes: 3 additions & 0 deletions src/a4s/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def system_prefix(role: str) -> str:
"Think step by step with explicit causal reasoning. "
"Prefer probabilistic, non-deterministic phrasing. "
"Model substitution and adaptation when projecting impacts. "
"Explicitly flag any catastrophic or extinction-level risks when relevant. "
"Keep outputs mostly natural language; use compact JSON only if essential. "
"Outputs MUST be in English; concise, structured bullet points acceptable."
)
Expand Down Expand Up @@ -40,6 +41,7 @@ def expert_round_prompt(role: str, scenario_text: str, shared_summary: str | Non
"Produce: \n"
"- Reasoning Steps (chain of thought summaries; no private thoughts)\n"
"- Conclusions (concise, quotable)\n"
"- Catastrophic Risks (explicit catastrophic or extinction-level outcomes, if any)\n"
"- Assumptions (explicit)\n"
"- Uncertainties (what could change conclusions)\n"
"- Dependency Notes (which other domains strongly affect your conclusions)\n"
Expand Down Expand Up @@ -73,6 +75,7 @@ def report_generation_prompt(proposition: str, rounds_summaries: List[str]) -> s
"- Causal map (nodes and edges in bullet form)\n"
"- Multi-scenario analysis (Scenario 1..N)\n"
"- Timeline of events (short/medium/long)\n"
"- Catastrophic risk assessment (highlight catastrophic or existential threats)\n"
"Prefer natural language; include minimal JSON only if helpful."
)

1 change: 1 addition & 0 deletions src/a4s/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ExpertOutput:
role: str
reasoning_steps: List[str]
conclusions: List[str]
catastrophic_risks: List[str]
assumptions: List[str]
uncertainties: List[str]
dependency_notes: List[str]
Expand Down