Спасибо всем за ваши попытки и комментарии. Я наконец нашел решение:
s = 'I hate *some* kinds of duplicate. This string has a duplicate phrase, duplicate phrase.'
re.sub(r'((\b\w+\b.{1,2}\w+\b)+).+\1', r'\1', s, flags = re.I)
# 'I hate *some* kinds of duplicate. This string has a duplicate phrase.'
Объяснение
Регулярное выражение
r'((\b\w+\b.{1,2}\w+\b)+).+\1'
находит каждое вхождение нескольких серий буквенно-цифровых символов, разделенных одним или двумя [любым символом] (чтобы охватить случай, когда слова разделяются не просто пробелом, но, возможно, точкой или запятой и пробелом), а затем повторяются после некоторого прогона [любого символа] неопределенной длины. Тогда
re.sub(r'((\b\w+\b.{1,2}\w+\b)+).+\1', r'\1', s, flags = re.I)
заменяет такие вхождения первым многократным набором буквенно-цифровых символов, разделенных одним или двумя [любым символом], при этом обязательно игнорируйте регистр (поскольку дублирующая фраза может иногда встречаться в начале предложения).