代理策略说明 - tedrepo/SimDial GitHub Wiki


###### ============================== user agent 部分逻辑===============================================


1. step 函数

   外界传入数据 inputs
   step函数首先根据 inputs更新 user state中的当前的 1.对话者  2. 对话历史 3.将系统动作存入buffer中
   然后在while循环中中调用 policy 函数


2. policy 函数
   每次从 buffer 中 取出一个 system action ,然后生成对应 user action
   1.  sys action 是 非确定性澄清 IMPLICIT_COFIRM
        从 action 参数中取出 需要澄清的 slot name 和slot value
        判断 该slot 是否是用户slot 如果不是跑出异常
        如果是 当前曹值同用户约束的值一样 或者用户约束的值是none(ignore)
               不做任何处理

        如果不满足约束,生成否认曹值的动作(reject) 或 否认+inform (reject + inform) 并返回
          同时更新 user state  如果是 reject 将当前槽位置为 未输出  如果是 reject + inform 将当前槽位值置为已输出
   2. sys action 是确定性澄清 EXPLICIT confirm
          取出当前的slot name 和slot 值
          判断是否是用户约束槽位
          如果满足约束 返回 Action(UserAct.CONFIRM, (slot_type, slot_val)) 将user state 的slot 置为True
          如果不满足约束 返回 Action(UserAct.DISCONFIRM, (slot_type, slot_val)) 将 user state 的 slot 置为 false
   3. 系统的动作是INFORM
      这时系统会将所有已经收集到的槽位返回, 需要执行 全部约束 是否满足的判断,返回不满足约束的槽位
      如果满足约束
           更新goal的状态 将当前 goal置为True: update_goals_met()  , 随机的选择下一个goal state.unmet_goal()
           如果新采样到的goal 为 none
              随机改变约束槽位  _increment_goal()
              如果随机采样的约束槽位不为None
                    更新 user state 中的约束改变, 并更新user state 中对应的约束曹值, 返回的动作是:
                       [Action(UserAct.NEW_SEARCH, (BaseSysSlot.DEFAULT, None)),
                                Action(UserAct.INFORM, (slot_key, self.usr_constrains[slot_key]))]
              如果随机采样的约束槽位值为None:
                    告诉系统同对当前的搜索结果满意的动作, 并返回结束动作
                         [Action(UserAct.SATISFY, [(g, None) for g in complete_goals]),
                                Action(UserAct.GOODBYE)]
          如果采样到的goal不为 None
               将 user state 中的当前goal val 置为 新的goal val
               user state 中的goal change 标记置为 True
               将Action(UserAct.MORE_REQUEST, [(g, None) for g in complete_goals]) 作为待返回的 act前缀
                  随机采样当前是否 为yn 问题
                      如果是yn问题,name 找出goal对应的slot, 并采样一个slot值,将yn更新到 user state 中
                       追加 动作: [ack_act, Action(UserAct.YN_QUESTION, (slot.name, expected_val))]
                      如果不是yn问题, 返回正常问: [ack_act, Action(UserAct.REQUEST, (next_goal, None))]
         如果不满足约束条件:
                返回inform动作告诉系统不满足约束的槽位的值 Action(UserAct.INFORM, (wrong_slot, self.usr_constrains[wrong_slot]))
  4. 如果系统的动作是request
        取出当前request 对应的槽位 以及对应的值
        如果 slot type 为 BaseSlot Need
            返回一个没有满足的 goal Action(UserAct.REQUEST, (next_goal, None)) 并更新 user state 为当前的goal
        如果 系统返回的槽位为 BaseSlot.HAPPY
            就什么也不做返回 None
        如果是用户约束槽位
             # 采样出随机个数的多余槽位, 返回inform slot slotval 列表 ,更新 user state的状态
        如果不满足上面所有的条件
             抛出异常

 5. 如果系统的动作是 澄清 SystemAct.CLARIFY
       抛出异常,没有对应的处理方法
 6. 再说一遍 : SystemAct.ASK_REPEAET:
      从 userstate中取出上一次说的内容, 返回
 7. 换一种方式说: SystemAct.ASK_REPHRASE:
     # 取出最近一轮 , 向 action列表中追加again 标志
           for a in last_usr_actions:
                a.add_parameter(BaseUsrSlot.AGAIN, True)

 8 如果系统动作是 QUERY: 这时需要查询数据库
        从action 的参数中取出 参数列表 含有query 和goals
        调用api 从数据库中查询出想要的数据
            随机的选择一条返回的记录 为goals赋值
       返回用户动作: Action(UserAct.KB_RETURN, [query, results])

以上就是policy 的处理逻辑




