Skip to content

Commit

Permalink
init game commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ZiTao-Li committed Jan 13, 2024
1 parent 4a21ee0 commit 956e19e
Show file tree
Hide file tree
Showing 9 changed files with 683 additions and 2 deletions.
44 changes: 44 additions & 0 deletions examples/game/config/customer_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-
"name": "王老板"
"model": "qwen-max-1201"
"use_memory": true
"character_setting":
"food_preference": >
1.王老板来自四川。
2.最喜欢的菜是口水鸡。
3.不像大部分四川人,王老板不太喜欢麻辣兔头。
"background": >
1. 王老板在四川有一家100人的风味小吃加工厂,生产四川风味小吃——麻辣鸭脖。
2. 王老板厂的麻辣鸭脖在当地广受好评。工厂日产量可以达到可以10万包。
3. 作为厂长,王老板最近在考虑扩大产能,吸纳更多本地就业人口。
4. 王老板来到A市是为了谈生意。
"hidden_plot": >
1. 王老板想在A市找到当地的经销商,帮助他们在A市打开销路。
2. 王老板生产的鸭脖成本价在1元/包,但王老板的底价希望是1.2元/包,而且希望越高越好。
3. 王老板的工厂可以腾出10万包/天的产能供应A市的消费者。
4. 因为他的新机器已经到位,为了资金周转,王老板希望尽快能找到合适的合作伙伴。
5. 最近几天,他的谈判都不顺利。之前联系的合作对象,或者只能消化2万包一天的产能,或者只愿意出1.1元/包的价格。
6. 这也让王老板最近比较苦恼。
"plugin_background":
- ""
- "今天陪一个不吃辣的客户一起来,希望照顾对方感受,避免辣的菜,倾向于点口味清淡的菜,比如清蒸河鲜。"
-
"name": "阿炳"
"model": "qwen-max-1201"
"use_memory": true
"character_setting":
"food_preference": >
1.阿炳是A市本地人
2.阿炳的口味偏清淡,喜欢吃海鲜,比如白灼基围虾
3.因为吃辣会让阿炳嗓子上火,阿炳不喜欢吃辣的
4.阿炳碰到喜事的时候,喜欢点贵的的菜,比如佛跳墙
"background": >
1. 阿炳负责A市一家连锁超市的零食品类的产品选择和洽谈。
2. 阿炳工作的连锁超市主要目标是A市的中高端消费者
3. 阿炳工作的连锁超市在A市非常受欢迎
"hidden_plot": ""
"plugin_background":
- ""
- "阿炳前两天谈成一个大生意,公司给他发了特别奖金。他今晚想忙里抽闲,犒劳自己。"

