티스토리 뷰

이 절의 내용은 소스화일과 함께 딸려 오는  “Chatscript Common Beginner Mistakes”을 기준으로 한국어 어법에 맞게 내용을 대폭 추가하고 예제도 한국어 대화예제로 수정한 것이다.

 

A.     토픽 키워드와 무관하게 룰 작성

 

예제 입력문과 패턴을 일치시키는 데만 신경 쓰고 예제 입력문과 토픽의 키워드 공유에는 무관심한 경우가 가장 흔한 실수이다. 이런 경우에는 아무리 패턴을 잘 작성하여도, 즉 룰을 잘 작성하여도 그 룰을 포함하고 있는 토픽이 활성화 되지 않기 때문에 그 룰은 매칭 여부가 검토조차 되지 못한다. 결국 매칭에 실패한다. 그러므로 룰을 작성할 때는 반드시 토픽의 키워드 목록을 공유하는 단어가 있도록 작성해야 하며, 반대로 매칭에 실패할 경우, 가장 먼저 점검해야 할 일은, 매칭되어야 할 룰이 활성화 되었는가, 다시 말해 그 룰을 보유한 토픽의 키워드와 예제 입력문의 단어 중 공유하는 단어가 있는가를 점검하는 것이다. 이를 점검해 주는 몇 가지 툴에 대해서 6 (4)절에서 자세히 다루기로 한다.

 

B.     사전에 없는 단어를 토픽 키워드로 등재

 

사전에 없는 단어를 토픽의 키워드로 등재하면 CS un-known word라는 경고를 주되 프로그램을 멈추지는 않고 계속 실행한다. 문제는 CS가 내부적으로 그 키워드가 없는 것과 동일하게 해당 토픽을 처리한다는 점이다. 따라서 그 단어를 입력해도 해당 토픽이 활성화되지 않는다. 겉보기에는 그 단어가 토픽의 키워드인 것처럼 보이지만 내부적으로는 그 단어가 제외된 토픽인 것이다. 그리고 이는 매칭되는 룰을 찾을 수 없다는 에러로 이어진다. 룰 단위에서는 매칭이 되지만 정작 그 룰을 보유한 토픽이 활성화 되지 않기 때문에 대화상황에서 룰 매칭이 일어나지 않는 것이다.

따라서 토픽의 키워드에 사용할 단어는 반드시 먼저 사전(dictionary)에 등록을 해야 하며, 빌드 과정에서 토픽에 등록한 키워드가 un-known 경고를 받는지 확인해야 한다.

 

C.     패턴을 문장으로 작성

 

패턴은 문장으로 작성하기 보다는 핵심 단어들로 나열해야 한다. 예상되는 사용자 대화문 그 자체로 패턴을 작성하면 오직 그 문장에만 대응할 수 있게 되며 따라서 무수히 많은 룰을 준비하여야 하기 때문이다. 문장보다는 사용자 대화에 포함될 것으로 예상되는 주요 단어를 패턴에 나열하여야 사용자 의도에 부합하는 룰을 더 잘 찾아낼 수 있다. 최대 5개의 단어까지 패턴에 포함시킬 수 있으며 이때 반드시 소속 토픽의 키워드 중 어느 하나를 공유해야 한다. (예상응답은 예외임.)

 

D.    조사를 고려하지 않은 채 패턴 작성

 

명사나 대명사, 고유명사 등은 단독으로 사용되기 보다는 조사와 합쳐진 형태로 사용된다. 따라서 토픽의 키워드를 작성할 때 그리고 패턴을 작성할 때 이를 반영해야 한다. 예를 들어

 

#! ugly가 누구 히트곡이죠?

u: (<<ugly 히트곡>>) . 투애니원의 히트곡이에요.

 

에서 이 룰은 매칭되지 않을 것이다. 왜냐하면 “ugly“ugly”와 다르고 히트곡이죠히트곡과 다르기 때문이다. 이를 아래와 같이 고치면,

 

#! ugly가 누구 히트곡이죠?

u: (<<ugly* 히트곡*>>) . 투애니원의 히트곡이에요.

 

룰은 매칭이 되겠지만 이 룰이 속해있는 토픽이 “ugly또는 히트곡이죠라는 사용자 입력문에 활성화되는지는 추가로 확인해 보아야 한다. 토픽의 키워드에 조사가 붙어있지 않은 형태로 등록했을 가능성이 높기 때문이다. 명사, 대명사, 고유명사 등은 조사가 붙은 어절 단위로 취급하는 습관을 가져야 한다.

 

E.     한글을 EUC-KR로 저장

 