user state 中含有的记录内容
    1. hisotry [] 历史对话 内容 speaker + action_list
    2. spk_state : 当前的会话状态 listen or exit
    3. input_buffer: 当前的系统动作列表
    4. goal_met 有序字典:  当前goals是否满足
    6. constrain_slot_state 有序字典 当前用户约束slot 是否满足
    7. change_dic  goal 约束 yn 的字典
    8. change_slot_dic  改变的slot 以及对应的值
    9. cur_goal:  当前的goal

 含有的函数:
     update history( speaker , actions)
     is_terminal()返回当前的对话状态是否为 exit
     yield_floor() 返回当前的对话状态是否为listen
     unmet goal() 找到一个没有满足的goal
     update_goals_met(top_action) 更新当前goal的met状态
     update_usr_constrains(usr_slots, is_fill)  更新当前slot 填充状态
     reset_goal() : 重置当前的goals 为未满足状态 当前的用户约束槽位为未输出状态
     reset_change_dict: 置空change dict


###### ============================== system agent 部分的逻辑 ===============================================

 system 部分使用的一个概率模型的 状态追踪类实现的, action的处理完全是基于概率的


1. belief slot 的逻辑

  计算 一个槽位上各个曹值概率分布
  首先设置 阈值
     EXPLICIT THRESHOLD = 0.2      显式确认的阈值
     IMPLECT THRESHOLD = 0.6       隐式确认的阈值
     GROUND_THRESHOLD = 0.95       基础阈值


  a 初始化方法:
         uid slotname
         value_map  map key: slot  value: prob
         last_update_turn : 上一次更新的轮次
  b 添加一个新的观测 参数为 : value值, conf 置信概率, turn_id 轮次
        将最近一次更新改为当前的turn_id

        # 更新曹值
        如果 这个值在value map中,更新置信概率:
            prev_conf = self.value_map[value]
            self.value_map[value] = max([prev_conf, conf]) + 0.2
        如果之前没有出现过, 将其他出现的曹值的置信都减少一半
        记录当前曹值的置信为conf

  c 根据 confirm_conf 和 disconfirm conf 更新槽位的置信值 参数 : confirm_conf, disconfirm_conf, turn_id, target_value=None)
      更新 最后一次更新的轮次
      如参数中没有目标曹值,选取置信最大的曹值为目标曹值

      使用如下公式更新:
            up_conf = confirm_conf * (1.0 - self.EXPLICIT_THRESHOLD)        # 对曹值的确认概率增益
            down_conf = disconfirm_conf * (1.0 - self.EXPLICIT_THRESHOLD)   # 对曹值的不确定概率增益
            old_conf = self.value_map[grounded_value]
            new_conf = max(0.0, min((old_conf + up_conf - down_conf), 1.5))


  d 获取最大置信的曹值
       从 value map 中找到置信最大的曹值

  e max_conf
     从 value map 中找到做大的置信值
 f clear:
    将各个曹值的置信初始化为 self.IMPLICIT_THRESHOLD+self.EXPLICIT_THRESHOLD)/2.


BeliefGoal 类
    goal slot 的状态追踪,
    设置 阈值为 0.7

    初始化:
        uid:  用户id
        conf : 置信度
        ddelivered: 是否发出了
        value : 当前的 value
        expected_value : 期望的值

     添加观测  参数 : 置信 conf , 期望值 exptected_value
         首先根据观测置信,更新 值的置信:  max(conf, self.conf) + 0.2
         更新 期望goal值 为 expected_value

     获取当前的置信

     发出:
       设置 delivered 为 true
     clear
       清空 置信 发出标志 期望goal值



DialogState(state):
    dm 的状态跟踪类
    设置inform 阈值

    初始化函数:
          持有的数据结构:
           history list 历史对话信息
           spk_state : 当前的对话状态
           user_beliefs : 有序字典,当前曹值的置信度列表
           sys_goals: 有序字典 当前 goal的置信列表
                初始化 默认goal 的置信为1.0
           valid_entries : 合法sample 从数据库中查询得到
           pending return :  发出 当前追踪到的所有约束曹值
           domian : 当前的domain

    turn_id() : 返回当前的轮次

     gen_query() :
       获取用户slot 最大置信的value 组成数据库查询语句

    has_pending_reture():
       判断当前约束是否追加到return上

   ready_to_inform()
      判断当前是否可以输出信息了
      遍历用户置信字典,判断每个槽位的最大置信是否大于基础阈值
      遍历 goal slot 置信列表判断是否大于基础阈值

   yield_floor:
      判断系统动作是否在一下动作中 : SystemAct.REQUEST, SystemAct.EXPLICIT_CONFIRM, SystemAct.QUERY]

  is_terminal(self):
    self.spk_state == State.exit

  reset_sys_goals():
     重置 系统的goal置信列表, 将default goal 的置信设置为1.0


 state_summary():
    当前状态的总结
    1.  当前各个用户约束的置信最大的曹值和置信度
    2. goal 的置信 发出状态 当前值 期望值
    3. 当前的return 是否有追加的数据查询语句