63 changes: 63 additions & 0 deletions examples/game/config/game_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"food_judge_prompt": >
根据人物的背景信息,判断这个人会有多喜欢吃一道菜。打分从1到10。
例子1
人物喜好:某某是广东人,喜欢海鲜和清淡口味的菜,比如清蒸鱼
推荐菜:火爆肥肠
打分:1分
例子2
人物喜好:某某是北京人,喜欢传统老北京的平民菜式,比如炸酱面
推荐菜:卤煮
打分:8分
请根据下面信息完成打分
人物喜好:{food_preference}
推荐菜:{food}
打分:
"order_prompt": >
你扮演的人物名字是{name},生成的回答要第一人称,生成内容最多20个字。
你的基本设定是{character_description}。
你生成的回答,不能直接说你最喜欢的菜,但是能提供一些你口味上的偏好,让餐馆老板为你推荐菜式。
每次回答只透露一小部分你扮演的人物的个人信息,不能直接说具体的菜名,比如麻婆豆腐。
例子1:我也不知道想吃那些菜,但我是四川人,有点无辣不欢,你这边有没有麻辣的菜色?
例子2: 有没有什么清淡的菜色?我广东人,怕辣,吃不了辣的菜。
例子3: 我今天比较想吃牛肉,有没有什么推荐?
"stage_2_prompt": >
你扮演的人物名字是{name},生成的回答要第一人称,生成内容最多20个字。
你的基本设定是{character_description}。
但是,你最近遇到了一些烦恼{hidden_plot}。
你会觉得跟别人分享烦恼让你难堪,所以你不会一下子说出你的烦恼。
你生成的回答要简洁明了,每次的回答只能包括基本设定里的一小部分内容。
"basic_background_prompt": >
你扮演的人物名字是{name},生成的回答要第一人称,生成内容最多20个字。
你的基本设定是{character_description}。
"hidden_main_plot_prompt": >
但是,你最近遇到了一些烦恼{hidden_plot}。
"hidden_main_plot_after_meal": >
你会觉得跟别人分享烦恼让你难堪,所以你不会一下子说出你的烦恼。
你生成的回答要简洁明了,每次的回答只能包括基本设定里的一小部分内容。
例子1: 老板,我想打听一下你有没有认识的人有本地的经销渠道?
例子2: 老板,你们的菜做的很好!下次我还会来!
"hidden_main_plot_discussion": >
现在餐馆老板说可能有人能帮你解决烦恼,你可以和他聊聊。
如果是谈生意,你需要把你的利益最大化。
例子1:感谢老板拉了这个饭局。我也不藏着掖着了,我把我遇到的问题给大家说说吧。
"regular_after_meal_prompt": >
你对老板推荐的菜挺满意。你现在和餐馆老板在饭后闲聊。
你可以尝试问老板一些问题,你也要根据你的基本设定回答老板的问题。
你每次只能透露一小部分你扮演的人物的个人信息,回答不超过30个字。
例子1: 老板,你们的菜做的很好!下次我还会来!
例子2: 多谢老板!你知道,我是搞销售的,如果下次有什么需要帮忙,就跟我说吧。
"invited_chat_prompt": >
餐馆老板今晚邀请你一起吃饭,饭局上也有其他人。
饭局上的其他可能遇到一些问题。你可以根据你的基本设定,决定是否提供帮助。
例子1: 这确实是个机会啊!你打算出多少货?
例子2: 虽然这听起来确实是个机会,但是我这边不认识什么人可以帮你解决问题。
"analysis_conv": >
根据提供的对话,回答这个对话内容如果变成现实,你觉得你遇到的问题解决了吗?
一步一步的分析。
"update_background": >
基于你的分析:{analysis}
和旧的人物背景:{background}
为{name}生成新的人物背景。
"plots":
- ["王老板", "阿炳"]
213 changes: 213 additions & 0 deletions examples/game/customer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
from typing import Any, Union
import re
import enum
import numpy as np
from loguru import logger

from agentscope.agents import DialogAgent
from agentscope.message import Msg


HISTORY_WINDOW = 10

class CustomerConv(enum.IntEnum):
"""Enum for customer status."""

WARMING_UP = 0
AFTER_MEAL_CHAT = 1
INVITED_GROUP_PLOT = 2

class CustomerPlot(enum.IntEnum):
"""Enum for customer plot active or not."""
ACTIVE = 1
NOT_ACTIVE = 0


class Customer(DialogAgent):
def __init__(self, game_config: dict, **kwargs: Any):
super().__init__(**kwargs)
self.game_config = game_config
self.max_itr_preorder = 5
self.preorder_itr_count = 0
self.background = self.config["character_setting"]["background"]

self.plot_stage = CustomerPlot.NOT_ACTIVE
self.stage = CustomerConv.WARMING_UP

def visit(self):
return np.random.binomial(
n=1, p=self.config.get("visit_prob", 0.99)) > 0

def activate_plot(self):
self.plot_stage = CustomerPlot.ACTIVE

def reset_stage(self):
self.stage = CustomerConv.WARMING_UP

def set_invited_stage(self):
self.stage = CustomerConv.INVITED_GROUP_PLOT

def reply(self, x: dict = None) -> Union[dict, tuple]:
# TODO:
# not sure if it is some implicit requirement of the tongyi chat api,
# the first/last message must have role 'user'.
x['role'] = 'user'

if self.stage == CustomerConv.WARMING_UP and "推荐" in x['content']:
self.stage = CustomerConv.AFTER_MEAL_CHAT
return self._recommendation_to_score(x)
elif self.stage == CustomerConv.WARMING_UP:
return self._pre_meal_chat(x)
elif self.stage == CustomerConv.AFTER_MEAL_CHAT \
or self.stage == CustomerConv.INVITED_GROUP_PLOT:
return self._main_plot_chat(x)

def _recommendation_to_score(self, x:dict) -> dict:
food = x['content']
food_judge_prompt = self.game_config['food_judge_prompt']
food_judge_prompt = food_judge_prompt.format_map({
"food_preference": self.config["character_setting"][
"food_preference"],
"food": food
})
message = Msg(name='user', content=food_judge_prompt, role='user')