비영어권에서 컴퓨터 작업을 할 때 가장 불편하고 서러운 점이 영어 아닌 문자의 처리라는 말이 있을 정도로 영어 아닌 문자의 처리는 골치가 아픈 문제다. 다행히 CS UTF-8 형식을 지원하고 있기 때문에 한국어, 일본어, 중국어를 모두 처리할 수 있다. 파일은 반드시 UTF-8 형식으로 저장해야 하며, MS-Windows에서 테스트 할 때는 MS-Windows가 제공하는 콘솔의 한글 입출력도 UTF-8로 바꿔야 한다. 이에 대해서는 6 (2)절에서 설명하였다. 이곳에서 꼭 기억해 두어야 할 점은 한글의 입출력은 무조건 UTF-8 형식으로 해야 한다는 점이다.

 

F.     룰의 이름과 함수 이름을 혼돈

 

    t: KPOP(xxx )

    t: ^KPOP(xxx )

    t: ( ) KPOP(xxx )

 

1번의 KPOP은 룰의 이름(라벨)이고 뒤따르는 괄호는 패턴이다. 2번의 KPOP은 함수 이름이고 괄호 안은 인수이다. 예를 들면 ^KPOP(“I love you”)라는 함수는 인수로 넘겨진 I love you 라는 노래를 들려주는 함수일 수 있겠다. 3번은 패턴이 있기 때문에, 비록 내용이 비어있지만, 뒤따르는 KPOP은 함수라고 인식하게 된다. 그러나 정석은 함수이름 앞에 ^을 붙이는 것이니 3번은 t: ( ) ^KPOP(xxx ) 이라고 쓰는 것이 바람직하다.

 

 

G.    패턴을 와일드 카드로 시작하기 또는 끝내기

 

u: (* 집 간다 *)

 

위 패턴의 와일드 카드(“*”)는 무의미하다. 앞뒤의 와일드 카드가 없어도 동일한 효과를 내기 때문이다. CS는 사용자 입력문과 패턴을 비교할 때, 패턴의 첫번째 단어를 사용자 입력문에서 찾기 시작하여 패턴의 마지막 단어를 찾을 때가지 비교를 계속한다. 이 중의 한 단어라도 없으면 매칭은 실패하지만 사용자 입력문이 패턴에 있는 단어를 모두 포함한다면 매칭은 성공하고 규칙이 작동된다. 즉 와일드 카드가 없더라도 사용자 입력문 전체를 받아들여 비교를 하기 때문에 와일드카드로 패턴을 시작하고 끝내는 것은 의미가 없다. 와일드 카드는 단어와 단어 사이에 놓일 때 의미가 있다.

 

H.    예상응답(rejoinder)의 시작기호를 잘못 작성하기

 

챗봇의 질문과 그에 대한 사용자의 예상응답은 들여쓰기를 함으로써 구조적으로 한 묶음으로 읽힐 수 있도록 하는 것이 좋다. 또한 동일 수준에서 검토되는 후보 규칙들은 동일한 레벨명칭(a:, b: )을 가져야 한다.

 

t: 몇 살이에요?

a: (~number<5) 아기이군요

b: (~number<19) 청소년이군요

c: (~number<100) 나이드셨네요

 

위의 예상응답 a, b, c는 동일한 질문 몇 살이에요에 대한 예상응답으로 작성한 것이다. 그렇다면 시작 기호가 모두 같아야 한다. 또한 들여쓰기를 하는 것이 읽기 쉽고 이해하기도 쉽다. 올바르게 고쳐 쓰면,

 

t: 몇 살이에요?

a: (~number<5) 아기이군요

b: (~아니오) 아기가 아니라고요?

a: (~number<19) 청소년이군요

a: (~number<100) 나이드셨네요

 

이제 몇 살이에요?”라는 질문의 예상응답 세 가지가 모두 같은 머리기호 a로 쓰여졌기 때문에 CS는 사용자 입력문을 예상응답 세 가지와 비교하게 되고, 예상응답 b:아기이군요라는 챗봇의 답변에 대한 예상응답으로만 비교하게 된다.

들여쓰기를 꼭 해야 만 하는 것은 아니지만 들여쓰기를 하면 질문과 그에 대한 예상응답을 한 묶음으로 작성하거나 검토할 수 있기 때문에 작업의 효율성이 더 높아진다

 

I.      예제문 입력하는 것을 누락하기

 

응답이나 예상응답의 패턴을 작성할 때 어떤 대화 상황을 예상하고 작성하였는지, 예상했던 있는 사용자의 입력문이 무엇이었는지를 작성해 넣는 것이 좋다.

 

#! 저는 씨엘을 특히 좋아해요

