code: def poor_knowledge_selection(
      messages, persona, stemmer, knwldg_ovlp_thrshld
  ):
      """
      Check for poor search and knowledge selection by wizard.
      """
      # Collecting search and knowledge selections
      search_terms = []
      selected_knowledge = []
      message_history_tokens = tokenize_text(persona, stemmer)
  
      n_search_query_not_in_history = 0
      for msg in messages:
          if msg.get("text", None):
              message_history_tokens = message_history_tokens.union(
                  tokenize_text(msg["text"], stemmer)
              )
  
          if msg["id"] != "Wizard":
              continue
  
          selections = msg.get("task_data", {}).get(
              "selected_text_candidates"
          )
          if not selections or selections[0][0]:
              continue
  
          search_query = msg["task_data"]["search_query"]
          search_terms.append(search_query)
          if message_history_tokens.isdisjoint(
              tokenize_text(search_query, stemmer)
          ):
              n_search_query_not_in_history += 1
  
          selected_parts = []
          for doc_id in range(1, len(selections)):
              doc_selections = selections[doc_id]
              for sentence_id in range(len(doc_selections)):
                  if doc_selections[sentence_id]:
                      selected_parts.append(
                          msg["task_data"]["text_candidates"][
                              doc_id - 1
                          ]["content"][sentence_id]
                      )
  
          selected_knowledge.append(
              {
                  "text": msg["text"],
                  "knowledge": " ".join(selected_parts),
              }
          )
  
      knowledge_length = []
      knowledge_overlaps = []
      for knwldg in selected_knowledge:
          knowledge_tokens = tokenize_text(knwldg["knowledge"], stemmer)
          knowledge_length.append(len(knowledge_tokens))
  
          response_tokens = tokenize_text(knwldg["text"], stemmer)
          knowledge_overlaps.append(
              overlap_ratios(knowledge_tokens, response_tokens)
          )
  
      violations = []
  
      # Repeated the same search queries
      if len(search_terms) - len(set(search_terms)) > 3:
          violations.append(POOR_SEARCH_QUERIES)
  
      # Search doesn't have overlap with message history
      if n_search_query_not_in_history > 2:
          violations.append(IRRELEVANT_SEARCH__QUERIES)
  
      # No selection
      if not knowledge_length:
          violations.append(NOT_ENOUGH_SEARCH)
  
      # Only selecting short sentences
      if np.average(knowledge_length) < 5:
          violations.append(SELECTED_SHORT_PIECES)
  
      # Small overlap between response and the selected knowledge parts
      knowledge_overlap_avg = np.average(knowledge_overlaps)
      if knowledge_overlap_avg < knwldg_ovlp_thrshld:
          violations.append(
              f"{LOW_KNOWLEDGE_OVERLAP} ({knowledge_overlap_avg})"
          )
  
      return violations
  
file_path: ParlAI/parlai/crowdsourcing/projects/wizard_of_internet/acceptability.py
imports: import numpy
  from typing import Iterable, List
  from nltk.stem import PorterStemmer
  from parlai.crowdsourcing.utils.acceptability import AcceptabilityChecker, normalize_answer
  import parlai.utils.logging
label_desc: Use image node_id 5 for calling a global function with example usage: poor_knowledge_selection(messages, persona, stemmer, knwldg_ovlp_thrshld) and returns: violations