This commit is contained in:
2026-09-27 23:06:24 +08:00
parent 2d4f9230b1
commit 22450e5bb3
40 changed files with 3040 additions and 697 deletions
+50 -10
View File
@@ -11,7 +11,29 @@ from app.constants import TIMES_OF_DAY, normalize_emotional_state
logger = logging.getLogger(__name__)
class ActionResolver:
class ActionResolver:
@staticmethod
def _resolve_resource_name(raw_name, available: set[str]) -> str | None:
"""Return a valid resource key or None for an LLM-invented key.
Action arguments are generated text, so a model may describe a
resource (for example, "my own labor and what scraps I have") instead
of using one of the machine-readable keys in the world blueprint.
Resource mutations must never use that unchecked value as a dict key.
"""
if not isinstance(raw_name, str):
return None
value = raw_name.strip()
if not value:
return None
if value in available:
return value
lowered = value.lower()
for resource in available:
if str(resource).lower() == lowered:
return resource
return None
@staticmethod
def _resolve_agent_id(ref, agent_map: dict[int, AgentPersona]) -> int | None:
if ref is None:
@@ -351,15 +373,33 @@ class ActionResolver:
self._strengthen_social_edge(other, agent.id, 0.05, 0.1)
rel_changes[str(other.id)] = "impressed"
elif at == ActionType.BUILD:
cost_resource = action_args.get("resource", "goods")
try:
cost_amount = int(action_args.get("cost", 20))
except (ValueError, TypeError):
cost_amount = 20
if agent.resources.get(cost_resource, 0) >= cost_amount:
agent.resources[cost_resource] -= cost_amount
world_changes["built"] = action_args.get("what", "something")
elif at == ActionType.BUILD:
# Only blueprint resources are valid action keys. Persona data
# can also contain arbitrary model-generated labels, so accepting
# every key currently present in ``agent.resources`` would let an
# invented phrase become part of the simulation vocabulary.
available_resources = {
str(resource) for resource in world_state.blueprint.resources
}
raw_resource = action_args.get("resource", "goods")
cost_resource = self._resolve_resource_name(raw_resource, available_resources)
try:
cost_amount = int(action_args.get("cost", 20))
except (ValueError, TypeError):
cost_amount = 20
if cost_amount <= 0:
cost_amount = 20
if cost_resource is None:
logger.warning(
"居民 %s 的 BUILD 行动使用了无效资源 %r,已忽略该行动",
agent.name,
raw_resource,
)
world_changes["build_failed"] = "invalid_resource"
elif agent.resources.get(cost_resource, 0) >= cost_amount:
agent.resources[cost_resource] -= cost_amount
world_changes["built"] = action_args.get("what", "something")
elif at == ActionType.OBSERVE:
agent.resources["knowledge"] = agent.resources.get("knowledge", 0) + 3