It's not possible to look at this as every NPC counting as "1 processing function".
When I code an AI routine like this, I first need to account for a single NPC's individual, basic functionality. Let's take a really simple example like, "Walk from point A to point B":
I'll have some sort of navigational grid (I use the term navmesh, since I've primarily worked with Gamebryo / Creation Engine) which is a web of "points" that are valid places for actors to move in 3D space in order to create various paths from A to B. I then need to write an algorithm that dictates how many different times the AI routine will try to calculate different pathways in its attempt to find an open route from the starting point to the destination. The more points there are in a web, the more detailed the movement can be, but the more processing it will take to find a chain of points that goes all the way from A to B.
Now, if a point along the route that is ultimately selected is "blocked" because something in the world-state changed, like another NPC decided to stand on one of the points chosen along the path, I need the algorithm to both recognize that (constant processor cycles dedicated to checking), and I'll then need to recalculate the route, with the blocked point now counting as A, and the final destination point remaining as B.
That process must be continuously running, indefinitely, from the start of the NPC's journey until the end. If, at any point, the algorithm is unable to calculate a valid route to the final destination at point B, the NPC will become "stuck". In lay experience, this is where you'll see an NPC just walk in circles, jitter back and forth, or randomly start walking in a different direction. In actuality, what's happening is the AI routine is actively trying to work around the issue by either repeatedly re-calculating the route, but is continuously unable to reach destination B because the algorithm does not allow enough passes to find a route complex enough to get all the way from A to B...or it simply gets itself unstuck because the algorithm has a failsafe function that allows the NPC to simply pick a new destination B.
Next, that simple process of making a single NPC do nothing but "walk from point A to B" must be individually processed for every, single, individual NPC that I intend to populate the world-space with. Every, single, individual NPC must be given an equal share of processing time in order to figure out what grid point on the navmesh they're going to move to next, make adjustments as needed, and then execute it.
And all that does is let random NPCs simply move from one point to another. We haven't even gotten into the AI routines that would need to be written to have NPCs react to the world around them, move at only certain times of the day, interact with things like vehicles, crosswalks, vending machines or store stalls, display idle animations, fetch and play specific sound files for speech, etc., etc., etc. For each of these different elements, I'll need specific code that governs all of it constantly loaded into RAM and being actively processed for each, individual NPC presently loaded into the gameworld. If I want to create a persistent world with static (instead of randomly generated) NPCs, I also need to have algorithms specifically tracking the behavior of NPCs that are not actively on-screen at the moment.
As the complexity of the system increases, especially the number of individual NPCs involved, the amount of processing required increases in an exponential curve. The more processing I dedicate to creating these types of complex AI routines, the less I have to dedicate to all other processing the game may need to do.
Hence, we have to balance. The end result is that we want to create an illusion of bustling life while still allowing for all of the game's other functions to execute as smoothly as possible, leaving an overhead of system resources in case something unexpected happens and the game suddenly needs to "pull" and do a lot of complex processing all at once. Generally, as is the case with all game design and development, if we need more resources in one area, we need to take it from another area.
Think of a game's minimum specs as a "budget" that determines what all game functions are able to afford. If a process cannot do what it needs to do on that budget, then process needs to be simplified or reworked in clever ways so that the game engine is not "over-spending" in any one area.
_______________
TL; DR / conclusion only:
Is it possible to create much more complex crowd-AI? Certainly! But that will necessarily create an exponential increase in demand from system resources -- and not by a little -- by tremendous amounts as we add additional NPCs. If it's too much, then only ultra-high-end hardware will be able to handle it. That greatly limits the market. It also limits the overall functionality gameplay-wise, as resources dedicated to AI routines cannot be used for other game functions. It's all a big balancing act.