agent_urban_planning.HybridDecisionEngine

class HybridDecisionEngine(params, elicitor=None, *, llm_client=None, **kwargs)[source]

Bases: object

LLM-elicited preferences + closed-form mixed-logit choice (V4).

The hybrid pattern: an LLM is queried once per agent cluster to elicit the cluster’s per-agent preference weights (β: housing share; κ: commute disutility). The closed-form mixed-logit choice then computes discrete zone selection deterministically. This pattern keeps the LLM in the role of parameter provider rather than decision maker — much cheaper than the full-LLM V5 approach while still capturing demographic-driven preference heterogeneity.

Parameters:
  • params (Any) – An AhlfeldtParams instance carrying structural elasticities (alpha, beta, epsilon, kappa_eps).

  • llm_client (Any) – An agent_urban_planning.llm.LLMClient instance (or anything with a .complete(user, system="") method returning a string).

  • **kwargs (Any) – Forwarded to the underlying AhlfeldtArgmaxHybridEngine. Common kwargs: cluster_k (default 50), clustering_algo (default "kmeans"), num_agents, batch_size, seed, llm_concurrency, cache_dir.

  • elicitor (Any)

Raises:

ValueError – If llm_client is None.

Examples

V4 reproduction with codex-cli:

>>> import agent_urban_planning as aup
>>> engine = aup.HybridDecisionEngine(
...     params=scenario.ahlfeldt_params,
...     llm_client=aup.llm.CodexCliClient(),
...     cluster_k=50,
...     num_agents=1_000_000,
...     seed=42,
... )
>>> # sim = aup.SimulationEngine(scenario, agent_config, engine=engine)
>>> # results = sim.run()

V4 with claude-code (alternate provider):

>>> engine = aup.HybridDecisionEngine(
...     params=scenario.ahlfeldt_params,
...     llm_client=aup.llm.ClaudeCodeClient(),
... )

See also

agent_urban_planning.UtilityEngine — closed-form V1/V2/V3 baselines (no LLM involvement). agent_urban_planning.LLMDecisionEngine — V5 full-LLM-as- decision-maker pattern.

decide_batch(*args, **kwargs)[source]

Forward to the underlying implementation’s decide_batch.

Transparently forwards to AhlfeldtArgmaxHybridEngine.decide_batch(). Calling this triggers (and caches) LLM elicitation of per-type preference weights on the first invocation; subsequent calls reuse the cached weights.

Parameters:
  • *args (Any) – Positional arguments forwarded unchanged.

  • **kwargs (Any) – Keyword arguments forwarded unchanged.

Return type:

Any

Returns:

List of agent_urban_planning.LocationChoice, one per input agent and in the same order.

Examples

>>> import agent_urban_planning as aup
>>> # engine = aup.HybridDecisionEngine(params, elicitor=elicitor)
>>> # choices = engine.decide_batch(agents, env, zones, prices)