Extending string substitutions in bot replies (NLG)

Is there a way to supply a list of string substitutions that will be used across the board when composing bot replies? I’ve had a couple of attempts at this and have some partial solutions that I’m unhappy with.

What I’m aiming for is to be able to do simple string replacements (let’s use a mailto link for example) in the bot’s utterances as listed in the domain yaml file and in any text output from calls to utter_message() and utter_template() from custom actions.

So, in the domain file I’d have something like this…

    - text: "Our email address is {_email_link}"
    - text: "Send email to {_email_link}"

…rather than embedding the email link text right in the string.

I’ve achieved this in two ways already - but both of them are far from an ideal solution.

Method A: define an unfeaturized slot with a default value…

        type: unteaturized
        initial_value: "mailto:someone@mycompany.com"

This works fine and produces output with {_email_link} suitably substituted, but requires re-training when any substitution value changes.

Method B: Use a custom version of the IO channel.

I’ve tweaked a local version of the socketIO channel to make the substitutions before emitting the reply to the user.

Again, this works - but it’s limited to the one channel and fails to make use of the existing templating code in the Templated NLG.

So now I’m thinking that I need to either define my own NLG generator - or find a way to coerce the existing templated generator to take a list of extra arguments.

I like the idea of using the existing templated NLG for this but am having trouble seeing how a list of additional replacements may be passed to it.

Has anyone else tried to solve this kind of problem before? If so, how did you address it? Open to suggestions.

Thanks in advance for considering this!

Hi Steve,

Thanks for the post! I can definitely see the use case for this. The simplest solution is to add this information as an unfeaturized slot with the default value you want, e.g.:

    type: unfeaturized
    initial_value: "mailto:hello@me.com"

More generally you could implement a custom nlg_server.py

and tell your endpoints.yml where to find that server.

Thank you for the reply Alan.

Using an unfeaturized slot was my first attempt to solve this. Whilst it did work, it had several drawbacks that made it unsuitable for my use…

  • it required re-training on changes
  • it adds essentially unneeded information to the slots in the tracker
  • potentially looses initial values on /reset? (I didn’t actually verify this)

I decided against a custom NLG endpoint when I looked at the code in dispatcher.py. The NLG layer only seems to be called by utter_template and the deprecated utter_button_template method. I need a solution that also works for utter_message and utter_response and utter_template.

In the end, I chose to run a modified dispatcher.py that applies the substitutions to any utter_ method.

1 Like

@netcarver How did you solve it? What I’m trying to achieve is to pass some additional information to the main code regarding the components to be used: text_msg, buttons, quick_reply, etc. I tried sending additional keys from the custom_action file, but I can only access the response key and all other keys are lost, now I’m trying to build custom NLG which isn’t the suitable method, any suggestions on how I can go about solving this?

@rohitnairtech Hi Rohit

I updated my local dispatcher.py file and had each utter_ method call through to a common method that looks for {variable_name} markers in the utterance and substitutes them from a lookup table.

It’s not an ideal solution as I now have to maintain that code going forward.