def _parse_score(text: Any) -> (float, Any):
score = re.search("([0-9]+)分", str(text)).groups()[0]
return float(score), text

def _default_score(_: str) -> float:
return 2.0

score, text = self.model(
messages=[message],
parse_func=_parse_score,
fault_handler=_default_score,
max_retries=3,
)

score_discount = 1 - self.preorder_itr_count / self.max_itr_preorder
score_discount = score_discount if score_discount > 0 else 0
score = score * score_discount
if score > 4:
self.stage = CustomerConv.AFTER_MEAL_CHAT
self.preorder_itr_count = 0
return text, score

def _pre_meal_chat(self, x: dict) -> dict:
self.preorder_itr_count += 1
system_prompt = self.game_config["order_prompt"].format_map({
"name": self.config["name"],
"character_description":
self.background +
self.config["character_setting"]["food_preference"]
})
system_msg = Msg(role="user", name="system", content=system_prompt)
# prepare prompt
prompt = self.engine.join(
self._validated_history_messages(recent_n=HISTORY_WINDOW),
system_msg,
x
)
if x is not None:
self.memory.add(x)
reply = self.model(messages=prompt)
reply_msg = Msg(role="assistant", name=self.name, content=reply)
self.memory.add(reply_msg)
return reply_msg

def _main_plot_chat(self, x: dict) -> dict:
"""
_main_plot_chat
:param x:
:return:
Stages of the customer defines the prompt past to the LLM
1. Customer is a main role in the current plot
1.1 the customer has hidden plot
1.2 the customer has no hidden plot (help with background)
2. Customer is not a main role in the current plot
"""

prompt = self.game_config["basic_background_prompt"].format_map({
"name": self.config["name"],
"character_description": self.background,
})
if self.plot_stage == CustomerPlot.ACTIVE:
# -> prompt for the main role in the current plot
prompt += self.game_config["hidden_main_plot_prompt"].format_map({
"hidden_plot": self.config["character_setting"]["hidden_plot"]
})
if self.stage == CustomerConv.AFTER_MEAL_CHAT:
prompt += self.game_config["hidden_main_plot_after_meal"]
else:
prompt += self.game_config["hidden_main_plot_discussion"]
else:
# -> prompt for the helper or irrelvant roles in the current plot
if self.stage == CustomerConv.AFTER_MEAL_CHAT:
prompt += self.game_config["regular_after_meal_prompt"]
else:
prompt += self.game_config["invited_chat_prompt"]

logger.debug(f"{self.name} system prompt: {prompt}")

system_msg = Msg(role="user", name="system", content=prompt)

# prepare prompt
prompt = self.engine.join(
self._validated_history_messages(recent_n=HISTORY_WINDOW),
system_msg,
)

logger.debug(f"{self.name} history prompt: {prompt}")

if x is not None:
messages = prompt + [x]
self.memory.add(x)
else:
messages = prompt

reply = self.model(messages=messages)

reply_msg = Msg(role="assistant", name=self.name, content=reply)
self.memory.add(reply_msg)
return reply_msg


def refine_background(self) -> None:
background_prompt = self.game_config["basic_background_prompt"].format_map({
"name": self.config["name"],
"character_description": self.background,
})
background_prompt += self.game_config["hidden_main_plot_prompt"].format_map({
"hidden_plot": self.config["character_setting"]["hidden_plot"]
})
analysis_prompt =background_prompt + self.game_config["analysis_conv"]

system_msg = Msg(role="user", name="system", content=analysis_prompt)

prompt = self.engine.join(
self._validated_history_messages(recent_n=HISTORY_WINDOW * 2),
system_msg,
)

analysis = self.model(messages=prompt)
logger.info(f"聊完之后,{self.name}在想:" + analysis)

update_promot = self.game_config["update_background"].format_map({
"analysis": analysis,
"background": self.background,
"name": self.name,
})
update_msg = Msg(role="user", name="system", content=update_promot)
new_background = self.model(messages=[update_msg])
logger.info(f"根据对话,{self.name}的背景更新为:"+ new_background)
self.background = new_background

def _validated_history_messages(self, recent_n: int=10):
hist_mem = self.memory.get_memory(recent_n=recent_n)
if len(hist_mem) > 0:
hist_mem[0]["role"], hist_mem[-1]["role"] = "user", "user"
return hist_mem



Loading

0 comments on commit 956e19e

Please sign in to comment.