¶¶Ňő̽̽

Author

Clayton Cafiero

Published

2025-01-31

Example: Generative AI for advice and feedback

What follows is an example of providing a completed active learning exercise with context to ChatGPT 4o and Anthropic’s Claude. Notice that the prompt begins by providing some context to indicate how much Prolog students have seen and some additional background. (Prolog is a declarative programming language used for logic programming, theorem proving, and computational linguistics.) Providing context like this can help guide LLMs in their responses, with the goal of producing useful evaluation and level-appropriate suggestions.

The prompt

Prompt


Here is an active learning exercise (in Markdown format) for a first course in artificial intelligence. Students have already seen predicate and first order logic (in class and in readings) and have seen an introductory live coding demo for Prolog. The second live coding example involves writing predicates which use list construction and deconstruction and recursion to generate lists of natural numbers in descending order, e.g., [5, 4, 3, 2, 1]. This exercise is the first actual hands-on experience students will have with Prolog. It is the first part in a two part exercise (the second part will involve adding additional constraints and producing a finished Prolog program).

— — BEGIN EXERCISE — —

Getting started with Prolog

Objective

Students will learn to represent knowledge in Prolog by translating a domain into Prolog facts and rules, and then using queries to derive information from the knowledge base. The domain in question consists of students and courses. The Prolog program should represent facts about students and the courses they’ve completed, facts about course prerequisites, and should be able to respond to queries which ask questions like: can such-and-such a student enroll in such-and-such a course.

Expected learning outcomes

  • Understand the basics of Prolog syntax and semantics.
  • Gain experience in translating a domain or scenario into Prolog facts and rules.
  • Gain familiarity with use of lists and recursion in Prolog
  • Learn how to formulate and run queries to derive information from a Prolog knowledge base.
  • Appreciate the strengths and limitations of logical programming in AI.

Materials needed

Laptops with Prolog installed (e.g., SWI-Prolog) or access to SWISH.

Submit your code

Submit your code by 6:00 PM ET today on Brightspace. Only one person need submit on behalf of your group. Just be sure that all names are in a comment atop your Prolog file, e.g.,

% enrollment.pl : A Prolog program representing courses, students, and valid enrollment.
% 
% Team: Egbert Porcupine, Edwina Pickle, Mephista Garply, and Winston Quux
% 
% (code follows)

Some things that may be helpful

Anonymous variables

Just like we do in Python, in Prolog we use _ as an anonymous variable. We use this in cases where we need an argument but don’t really care what it is and won’t use the value in any downstream computation. This is a convention and not a syntactic feature.

Use of an anonymous variable here and there is not required, but it may improve the readability of your code. Up to you.

Prolog operators

Remember the following operators in Prolog:

  • , is logical AND,
  • ; is logical OR, and
  • \+ is logical NOT.

Prolog lists

Prolog lists are enclosed in square brackets. If we use [H | T] with a list literal or bound list variable, this will deconstruct the list into head (the first element of the list) and tail (all remaining elements of the list). Note: You will not need to construct any lists recursively using this syntax.

findall/3

Recall that findall/3 is a three-place predicate. The third argument is the variable name of the returned list. This is convenient when you wish to pass a list constructed by findall/3 to another predicate downstream (which you will do in this assignment).

Construct a Prolog program for course registration

Some facts about prerequisites

  • CS1640 requires CS1210 as prerequisite.
  • CS2100 requires CS1210 as prerequisite.
  • CS2210 requires CS2100 as prerequisite.
  • CS2240 requires CS2100 as prerequisite.
  • CS2250 requires CS1640 and CS2240 as prerequisites.
  • CS2300 requires CS2100 and CS2240 as prerequisites.

Some facts about students

  • Alvin, Betty, Dave, Diana, Florence, Mary, Simon, and Theodore are students.
  • Alvin has completed CS1210, CS2100, and CS1640.
  • Betty has completed CS1210, CS2100, CS1640, CS2240, and CS2250.
  • Diana has completed CS1210, CS2100, CS1640, and CS2240.
  • Florence has completed CS1210, and CS2100.
  • Simon has completed CS1210.