u: (! << ~씨엘 * 좋아* >>) 그렇군요. 저도 씨엘을 좋아해요

 

위와 같이 패턴을 작성하면서 매칭하려고 의도했던 사용자 입력문을 패턴 바로 위에 적어 놓으면 잘못 작성할 가능성이 줄어들 뿐만 아니라 나중에 CS로 하여금 단위테스트를 하도록 할 수 있다. (verify 명령어를 통해 예제 입력문이 패턴과 매칭되는지 단위테스트를 할 수 있다) 또한 시간이 흐른 후에 규칙을 갱신하거나 추가할 때도 이러한 예제 입력문이 있으면 코드를 쉽고 빠르게 이해할 수 있다. 따라서 패턴을 작성할 때 매칭하려 했던 사용자 입력문을 패턴 바로 위에 예제로 적는 습관을 갖는 것이 좋다.

 

J.      주석 기호를 혼돈하기

 

주석 기호는 세가지가 있는데 각각의 용도가 다르다. “#+빈칸으로 시작하는 주석은 오직 사람을 위한 것으로 스크립트를 이해하기 쉽도록 설명을 다는 것이다. 빈칸이라고 표시한 것은 #을 주석과 붙여 쓰면 안된다는 뜻이다.

 

#좋아하는 가수를 기억하기 위한 변수 선언 (X) : #좋아하는을 붙여 썼음

# 좋아하는 가수를 기억하기 위한 변수 선언 (O)

 

주석은 필요한 곳에 얼마든지 달 수 있다. 다만 줄단위 주석문이므로 줄바꿈을 할 경우 첫 칸에 다시 “#+빈칸을 써야 한다. “#!+빈칸으로 시작하는 주석은 앞에서 얘기한 것처럼 사람과 CS 모두에게 도움이 되는 주석이다. 패턴 바로 위에 작성하며 아래의 패턴이 어떤 입력문을 가정하고 작성한 것인지, 어떤 입력문과 매칭되도록 의도한 것인지를 작성하는 것이다. 세 번째 주석은 ##<<로 시작하고 ##>>로 끝나는 블록단위 주석이다. ##!가 줄단위 주석인데 반해 ##<<로 시작하면 ##>>가 나타날 때까지 몇 줄이고 그 안에 포함된 모든 내용을 주석으로 처리한다. 여러 줄에 걸쳐 주석을 달거나 특정 토픽 전체를 잠시 동안 주석처리 한다든가 할 때 유용하다.

 

 

K.     함수를 호출할 때 ^을 누락

 

함수이름 앞에 ^(윗꺽쇠, caret, 숫자6 위에 있음)표시를 생략하면 CS엔진은 그것을 자연어로 인식하게 된다. 자연어가 아니라 함수로 기능하게 하려면 함수이름 앞에 표식자 ^을 붙여야 한다.

 

outputmacro (X)

^outputmacro (O)

 

L.     기본형(Canonical form)의 이점을 살리지 못하는 패턴 작성

 

패턴을 작성할 때 기본형으로 작성하지 않고 활용형으로 작성하는 것은 CS의 뛰어난 장점을 제대로 사용하지 못하는 것이다. 패턴을 기본형으로 작성하면 사용자가 기본형으로 입력하든 활용형으로 입력하든 CS엔진이 알아서 매칭시켜 줄 수 있기 때문이다. 예를 들어 아래의 패턴은

 

u: ( * [좋아하고 좋아하며 좋아하지만 좋아해서] 음악* 좋아했다)

 

아래의 문장과 모두 매칭이 되지만, 이는 CS의 기능을 제대로 이용하지 못한 패턴문 작성이다.

 

그녀는 술을 좋아하고 음악도 사랑했다

그녀는 술을 좋아하며 음악도 사랑했다

그녀는 술을 좋아하지만 음악도 사랑했다

그녀는 술을 좋아해서 음악도 사랑했다

 

좋아하다를 기본형으로 등재한 후 아래와 같이 기본형으로 패턴을 작성하면 룰은 단순해지고 읽기 쉬워지고 누락이나 중복 등의 오류도 최소화된다.

 

u: (* 좋아하다 음악* 사랑했다)

 

좋아하다를 기본형으로 등재하는 방법은 제5 (5)절에 설명하였다. 위의 예제에서 사랑하다도 마찬가지로 기본형으로 등재해서 기본형으로 패턴에 작성할 수 있다. 한국어는 동사와 형용사의 활용형이 매우 다양한 언어이므로 CS의 기본형 매칭은 매우 유용한 기능이라 하겠다. 다만 기본형 등록에 많은 노력이 필요하므로 그렇게 못하는 경우에 한해 컨셉으로 등록하여 사용하거나 부분 와일드 카드를 사용한다.

 

