사람인 듯한 ....

(Appearing Human)

 

C 인공지능 프로그래밍 : Herbert Schildt 지음, 신경숙.류성렬 옮김, 세웅, 1991 (원서 : Artificial Intelligence using C, McGraw-Hill, 1987), page 405~435

 

1. 속임수 ? (TRICKERY ?)

2. 얼마만큼 좋은가? (WHAT GOOD IS IT ?)

3. 사람 대 기계 (HUMAN VERSUS MACHINE)

  3.1. 기계 감정 (Machine Emotions)

  3.2. 성격 (Personality)

4. 의사에게 진찰 받기 (GOING TO THE DOCTOR)

  4.1. 최초의 ELIZA 프로그램이 작동시킨 방법 (How the Original ELIZA Program Worked)

  4.2. 간단한 최초의 버전 (A Simple First Version)

  4.3. 의사 프로그램 개선 (Improving the Doctor Program)

  4.4. 성격과 감정을 첨가하기 (Adding Some Personality and Emotion)

  4.5. 의사 프로그램 확장하기 (Expanding the Doctor Program)

5. 인간다운 컴퓨터의 암시 (IMPLICATIONS OF HUMANLIKE COMPUTERS)

 

이 장에서는 사람처럼 행동하는 프로그램을 작성하는 것을 다룬다. 이런 유형의 프로그램의 전통적인 예는 1 장에서 언급된 ELIZA 프로그램인데, 이것은 Rogerian 정신과 의사처럼 행동한다. 이 장에서는 컴퓨터 프로그램으로 하여금 성격과 감정을 갖는 것처럼 만드는 방법을 조사하고 ELIZA 유형의 프로그램에 대한 세 가지 간단한 버전을 개발한다.

아마도 단지 스크린과 키보드만 갖고 있을 것이기 때문에, 동작되는 순서 (operating scenario) 는 다음과 같다. 두 대의 컴퓨터 터미널들이 나란히 놓여 있다고 상상하자. 첫 번째 것은, 옆방에 놓여 사람 오퍼레이터가 있는 또 다른 터미널에 연결되어 있다. 반면에 두 번째 터미널은 컴퓨터에 연결되어 있다. 컴퓨터와 사람은 둘 다 타이프하는 어떤 것에도 반응할 것이다. 만약 어느 터미널의 컴퓨터에 연결되어 있고 어느 터미널이 사람 오퍼레이터에 연결되어 있는지 알 수 없다면, 컴퓨터에서 수행되는 프로그램은 사람처럼 행동하는 데에 성공했을 것이다.

1. 속임수 ? (TRICKERY ?)

원래의 ELIZA 프로그램이 기능을 하는 방법은 진지한 AI 연구와 거의 관계가 없다는 것과, 지능적인 것처럼 보이는 것은 기술 (trick) 에 기초한 것이지, 일반화 될 수 있는 확고한 원리에 기초한 것이 아니라는 것을 많은 사람들이 말한다. 그러나, 이 비평가들은 프로그램에 대한 아마도 가장 중요한 것을 잊고 있다 : ELIZA 는 실제로 사람과 구별할 수가 없다.

볼 것처럼, ELIZA 유형의 프로그램은 형식적 자연언어 처리 기법들 중 어떤 것도 필요로 하지 않는다. (그러나, 요구되면 사용할 수도 있다). 사실상, 이러한 종류의 보통 프로그램은 다소 어리석다 : 무엇이 진행하고 있는지에 대하여 거의 모른다. 오히려, 그것의 반응은 단순히 미리 준비된 대답이다. 그러나, ELIZA 유형의 프로그램에 대하여 재미있는 것은 그 프로그램들이 매우 성공적으로 사람 흉내를 낼 수 있다는 것이다. 이 장에서 주로 강조를 할 것은 바로 이런 면이다.

2. 얼마만큼 좋은가? (WHAT GOOD IS IT ?)

어떤 사람들은 컴퓨터로 하여금 사람처럼 보이게 하는 방법을 연구하는 것의 가치를 묻는다. 그러나, 이런 몇몇 사람들은 한가지 중요한 점을 잊고 있다 : AI 연구를 통한 업적이 증가하고 실제 사용될 때, 컴퓨터-사용자 간의 좋은 인터페이스를 생성할 필요가 증가할 것이다. 사람들이 정말 영리한 기계를 사용하려 한다면, 사용자들은 이런 유형의 인터페이스에 가장 익숙해 있기 때문에, 이 기계들은 다소 사람처럼 행동해야 할 것이다.

이 점을 이해하기 위해서, 제 3 장에서 개발된 전문가시스템을 다시 생각하자. 알고 있듯이, 전문가시스템은 AI 의 최초의, 그리고 맨 먼저 상업적으로 생존할 수 있는 생산품이다. 그러나, 이 글에서 어떤 전문가시스템도 실제로 사람 전문가가 하는 것과 같은 방법으로 사용자와 상호작용하지 않는다. 가장 좋은 시스템에서도 신속처리 (prompting) 는 아직 과장되지 않는다.

