code: def add_or_replace_annotation(self,  # pylint: disable=R0201
                                    obj,
                                    annotation,
                                    agent,
                                    add_agent_only=False):
          
          nex = get_nexml_el(obj)
          nvers = detect_nexson_version(obj)
          _LOG.debug('detected version as ' + nvers)
          agents_obj = find_val_literal_meta_first(nex, 'ot:agents', nvers)
          if not agents_obj:
              agents_obj = add_literal_meta(nex, 'ot:agents', {'agent': []}, nvers)
          agents_list = agents_obj.setdefault('agent', [])
          found_agent = False
          aid = agent['@id']
          for a in agents_list:
              if a.get('@id') == aid:
                  found_agent = True
                  break
          if not found_agent:
              agents_list.append(agent)
          if add_agent_only:
              delete_same_agent_annotation(obj, annotation)
          else:
              replace_same_agent_annotation(obj, annotation)
docs: Takes an `annotation` dictionary which is
          expected to have a string as the value of annotation['author']['name']
          This function will remove all annotations from obj that:
              1. have the same author/name, and
              2. have no messages that are flagged as messages to be preserved (values for 'preserve'
                  that evaluate to true)