M.   대명사를 토픽 키워드로 등록

 

토픽의 키워드 목록에는 대명사, 조사, 감탄사 등이 포함될 수 없다. 의미가 없기 때문이다. 토픽의 키워드들은 해당 토픽이 언제 활성화될 지를 알려주는 지표의 역할을 하는데 대명사, 조사, 감탄사 등은 그런 식별 기능이 없기 때문이다. 토픽의 키워드는 그 키워드를 보면 곧 바로 그 토픽이 연상될 수 있는 단어들로 구성되어야 한다.

 

N.    키워드를 콤마로 분리하기

 

CS는 콤마를 자연어로 취급하기 때문에 키워드를 나열할 때 콤마로 분리하면 안된다. 단순히 공백으로 분리하면 된다.

 

Topic: ~myTopic [키워드1, 키워드2, 키워드3, 키워드4]

Topic: ~myTopic [키워드1 키워드2 키워드3 키워드4]

 

O.    띄어쓰기를 잘못해 관계식으로 만들기

 

계산 결과를 할당하는 산술식에서 좌우 띄어쓰기를 하지 않음으로써 좌우항을 비교하는 관계식으로 만드는 경우가 있다.

 

$$tmp = join(^topic ^subtopic)

$$tmp=join(^topic ^subtopic)

 

연산식은 띄어쓰기를 해야 하고 관계식은 띄어쓰기를 하면 안된다.

 

P.     사전에 없는 단어를 변수로 받기

 

사용자가 입력한 단어가 CS의 사전에 정의되어 있지 않은 단어라면 그것을 변수로 넘겨 받을 때 입력형 그대로로 넘겨 받아야 한다. 사전에 등록되어 있지 않으니 기본형을 알 수 없기 때문이다. 입력한 형태 그대로 넘겨 받기 위해서는 변수 앞에 어포스트로피를 추가해 주면 된다. 아래의 예에서 2NE1은 사전에 없는 단어이므로 _0 앞에 어포스트로피를 붙여야 올바르게 출력이 된다.

 

#! 2NE1을 좋아해요

u: ( _*1 좋아* ) 세상 사람들도 모두 ‘_0 좋아해요.

 

만약 이를 빼먹으면 unknown-word 에러를 보여준다.

 

Q.    띄어쓰기 한 복합명사의 처리

 

띄어쓰기한 복합명사는 겹따옴표 또는 _(언더바)로 묶어서 하나의 단어로 취급하도록 알려줘야 한다. 특히 [ ] 안에 복합 명사를 나열할 때는 겹따옴표로 묶어 하나의 단어로 처리하도록 해야 한다. 그렇지 않으면 각각 별도의 단어로 처리하게 되며 [ ]는 나열된 단어 중에 하나를 취하라는 명령어이기 때문에 의도와 아주 다른 결과가 나온다. 예를 들어,

 

u: ( [ 내가 제일 잘 나가 어글리] )

 

에서 내가 제일 잘 나가라는 문장이 곡명을 지칭하는 고유명사라면 겹따옴표로 묶어서 CS가 하나의 단어로 취급하도록 해야 한다.

 

u: ( [ “내가 제일 잘 나가어글리] )

 

이렇게 해야 내가 제 잘 나가라는 곡과 어글리라는 곡 둘 중의 하나를 선택(꺽은 괄호의 의미는 그 안에 포함된 단어들 중의 하나를 선택하라는 명령어)하는 규칙이 된다. 복합명사, 영화제목, 노래제목, 책제목 등 두 단어 이상으로 이뤄진 모든 고유명사는 띄어쓰기를 한다면 겹따옴표로 묶어야 한다.

 

R.     룰 순서를 잘못 배열하기

 

다양한 대화 상황에 빈틈없이 대응하도록 룰을 작성하다 보면 룰 간에 단어 한 두 개만 차이 나는 경우가 생긴다. 이럴 경우 자칫하면 매칭되어야 할 룰이 매칭이 안되고 앞쪽에 먼저 배치된 비슷한 룰과 매칭되는 일이 발생한다. 이를 블로킹이라고 부르는데 아래의 예는 당신의 취미를 사랑하세요?”가 블로킹 당하는 예이다.

 

#! 당신의 취미는 무엇이에요

u: (<< 당신* 취미* >>) 제 취미는 인형 수집이에요

#! 당신의 취미를 사랑하세요

u: (<< 당신* 취미* 사랑* >>) 그럼요, 몹시 사랑하죠.

 

