Previously in Part 1 and Part 2 of Prolog’s Role in the LLM Era:
- I made a case for partnering fuzzy LLMs with deterministic rules.
- This blog is really “Appendix C” of my book, Enterprise Intelligence.
- Presented a simple introduction to Prolog.
- Introduced my Prolog variant named SCL.
- Discuss Prolog as an alternative RDF-based Knowledge Graphs.
- Discuss Prolog processed with a Prolog engine as well as Prolog as unambiguous parts of an LLM prompt.
- I discussed the big picture use case for Prolog in partnership with large language models (LLM).
- How authoring Prolog with assistance from LLMs opens the door to that vast majority of people whose voices don’t contribute to the commercial LLMs.
I do recommend reading Part 1 and Part 2 first.
In this episode, we’ll play a bit with Prolog to get our feet wet. To play with Prolog in Python, these are the steps:
- Download and install the SWI-Prolog binary.
- Install Python – I’m using 3.10 right now.
- Install the pyswip Python library.
- Install Visual Studio Code (any IDE for Python).
- Write a Prolog sample.
- Write a small Python program for processing the Prolog.
I’m going to assume you already have Python and an IDE to run Python such as Visual Studio Code.
Additionally, because Prolog is rather fringe, the Prolog application (SWI-Prolog) and Python library (pyswip) aren’t widely used or widely known, relative to say, ChatGPT. Prolog has always been a small community – especially back in the 1980s when IT as a whole was still a small community. I cautiously went through the process of installing this environment, first on a VM. Despite some complications, it seems to have safely installed.
So I’m providing fair warning to proceed at your own risk – even though it all seems OK to me.
I mentioned in Part 1 that the Prolog variant I wrote back in 2004 (SCL) is much too old to resurrect. A Prolog interpreter is very difficult to write and maintain, so I’m choosing to demonstrate my thoughts with readily obtainable components. Based on admittedly “not-exhaustive” testing, it seems like the combo of SWI-Prolog and pyswip will work very well.
Things do become more technical from this blog through the next few parts of the series. However, I do try as best as I can to explain what is going on as opposed to explaining the code. I also begin each blog with a non-technical explanation about the subject.
Download SWI Prolog
I very much admire the work of the SWI Prolog community over the decades. I hope that this series might kindle significant interest in Prolog today. They deserve it for their diligence in keeping a very nice version of Prolog available to the world.
But please understand that I’m not suggesting Prolog as simply an alternate AI path to LLMs and/or Knowledge Graphs. The theme is Prolog’s possible symbiotic relationship with LLMs – in a manner similar to the symbiotic relationship between Knowledge Graphs (KG) and LLMs, which I discuss in my book and in Part 1.
SWI Prolog is a relatively widely used Prolog implementation. When I developed my Prolog variant, SCL, I don’t recall SWI Prolog, although it seems to have been in existence well before then. But back in 2004, there were features I required for my use cases that I’m pretty sure didn’t exist in any Prolog implementation back then (or at least I couldn’t find any). I needed full control over the source code to meet what were unknown requirements back then.
To download the SWI-Prolog application, navigate to https://www.swi-prolog.org/Download.html