3. 사람 대 기계 (HUMAN VERSUS MACHINE)

제 1 장에서는 프로그램이 같은 문제에 부딪친 사람과 비슷한 방식으로 작동하면 지능이 있다고 결정했다. 그러나, 지능적으로 작동하는 프로그램과 사람처럼 행동하는 프로그램 사이에 미묘한 차이가 있다는 것을 이해해야 한다. 이 차이는 감정과 성격에 기초한다 : 일반적으로 사람같은 것은 단순히 문제를 해결하는 지능을 갖는 것 이상의 것을 요구한다. 컴퓨터가 사람처럼 어떤 종류의 완전한 개성 (identity) 을 가져야 한다는 것을 의미한다. 이 의미는 다음의 재미있는 질문을 야기시킨다 :

이 질문들에 완벽하게 답하는 것은 이 책의 범위를 넘지만 이 장에서 몇 개의 설명문을 제공한다.

3.1. 기계 감정 (Machine Emotions)

컴퓨터가 인간의 행동을 완벽하게 흉내내는 능력에 대한 가장 빈번한 논쟁들 중의 하나는, 사람은 감정을 느낄 수 있지만 컴퓨터는 그렇지 못하다는 것이다. 실제로 이 논쟁은 무의미하다. 사람은 컴퓨터가 감정을 가졌는지 아닌지 알 방법이 없다. 사실상, 사람들은 자신의 감정이 실제로 무엇인지 모른다. 그러므로, 컴퓨터가 감정을 갖지 않았다고 말하는 것은 결론을 추측하는 것이다 !

감정에 대한 한가지 관점은 사람의 인지 사고 과정에 연결되는 것이 아니라, 본능에 토대를 둔다고 말한다. 본질적으로, 이 관점은 감정이란 거의 또는 전혀 제어되지 않는 힘으로부터 생겨난다는 것이다 (감정에 따라 행동하는 것을 피하는 것은 가능할 수도 있지만 감정을 느끼지 않는 것은 불가능하다). 더 중요하게, 무엇이 감정을 일으키는지 아무도 모른다. 이 추론이 참이면 비슷한 상황이 컴퓨터에서 존재할 것 같다. 컴퓨터는 또한 ROM 루틴과 운영체제 프로시저들의 형태로 "본능" 을 갖는다. 고급 프로그램 - 영리한 프로그램과 같은 - 이 컴퓨터에서 수행하고 있을 때, 저급 루틴들 중 하나 이상이 수행되게 할 수 있다. 고급 프로그램은 이 루틴들의 행동을 직접적으로는 모른다 : 사실상, 실시간 인터럽트의 경우 고급 프로그램은 그 인터럽트가 발생하는 것조차 멈출 수가 없다. 그러므로, 컴퓨터의 감정은 ROM, 운영체제 또는 지능적인 프로그램의 절차적 단편 (procedural fragment) 에서 발견되는 그러한 특성들이다. 우리가 우리의 감정을 정확히 알지 못하는 것처럼, 지능적인 컴퓨터는 그 행동에 영향을 주는 몇몇 저급 루틴들에 대하여 명확히 알지 못할 수도 있다.

그러나, 컴퓨터가 감정을 가질 수 있는지 아닌지는 문제가 되지 않는다. 왜냐하면 컴퓨터의 감정은 사람의 감정과 아주 다를 것이기 때문이다. 문제는 이것이다. 만약 컴퓨터로 하여금 사람처럼 행동하게 할 것이라면 컴퓨터로 하여금 사람의 감정과 비슷한 것을 어떻게 디스플레이하게 할 수 있는가? 컴퓨터가 해야 할 모든 것은 마치 사람과 같은 유형의 감정을 가진 것처럼 행동하는 것이다 - 실제로 그 감정을 느낄 필요가 없다. 이것은 막연히 문제에 대한 행동주의자의 접근 방식이다 : 컴퓨터가 무엇을 느끼는지는 중요하지 않다 - 단지 컴퓨터가 무엇처럼 행동하는가가 중요할 뿐이다.

이 장의 목적을 위해서, 프로그램이 단순히 감정과 비슷한 것을 암시하는 행동을 디스플레이한다면 충분할 것이다. 프로그램은 사람이 느끼는 것과 같은 방식으로 그 감정들을 느낄 필요는 없다.

3.2. 성격 (Personality)

만약 공상과학 영화의 열렬한 팬이라면, 아마도 컴퓨터는 감정이 없는 것으로 되어 있다는 것을 알 것이다. 그러나, 컴퓨터가 사람처럼 행동한다면, 어떤 종류의 성격을 가져야 할 것 같다.

