This is certainly not a trivial issue, but mostly because you cant just write code that “acts like a human” - not last since everybody would act differently. Since that’s the case i would start by giving NPCs a list of base priority values which they receive on generation / birth. These values can roll in a certain range, so for example hunger priority can roll (10-20) while protection priority can roll (18-25). These ranges give you control over the behavior, while still allowing for individuals to act, well, differently. In the above example, most people would tend to prioritize protection over getting something to eat, however someone may just roll a higher hunger priority than his protection value, which can result in interresting behaviour.
You should then couple this base priority value with the current situation, similar to what you wrote about with the “NeedToEat”. We dont want a person who just ate to prioritize eating again, so the dynamic priority must include the need for action. The hungrier the person gets, the higher this base priority value scales, obviously. This can be applied to everything. A family member being attacked by a squirrel is something to laugh at and move on, while you might come and help a family member that is being attacked by a boar, but you wouldnt help a stranger in the same situation, and so on.
The above should allow for dynamic individual behavior, while giving you plenty of control overall, by adjusting ranges, threat levels, or scalings. When to perform this behaviour check is a whole other topic, which depends on many factors. If you have a simulation with just a couple people, this does not matter. If we talk about thousands of individuals, you will have to approach this with performance in mind. Other than performance, it depends on the specific system you design. You might just perform the check every Update(). Or once per second. Or have a mixed system, where general priorities (that dont change often, like hunger) are checked every couple seconds only, but then you have an event / interrupt based system in place, which can trigger this check for other reasons. If you get hurt and break your leg, this should trigger a decision making process. If you hear your child shouting for help, this should trigger a decision making process. If you dont hear it, you cant do anything about it. So now you have a system where you only need to be able to “notify” individuals in some area of certain events, which can be efficiently handled with Octrees.
Other than this decision making process, the actual behavior of the individuals can probably be handled with a state machine that is completely ignorant of the decision making process. This would simply deal with the relation between running, working, swimming, eating and so on.
The above is how i would approach my first prototype, and then go with the flow. I did not look for existing algorithms, but it seems you already did so anyways, so instead i simply had fun thinking about the topic.
Hope this helps in any way, shape or form