What we did in our racing games was rather simple.
We started with a closed polyline that defined the ideal line the car should try to follow. Getting your AI to follow this line while not looking goofy is a task on it's own. You need a good amount of look ahead and make sure the AI can't steer faster than a human would steer.
The next step was to added some helpers (basically spheres) to the track. We added some userproperties that where used to hint the AI. We used hints like "Use the handbrake in the next curve", "70mph is the maximum speed here" ect.
Then we added logic to the AI that it was able to recover from crashes, e.g. when driven into a wall, drive a bit backwards, steer towards you ideal line and then switch back into normal driving mode. This is important once a car gets stuck.
The final touch is to add more polylines to the level and add logic that cars detect players and other cars as obstacles. The other lines where used as alternative routes for the AI if they detected that they would run into a crash if they keep driving on the ideal line.
The alternative polylines also give varity to the gameplay and can be used to adjust the difficulty level of the AI. Hint the AI to not use the ideal line for more than 70%, and the enemies will be a bit easier to beat.
Oh yes, before I forget this: To give more varity make sure, that if an AI car is in the first position, let him drive a bit slower (unless you have some kind of slipstream physic *hint, hint, easy to implement*).
That's basically all. A low tech solution, but with some tweaking and fiddeling of the polylines it gave impressive smart and entertaining enemies.