성격이 사람에게서 어떻게 발달하는지는 명백하지 않다. 성격의 어떤 면들은 사람의 유전적인 성질에 있는 것 같고, 어떤 면들은 경험의 영향을 받는다. 사람의 영역의 일부가 제어할 수 없기 때문에, 컴퓨터에게 성격을 주는 한가지 받아들일 만한 해결은 단순히 반응을 통해 성격의 특성들을 나타내도록 프로그램하는 것이라고 주장 된 것 같다. 만약 행동주의자 (행동주의 (Behaviorism)) 의 관점으로 생각하면, 컴퓨터는 실제로 어떤 성격이 있어야 할 필요가 없다는 것을 알 수 있다 : 오히려 성격을 암시하는 행동을 디스플레이하기만 하면 된다. (사람들 조차도 때때로 그들의 성격에 반대되는 행동을 나타내거나 또는 "격에 맞지 않게 행동한다")

이 장에서 컴퓨터의 성격은 전적으로 컴퓨터가 다양한 입력에 반응하는 방법에 따라 생성될 것이다. 이제 알 것처럼, 컴퓨터에게 여러 가지 성격을 주는 것은 실제로 아주 쉽다.

4. 의사에게 진찰 받기 (GOING TO THE DOCTOR)

의사의 종류의 최초이 것 중 하나는 - 그리고 사람의 외모를 갖는 가장 유명한 프로그램은 - Joseph Weisenbaum 에 의해 만들어진 ELIZA 이다. 앞에서 언급했듯이, ELIZA 는 Rogerian 정신과 의사처럼 행동한다. (Rogerian 정신과 의사는 단순히 환자의 반응을 에코우 함으로써 동작한다 - 이것은 이론상 환자가 자기분석할 수 있게 한다.)

이러한 유형의 프로그램들이 현대의 컴퓨터 프로그래머들에게 길들여져 있는 것 같지만, 프로그램들은 여전히 "장사의 기술" 에 익숙하지 않은 사람에게 감명을 줄 수 있다. 이 절에서, ELIZA 같은 프로그램의 간단한 버전인 Doctor 를 개발하는 것은 이 정신에 있다.

4.1. 최초의 ELIZA 프로그램이 작동시킨 방법 (How the Original ELIZA Program Worked)

최초의 ELIZA 프로그램은 단순히 키워드 인식을 사용하여 환자의 말을 에코우 했다. 프로그램이 환자의 문장에서 "love", "hate", "want" 와 어떤 단어를 만났을 때 프로그램은 그 키워드와 관계있는 반응을 낼 것이다. 프로그램은 또한 대화가 계속 진행하기 위하여 사용할 수 있는 선택된 주제를 기억할 것이다. 마지막으로, 프로그램은 "I hate you." 와 같은 주요구를 인식해서 특정한 반응을 나타내 보일 수 있다. 다음의 대화는 일반적인 패턴을 증명한다 :

의사 : 안녕하십니까? (How are you?)

환자 : 안녕하십니까? (Fine)

의사 : 행복하십니까? (Are you happy?)

환자 : 아니오, 나는 내 자신이 밉습니다. (No, I hate my life.)

의사 : 왜 그렇죠? (Why do you hate your life?)

환자 : 아무도 나를 좋아하지 않아요. (Because no one likes me.)

의사 : 제가 좋아 하잖아요. (I like you.)

환자 : 그럴지도 모르죠. (Well, you would !)

의사 : 얘기를 계속 할까요? (But we were talking about you. Go on.)

