This project is another learning exercise that explores the idea of using LLM to perform high level planning using available plugin or "functions" to tackle more complex tasks. For example, LLM could not write a long story with multiple chapters and illutrations with a single prompt. But it could be given a list of functions, and reason the usefulness of these functions in relation to its goal, and figure out how to compose these functions together to achieve the goal. I got this idea from a cursory look of Microsoft Semantic Kernel. I want to explore the idea and implemntation of using LLM to plan and compose functions on my own, so I took a few hours to flesh out this demo. Now I can go look at Semantic Kernel source and compare how our approaches differ (or not). :-)
In my implementation, I have a set of functions that each has some self-describing metadata for the planner to reason its usefulness. Each function contains a prompt to drive LLM to perform a small unit of test that can be done within a single prompt, like plan out chapters of a book or write a small portion of a larger story. The planner would be given a goal and a set of available functions, and it will produce a mini-program which makes use of available functions and the data flow between these functions. Finally, I have a simple code interpreter that runs this program and gather the results.
Overall, the LLM is very capable of making use of functions and reason how to compose them in service of a larger goal.
List<Function> functions = new()
{
new PlanBookChaptersFunction(),
new WriteBookChapterFunction(),
new CreateIllustrationPromptFunction(),
new CreateIllustrationFunction()
};
Planner planner = new(
"create a children's story book about a genius dog " +
"saving all the pizza in the wrold from being kidnapped by evil professor cat " +
"with 3 chapters, each chapter should contain 3 acts and " +
"with illustration for each chapter based on chapter synopsis.",
functions);
string result = await planner.RunAndGetResults();
This is the mini program generated by the planner and could be run by the interpreter.
# Chapter 1
chapters = PlanBookChaptersFunction(bookTheme: "Chef Cat", chapterCount: 3)
chapter1 = chapters[0]
print(chapter1.name)
chapter1Content = WriteBookChapterFunction(bookTheme: "Chef Cat", chapterName: chapter1.name, actCount: 3)
print(chapter1Content)
chapter1Prompt = CreateIllustrationPromptFunction(synopsis: chapter1.synopsis)
chapter1Image = CreateIllustrationFunction(prompt: chapter1Prompt)
print(chapter1Image)
# Chapter 2
chapter2 = chapters[1]
print(chapter2.name)
chapter2Content = WriteBookChapterFunction(bookTheme: "Chef Cat", chapterName: chapter2.name, actCount: 3)
print(chapter2Content)
chapter2Prompt = CreateIllustrationPromptFunction(synopsis: chapter2.synopsis)
chapter2Image = CreateIllustrationFunction(prompt: chapter2Prompt)
print(chapter2Image)
# Chapter 3
chapter3 = chapters[2]
print(chapter3.name)
chapter3Content = WriteBookChapterFunction(bookTheme: "Chef Cat", chapterName: chapter3.name, actCount: 3)
print(chapter3Content)
chapter3Prompt = CreateIllustrationPromptFunction(synopsis: chapter3.synopsis)
chapter3Image = CreateIllustrationFunction(prompt: chapter3Prompt)
print(chapter3Image)