The reader does not return an AST, but is a Lisp procedure which returns nested lists of atoms. As such it is independent of code and it just reads s-expression data. The input to EVAL or COMPILE then is also not an AST, but an s-expression representation. Thus the first thing one would do is to think about a data representation for s-expressions.
READ then needs to turn s-expressions into that data representation. READ then also does not work over a string, but a text stream.
People implement then a Clojure-like language, but miss the implementation implications that the input to EVAL is actually a basic Lisp data structure and they miss the nature of READ producing such a Lisp data structure from basic stream operations like READ-CHAR... Also in many cases it is useful to make the reader table-driven and not hard-wired.
Thus a typical question is to think how much of Lisp do I have to implement in that other language and how much Lisp then can be then implemented in Lisp.
So, one of the really basic ideas of Lisp is that READ is defined to take input from a stream (keyboard, file, ...) and returns basic Lisp data. It is a general tool for de-serialization of Lisp data, not a special tool for reading Lisp code.
READ is not converting a STRING to an AST.
Make a Lisp is more Clojure-flavored than Common Lisp, but it might still be nice to examine because of how many different implementation languages it has been done in by various authors.
It also breaks down the implementation process into 11 distinct steps which are easy to understand.