환자 : 싫어요. (I don't want to.)

의사 : 아무도 좋아하지 않는다고 했는데, 계속 얘기해 보세요. (You said that no one likes you. Tell me more.)

이러한 유형의 대화는 환자나 사용자가 지칠 때까지 계속될 것이다. ELIZA 유형의 프로그램의 여러 버전들이 수년간에 걸쳐 개발되었다. 몇몇은 매우 잘 개발되었고 사람 정신과 의사와 구별될 수가 없다.

4.2. 간단한 최초의 버전 (A Simple First Version)

이 장에서 개발할 첫 버전은 아주 적은 일을 하고 쉽게 혼동되지만, 개선의 발판을 설정한다. 이 버전은 어떤 주제도 기억하지 않고, 환자가 키워드 yes 나 no 를 입력하지 않으면 단순히 환자의 반응을 반복한다. 이 버전은 완전히 성격이나 감정이 없다. 전체 프로그램이 여기에 있다 :

/* Doctor #1 */

char *respond[]={

    "How are you ?",

    "Did you have a happy childhood ?",

    "Did you hate your father ?",

    ""

};

char *trans[]={

    "i", "you",

    "you", "I",

    "your", "my",

    "my", "your",

    "am", "are",

    ""

};

char token[80];

char *p_pos;

int res=0;            /* index into response array */

main()

{

    char s[80];

    printf("%s\n",response[res++]);

    do {

      printf(" : ");

      p_pos=s;

      gets(s);

      if (!strcmp(s, "yes"))

        printf("Ah.... tell me more.\n");

      else if (!strcmp(s, "no"))

        printf("Tell me more. \n");

      else if (!strcmp(s, "I hate you"))

        printf("Why do you hate me?\n");

      else if (!*s) {

        if (!*response[res])  res=0;      /* start over again */

        printf("%s\n",response[res++]);

      }

      else

        respond();

    } while(strcmp(s, "bye"));

}

 

/* create the doctor's responses */

respond()

{

    char t[80];

    do {

      get_token();

      if (translate(token, t)  printf("%s ",t);

      else printf("%s ", token);

    } while(*token);

    prinft("?\n");

}

 

/* translate context from the patient to the doctor */

translate(token, tr)

char *token, *tr;

{

    int t;

    t=0;

    while(*trans[t]) {

      if(!strcmp(trans[t], token)) {

        strcpy(tr, trans[t+1]);

        return 1;

      }

      t+=2;

    }

    return 0;

}

 

/* return a token from the input stream */

get_token()

{

    char *p;

    p=token;

    /* skip spaces */

    while (*p_pos==' ') p_pos++;

    if (*p_pos=='\0') {      /* is end of input */

      *p++='\0';

      return;

    }

    if (is_in(*p_pos, ",.!?")) {

      *p=*p_pos;

      p++, p_pos++;

      *p='\0';

      return;

    }

    /*read word until */

    while(*p_pos!=' ' && !is_in(*p_pos, ".,;?!") && *p_pos) {

      *p=tolower(*p_pos++);

      p++;

    }

    *p='\0';

}

is_in(c, s)

char c, *s;

{

    while(*s) {

      if (c==*s)  return 1;

      s++;

    }

    return 0;

}

Doctor 의 이 버전을 질문으로 환자의 마지막 문장을 단순히 반복함으로써 작동한다. 함수 respond() 는 "my" 를 "your" 하는 것처럼 몇 단어의 내용을 바꾸기 위하여 trans 데이터베이스를 사용한다. 프로그램이 trans 데이터베이스에서 단어를 찾지 못하면, 현재의 단어를 단순히 다시 프린트한다. 유일한 예외는 환자가 "yes" 나 "no" 또는 "I hate you." 구절을 입력할 때 발생한다. 이런 경우에 프로그램은 특별한 반응을 낸다. 프로그램이 더 다양한 반응을 처리할 수 있도록 프로그램이 알고 있는 특별한 구와 단어를 쉽게 확장할 수 있다. ( 이 장에서는 나중에 이런 식으로 프로그램을 확장할 것이다.) 환자가 캐리지 리턴을 하면 respond() 는 반응 데이터베이스로부터 이미 있는 반응 (stock response) 을 사용할 것이다. 프로그램은 이 책 전체에서 사용되는 get_token() 함수의 약간 변형된 버전을 사용하여 환자의 반응을 분석한다.

이 버전이 생성할 대화 유형의 보기가 여기 있다. 의사는 항상 각 대화를 시작하고 프로그램은 콜론을 사용하여 환자에게 프롬프트를 내보낸다.

How are you? : I am fine

You are fine? : Yes, but my friend isn't

Yes, but your friend isn't? : He has a problem?

He has a problem? : I hate you

You hate me? :

Did you have a happy childhood? : No

Tell me more.

이 의사의 처방은 매우 지루할 것이다 ! 다음, 약간 향상시킬 방법을 볼 것이다.

4.3. 의사 프로그램 개선 (Improving the Doctor Program)

초기의 ELIZA 프로그램의 가장 중요한 특징들 (features) 중의 하나는 환자가 언급한 주제들의 trail 을 유지한다는 것이다. 대화가 주춤하기 시작할 때, 의사는 주제들중 하나를 다시 꺼내도록 환자에게 프롬프트를 내보낼 수 있다. 이 기능을 첨가하는 것은 환자의 반응을 저장할 큐를 만들 것을 요구한다. 또, 방금 주어진 간단한 Doctor 프로그램에서, 단순히 환자의 반응을 반복하는 것은 지루하고 충분히 "인간적" 이 아니다. 이를 처방하기 위해서 trans 데이터베이스는 키워드와 반응을 간직할 것이다. 프로그램이 키워드를 인식할 때마다 적당한 반응을 디스플레이 할 것이다.

방금 설명된 변화를 만드는 것은 Doctor 프로그램의 총괄적 데이터와 정의를 재구성하고 확장할 것을 요구한다. 총괄적 2 차원 문자 배열 topics 는 환자의 반응에 대한 환형 큐 (circular queue) 를 유지할 것이다. (큐는 first-in, first-out 저장장치이다) 프로그램은 topics 로 인덱스 하고 큐를 만들기 위하여 head 와 tail 변수들을 사용한다. MAX 는 큐의 크기를 설정하고 임의의 값이다. 프로그램은 환자가 답을 넣으면 - 나중에 볼 것처럼 - 행위의 어떤 과정이 취해질 것인지 결정하기 위해서 SHORT 와 VERYSHORT 를 사용한다. 프로그램의 전체 총괄 부분이 여기 있다. 확장된 trans 데이터베이스를 주목해야 한다.

#define MAX 100

#define SHORT 10

#define VERYSHORT 3

char *response[]={

    "How are you?",

    "Did you have a happy childhood?",

    "Did you hate your father?",

    "i'm not sure I understand.",

    ""

};

char *trans[]={

    "you", "Let's not talk about me.",

    "think", "Why do you think that?',

    "hate", "so you hate something - tell me more.",

    "what", "Why do you ask?",

    "want", "Why dou you want that?",

    "need", "We all need many things - is this special?",

    "why", "Remember, therapy is good for you.",

    "know", "How do you know that?",

    "bye", "Your bill will be mailed to you.",

    ""

};

char topics[MAX][80];    /* holds old topics */

char token[80];

char *p_pos;

int res=0;       /* index into response array */

int head=0;    /* head of topics queue */

int tail=0;       /* tail of topics queue */

프로그램의 가장 큰 변화는 여기 있는 것처럼 respond() 함수에 있다.

/* create the doctor's response */

respond(s)

char *s;

{

    char t[80];

    int loc;

    if (strlen(s)<VERYSHORT && strcmp(s, "bye")) {

      if (find_topic(t)) {

        printf("You just said : ");

        printf("%s \n",t);

        printf("Tell me more. \n");

      }

      else {

        if (!*response[res])  res=0;       /* start over again */

        printf("%s \n", response[res++]);

      }

      return;

    }

    if (strlen(s)>SHORT) assert_topic(s);

    do {

      get_token();

      loc=lookup(token);

      if (loc!=-1) {

        printf("%s\n", trans[loc+1]);

        return;

      }

    } while(*token);

    /* comment of last resort */

    printf("Tell me more .... \n");

}

환자가 단순히 캐리지 리턴을 넣거나 길이가 VERYSHORT 개의 문자보다 짧은 반응을 넣으면 respond() 의 호출함으로써 이전의 주제로 되돌아 가려고 한다. topics 큐에 주제가 있으면, find_topic() 는 참을 리턴하고 주제를 배열 t 에 놓을 것이다. 이전의 주제가 없으면 respond() 는 response 데이터베이스로부터 전에 있던 반응 (stock response) 을 선택한다. 환자가 SHORT 문자수보다 더 긴 반응을 넣으면, respond() 는 대답을 topics 큐에 놓는다. 이 체크는 의사가 매우 짧은 대답을 주제로 잘못 인식하는 것을 막기 위하여 필요하다. 마지막으로, respond() 는 trans 데이터베이스에 있는 어떤 단어에 대해서도 환자의 반응을 자세히 조사한다. respond() 가 하나를 발견하면 적당한 반응을 내보낸다. 문장의 끝에서 어떤 단어도 데이터베이스에 있는 것과 일치하지 않았으면 의사는 단순히 "Tell me more." 라고 말한다. 전체 프로그램이 다음에 있다.

/* Doctor #2 - includes keyword lookup and previous topics queue. */

#define MAX 100

#define SHORT 10

#define VERYSHORT 3

char *response[]={

    "How are you?",

    "Did you have a happy childhood?",

    "Did you hate your father?",

    "I'm not sure I understand.",

    ""

};

char *trans[]={

    "you", "Let's not talk about me.",

    "think", "Why do you think that?',

    "hate", "so you hate something - tell me more.",

    "what", "Why do you ask?",

    "want", "Why dou you want that?",

    "need", "We all need many things - is this special?",

    "why", "Remember, therapy is good for you.",

    "know", "How do you know that?",

    "bye", "Your bill will be mailed to you.",

    ""

};

char topics[MAX][80];    /* holds old topics */

char token[80];

char *p_pos;

int res=0;       /* index into response array */

int head=0;    /* head of topics queue */

int tail=0;       /* tail of topics queue */

 

main()

{

    char s[80];

    printf("%s\n",response[res++]);

    do {

      printf(" : ");

      p_pos=s;

      gets(s);

      respond(s);

    } while(strcmp(s, "bye"));

}

 

/* create the doctor's response */

respond(s)

char *s;

{

    char t[80];

    int loc;

    if (strlen(s)<VERYSHORT && strcmp(s, "bye")) {

      if (find_topic(t)) {

        printf("You just said : ");

        printf("%s \n",t);

        printf("Tell me more. \n");

      }

      else {

        if (!*response[res])  res=0;       /* start over again */

        printf("%s \n",response[res++]);

      }

      return;

    }

    if (strlen(s)>SHORT) assert_topic(s);

    do {

      get_token();

      loc=lookup(token);

      if (loc!=-1) {

        printf("%s\n", trans[loc+1]);

        return;

      }

    } while(*token);

    /* comment of last resort */

    printf("Tell me more .... \n");

}

 

/* Lookup a keyword in translation table */

lookup(token)

char *token;

{

    int t;

    t=0;

    while(*trans[t]) {

      if (!strcmp(trans[t], token))  return t;

      t+=2;

    }

    return -1;

}

 

/* place a topic into the topics database */

assert_topics(t)

char *t;

{

    if (head==MAX)  head=0;     /* wrap around */

    strcpy(topics[head], t);

    head++;

}

 

/* retrieve a topic */

find_topic(t)

char *t;

{

    if (tail!=head) {

      strcpy(t, topics[tail]);

      tail++;

      /* wrap around if necessary */

      if (tail==MAX)  tail=0;

      return 1;

    }

    return 0;

}

 

/* return a token from the input stream */

get_token()

{

    char *p;

    p=token;

    /* skip spaces */

    while (*p_pos==' ') p_pos++;

    if (*p_pos=='\0') {              /* is end of input */

      *p++='\0';

      return;

    }

    if (is_in(*p_pos, ".!?")) {

      *p=*p_pos;

      p++, p_pos++;

      *p='\0';

      return;

    }

    /*read word until */

    while(*p_pos!=' ' && !is_in(*p_pos, ".,;?!") && *p_pos) {

      *p=tolower(*p_pos++);

      p++;

    }

    *p='\0';

}

is_in(c, s)

char c, *s;

{

    while(*s) {

      if (c==*s)  return 1;

      s++;

    }

    return 0;

}

프로그램의 이 버전은 다음의 대화를 생성한다 :

How are you? : OK

Did you have a happy childhood? : Yes, but I hated my neighbor's dog

Tell me more ... : why

Remember, therapy is good for you : I think that everyone is out to get me

Why do you think that ? : Because it is true

Tell me more ... : no

You just said : Yes, but i hated my neighbor's dog

Tell me more. : I want to find a friend

Why do you want that ? : I need someone to talk to

We all need many things - is this special ? : No, but I am lonely

Tell me more.

볼 수 있듯이, 프로그램의 이 버전은 최초의 버전에 대한 실질적인 개선이 이루어진 것이다. 의사가 인식할 키워드와 구의 수를 확장하는 것이 재미있다는 것을 알 수 있다 - 이 버전은 얼마나 "영리해" 보일 수 있는지에 대하여 놀랄지도 모른다.

4.4. 성격과 감정을 첨가하기 (Adding Some Personality and Emotion)

어떤 특별한 단어에 대한 반응을 변경함으로써 의사가 감정을 가진 것처럼 만들 수 있다. 예를들어, 단어 "murder" 는 반응 "I don't like people who murder." 을 얻을 수 있다. 또한 의사에 대한 성격을 선택하고 그것에 대한 반응을 맞출 수 있다. 예를들어, 의사가 낙천주의자이기를 원하면, 단어 "never" 의 사용에 대한 반응을 "Don't be negative ; be positive." 로 바꿀 수 있다. 마지막으로, 환자가 문장을 반복할 때 의사가 화나게 되면 재미있을 것이다.

이 변화를 만들 수 있는 방법을 부여하기 위해, Doctor 프로그램의 최종 버전은 뚜렷하게 낙천적이고 쉽게 성내는 정신과 의사를 만들 것이다. 의사가 낙천적이게 하는 것은 다음에 있는 것처럼 trans 데이터베이스를 변경할 것을 요구한다.

char *trans[]={

    "you", "Let's not talk about me. ",

    "think", "Why do you think that ? ",

    "hate", "So you hate something - tell me more.",

    "what", "Why do you ask ?",

    "want", "Why do you want that ?",

    "need", "We all need many things - is this special ?",

    "why", "Remember, therapy is good for you.",

    "know", "How do you know that ?",

    "bye", "Your bill will be mailed to you.",

    "murder", "I don't like killing.",

    "kill", "It is wrong to kill.",

    "jerk", "Don't ever call me a jerk !",

    "can't", "Don't be negative - be positive.",

    "failure", "Strive for success.",

    "never", "Don't be negative - be positive.",

    "unhappy", "Why are you unhappy ?",

    ""

};

또한 환자가 같은 대답을 반복할 때 의사가 성낼 수 있도록 respond() 의 코드를 변경해야 한다.

if (in_topics(s)) {

    printf("stop repeating yourself ! \n");

}

여기서, 새로운 함수 in_topics() 는 s 에 있는 현재의 대답이 또한 topics 데이터베이스에 있는지를 결정하기 위하여 호출된다. 그렇다면, 프로그램은 다소 성난 반응을 보인다. 변형된 respond() 와 새로운 in_topics() 함수들에 대한 전체 코드가 다음에 보여진다 :

/* create the doctor's response */

respond(s)

char *s;

{

    char t[80];

    int loc;

    if (strlen(s)<VERYSHORT && strcmp(s, "bye")) {

      if (find_topic(t)) {

        printf("You just said : ");

        printf("%s \n",t);

        printf("Tell me more. \n");

      }

      else {

        if (!*response[res])  res=0;       /* start over again */

        printf("%s \n", response[res++]);

      }

      return;

    }

     

    if (in_topics(s)) {

      printf("stop repeating yourself ! \n");

      return;

    }

     

    if (strlen(s)>SHORT) assert_topic(s);

    do {

      get_token();

      loc=lookup(token);

      if (loc!=-1) {

        printf("%s\n", trans[loc+1]);

        return;

      }

    } while(*token);

    /* comment of last resort */

    printf("Tell me more .... \n");

}

 

/* see if in topics queue */

in_topics(s)

char *s;

{

    int t;

    for (t=0; t<MAX; t++)

      if (!strcmp(s, topics[t]))  return 1;

    return 0;

}

이 변화를 가지고, 환자가 이전에 넣었던 것과 동일한 반응을 입력할 때 의사는 비난으로 반응하고 환자에게 새로운 문장에 대하여 다시 프롬프트를 내보낸다. 확장된 전체 버전이 여기에 보여진다.

/* Doctor #3 - optimistic and easily annoyed */

#define MAX 100

#define SHORT 10

#define VERYSHORT 3

char *response[]={

    "How are you this beautiful day ?",

    "Did you have a happy childhood ?",

    "Did you hate your father ?",

    "I'm not sure I understand.",

    ""

};

char *trans[]={

    "you", "Let's not talk about me. ",

    "think", "Why do you think that ? ",

    "hate", "So you hate something - tell me more.",

    "what", "Why do you ask ?",

    "want", "Why do you want that ?",

    "need", "We all need many things - is this special ?",

    "why", "Remember, therapy is good for you.",

    "know", "How do you know that ?",

    "bye", "Your bill will be mailed to you.",

    "murder", "I don't like killing.",

    "kill", "It is wrong to kill.",

    "jerk", "Don't ever call me a jerk !",

    "can't", "Don't be negative - be positive.",

    "failure", "Strive for success.",

    "never", "Don't be negative - be positive.",

    "unhappy", "Why are you unhappy ?",

    ""

};

char topics[MAX][80];    /* holds old topics */

char token[80];

char *p_pos;

int res=0;       /* index into response array */

int head=0;    /* head of topics queue */

int tail=0;       /* tail of topics queue */

 

main()

{

    char s[80];

    printf("%s\n",response[res++]);

    do {

      printf(" : ");

      p_pos=s;

      gets(s);

      respond(s);

    } while(strcmp(s, "bye"));

}

 

/* create the doctor's response */

respond(s)

char *s;

{

    char t[80];

    int loc;

    if (strlen(s)<VERYSHORT && strcmp(s, "bye")) {

      if (find_topic(t)) {

        printf("You just said : ");

        printf("%s \n",t);

        printf("Tell me more. \n");

      }

      else {

        if (!*response[res])  res=0;       /* start over again */

        printf("%s \n", response[res++]);

      }

      return;

    }

     

    if (in_topics(s)) {

      printf("stop repeating yourself ! \n");

      return;

    }

     

    if (strlen(s)>SHORT) assert_topic(s);

    do {

      get_token();

      loc=lookup(token);

      if (loc!=-1) {

        printf("%s\n", trans[loc+1]);

        return;

      }

    } while(*token);

    /* comment of last resort */

    printf("Tell me more .... \n");

}

 

/* Lookup a keyword in translation table */

lookup(token)

char *token;

{

    int t;

    t=0;

    while(*trans[t]) {

      if (!strcmp(trans[t], token))  return t;

      t++;

    }

    return -1;

}

 

/* place a topic into the topics database */

assert_topics(t)

char *t;

{

    if (head==MAX)  head=0;     /* wrap around */

    strcpy(topics[head], t);

    head++;

}

 

/* retrieve a topic */

find_topic(t)

char *t;

{

    if (tail!=head) {

      strcpy(t, topics[tail]);

      tail++;

      /* wrap around if necessary */

      if (tail==MAX)  tail=0;

      return 1;

    }

    return 0;

}

 

/* see if in topics queue */

in_topics(s)

char *s;

{

    int t;

    for (t=0; t<MAX; t++)

      if (!strcmp(s, topics[t]))  return 1;

    return 0;

}

 

/* return a token from the input stream */

get_token()

{

    char *p;

    p=token;

    /* skip spaces */

    while (*p_pos==' ') p_pos++;

    if (*p_pos=='\0') {              /* is end of input */

      *p++='\0';

      return;

    }

    if (is_in(*p_pos, ".!?")) {

      *p=*p_pos;

      p++, p_pos++;

      *p='\0';

      return;

    }

    /*read word until */

    while(*p_pos!=' ' && !is_in(*p_pos, ".,;?!") && *p_pos) {

      *p=tolower(*p_pos++);

      p++;

    }

    *p='\0';

}

is_in(c, s)

char c, *s;

{

    while(*s) {

      if (c==*s)  return 1;

      s++;

    }

    return 0;

이 버전의 프로그램이 다음 대화를 산출한다. 

How are you this beautiful day ? : I don't know, I feel bad

How do you know that ? : I feel like I could kill

                                         : I never seem to win

Don't be negative - be positive ! : why, are you sure ?

Remember, therapy is good for you. : why, are you sure ?

Stop repeating yourself. : Hey, who cares jerk

Don't ever call me a jerk ! : I'll call you a jerk any time I want to

Let's not talk about me. : I am unhappy

Why are you unhappy ?

의사 (Doctor 프로그램) 는 정확히 성격과 감정을 갖는 것 같다. 초기 버전들에서, 의사는 완전히 수동적이었고 단순히 환자의 대화를 감독하는 데에 만족하는 것 같았다. 그러나 이 대화에 있는 것처럼 의사는 충고를 하고 반복하는 성질이 있는 환자에게 성냄으로써 더 공격적으로 행동한다. 약간의 부가적인 코드가 감정과 성격에 대한 묘사를 어떻게 만들어 낼 수 있는가는 놀라운 일이다. 다른 성격 유형과 감정을 가지고 실험해야 한다.

4.5. 의사 프로그램 확장하기 (Expanding the Doctor Program)

프로그램을 확장하기 위한 여러 가지 접근방식이 있다. 먼저, 더 많은 키워드와 반응구를 trans 데이터베이스에 첨가할 수 있다. 이 방식은 아주 쉽다 : 재미있는 첨가를 고안해내는 어려움이 없어야 한다. 의사의 반응이 기본적인 성격의 유형을 반영하게 할 수도 있다.

또다른 향상은 훨씬 더 어렵다 : 프로그램이 단순히 키워를 찾는 대신에 환자의 말의 각 구절에 더 특별하게 반응할 수 있도록 제 4 장의 되부름 하향 자연언어 파서 (recursive-descent natural-language parser) 를 프로그램에 합칠 수 있다.

마지막으로, 토론이 진행함에 따라 의사의 감정이 변화하게 하려고 시도하기 원할 수도 있다 : 이런 변화를 환자의 반응에 기초를 두고 있다. 본질적으로, 분위기 변화를 첨가할 것이다. 성공하고 안하고는 인간의 행동에 대한 시뮬레이션에 얻을 통찰만큼 중요하지 않다.

5. 인간다운 컴퓨터의 암시 (IMPLICATIONS OF HUMANLIKE COMPUTERS)

인간의 행동을 시뮬레이트하는 최종적 생각으로, 인공적인 사람 (인간같은 로봇) 의 사회적인 의미를 생각하는 것은 재미있다. 자율적인 로봇이 인간다운 행동을 보이는데에 충분히 성공적이며, 사람들은 그것을 기계 이상의 어떤 것으로 인식하기 시작할 것인가?

잠시동안, 로봇이 원하거나 원하지 않는 것을 무시하자. 문제는 다음이다. 즉, 사회는 이런 유형의 로봇에 대하여 무엇을 원할까? 사람들은 흔히 감정적으로 애완용 개와 고양이에게 애착을 갖는다. 사람들이 인간같은 로봇에게 같은 애정과 충의를 느낄 것은 확실한 것 같다. 이런 유형의 감정적 유대의 가능성은 이런 유형의 기계에 대한 프로그래머에게 색다른 책임을 부과한다 - 이것의 중요성은 충분히 이해되지 않는다.

또다른 재미있는 점은 책임 문제이다. 인간다운 자율로봇이 광폭해져서 사람을 죽이면 누가 비난받아야 하는가? 로봇인가? 이 우연한 사건을 보호하지 못한 프로그래머인가? 혹시 실수없는 시스템을 공급하지 못한 하드웨어 제조업자인가? 이 질문들은 선례가 없기 때문에 어려운 것이다. 대부분의 생산 책임의 경우는 계획적인 무시에 따라 결정된다. 그러나, 이런 수준의 복잡성을 갖는 로봇의 경우에 정신을 형성하는 데에 문자 그대로 수백만 또는 수십억 바이트의 코드를 가지고, 또 학습하는 능력과, 인간의 환경과의 인터페이스를 가지고 - 실패의 확정이 있을 수 있다는 것은 명확하지 않다.

마지막으로, 사람만큼 또는 사람보다 더 영리하고 사람처럼 행동하는 기계는 어떤 합법적인 권리를 부여받을 것인가? 이 질문은 현재에는 우스운 것 같지만, 1800 연대에 동물의 권리가 처음 제안되었을 때 많은 사람들에게 우스웠던 것 같다. 그러나 오늘날, 애완동물에 대한 엉터리없는 부당한 대우를 인정할 사람은 거의 없다.

이 의미와 다른 의미들은 이런 유형의 기계를 만드는 데에 관계된 모든 프로그래머들에게 영향을 주는데 왜냐하면 그들은 새로운 세계 - 현재 살고 있는 세계와는 매우 다른 세계 - 로 안내할 사람들이기 때문이다.