Wednesday, October 29, 2014

Praise for Siri and a quick (and surprisingly satisfying) mobile version of Eliza

My friend (and famous author!) Christian Cantrell posted a touching story on Google Plus: To Siri, With Love: How One Boy With Autism Became BFF With Apple’s Siri. It's more than worth your time to read. However, a quick take on it is it that Siri has a number of attributes that make it an excellent companion for the author's Autistic Son(for example: Siri doesn't mind talking minutia for hours, and her gentle corrections help teach important social skills).

Besides this being another see, technology can be a force for good in people's lives! article, it got me thinking about chat bots in general. And no discussion of chat bots is complete without a mention of Eliza, Siri's great-great-great-great-great grandmother. Eliza was a "doctor" who could psychoanalyze you using little more than a set of text matching rules. Still, it managed to give the impression of true intelligence.

With Eliza on the brain, I started wondering how tricky it would be to implement an Eliza clone for my phone. Turns out, not tricky at all. Here's what I did.

First, I cheated and grabbed this implementation of Eliza in Scheme. Yes, I should have written my own. Heck one day, I probably will.

Next, I wrote some wrapper functions around that code to make it accept arbitrary string input:

(define random random-integer)

(define (eliza-scrub text)
 (define (valid-char? c)
  (let ((v (char->integer c)))
   (or (equal? c #\space)
       (and (>= v 97) (<= v 122)))))
 (let* ((chars (string->list text))
        (lower (map char-downcase chars))
        (valid (filter valid-char? lower)))
  (apply string valid)))
  
  
(define (eliza-it text)
 (let ((input (map string->symbol (explode " " (eliza-scrub text)))))
  (implode " "
           (apply-rule ELIZA-RULES input))))

I then busted out my web-apply framework, and attached this function to the local URL: http://localhost:9000/doc:

 (tcp-service-register!
   (list server-address: "*"
    port-number: 9000
    eol-encoding: 'cr-lf)
  (web-fn-dispatcher `(("/doc" . (,eliza-it #t)))))

At this point, I could visit the URL in my browser and have a crude discussion with Eliza. But it's hardly the feel I was after. Next up, I turned to Tasker and created this quick Task:

Eliza (40)
A1: Get Voice [ Title:Talk to the Doctor Language ]
A2: If [ %VOICE eq bye ]
  A3: Say [ Text:Good bye ]
A4: Else
  A5: HTTP Get [ Server:Port:http://localhost:9000
                 Path:/doc
                 Attributes:p0=%VOICE output=display ]
  A6: Say [ Text:%HTTPD ]
  A7: Goto [ Type:Action Number Number:1 ]

The magic is in the Tasker action Get Voice. This prompts a user to say something which is then turned into text and stored in %VOICE. The action Say does the reverse, taking arbitrary text and speaking it aloud. Finally, there's the web invocation of http://localhost:9000/doc which actually executes the above Eliza code.

While the above code is more fragile than I'd like (you need to explicitly bind the the eliza-it function to a port and path), it's also surprisingly effective. The whole experience is voice driven and feels remarkably powerful. It definitely brings back the magic of Eliza that had secretaries and staff confiding in it so long ago.

1 comment: