.* is greedy¹, meaning it would try to cover as much as possible, as long as the string matches the regex. You want to make it non-greedy, to stop as soon as the match is found. Adding ? just after * or + makes it non-greedy. Compare:
(let ((s "abcabcabc"))
  (string-match ".*c" s)
  (match-string 0 s)) ; => "abcabcabc"
(let ((s "abcabcabc"))
  (string-match ".*?c" s)
  (match-string 0 s)) ; => "abc"
.*? is a non-greedy version of .*, so just adding ? makes it work:
(let ((s "test_blah_agent_pkg
test_blah_agent
test_blah_pkg
test_blah_driver
test_blah_abs_if
test_blah_abs_if_pkg
test_blah_if_pkg
test_blah_if"))
  (string-match "\\(.*?\\)_\\(agent_pkg\\|agent\\|driver\\|abs_if\\|if\\|pkg\\)" s)
  (match-string 1 s)) ; => "test_blah"
FYI, third-party string manipulation library s has plenty of string functions that you mind useful instead of relying on regular expressions all the time. E.g. s-shared-start can find a common prefix for 2 strings:
(s-shared-start "test_blah_agent" "test_blah_pkg") ; "test_blah_"
Combined with s-lines, which breaks a string into a list of strings by newline character, and -reduce function from the amazing third-party list manipulation library dash, you can find a prefix that is common for every string:
(let ((s "test_blah_agent_pkg
test_blah_agent
test_blah_pkg
test_blah_driver
test_blah_abs_if
test_blah_abs_if_pkg
test_blah_if_pkg
test_blah_if"))
  (-reduce 's-shared-start (s-lines s))) ; => "test_blah_"
¹ Read under section Greediness to understand this concept.