Using ERG for a Natural Language Interface Flow

This is the overview of how I’ve built the Perplexity Prototype which is a Natural Language “Proof of Concept” game. The goal of the prototype was to see if I could build a system that truly understood and acted upon every single word and construction in phrases the player gives it (i.e. deep semantic analysis) so that it really does seem like the computer understands what is being said. I wanted to avoid the common experience of having the computer “cherry-pick keywords” or try to get the “gist” of a phrase and do something that is not quite right. I wanted to see how far current technologies could get me.

You can decide for yourself whether or not the prototype was successful by running it here.

To build it, I took the output of the Delph-In English Resource Grammar and executed it in SWI-Prolog with some Python glue in-between. To fully understand what I’ve done, you’ll need to have at least a passing understanding of the MRS (Minimal Recursion Semantics) format, the Prolog Programming Language, and Hierarchical Task Networks (HTN). That last one is only necessary to get really deep into how some of the engine is implemented. An understanding of Python is only necessary if you want to understand the sample code itself. It isn’t necessary for understanding the conceptual flow.

This page serves as an overview of the whole process but each of the steps has a drill-down that gives more detail.

The Flow of Execution

The flow of execution starts with the player typing a phrase like "Every book is in a cave":

MRS for `Every book is in a cave`:

[ TOP: h0
INDEX: e2
RELS: < 
[ _a_q__xhh LBL: h10 ARG0: x9 [ x PERS: 3 NUM: sg IND: + ] RSTR: h11 BODY: h12 ]
[ _cave_n_1__x LBL: h13 ARG0: x9 [ x PERS: 3 NUM: sg IND: + ] ]
[ _every_q__xhh LBL: h4 ARG0: x3 [ x PERS: 3 NUM: sg IND: + ] RSTR: h5 BODY: h6 ]
[ _book_n_of__xi LBL: h7 ARG0: x3 [ x PERS: 3 NUM: sg IND: + ] ARG1: i8 ]
[ _in_p_loc__exx LBL: h1 ARG0: e2 [ e SF: prop TENSE: pres MOOD: indicative PROG: - PERF: - ] ARG1: x3 ARG2: x9 ]
>
HCONS: < h0 qeq h1 h5 qeq h7 h11 qeq h13 > ]
The two scope-resolved trees for the above MRS:

                       ┌_book_n_of__xi:x3,i8
 _every_q__xhh:x3,h5,h6┤
                       │                    ┌_cave_n_1__x:x9
                       └_a_q__xhh:x9,h11,h12┤
                                            └_in_p_loc__exx:e2,x3,x9

                     ┌_cave_n_1__x:x9
 _a_q__xhh:x9,h11,h12┤
                     │                      ┌_book_n_of__xi:x3,i8
                     └_every_q__xhh:x3,h5,h6┤
                                            └_in_p_loc__exx:e2,x3,x9
The "naive" and "final" Prolog transformations for first of the above trees:

naive: 
_a_q__xhh(
    x9, 
    _cave_n_1__x(x9), 
    _every_q__xhh(
        x3, 
        _book_n_of__xi(x3, i8), 
        _in_p_loc__exx(e2, x3, x9)))

final: 
erg(1, d_a_q__xhh(
    arg(X9104, var([name(x9), type(reg), pers(3), num(sg)])), 
    arg(erg(2, d_noun__x(arg('cave', word('cave')), 
        arg(X9104, var([name(x9), type(reg), pers(3), num(sg)])))), term), 
    arg(erg(3, d_every_q__xhh(
        arg(X9106, var([name(x3), type(reg), pers(3), num(sg)])), 
        arg(erg(4, d_noun__xi(
            arg('book', word('book')), 
            arg(X9106, var([name(x3), type(reg), pers(3), num(sg)])), 
            arg(i, term))), term), 
        arg(erg(5, d_in_p_loc__exx(
            arg(E9107, var([name(e2), index(yes), tense(pres)])), 
            arg(X9106, var([name(x3), type(reg), pers(3), num(sg)])), 
            arg(X9104, var([name(x9), type(reg), pers(3), num(sg)])), 
            arg(create, var([type(quote)])))), term), 
        arg(Quote9110, var([type(quote)])))), term), 
    arg(Quote9108, var([type(quote)])))).