Some facts about enrollment

  • If someone is not a student, they may not enroll in any courses.
  • If a course has no prerequisites, any student may enroll.
  • If a course has prerequisites, all prerequisites must be satisfied by having completed the required course or courses before a student can enroll.

Simplifying assumptions

We won’t worry about preventing a student from enrolling if they’ve taken the course before. (In fact, this is permitted at ¶¶Ňő̽̽ if the student wishes to retake in hopes of getting a better grade).

Tasks

Represent the facts

First, represent the facts about courses and prerequisites, students, and courses completed.

Remember to group your facts. Arities should be as follows:

  • student/1
  • prerequisite/2
  • completed/2

Rules to determine if enrollment is permitted

There’s a tricky bit in determining if a student can enroll in certain courses. If the course has no prerequisites or only one, that’s easy, but some courses have more than one prerequisite. Accordingly, to ensure a student can enroll in a course with prerequisites, we’ll want to gather all the prerequisites, and then check to see if the student has completed all of them. It helps to decompose this into smaller chunks. Hence, we will use a helper rule, all_completed/2. This should take a student and list of courses, and then check to see that all courses have been completed. This will be done recursively, by deconstructing the list provided. As with any recursion, we must have a base case, and a recursive case. Here’s the base case.

all_completed(_, []).  % Base case: No prerequisites to check

This makes use of an anonymous variable _ (since we won’t be doing anything with the student downstream) and an empty list [].

The recursive case should start with the head

all_completed(Student, [Prereq | Rest]) :-
    % body goes here

Notice that if the list passed in is bound (not free), this syntax will deconstruct the list into Prereq which you can then use to see if the student has completed that particular course, and Rest, the remainder of the list of prerequisites yet to test.

Hint: The body of this predicate requires only two lines.

Test all_completed/2

You can test the all completed rules like this:

?- all_completed(alvin, [cs1210, cs2240].
false.

?- all_completed(alvin, [cs1210, cs1640, cs2100].
true .

?- all_completed(mary, []].
true .

Once you have the all_completed/2 working OK, you should produce two rules for can_enroll/2.

Both cases should start with this:

can_enroll(Student, Course) :-
    % complete body here

The first rule should verify that Student is indeed a student, and then check to see if the course has no prerequisites. Hint: Recall that \+ is Prolog’s way of writing negation.

The second rule should:

  • check to see if Student is indeed a student,
  • verify that the course has at least one prerequisite,
  • find all the prerequisites for the course (hint: use findall/3), and
  • verify that all prerequisites have been met (hint: use all_completed/2).

Consult and test your program

Run the following tests. These queries should return true.

?- can_enroll(simon, cs1210).  % no prereqs

?- can_enroll(alvin, cs2240).  % one prereq

?- can_enroll(diana, cs2250).  % two prereqs

Run the following tests. These queries should return false.

?- can_enroll(jason, cs1210).     % Jason isn't a student!

?- can_enroll(dave, cs2240).      % Dave hasn't taken cs2100

?- can_enroll(florence, cs2250).  % Florence doesn't have either prereq

Submit your code

Submit your code by 6:00 PM ET today on Brightspace.

If you have time… (but absolutely not required)

Including class standing requirements

Consider the fact that some courses require student standing, e.g., student must be a sophomore to enroll—first-year students may not.

How would you represent this as a fact?

How would you modify your rules to work with student standing? Might you need other “helper” predicates?

For example, add these facts

standing(cs2240, sophomore).

senior(alvin).
senior(betty).

junior(diana).
junior(theodore).

sophomore(florence).
sophomore(simon).

firstyear(mary).
firstyear(dave).

How would you add new rules and modify existing rules to enforce this kind of requirement?

More with lists

Consider how we might modify the prerequisite/2 to use a list. In this way we could represent

prerequisite(cs2250, cs1640).
prerequisite(cs2250, cs2240).

as

prerequisite(cs2250, [cs1640, cs2240]).

How might you modify your rules to use this approach?

A little fun with the names

The names of students in this assignment come from two different sources. Can you identify the sources?

— — END EXERCISE — —

Please review and comment concisely.

ChatGPT’s response

ChatGPT response


Here is a review of your exercise:

Strengths:

Clear Objectives and Outcomes: The exercise has well-defined objectives and expected learning outcomes, which help students understand the purpose of the activity.

Structured and Incremental: The exercise breaks down tasks into manageable steps, progressing from facts to rules and testing, which aligns well with how students typically learn Prolog.

Helpful Hints: Including examples, such as all_completed/2 and findall/3, offers useful scaffolding, ensuring students aren’t left struggling with syntax or logic.

Tests and Debugging Guidance: Explicit test cases provide students with clear goals and immediate feedback.

Optional Challenge: The “If you have time…” section offers extra learning opportunities for motivated students, expanding their understanding.

Suggestions for Improvement:

Clarify Testing Instructions: Add a step explaining how to consult the Prolog file and run the tests (e.g., ?- consult('enrollment.pl').).

Group Facts with Comments: Encourage students to group and comment on their facts (% Students, % Prerequisites) for better readability.

Use of Variables in Facts: Consider suggesting standardized variable names (e.g., student(StudentName)) to improve scalability and readability.

Hints on Debugging: Briefly mention how students can debug by using trace/0 in SWI-Prolog if their code isn’t working as expected.

Diversity of Test Cases: Add a test case where a student is eligible for multiple courses simultaneously to explore findall/3 further.

Acknowledge Syntax Variations: Warn students about common syntax errors, such as forgetting the period at the end of Prolog statements.

This exercise is well-prepared and thoughtfully constructed for a first hands-on experience in Prolog. The adjustments above would enhance clarity and usability, especially for students new to logic programming.

Commentary on ChatGPT’s feedback

As an experiment I ran the active learning session with the specification as written above on Monday 2025-01-27—without incorporating ChatGPT’s feedback. This trial run informs some of my comments.

Clarify testing instructions: This was a good call on ChatGPT’s part. While we did go over how to consult a Prolog file in class, at least one team needed a reminder of how to do it. That said, I’ve taught the more concise ?- [enrollment]. over the syntax used by ChatGPT:

?- consult('enrollment.pl').

Had I included this suggestion without changing the syntax, I might have confused some students. Nevertheless, I have incorporated this suggestion for use in future semesters using my preferred syntax.

Group facts with comments: I’m not entirely happy with this. While adding comments is harmless, adding comments to something that should be entirely self-explanatory is something I discourage. Adding comments as suggested is no different than commenting like this: x = 1 # let x equal 1. Instead, I favor self-documenting code to the greatest extent possible. In Prolog (as in other programming languages) using good names for atoms and identifiers should be favored over comments.

Use of “standardized” variables in facts: This is pure nonsense.

Hints on debugging: trace/0 is something that shouldn’t be introduced at this stage in learning Prolog. It would require more lecture time and demonstration to prepare for use of trace/0. With myself and a TA in the room during the exercise, introducing trace/0 would have been unnecessary and potentially confusing.

Diversity of test cases: ChatGPT is correct here. I could have included more test cases. The specification will include more test cases in future semesters.

Acknowledge syntax variations: This is a little puzzling. Is ChatGPT warning about variations between dialects of Prolog? I clearly state we’re using SWI-Prolog. Warning students about common syntax errors statements is a different matter.

What ChatGPT didn’t catch

There’s a defect in one of my code snippets:

?- all_completed(alvin, [cs1210, cs2240].
false.

?- all_completed(alvin, [cs1210, cs1640, cs2100].
true .

?- all_completed(mary, []].
true .

The first two queries are missing a closing parenthesis, and the last query has a closing bracket rather than a closing parenthesis. It would have been nice if ChatGPT had caught these defects.

Another thing that was missed (and would have been helpful in lab) was to remind students that Prolog syntax requires that atoms are named using all lower-case, and that variables start with an upper-case letter. Out of 35 students in 11 teams, three teams had to revise code after using upper-case letters for atoms (and getting abundant error messages from Prolog).

Summary

While ChatGPT provided some useful feedback, it included some useless or inappropriate suggestions as well. Useful feedback would have required some editing.

Claude’s response

Claude’s response


This is a well-structured active learning exercise for introducing Prolog programming. Here are my key observations:

Pedagogical Structure

  • Builds appropriately on prior knowledge (predicate logic, FOL, initial Prolog demo)
  • Clear progression from facts to rules to testing
  • Good scaffolding with the all_completed/2 predicate partially provided
  • Smart to focus on a relatable domain (course enrollment) that students understand

Technical Content

  • Covers core Prolog concepts: facts, rules, recursion, list manipulation
  • Good introduction to important built-ins like findall/3
  • The prerequisite checking problem naturally demonstrates Prolog’s strengths
  • Appropriate scope for a first hands-on exercise

Suggested Improvements

  • Could include a brief example of expected output format for the facts section

  • Might help to show a sample findall/3 usage since it’s crucial for the solution

  • Consider providing hints about typical pitfalls (e.g., capitalization rules for variables)

Fun Element

The student names appear to come from:

  • Alvin, Simon, Theodore: The Chipmunks
  • The other names seem to be from various fairy tales/children’s stories

Overall, this is a solid exercise that teaches important concepts while remaining engaging and manageable for students new to Prolog.

Commentary on Claude’s feedback

Claude’s response is quite concise, and comments are nicely grouped.

Expected output: The suggestion “Could include a brief example of expected output format for the facts section” isn’t at all clear. I have no idea how to act on this.

More scaffolding for findall/3: We covered in class the use of findall/3 but it might have been helpful to show a sample findall/3 by way of example. I have incorporated this change for future semesters.

Providing hints about typical pitfalls: Claude made this suggestion which ChatGPT missed (or obscured with vague language).

Fun: Claude commented on the final section of the assignment which was there only for fun. Claude correctly identified characters from Alvin and the Chipmunks: Alvin, Dave, Simon, and Theodore. However, it was unable to identify the source of the names: Betty, Diana, Florence, and Mary. Can you?

What Claude didn’t catch

Like ChatGPT, Claude didn’t catch the syntax errors in this section:

?- all_completed(alvin, [cs1210, cs2240].
false.

?- all_completed(alvin, [cs1210, cs1640, cs2100].
true .

?- all_completed(mary, []].
true .

(See above for explanation of defects.)

Summary

Claude’s responses were of a similar quality to ChatGPTs, but were better organized and more concise.

It’s interesting that neither model detected errors in one code snippet provided in the specification!

My revisions

While I asked ChatGPT and Claude to review and comment, I did not ask them to rewrite or revise the assignment. I revised the assignment myself, based on their feedback. In this way, I retain control over the exercise, authorship, and voice.

Here are the revisions. Add the following:

Write, consult, test with queries

Write facts and rules in a .pl file and test at a SWI-Prolog prompt. Then consult the file with

?- [enrollment].

If you make changes to your .pl file you can update Prolog’s memory with

?- make.

Don’t forget the .

Prolog facts, rules, and queries end with .

Case

Unlike many other languages, Prolog requires lower-case for atoms and variables start with an upper-case letter. For example, we write facts like this.

blue(sky).             % these are facts 
green(grass).

Here are rules which make use of a variable, X:

happy(X) :-             % X is a variable
    early_to_bed(X),
    early_to_rise(X).

and revise the findall/3 section to include an example.

findall/3

Recall that findall/3 is a three-place predicate. The third argument is the variable name of the returned list. This is convenient when you wish to pass a list constructed by findall/3 to another predicate downstream (which you will do in this assignment).

Example:

?- findall(X, happy(X), HappyPeople).

This finds all X which satisfy the predicate happy(X), returning the result in a list named HappyPeople.

I fixed the syntax errors on my own, since neither Claude nor ChatGPT detected them.

What if I were to ask an LLM to write the assignment for me?

The prompt

Prompt


Please write an active learning exercise (in Markdown format) for a first course in artificial intelligence. Students have already seen predicate and first order logic (in class and in readings) and have seen an introductory live coding demo for Prolog. The second live coding example involves writing predicates which use list construction and deconstruction and recursion to generate lists of natural numbers in descending order, _e.g.-, [5, 4, 3, 2, 1]. This exercise is the first actual hands-on experience students will have with Prolog. Should be the first part in a two part exercise (the second part will involve adding additional constraints and producing a finished Prolog program). The assignment should be centered around a course registration system including students, courses with prerequisites, and courses completed by students. It should make use of recursion and findall/3 for determining whether or not a student may enroll in a course, given the courses they’ve completed and the prerequisites in force. The goal is to produce a program which, when consulted would allow us to ask if a student can register for a particular course, e.g., ?- can_register(jane, cs2250).

ChatGPT’s response

ChatGPT’s response


Assignment:

Active Learning Exercise: Introduction to Prolog Programming with Lists and Facts

Objective

Students will write a Prolog program to manage and query facts about students, course enrollments, and prerequisites. By the end of this exercise, students will:

  1. Understand how to define and query facts in Prolog.
  2. Use list construction, deconstruction, and recursion to manipulate data.
  3. Write a predicate can_enroll(Student, Course) to determine if a student meets the prerequisites for a course.

Part 1: Setting Up Facts and Basic Queries

Step 1: Define the Problem Domain

Write the following facts in Prolog to model the domain:

  • Students: Who the students are.
  • Enrollments: What courses students are currently enrolled in.
  • Prerequisites: Which courses are prerequisites for other courses.

For example:

prolog % Facts about students student(alice). student(bob). student(charlie).

% Facts about course enrollments enrolled(alice, cs101). enrolled(bob, cs102).

% Facts about course prerequisites prerequisite(cs101, cs201). prerequisite(cs201, cs301).

Exercise Task:

  1. Add at least three more students, enrollments, and prerequisites to the knowledge base.
  2. Write queries to:
    • Check if a specific student is enrolled in a specific course.
      Example: enrolled(alice, cs101).
    • List all courses a specific student is enrolled in. Example: enrolled(bob, Course).

Part 2: Writing Recursive Predicates

Step 2: Implement a Recursive Predicate

Write a predicate prerequisite_chain(Course, PrereqList) that generates a list of all prerequisites for a given course in descending order of dependency. For example:

prolog ?- prerequisite_chain(cs301, PrereqList). PrereqList = [cs201, cs101].

Hint: Use recursion to traverse the prerequisite/2 facts.

Exercise Task:

  1. Write the prerequisite_chain/2 predicate.
  2. Test it with queries for different courses.

Part 3: Defining the can_enroll/2 Predicate

Step 3: Implement the Core Predicate

Write the predicate can_enroll(Student, Course) to determine if a student can enroll in a given course. The predicate should:

  1. Check if the student is enrolled in all the prerequisites for the course.
  2. Use prerequisite_chain/2 to find the list of prerequisites.

Example Usage:

prolog ?- can_enroll(alice, cs201). true.

?- can_enroll(bob, cs301). false.

Hint: Use list processing to check if all prerequisites are satisfied. Use recursion to compare the list of a student’s enrollments with the list of prerequisites.


Deliverables

  1. A Prolog file containing:
    • Facts about students, enrollments, and prerequisites.
    • The prerequisite_chain/2 predicate.
    • The can_enroll/2 predicate.
  2. Test queries demonstrating the functionality of each predicate.

Follow-Up Discussion

In preparation for Part 2 of the exercise, consider the following: - What happens if the same course is listed as a prerequisite for multiple courses? - How would you extend the can_enroll/2 predicate to add additional constraints, such as a student’s GPA or credit limit?


This hands-on exercise encourages students to practice basic Prolog syntax, recursion, and logical reasoning while working toward a practical goal.

Commentary on the exercise written by ChatGPT

This looks and feels generic and uses the names commonly used in examples on-line (which I try to avoid). It is not tailored as well to the students in the course, and leaves out many details I think are important. It leaves out many of the suggestions that ChatGPT made on the human-written assignment, and leaves out many things that were considered positive in review of the human-written assignment.

ChatGPT did come up with something unexpected, that is, what it refers to as a “prerequisite chain.” While this is interesting, it’s neither germaine nor correct. A course could have multiple prerequisites, and prerequisites might not form a linear chain.

Overall, the responses from LLMs here suggest that LLMs work better as guides or advisors, rather than authors, at least in this context. While it is possible that the generated assignment could be improved with additional prompt engineering, it’s unlikely it would produce an assignment of similar quality as the human-written assignment in less time. That is, it’s probably more efficient to write the assignment oneself, rather than go through a lengthy, iterative approach which does not guarantee an equivalent result.

Claude’s response

Claude’s response


Active Learning Exercise: Building a Course Registration System in Prolog

Part 1: Foundations with Lists and Recursion

Learning Objectives

By the end of this exercise, you will be able to: - Write basic Prolog facts and rules - Manipulate lists using construction and deconstruction - Implement recursive predicates for list processing - Use the findall/3 predicate for collecting solutions - Model a simple academic domain in Prolog

Prerequisites

  • Understanding of predicate logic and first-order logic
  • Familiarity with basic Prolog syntax from previous demo
  • Access to a Prolog interpreter (SWI-Prolog recommended)

Background

We’ll be building a course registration system that will eventually determine if students can register for courses based on completed prerequisites. In this first part, we’ll focus on the fundamental Prolog concepts needed to build this system.

Exercise Tasks

Task 1: Representing Basic Facts

  1. Create facts to represent students and courses. Start with this template:

    % student(StudentName)
    student(jane).
    student(john).
    % Add 3 more students
    
    % course(CourseCode)
    course(cs1100).
    course(cs2250).
    % Add 3 more courses
  2. Create facts to represent which courses students have completed:

    % completed(StudentName, CourseCode)
    completed(jane, cs1100).
    % Add at least 5 more completed course facts

Task 2: List Processing Fundamentals

  1. Write a predicate has_completed/2 that checks if a student has completed a specific course:

    % has_completed(StudentName, CourseCode)
    % Example usage:
    % ?- has_completed(jane, cs1100).
    % true.
  2. Write a predicate count_courses/2 that counts how many courses a student has completed:

    % count_courses(StudentName, Count)
    % Hint: Use findall/3 to collect all completed courses

Task 3: Working with Course Lists

  1. Write a predicate get_completed_courses/2 that returns a list of all courses a student has completed:

    % get_completed_courses(StudentName, CourseList)
    % Example usage:
    % ?- get_completed_courses(jane, Courses).
    % Courses = [cs1100, cs2250].
  2. Write a predicate courses_in_list/2 that checks if a course appears in a list of courses:

    % courses_in_list(Course, CourseList)
    % Use recursion!

Task 4: Putting It All Together

  1. Create a predicate has_completed_all/2 that checks if a student has completed all courses in a given list:

    % has_completed_all(StudentName, CourseList)
    % Should use recursion and your previous predicates

Check Your Understanding

  1. What happens when you query get_completed_courses/2 for a student who hasn’t completed any courses?
  2. How does findall/3 work in your count_courses/2 predicate? What would happen if you used bagof/3 instead?
  3. Why is recursion necessary for processing lists in Prolog?

Debug Corner

Common issues to watch for: - Forgetting to handle empty list base cases - Incorrect list construction in recursive cases - Missing cuts (!) where needed - Improper use of findall/3

Preparation for Part 2

In Part 2, we will: - Add course prerequisites using new predicates - Implement the final can_register/2 predicate - Handle complex prerequisite chains - Add validation rules for registration

Save your working code – you’ll build on it in Part 2!

Testing Your Code

Test your implementation with these queries:

?- get_completed_courses(jane, C).
?- count_courses(john, N).
?- has_completed_all(jane, [cs1100, cs2250]).

Submission Guidelines

Submit a single Prolog file containing: 1. All the basic facts (students, courses, completions) 2. All implemented predicates with comments explaining their purpose 3. Example queries and their expected results as comments 4. Answers to the “Check Your Understanding” questions as comments

Hint Section

  • When working with findall/3, remember its template:

    findall(What_to_collect, Goal_to_satisfy, List_of_results)
  • For recursive predicates, always start by writing your base case(s)

  • Test each predicate individually before combining them

Commentary on the exercise written by Claude

This is somewhat better than the result from ChatGPT. While it still has a generic feel to it, it is better structured, it lists target learning outcomes, and breaks the activity down into nice sized chunks.

Some nice things:

  • Reminders about base cases for recursion (which, alas, always bears repeating)
  • “Check your understanding” questions
  • Hint section

There are some issues:

  • Claude included a little detour with “Write a predicate count_courses/2 that counts how many courses a student has completed.” This is not necessary for the task at hand.

  • At the stage in Prolog learning we’re at for this assignment, students haven’t seen cuts (with !) and cuts are not necessary for any of the tasks in this exercise.

While this is not an adequate specification, it could serve as a starting point, but it would require substantial additions and revisions.

Reuse