microsoft / semantic-kernel

Integrate cutting-edge LLM technology quickly and easily into your apps
https://aka.ms/semantic-kernel
MIT License
21.87k stars 3.26k forks source link

.NET Sequential Planner: Invalid plan XML caused by single quotes in function payload #2246

Closed samleegithub closed 1 year ago

samleegithub commented 1 year ago

Describe the bug Error parsing plan XML due to single quotes in function payload.

Using GPT-4 in Azure OpenAI Service with Chat Copilot, Sequential Planner and CodeSherpa plugin: https://github.com/iamgreggarcia/codesherpa

fail: Microsoft.SemanticKernel.IKernel[0]
      Something went wrong while executing the native function. Function: <GetDelegateInfo>b__0. Error: Create plan error: Unable to create plan
      Microsoft.SemanticKernel.Planning.PlanningException: Create plan error: Unable to create plan
       ---> Microsoft.SemanticKernel.Planning.PlanningException: Invalid plan: Failed to parse plan xml strings: '<plan>
    <!-- The user wants to plot the function sin(x)/x. We can use the repl_repl_post function to execute the Python code for plotting. -->
    <function.codesherpaPlugin.repl_repl_post server_url="http://localhost:3333/" payload='{"code": "import matplotlib.pyplot as plt\nimport numpy as np\nx = np.linspace(-10, 10, 1000)\ny = np.sinc(x)\nplt.plot(x, y)\nplt.savefig('sinx_over_x.png')"}' content_type="application/json" setContextVariable="PLOT_RESULT"/>
    <!-- After plotting, we need to serve the image file. We can use the command_endpoint_command_post function to move the image file to the static/uploads directory. -->
    <function.codesherpaPlugin.command_endpoint_command_post server_url="http://localhost:3333/" payload='{"command": "mv sinx_over_x.png static/uploads/"}' content_type="application/json" setContextVariable="MOVE_RESULT"/>
</plan>' or '<plan>
    <!-- The user wants to plot the function sin(x)/x. We can use the repl_repl_post function to execute the Python code for plotting. -->
    <function.codesherpaPlugin.repl_repl_post server_url="http://localhost:3333/" payload='{"code": "import matplotlib.pyplot as plt\nimport numpy as np\nx = np.linspace(-10, 10, 1000)\ny = np.sinc(x)\nplt.plot(x, y)\nplt.savefig('sinx_over_x.png')"}' content_type="application/json" setContextVariable="PLOT_RESULT"/>
    <!-- After plotting, we need to serve the image file. We can use the command_endpoint_command_post function to move the image file to the static/uploads directory. -->
    <function.codesherpaPlugin.command_endpoint_command_post server_url="http://localhost:3333/" payload='{"command": "mv sinx_over_x.png static/uploads/"}' content_type="application/json" setContextVariable="MOVE_RESULT"/>
</plan>'
       ---> System.Xml.XmlException: 'sinx_over_x.png' is an unexpected token. Expecting whitespace. Line 3, position 232.
         at System.Xml.XmlTextReaderImpl.Throw(Exception e)
         at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
         at System.Xml.XmlTextReaderImpl.ParseAttributes()
         at System.Xml.XmlTextReaderImpl.ParseElement()
         at System.Xml.XmlTextReaderImpl.ParseElementContent()
         at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
         at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
         at System.Xml.XmlDocument.Load(XmlReader reader)
         at System.Xml.XmlDocument.LoadXml(String xml)
         at Microsoft.SemanticKernel.Planning.Sequential.SequentialPlanParser.ToPlanFromXml(String xmlString, String goal, Func`3 getSkillFunction, Boolean allowMissingFunctions)
         --- End of inner exception stack trace ---
         at Microsoft.SemanticKernel.Planning.Sequential.SequentialPlanParser.ToPlanFromXml(String xmlString, String goal, Func`3 getSkillFunction, Boolean allowMissingFunctions)
         at Microsoft.SemanticKernel.Planning.SequentialPlanner.CreatePlanAsync(String goal)
         --- End of inner exception stack trace ---
         at Microsoft.SemanticKernel.Planning.SequentialPlanner.CreatePlanAsync(String goal)
         at SemanticKernel.Service.CopilotChat.Skills.ChatSkills.ExternalInformationSkill.AcquireExternalInformationAsync(String userIntent, SKContext context) in C:\Users\samulee\Documents\Microsoft Projects\ITF\chat-copilot\webapi\CopilotChat\Skills\ChatSkills\ExternalInformationSkill.cs:line 126
         at SemanticKernel.Service.CopilotChat.Skills.ChatSkills.ChatSkill.AcquireExternalInformationAsync(SKContext context, String userIntent, Int32 tokenLimit) in C:\Users\samulee\Documents\Microsoft Projects\ITF\chat-copilot\webapi\CopilotChat\Skills\ChatSkills\ChatSkill.cs:line 565
         at SemanticKernel.Service.CopilotChat.Skills.ChatSkills.ChatSkill.GetChatResponseAsync(String chatId, String userId, SKContext chatContext) in C:\Users\samulee\Documents\Microsoft Projects\ITF\chat-copilot\webapi\CopilotChat\Skills\ChatSkills\ChatSkill.cs:line 346
         at SemanticKernel.Service.CopilotChat.Skills.ChatSkills.ChatSkill.ChatAsync(String message, String userId, String userName, String chatId, String messageType, String planJson, String messageId, SKContext context) in C:\Users\samulee\Documents\Microsoft Projects\ITF\chat-copilot\webapi\CopilotChat\Skills\ChatSkills\ChatSkill.cs:line 298
         at Microsoft.SemanticKernel.SkillDefinition.SKFunction.InvokeAsync(SKContext context, CompleteRequestSettings settings)