사용자가 당신의 취미를 사랑하세요?”라고 입력했다면 두 번째 룰과 매칭이 되어야 하는데 CS가 룰 검토를 앞에서부터 해 오므로 첫번째 룰과 매칭이 되는 것이다. 즉 첫 번째 룰에 블로킹 당하게 된 것이다. 이 문제는 룰의 배열 순서를 바꿈으로써 해결할 수 있다. , 구체적인 룰, 패턴에 단어를 더 많이 보유하고 있는 룰을 앞쪽에 배치함으로써 대부분의 블로킹 현상은 막을 수 있다.

 

S.     [ ]의 매칭 순서에 대한 오해

 

CS [ ]를 만나면 그 안에 포함된 단어들 중 앞에서부터 순차적으로 매칭 여부를 조사한다. 그러다 매칭되는 단어를 발견하면 그 뒤에 열거된 단어들을 더 이상 조사하지 않고 [ ]를 떠난다. 예를 들어, “좋은 노래는 가사도 중요하죠라는 사용자 입력문을 예상하여 패턴을 다음과 같이 작성했다고 해 보자.

 

#! 좋은 노래는 가사도 중요하죠.

u: ( 좋은 [~노래 ~가사] 중요* )

 

이 패턴과 사용자 입력문 좋은 노래는 가사도 중요하죠는 매칭이 되지 않는다. 왜 안되는지 단계별로 살펴보자.

CS는 패턴을 기준으로 패턴이 입력문과 일치하는가를 살피는데, 위의 예에서 먼저 패턴의 첫단어 좋은이 입력문에 있는가를 살핀다. 사용자 입력문에서 발견했고 CS는 패턴의 그 다음 단어로 옮겨간다. 이때 입력문에는 좋은까지 찾았다는 위치를 표시해 놓는다. 다음 번 비교를 위해 어디까지 검색했는지 표시해 놓는 것이다.

CS는 패턴의 두 번째 요소인 [ ] 안의 단어가 사용자 입력문에 있는지 조사한다. 우선 [ ]에 포함된 첫번째 단어 “~노래가 사용자 입력문에 있는가 살핀다. 컨셉 ~노래에는 노래가, 노래는, 노래를, 노래의등이 선언되어 있을 것이다. 사용자 입력문에는 좋은까지 찾았다는 위치가 표시되어 있으므로 그 다음부터 비교를 해 나가게 되는데 문장의 두 번째 단어에서 노래는을 찾았다. 이제 [ ] 중의 한 단어를 찾았으므로 [ ]는 역할을 다 했고 CS[ ]안을 더 살펴보지 않는다. 한편 사용자 입력문에는 노래는까지 찾았다고 마크가 된다

이제 CS엔진은 패턴에 있는 중요*”라는 단어를 사용자 입력문에서 찾아야 하는데, 사용자 입력문에는 찾을 위치가 노래는에 와 있기 때문에 그 다음부터 찾게 된다. 그런데 그 다음 단어는 가사도라는 단어이므로 매칭에 실패한다. 왜냐하면 패턴에는 저 세 단어가 순서대로 연이어서 발견되어야 한다고 적혀 있기 때문이다.

이상의 설명은 당연하다고 생각할 수 있지만, 위의 사례는 언뜻 볼 때 패턴과 사용자 입력문에 같은 단어가 같은 순서로 출현하므로 매칭이 될 것이라고 오인하기 쉽기 때문에 별도로 강조하는 것이다.

위의 예제는 순서없이 매칭하라고 패턴을 작성하여도 실패한다.

 

#! 좋은 노래는 가사도 중요하죠.

u: ( <<좋은 [~노래 ~가사] 중요*>>)

 

요점은 [ ]는 그 안에 있는 단어 어느 하나만 매칭이 되면, 바로 빠져 나와서 다음 단어의 매칭을 조사한다는 점이다.

 

T.     컨셉의 중복 선언

 

컨셉이 많아지다 보면, 특히 다른 사람이 작성한 컨셉을 가져다 쓰게 되는 경우에는 컨셉이 중복 선언될 수 있다. 이 경우에는 다음과 같이 MORE 키워드를 덧붙여 주면 된다.

 

concept: ~과일 MORE [ 사과 배 포도 딸기 ]

 

또는 성격이 다른데 우연히 이름이 같은 경우라면 어느 한 쪽의 이름을 수정해 주면 된다. 스크립트를 작성하면서 컨셉이 중복될 것을 걱정할 필요는 없다. 컴파일 할 때 CS엔진이 중복되는 컨셉을 정리해서 알려주므로 그때 수정하면 된다.

 

댓글
댓글쓰기 폼