I clicked on “Stable release” (https://www.swi-prolog.org/download/stable):

I selected the latest Windows version, which brought me to the actual download page.

Going through the steps of downloading and installing SWI-Prolog, I was very wary of warnings from Windows about trusting the SWI-Prolog executable for the installation. I got the message:

I stopped to research further into SWI-Prolog for anything concerning. I’m not the CIA, so I can’t be 100% sure about anything without looking through their github site directly, scrutinizing all the code. But I’m satisfied this is a genuine false positive, and so I went ahead to install SWI-Prolog.
I’m providing fair warning. We all need to be careful no matter what.
I navigated to my Downloads folder and executed the file anyway. Just in case, I ran my malware software afterwards. It seems OK.
I also manually ensured that:
- C:\Program Files\swipl\bin is in my path System variable.
- There is the system variable: SWI_HOME_DIR with the value: C:\Program Files\swipl
pyswip – Python Library for SWI Prolog
pyswip is a Python library wrapping the functionality in SWI-Prolog.
I originally just pip installed pyswip with no frills: pip install pyswip
But when I ran my Python test program, I would get errors. From a stackoverflow thread I found I needed to pip install pyswip like this:
pip install git+https://github.com/yuce/pyswip@master#egg=pyswip
Notice that the command doesn’t reference SWI-Prolog github directory linked from https://www.swi-prolog.org/, which is https://github.com/SWI-Prolog.
pyswip is a Python wrapper developed by another party for the SWI-Prolog application. The “yuce” in the command above (the github repository) is Yuce Tekol, the principal contributor to pyswip. Again, SWI-Prolog is the Prolog engine and pyswip is a Python library wrapping functionality to SWI-Prolog.
Both are great efforts and I thank SWI-Prolog and Yuce very much for their great work.
Sample Prolog Python Program
Below is a very basic Python script demonstrating how to use pyswip. It’s simple a list of facts identifying the parents of a few people. It also includes a rule about how we can deduce the siblings of someone based on knowledge of parents. The rule states that X and Y are siblings if they share a common parent Z and are not the same individual:
from pyswip import Prolog
prolog = Prolog()
prolog.assertz("parent(john, mary)")
prolog.assertz("parent(john, mike)")
prolog.assertz("parent(susan, mary)")
prolog.assertz("parent(susan, mike)")
# Define a rule - X and Y are siblings if they share a common parent.
prolog.assertz("sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \= Y")
# Query the knowledge base
# Who are the siblings of mary?
siblings = list(prolog.query("sibling(X, mary)"))
print(f"Siblings of Mary: {[sibling['X'] for sibling in siblings]}")
The result is that the siblings of mary includes mike:

Authoring Prolog with an LLM
Thinking back on Part 2, my thought in terms of Prolog in the mix of LLMs (retrieval augmented generation (RAG) and even KGs is to open the door to many more knowledge workers (those who haven’t written books and blogs consumed by LLMs) to encode their knowledge in a deterministic manner.
In this scenario, I’m playing the part of a healthcare analyst. As a healthcare analyst, I’m focusing on identifying who might be at risk for serious long-term health conditions like diabetes. The goal is to find those at risk early on so we can take preventive steps. By doing this, we can avoid the more expensive and painful treatments that would be needed if these conditions were allowed to develop unchecked. For example, by recognizing who might be at risk for diabetes, we can introduce lifestyle changes or medical interventions that could prevent the disease from developing in the first place.
Starting Point
In this role, I don’t know Prolog nor do I have the medical expertise required for predicting risk for diabetes. I can ask ChatGPT to give me a good start:
could you write a prolog to determine if someone is at risk for diabetes? it should work in swi-prolog. also, could you write a python so i can load a prolog file, and read it into pyswip and query it.
ChatGPT returned a Prolog and a Python script. The Prolog lists facts for two people, a few other definitions, and a rule.
% Facts
age(john, 45).
bmi(john, 29).
family_history(john, no).
age(susan, 30).
bmi(susan, 22).
family_history(susan, no).
% Rules
high_risk_age(Person) :- age(Person, Age), Age >= 45.
high_bmi(Person) :- bmi(Person, Bmi), Bmi >= 25.
family_history_risk(Person) :- family_history(Person, yes).
at_risk_for_diabetes(Person) :-
(high_risk_age(Person);
high_bmi(Person);
family_history_risk(Person)),
write(Person), write(' is at risk for diabetes.'), nl.
The rule, at_risk_for_diabetes, states that one is at high risk if they are in a high-risk age (45 or older), or one has a high BMI (25 or more), or one has a family history of diabetes. Looking at the Prolog, john meets all the criteria, even though only one condition is required (it’s currently OR).
ChatGPT actually did offer an explanation:
- Facts: These define the age, BMI, and family history of individuals.
- Rules: These define the conditions under which a person might be at risk for diabetes:
high_risk_age/1: A person is at higher risk if they are 45 years old or older.high_bmi/1: A person is at higher risk if they have a BMI of 25 or more.family_history_risk/1: A person is at higher risk if they have a family history of diabetes.
- at_risk_for_diabetes/1: This rule determines if someone is at risk based on any of the conditions defined.
The Prolog above should be saved as “diabetes_risk.pl”.
Here is the Python script ChatGPT was also kind enough to provide:
from pyswip import Prolog
# Initialize Prolog
prolog = Prolog()
# Load the Prolog file
prolog.consult("diabetes_risk.pl")
# Query Prolog to find who is at risk for diabetes
query = "at_risk_for_diabetes(Person)"
results = list(prolog.query(query))
# Display the results
for result in results:
print(f"{result['Person']} is at risk for diabetes.")
Miraculously, the Prolog and Python actually worked out of the gate! Seriously, that’s a genuine miracle. However, at worst, for this level of relatively low complexity, it should have returned something that could be considered a good starting point.
The program correctly returns john (who meets two out of three conditions) but not susan (who doesn’t meet any of the conditions).
Note: If you’re specifying a directory with the file name, you need to use forward slashes:
prolog.consult("C:/MapRock/IntelligenceBusiness/book_code/appendix_c/diabetes_risk.pl")
Modifying the Rules
I actually don’t think this rule is good enough. The rule currently states anyone over 45 years old is at risk. I feel that anyone is at risk if they have a BMI over 30 at any age (even young people) and people over 45 are at risk with a BMI of over 20. Again, this is just for tutorial purposes.
Let’s ask ChatGPT:
I actually don't think this rule is good enough. The rule currently states anyone over 45 years old is at risk. I feel that anyone is at risk if they have a BMI over 30 at any age (even young people) and people over 45 are at risk with a BMI of over 20.
Here is the new Prolog:
% Facts
age(john, 45).
bmi(john, 29).
family_history(john, yes).
age(susan, 30).
bmi(susan, 22).
family_history(susan, no).
% Rules
% high risk age is 45 or older AND BMI > 20
high_risk_age_and_bmi(Person) :-
age(Person, Age),
Age >= 45,
bmi(Person, Bmi),
Bmi > 20.
high_bmi_any_age(Person) :-
bmi(Person, Bmi),
Bmi > 30.
family_history_risk(Person) :-
family_history(Person, yes).
% At risk for diabetes if they have a BMI > 30 at any age, or BMI > 20 and age > 45, or family history
at_risk_for_diabetes(Person) :-
(high_bmi_any_age(Person);
high_risk_age_and_bmi(Person);
family_history_risk(Person)).
Looking at the Prolog, we see that john is 45 and has a BMI of 29, so he will trigger as at risk for diabetes with the high_risk_age_and_bmi rule. However, susan younger with a BMI much less than 30 and no family history.
If we changed susan to have a BMI of 35, she will trigger, by the high_bmi_any_age rule.
Validate Our Work with the SMEs
Let’s say at this point, the rules seem reasonable to me, a non-MD. It passed my unit tests – john and susan triggered at correct times. So I’d like to submit the rules for approval.
But the approvers (a panel of doctors) don’t know anything about Prolog. ChatGPT can help:
Could you please explain the at risk rules in simple terms for a review with a panel of MDs?
ChatGPT responded with this English translation suitable for submitting to non-techie, but very medical people:
Explanation of "At Risk for Diabetes" Rules
The Prolog code is designed to determine whether an individual is at risk for diabetes based on three main factors: Body Mass Index (BMI), age, and family history. Here’s how each rule contributes to the overall assessment:
1. High BMI at Any Age
Clinical Interpretation: The rule states that if an individual has a BMI greater than 30, they are considered at risk for diabetes, regardless of their age.
Rationale: High BMI is strongly associated with insulin resistance and the development of type 2 diabetes. In clinical practice, a BMI over 30 typically categorizes a patient as obese, which is a well-known risk factor for diabetes. This rule emphasizes that even younger individuals should be considered at risk if their BMI is in the obese range.
2. Moderate BMI with Advanced Age
Clinical Interpretation: If an individual is over the age of 45 and has a BMI greater than 20, they are also considered at risk for diabetes.
Rationale: Age is an independent risk factor for diabetes, with the prevalence of type 2 diabetes increasing significantly after the age of 45. The rule lowers the BMI threshold for older individuals because the cumulative effects of aging, such as reduced insulin sensitivity and changes in body composition, make even a moderate BMI (greater than 20) a risk factor. This aligns with clinical guidelines that recommend more aggressive screening and preventive measures in older adults, even if their BMI is not in the obese range.
3. Family History of Diabetes
Clinical Interpretation: If an individual has a family history of diabetes, they are considered at risk, independent of their age or BMI.
Rationale: A family history of diabetes significantly increases the risk of developing the disease due to genetic predisposition and shared environmental factors. This rule recognizes that genetic factors can predispose individuals to diabetes, making family history a critical component of risk assessment. In practice, this highlights the importance of screening and preventive counseling for individuals with a family history of diabetes, even if they do not have other traditional risk factors.
Summary for the Panel:
BMI > 30: Any individual with a BMI over 30 is flagged as at risk for diabetes, emphasizing the importance of obesity as a risk factor.
Age > 45 with BMI > 20: Older individuals (over 45) are considered at risk even with a BMI as low as 21, acknowledging the combined effect of aging and body weight on diabetes risk.
Family History: A history of diabetes in the family puts an individual at risk, highlighting the genetic and familial component of diabetes risk.
These rules are designed to provide a logical framework for identifying individuals at risk of diabetes, based on well-established clinical criteria. They can be used as part of a broader screening and prevention strategy in clinical practice, ensuring that individuals who may benefit from early intervention are appropriately identified.
Should the medical folks require adjustments to the rules, the analyst can work with ChatGPT to reflect those changes in the Prolog.
Query Time Processing
Running this at scale could be implemented in the Cloud with scalable Web services. Or, AWS’s Lambda Functions or Azure Functions. By “at scale”, I mean Prolog for more than just predicting those at risk for diabetes, but for very many other possibly preventive conditions. It can include rules for many other questions, use cases, or domains.
When this is implemented, we need specify the Prolog file containing the rules. But we must also supply parameters. In this case, it’s the data the of patient in question. The code to run the Python with the arguments would look something like this:
import sys
def format_patient_info(name, age, bmi, family_history):
return f"age({name}, {age}).\nbmi({name},
{bmi}).\nfamily_history({name}, {family_history})."
if name == "main":
if len(sys.argv) != 5:
print("Usage: python script.py ")
sys.exit(1)
name = sys.argv[1]
age = sys.argv[2]
bmi = sys.argv[3]
family_history = sys.argv[4]
formatted_string = format_patient_info(name, age, bmi,
family_history)
rule_file = sys.argv[5]
The command would look something like this:
python format_info.py bill 70 30 yes diabetes_risk.pl
The result of format_patient_info would be:
age(bill, 70).
bmi(bill, 30).
family_history(bill, yes).
The formatted line just above will be concatenated to the rules in diabetes_risk.pl which should result that bill is indeed at risk for diabetes (if he hasn’t already been diagnosed).
Depending on the reception of this blog series, I may proceed to stress testing the components here and optimize the Python code. For now, at the time of writing this blog, I’m just sharing my thoughts.
Searching for the Right Rules
If there indeed were a large number of Prolog snippets generated, it must be easy to locate. Normally, we could extract the qualities of the Prolog such as the predicates it uses, comments, metadata such as the author and last update. The Prolog snippet’s unique ID would probably be its URI (the storage directory URL plus the file name). Those characteristics would be entered into some sort of search database matching search terms to Prolog snippets.
But in the LLM era, the Prolog snippets can be robustly searchable. We can ask an LLM to author a concise summary of the Prolog, generate its embedding, and add it to a vector database.
An embedding is a dense vector representation of data intended to capture the semantic meaning of data. For instance, a few sentences can be converted into embeddings, allowing the model to understand the relationships between different words or phrases based on their context.
Embeddings transform the data into a numerical format that is easier for models to process. The vector space in which the embeddings reside typically has a much lower dimensionality than the original data, allowing the model to make sense of the data’s underlying structure more efficiently.
This is a partial piece (first 5 of about 1500 numbers) of the embedding for “pyswip and SWI-Prolog is cool”:
[-0.003732851706445217, -0.011490019969642162, 0.0054640802554786205, -0.034273408353328705, -0.022909807041287422, ...]
I issued the following Prompt to ChatGPT. I mentioned I’m passing Prolog and that it should be of a size optimal for creating a robustly searchable summary by an embedding:
Study the following Prolog and provide a summary of what this is for. I will create an embedding of your summary for the purpose of enabling a user to search for a Prolog file existing among a large number in a storage directory. You must ensure this summary not more than 200 tokens:
% Eugene Asahara's drink of choice
businessmeal(true) :- companion(boss).
businessmeal(true) :- companion(client)
drinkorder(dietcoke) :- context(lunch), person(eugene).
drinkorder(dietcoke) :- context(dinner), businessmeal(true), person(eugene).
drinkorder(redwine) :- context(dinner), companion(wife), person(eugene).
drinkorder(beer) :- context(dinner), companion(friend), person(eugene)
ChatGPT’s composed summary of the Prolog:
This Prolog code determines Eugene Asahara's drink order based on the context (lunch or dinner) and his dining companions. If it's lunch, Eugene always orders Diet Coke. For dinner, he orders Diet Coke if it's a business meal (with a boss or client), red wine when dining with his wife, and beer when dining with friends. The code helps automate the selection of his preferred drink in various dining scenarios.
I chose the limit of 200 tokens (for our purposes, a token is roughly a syllable). How did I get that value? I asked ChatGPT. This is what it said:
A token limit of around 100-150 tokens would generally be optimal for a summary intended to search for a specific Prolog file among a large directory. This range allows the summary to be concise and focused on key details, making it easier to match with relevant queries without being too verbose or too brief.
The embedding could be obtained with something like this Python code that utilizes OpenAI’s API:
import openai
# Initialize the OpenAI API with your API key
openai.api_key = "your-api-key-here"
# The text you want to generate an embedding for
text = "This Prolog code determines Eugene Asahara's drink order based on the context and companions …"
# Generate the embedding using OpenAI's embedding model
response = openai.Embedding.create(
input=text,
model="text-embedding-ada-002" # Example model for generating embeddings
)
# Extract the embedding vector from the response
embedding = response['data'][0]['embedding']
Print the embedding vector
print(embedding)
The embedding and the Prolog file’s URI can then be added to a searchable vector database.
I compared the embedding of the Prolog snippet of Eugene’s drinks to an embedding of the text, “Eugene Asahara’s meal preferences”, something that might be a user or AI might type to search for a particular resource. As I had hoped, the similarity between the two text is 0.85, very close. Contrast that 0.85 to Eugene’s drink Prolog to “Petosky stones. where to find” – a similarity of 0.64.
Sensitive Prolog
Lastly, this method of storing Prolog files (or other configuration files, code snippets, etc.), while only exposing the embeddings to an end user for searching, naturally obfuscates the actual content. Embeddings cannot be reversed to retrieve the original code, ensuring that the underlying information remains secure.
If a Prolog snippet is highly sensitive, the search could indicate the presence of a relevant Prolog file, but the file itself could be marked as secure and encrypted. With the appropriate access permissions, the Prolog file can be decrypted and accessed.
Conclusion
In Part 1 and Part 2 of “Prolog’s Role in the LLM Era,” I laid the groundwork for understanding how deterministic rules, such as those in Prolog, can complement the fuzzy, probabilistic nature of Large Language Models (LLMs). The goal is to create a symbiotic relationship where Prolog’s logic-based reasoning provides clarity and precision, while LLMs contribute expansive knowledge and natural language understanding.
This blog series, which can be seen as an “Appendix C” to my book Enterprise Intelligence, introduced Prolog through practical examples and discussed the potential of a Prolog variant I developed, named SCL. We explored how Prolog could serve as an alternative to RDF-based Knowledge Graphs and how it could be processed both by a Prolog engine and as unambiguous components within LLM prompts.
I demonstrated a use case where Prolog, assisted by LLMs, could empower a broader range of knowledge workers—those whose insights have not yet been encoded into commercial LLMs—to contribute meaningfully to AI-driven systems. Through a hands-on tutorial, I guided readers in setting up a Prolog environment using SWI-Prolog and pyswip, showing how these tools can be integrated with Python for practical applications.
Moreover, I emphasized that my intent is not to position Prolog as merely an alternative AI pathway but rather as a complementary partner to LLMs, similar to how Knowledge Graphs have been used to enhance LLMs’ capabilities. The example of predicting diabetes risk served as a practical demonstration of how Prolog’s deterministic rules can be applied to real-world problems, providing a starting point for further exploration and validation by subject matter experts.
This exploration of Prolog’s potential role in the LLM era is just the beginning. I hope this series sparks renewed interest in Prolog and encourages more collaboration between deterministic rule-based systems and the evolving landscape of AI.
The Prolog community, particularly those involved in maintaining SWI-Prolog and Yuce Tekol’s pyswip, deserves recognition for their ongoing efforts over several AI winters since the 1980s. And I look forward to seeing how Prolog can continue to play a vital role in AI development.
In Part 4, we explore the tie between machine learning and Prolog.
This blog is Chapter VII.3 of my virtual book, The Assemblage of Artificial Intelligence.