system 的 逻辑
  step
  用当前的inputs 更新 state tracker 历史状态
  函数循环遍历 处理用户actionlist
       调用policy处理各个action
  判断是否结束
     返回训练信息


 policy 中的具体内容:
       1. 首轮判断
           返回问候动作 和对 NEED 槽位的request
       2. 终止判断 获取最近一次的用户动作列表 查看是否有GOODBYE 动作

       3. 判断是否有 pending 的查询结果
           如果有就返回
            actions.append(Action(SystemAct.INFORM, [dict(query), goals]))
            actions.append(Action(SystemAct.REQUEST, (BaseUsrSlot.HAPPY, None)))
       4. 判断当前是否准备好输出了
             生成查询语句  (usr slot 约束 和goal 列表)
             返回动作 actions.append(Action(SystemAct.QUERY, [query, goals]))
       5. 其他状况
            填充3个list
                 隐式确认列表  implicit_confirms = []
                 显式确认列表   exp_confirm = []
                 请求列表     requests = []

            遍历各个 约束槽位的置信字典:
                如果 当前槽位 的最大置信 小于 显式确认
                    生成 一个Action(SystemAct.REQUEST, (slot.uid, None))) 添加到 显式确认列表中
                否则 小于 隐式确认
                    生成一个  Action(SystemAct.EXPLICIT_CONFIRM, (slot.uid, slot.get_maxconf_value()))) 添加到request列表中
                否则 小于 基础阈值:
                    生出一个 ction(SystemAct.IMPLICIT_CONFIRM, (slot.uid, slot.get_maxconf_value()))) 添加到  隐式确认列表中
           遍历各个 goal 的置信字典:
                如果 当前goal的置信 大于0 小于基础阈值
                     那么将 Action(SystemAct.REQUEST, (BaseUsrSlot.NEED, None)) 添加到request列表中
           总结上面三个列表:
               如果 exp_confirm 列表不为空
                      将 expconfirm的第一个 加上 implicit_confirm 列表输出
               否则如果request 列表不为空
                      将 request 的第一个 加上 implicit_confirm 列表输出
               否则
                     只输出 implict_confirm


装填更新逻辑  state_update()  参数 usraction 置信conf
     如果usraction 内容为空 直接返回

     更新 state 的 历史信息
     更新 state 的 对话状态

     遍历 用户动作:
         1. act 为 confirm:
             取出 对应的slot 更新其状态追踪
                   self.state.usr_beliefs[slot].add_grounding(conf, 1.0 - conf, self.state.turn_id())
         2. act 为 disconfirm
             取出对应的 slot 更新其状态追踪
                   self.state.usr_beliefs[slot].add_grounding(1.0 - conf, conf, self.state.turn_id())
         3. act 为 INFORM:
             取出 参数中的slot 和slotvalue
                 为这个值初始化置信
                  self.state.usr_beliefs[slot].add_new_observation(value, conf, self.state.turn_id())
         4. act 为 REQUEST:
              取出参数中的slot
              更新goal的观测置信
              self.state.sys_goals[slot].add_observation(conf, None)
         5. act 为 new search:
              重置状态追踪中的 slot 置信列表
              重置状态追踪中的goal置信列表

         6. 当前用户的动作是yes or no 问题
             取出当前参数中的slot 和 对应的 slot value
             更新goal 的置信
             self.state.sys_goals[slot].add_observation(conf, value)
         7. 如果用户满意 sys goal的值,或者要求更多的goal时, 标注 该goal值是被用户接受的, 已经发出
               for para, _ in action.parameters:
                    self.state.sys_goals[para].deliver()
         8.  如果 用户的动作是 kb——return , 那么根据 query 携带的goal值更新 goal的置信
                query = action.parameters[0]
                results = action.parameters[1]
                self.state.pending_return = query
                for slot_name, goal in self.state.sys_goals.items():
                    if slot_name in results.keys():
                        goal.value = results[slot_name]


update_grounding(sys_actions):
       根据已经选择的 sys action 更新基础置信
              遍历 每一个选中的系统动作
                  如果当前系统动作是 IMPLICIT CONFORM
                       取出 slot 和其值
                       更新状态跟踪中的置信
                          self.state.usr_beliefs[slot].add_grounding(1.0, 0.0, self.state.turn_id())