diff --git a/pycityagent/cityagent/blocks/cognition_block.py b/pycityagent/cityagent/blocks/cognition_block.py index d3b5cd6..81be58b 100644 --- a/pycityagent/cityagent/blocks/cognition_block.py +++ b/pycityagent/cityagent/blocks/cognition_block.py @@ -207,10 +207,6 @@ async def forward(self): async def emotion_update(self, incident): """Cognition - emotion update workflow""" - whether_trigger = await self.check_trigger() - if not whether_trigger: - return - print(f"Updating emotion for {incident}") description_prompt = """ You are a {gender}, aged {age}, belonging to the {race} race and identifying as {religion}. Your marital status is {marital_status}, and you currently reside in a {residence} area. @@ -228,7 +224,7 @@ async def emotion_update(self, incident): question_prompt = """ Please reconsider your emotion intensities: sadness, joy, fear, disgust, anger, surprise (0 meaning not at all, 10 meaning very much). - Return in JSON format, e.g. {{"sadness": 5, "joy": 5, "fear": 5, "disgust": 5, "anger": 5, "surprise": 5, "conclusion": "I feel ..."}}""" + Return in JSON format, e.g. {{"sadness": 5, "joy": 5, "fear": 5, "disgust": 5, "anger": 5, "surprise": 5, "conclusion": "I feel ...", "word": "Relief"}}""" question_prompt = description_prompt + incident_prompt + question_prompt question_prompt = FormatPrompt(question_prompt) emotion = await self.memory.status.get("emotion") diff --git a/pycityagent/cityagent/blocks/mobility_block.py b/pycityagent/cityagent/blocks/mobility_block.py index 09a6c36..852750f 100644 --- a/pycityagent/cityagent/blocks/mobility_block.py +++ b/pycityagent/cityagent/blocks/mobility_block.py @@ -33,6 +33,8 @@ RADIUS_PROMPT = """As an intelligent decision system, please determine the maximum travel radius (in meters) based on the current emotional state. +Current weather: {weather} +Current temperature: {temperature} Your current emotion: {emotion_types} Your current thought: {thought} @@ -82,7 +84,9 @@ async def forward(self, step, context): center = (center['xy_position']['x'], center['xy_position']['y']) self.radiusPrompt.format( emotion_types=await self.memory.status.get("emotion_types"), - thought=await self.memory.status.get("thought") + thought=await self.memory.status.get("thought"), + weather=self.simulator.sence("weather"), + temperature=self.simulator.sence("temperature") ) radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore try: @@ -158,7 +162,6 @@ async def forward(self, step, context): return { 'success': True, 'evaluation': f'Successfully returned home (already at home)', - 'from_place': home, 'to_place': home, 'consumed_time': 0, 'node_id': node_id @@ -170,7 +173,6 @@ async def forward(self, step, context): return { 'success': True, 'evaluation': f'Successfully returned home', - 'from_place': nowPlace['aoi_position']['aoi_id'], 'to_place': home, 'consumed_time': 45, 'node_id': node_id @@ -185,7 +187,6 @@ async def forward(self, step, context): return { 'success': True, 'evaluation': f'Successfully reached the workplace (already at the workplace)', - 'from_place': work, 'to_place': work, 'consumed_time': 0, 'node_id': node_id @@ -197,7 +198,6 @@ async def forward(self, step, context): return { 'success': True, 'evaluation': f'Successfully reached the workplace', - 'from_place': nowPlace['aoi_position']['aoi_id'], 'to_place': work, 'consumed_time': 45, 'node_id': node_id @@ -227,7 +227,6 @@ async def forward(self, step, context): return { 'success': True, 'evaluation': f'Successfully reached the destination: {next_place}', - 'from_place': nowPlace['aoi_position']['aoi_id'], 'to_place': next_place[1], 'consumed_time': 45, 'node_id': node_id diff --git a/pycityagent/cityagent/blocks/plan_block.py b/pycityagent/cityagent/blocks/plan_block.py index c860d21..19ba907 100644 --- a/pycityagent/cityagent/blocks/plan_block.py +++ b/pycityagent/cityagent/blocks/plan_block.py @@ -13,11 +13,13 @@ GUIDANCE_SELECTION_PROMPT = """As an intelligent agent's decision system, please select the most suitable option from the following choices to satisfy the current need. The Environment will influence the choice of steps. +Current weather: {weather} +Current temperature: {temperature} + Current need: Need to satisfy {current_need} Available options: {options} Current location: {current_location} Current time: {current_time} -Current Environment: {environment} Your emotion: {emotion_types} Your thought: {thought} @@ -40,10 +42,12 @@ DETAILED_PLAN_PROMPT = """Generate specific execution steps based on the selected guidance plan. The Environment will influence the choice of steps. +Current weather: {weather} +Current temperature: {temperature} + Selected plan: {selected_option} Current location: {current_location} Current time: {current_time} -Current Environment: {environment} Your emotion: {emotion_types} Your thought: {thought} @@ -191,6 +195,8 @@ async def select_guidance(self, current_need: str) -> Dict: environment = await self.memory.status.get("environment") options = self.guidance_options.get(current_need, []) self.guidance_prompt.format( + weather=self.simulator.sence("weather"), + temperature=self.simulator.sence("temperature"), current_need=current_need, options=options, current_location=current_location, @@ -224,6 +230,8 @@ async def generate_detailed_plan(self, current_need: str, selected_option: str) current_time = await self.simulator.get_time(format_time=True) environment = await self.memory.status.get("environment") self.detail_prompt.format( + weather=self.simulator.sence("weather"), + temperature=self.simulator.sence("temperature"), selected_option=selected_option, current_location=current_location, current_time=current_time, diff --git a/pycityagent/cityagent/initial.py b/pycityagent/cityagent/initial.py index ac5a7e0..2081a1f 100644 --- a/pycityagent/cityagent/initial.py +++ b/pycityagent/cityagent/initial.py @@ -73,16 +73,22 @@ async def bind_agent_info(simulation): infos = await simulation.gather("id") citizen_uuids = await simulation.filter(types=[SocietyAgent]) firm_uuids = await simulation.filter(types=[FirmAgent]) + locations = await simulation.gather("location", firm_uuids) + locations_plain = {} + for info in locations: + for k, v in info.items(): + locations_plain[k] = v government_uuids = await simulation.filter(types=[GovernmentAgent]) bank_uuids = await simulation.filter(types=[BankAgent]) nbs_uuids = await simulation.filter(types=[NBSAgent]) citizen_agent_ids = [] + firm_ids = [] for info in infos: for k, v in info.items(): if k in citizen_uuids: citizen_agent_ids.append(v) elif k in firm_uuids: - firm_id = v + firm_ids.append(v) elif k in government_uuids: government_id = v elif k in bank_uuids: @@ -90,7 +96,10 @@ async def bind_agent_info(simulation): elif k in nbs_uuids: nbs_id = v for citizen_uuid in citizen_uuids: - await simulation.update(citizen_uuid, "firm_id", firm_id) + random_firm_id = random.choice(firm_ids) + location = locations_plain[random_firm_id] + await simulation.update(citizen_uuid, "firm_id", random_firm_id) + await simulation.update(citizen_uuid, "work", location) await simulation.update(citizen_uuid, "government_id", government_id) await simulation.update(citizen_uuid, "bank_id", bank_id) await simulation.update(citizen_uuid, "nbs_id", nbs_id) @@ -104,5 +113,5 @@ async def bind_agent_info(simulation): await simulation.update(bank_uuid, "citizens", citizen_uuids) await simulation.update(bank_uuid, "citizens_agent_id", citizen_agent_ids) for nbs_uuid in nbs_uuids: - await simulation.update(nbs_uuid, "firm_id", firm_id) + await simulation.update(nbs_uuid, "firm_id", random.choice(firm_ids)) print("Agent info binding completed!") diff --git a/pycityagent/cityagent/memory_config.py b/pycityagent/cityagent/memory_config.py index 0c43504..e29fca2 100644 --- a/pycityagent/cityagent/memory_config.py +++ b/pycityagent/cityagent/memory_config.py @@ -196,6 +196,9 @@ def memory_config_societyagent(): def memory_config_firm(): EXTRA_ATTRIBUTES = { "type": (int, economyv2.ORG_TYPE_FIRM), + "location": { + "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1000, 10000)} + }, "price": (float, float(np.mean(agent_skills))), "inventory": (int, 0), "employees": (list, []), diff --git a/pycityagent/cityagent/societyagent.py b/pycityagent/cityagent/societyagent.py index c1b51bc..6a3c3a4 100644 --- a/pycityagent/cityagent/societyagent.py +++ b/pycityagent/cityagent/societyagent.py @@ -121,7 +121,6 @@ async def step_execution(self): elif step_type == "other": result = await self.otherBlock.forward(current_step, execution_context) if result != None: - logger.warning(f"Execution result: {result}") current_step["evaluation"] = result # Update current_step, plan, and execution_context information @@ -210,7 +209,7 @@ def __init__( self.enable_mobility = True self.enable_social = True self.enable_economy = True - + self.mindBlock = MindBlock( llm=self.llm, memory=self.memory, simulator=self.simulator ) @@ -231,7 +230,6 @@ def __init__( # Main workflow async def forward(self): self.step_count += 1 - logger.info(f"Agent {self._uuid} forward [step_count: {self.step_count}]") # sync agent status with simulator await self.update_with_sim() diff --git a/pycityagent/environment/simulator.py b/pycityagent/environment/simulator.py index 0e1c244..47ed024 100644 --- a/pycityagent/environment/simulator.py +++ b/pycityagent/environment/simulator.py @@ -105,6 +105,20 @@ def __init__(self, config: dict, secure: bool = False) -> None: self.poi_id_2_aoi_id: dict[int, int] = { poi["id"]: poi["aoi_id"] for _, poi in self.map.pois.items() } + self._environment_prompt:dict[str, str] = {} + + @property + def environment(self): + return self._environment_prompt + + def set_environment(self, environment: dict[str, str]): + self._environment_prompt = environment + + def sence(self, key: str): + return self._environment_prompt.get(key, "") + + def update_environment(self, key: str, value: str): + self._environment_prompt[key] = value # * Agent相关 def find_agents_by_area(self, req: dict, status=None): diff --git a/pycityagent/simulation/agentgroup.py b/pycityagent/simulation/agentgroup.py index 793a9d8..f486003 100644 --- a/pycityagent/simulation/agentgroup.py +++ b/pycityagent/simulation/agentgroup.py @@ -47,6 +47,7 @@ def __init__( embedding_model: Embeddings, logging_level: int, agent_config_file: Optional[dict[type[Agent], str]] = None, + environment: Optional[dict[str, str]] = None, ): logger.setLevel(logging_level) self._uuid = str(uuid.uuid4()) @@ -116,7 +117,7 @@ def __init__( logger.info(f"-----Creating Simulator in AgentGroup {self._uuid} ...") self.simulator = Simulator(config["simulator_request"]) self.projector = pyproj.Proj(self.simulator.map.header["projection"]) - + self.simulator.set_environment(environment) # prepare Economy client logger.info(f"-----Creating Economy client in AgentGroup {self._uuid} ...") self.economy_client = EconomyClient( @@ -355,6 +356,9 @@ async def update(self, target_agent_uuid: str, target_key: str, content: Any): agent = self.id2agent[target_agent_uuid] await agent.status.update(target_key, content) + async def update_environment(self, key: str, value: str): + self.simulator.update_environment(key, value) + async def message_dispatch(self): logger.debug(f"-----Starting message dispatch for group {self._uuid}") while True: diff --git a/pycityagent/simulation/simulation.py b/pycityagent/simulation/simulation.py index aa747b3..f06705b 100644 --- a/pycityagent/simulation/simulation.py +++ b/pycityagent/simulation/simulation.py @@ -226,6 +226,8 @@ async def run_from_config(cls, config: dict): - number_of_government: required, int - number_of_bank: required, int - number_of_nbs: required, int + - environment: Optional[dict[str, str]] + - default: {'weather': 'The weather is normal', 'crime': 'The crime rate is low', 'pollution': 'The pollution level is low', 'temperature': 'The temperature is normal'} - workflow: - list[Step] - Step: @@ -261,6 +263,16 @@ async def run_from_config(cls, config: dict): exp_name=config.get("exp_name", "default_experiment"), logging_level=config.get("logging_level", logging.WARNING), ) + environment = config.get( + "environment", + { + "weather": "The weather is normal", + "crime": "The crime rate is low", + "pollution": "The pollution level is low", + "temperature": "The temperature is normal" + } + ) + simulation._simulator.set_environment(environment) logger.info("Initializing Agents...") agent_count = [] agent_count.append(config["agent_config"]["number_of_citizen"]) @@ -308,6 +320,7 @@ async def run_from_config(cls, config: dict): ), memory_config_func=config["agent_config"].get("memory_config_func", None), **_message_intercept_kwargs, + environment=environment, ) logger.info("Running Init Functions...") for init_func in config["agent_config"].get( @@ -461,6 +474,7 @@ async def init_agents( message_listener: Optional[MessageBlockListenerBase] = None, embedding_model: Embeddings = SimpleEmbedding(), memory_config_func: Optional[dict[type[Agent], Callable]] = None, + environment: Optional[dict[str, str]] = None, ) -> None: """初始化智能体 @@ -470,6 +484,7 @@ async def init_agents( pg_sql_writers: 独立的PgSQL writer数量 message_interceptors: message拦截器数量 memory_config_func: 返回Memory配置的函数,需要返回(EXTRA_ATTRIBUTES, PROFILE, BASE)元组, 每个元素表示一个智能体类创建的Memory配置函数 + environment: 环境变量,用于更新模拟器的环境变量 """ if not isinstance(agent_count, list): agent_count = [agent_count] @@ -657,6 +672,7 @@ async def init_agents( embedding_model, self.logging_level, config_file, + environment, ) creation_tasks.append((group_name, group)) @@ -727,6 +743,11 @@ async def filter( filtered_uuids.extend(await group.filter.remote(types)) return filtered_uuids + async def update_environment(self, key: str, value: str): + self._simulator.update_environment(key, value) + for group in self._groups.values(): + await group.update_environment.remote(key, value) + async def update(self, target_agent_uuid: str, target_key: str, content: Any): """更新指定智能体的记忆""" group = self._agent_uuid2group[target_agent_uuid] @@ -802,9 +823,6 @@ async def step(self): try: # check whether insert agents simulator_day = await self._simulator.get_simulator_day() - print( - f"simulator_day: {simulator_day}, self._simulator_day: {self._simulator_day}" - ) need_insert_agents = False if simulator_day > self._simulator_day: need_insert_agents = True @@ -817,6 +835,9 @@ async def step(self): await asyncio.gather(*insert_tasks) # step + simulator_day = await self._simulator.get_simulator_day() + simulator_time = int(await self._simulator.get_time()) + logger.info(f"Start simulation day {simulator_day} at {simulator_time}, step {self._total_steps}") tasks = [] for group in self._groups.values(): tasks.append(group.step.remote()) diff --git a/pyproject.toml b/pyproject.toml index fc71867..a0cd0fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pycityagent" -version = "2.0.0a64" # change it for each release +version = "2.0.0a65" # change it for each release description = "LLM-based city environment agent building library" authors = [ { name = "Yuwei Yan", email = "pinkgranite86@gmail.com" },