fail: Microsoft.SemanticKernel.IKernel[0]
      Function call fail during pipeline step 0: ChatSkill.Chat. Error: Create plan error: Unable to create plan

To Reproduce Steps to reproduce the behavior:

  1. Setup CodeSherpa plugin as docker instance on local machine. Follow instructions in CodeShera repo: https://github.com/iamgreggarcia/codesherpa
  2. Add and enable CodeSherpa plugin in Chat Copilot app
  3. Ask chat copilot to plot sin(x)/x and save the plot as a file.
  4. See error

Expected behavior

Screenshots N/A

Platform

Additional context N/A

evchaki commented 1 year ago

@samleegithub - thank you for bringing this up. We are looking at ways to make sure if the LLM returns incorrectly formatted results that we can handle and fix it.

lemillermicrosoft commented 1 year ago

@samleegithub -- You can supply a custom prompt to SequentialPlanner. Could you try using the defined skprompt.txt with these changes applied to see if it helps in your scenario?

Image

samleegithub commented 1 year ago

@samleegithub -- You can supply a custom prompt to SequentialPlanner. Could you try using the defined skprompt.txt with these changes applied to see if it helps in your scenario?

Image

Hi @lemillermicrosoft,

I am using chat copilot and I'm not sure how to edit the skprompt.txt for the sequential planner. Could you point me in the right direction?

Thanks, Sam

lemillermicrosoft commented 1 year ago
Create an XML plan step by step, to satisfy the goal given, with the available functions.

[AVAILABLE FUNCTIONS]

{{$available_functions}}

[END AVAILABLE FUNCTIONS]

To create a plan, follow these steps:
0. The plan should be as short as possible.
1. From a <goal> create a <plan> as a series of <functions>.
2. A plan has 'INPUT' available in context variables by default.
3. Before using any function in a plan, check that it is present in the [AVAILABLE FUNCTIONS] list. If it is not, do not use it.
4. Only use functions that are required for the given goal.
5. Append an "END" XML comment at the end of the plan after the final closing </plan> tag.
6. Always output valid XML that can be parsed by an XML parser.
7. If a plan cannot be created with the [AVAILABLE FUNCTIONS], return <plan />.

All plans take the form of:
<plan>
    <!-- ... reason for taking step ... -->
    <function.{FullyQualifiedFunctionName} ... />
    <!-- ... reason for taking step ... -->
    <function.{FullyQualifiedFunctionName} ... />
    <!-- ... reason for taking step ... -->
    <function.{FullyQualifiedFunctionName} ... />
    (... etc ...)
</plan>
<!-- END -->

To call a function, follow these steps:
1. A function has one or more named parameters and a single 'output' which are all strings. Parameter values should be xml escaped.
2. To save an 'output' from a <function>, to pass into a future <function>, use <function.{FullyQualifiedFunctionName} ... setContextVariable="<UNIQUE_VARIABLE_KEY>"/>
3. To save an 'output' from a <function>, to return as part of a plan result, use <function.{FullyQualifiedFunctionName} ... appendToResult="RESULT__<UNIQUE_RESULT_KEY>"/>
4. Use a '$' to reference a context variable in a parameter, e.g. when `INPUT='world'` the parameter 'Hello $INPUT' will evaluate to `Hello world`.
5. Functions do not have access to the context variables of other functions. Do not attempt to use context variables as arrays or objects. Instead, use available functions to extract specific elements or properties from context variables.

DO NOT DO THIS, THE PARAMETER VALUE IS NOT XML ESCAPED:
<function.Name4 input="$SOME_PREVIOUS_OUTPUT" parameter_name='some value with a <!-- 'comment' in it-->'/>

DO NOT DO THIS, THE PARAMETER VALUE IS ATTEMPTING TO USE A CONTEXT VARIABLE AS AN ARRAY/OBJECT:
<function.CallFunction input="$OTHER_OUTPUT[1]"/>

Here is a valid example of how to call a function "_Function_.Name" with a single input and save its output:
<function._Function_.Name input="this is my input" setContextVariable="SOME_KEY"/>

Here is a valid example of how to call a function "FunctionName2" with a single input and return its output as part of the plan result:
<function.FunctionName2 input="Hello $INPUT" appendToResult="RESULT__FINAL_ANSWER"/>

Here is a valid example of how to call a function "Name3" with multiple inputs:
<function.Name3 input="$SOME_PREVIOUS_OUTPUT" parameter_name='some value with a &lt;!-- &apos;comment&apos; in it--&gt;'/>

Begin!

<goal>{{$input}}</goal>

This was copied from this https://[raw.githubusercontent.com/microsoft/semantic-kernel/cecad59b101ce035177cbeeb1c8047691d14287b/dotnet/src/Extensions/Planning.SequentialPlanner/skprompt.txt](https://raw.githubusercontent.com/microsoft/semantic-kernel/cecad59b101ce035177cbeeb1c8047691d14287b/dotnet/src/Extensions/Planning.SequentialPlanner/skprompt.txt)

In chat copilot, where SequentialPlanner is initialized, you can pass this in as the prompt:

new SequentialPlanner(kernel, prompt: prompt);

cc @samleegithub