Hydra Logo

프로젝트 버그 수정 과정 소개

이 문서는 프로젝트의 버그를 어떻게 수정하였는지 설명하기 위해 작성되어졌습니다.

버그 목록

원본 repository에 open된 issue 중, bug 또는 possible bug라고 태그가 달린 것들은 다음과 같습니다.

bug

  1. Plain Markdown text on Python Package Index bug help wanted
  2. Issue with parsing.py to extract date time from text like “meeting today at 9pm” bug
  3. IndexError and KeyError bug
  4. KeyError: ‘unary_operators’ bug
  5. Can’t connect with SQLStorageAdapter on Oracle 11g bug
  6. Connection to MSSQL Server bug question
  7. (sqlite3.OperationalError) cannot change into wal mode from within a transaction bug
  8. bug in datetime parsing bug help wanted

    possible bug

  9. [Django][Chatterbot] Two identical strings did not match while using SpecificResponseAdapter possible bug
  10. BestMatch not working as expected possible bug
  11. Default response not working for other langauges other than english possible bug
  12. Can’t feedback learning possible bug
  13. Answers Updating possible bug

수정할 버그 목록 선택

1번은 pip에 대해 추가적으로 알아봐야 했고, 테스트할 방법도 없는 데다가, 이미 이슈에 해결책이 제시되어 있었기에 제외하였습니다.

3번과 4번은 mathparse 라이브러리의 오류로 우리의 프로젝트 밖 범위였습니다. 또한 이미 수정되어져 배포되기를 기다리고 있는 상태였다. 따라서 제외하였습니다.

5번과 6번은 각각 Oracle과 MSSQL 데이터베이스에 관련한 이슈였는데, 이 둘은 상용 소프트웨어라 테스트가 불가능에 가까웠기에 제외하였습니다.

8번은 작동상에는 문제가 없었습니다. 이미 해결된 버그인데 Closed 되지 않았던 이슈였습니다.

10번은 이미 해결책이 제시되어져 있었습니다.

13번은 12번과 비슷한 상황에서 일어나는 것이기에 우선 12번을 다루기로 했습니다.

이슈 목록

결과적으로 다음과 같은 이슈들을 고쳐보기로 하였습니다.

  1. Issue with parsing.py to extract date time from text like “meeting today at 9pm” bug
  2. [Django][Chatterbot] Two identical strings did not match while using SpecificResponseAdapter possible bug
  3. Can’t feedback learning possible bug

활동 결과

1. am과 pm을 제대로 파싱하지 못하던 문제 수정

문제

ChatterBot은 문자열 내에서 시간을 파싱하는 함수를 가지고 있습니다. 그러나 실제 의도한 대로 시간이 파싱되고 있지 않았습니다. 예를 들어 today 9pm을 파싱하면 9시가 아닌 현재 시각에서 9시를 더한 시각이 반환되었습니다.

분석

먼저 이 코드 에서 today() 현재 시각을 포함하는 datetime 클래스를 반환하고 있었는데, 이는 현재 날짜 뿐만 아니라 현재 시각을 같이 반환하고 있었습니다. 이렇게 되면 서브쿼리에서 이 시각과 9시를 더하게 되어 현재 시각에서 9시를 더한 시각이 나왔던 것입니다.

두번째로, 이 코드에서는 만약 문자열이 (\sam|pm)?에 매칭되면 am 이나 pm 문자열이 convention 그룹에 대입되지 않았고, 따라서 pm이 문자열에 있더라도 결과값에 반영되고 있지 않았습니다.

해결

첫번째 코드의 문제는 반환받은 datetime 클래스에서 시각을 전부 0으로 초기화시켜 해결하였습니다. 두번째 코드의 문제는 정규표현식을 수정하여 해결했습니다.

그 결과로, 이 코드를 수행하면

from chatterbot import parsing

print(parsing.datetime_parsing(u"Your dental appointment with Dr P. Delvour is scheduled today at 9:00pm."))
print(parsing.datetime_parsing(u"meeting today at 9pm."))
print(parsing.datetime_parsing(u"Your dental appointment with Dr P. Delvour is scheduled for October 29, 16:00. ABC Dentist, 555-555-555."))
print(parsing.datetime_parsing(u"Your dental appointment with Dr P. Delvour is scheduled for October 29, 4 pm. ABC Dentist, 555-555-555."))
print(parsing.datetime_parsing(u"Your dental appointment with Dr P. Delvour is scheduled for October 29, 4:00pm. ABC Dentist, 555-555-555."))

올바른 답이 파싱되어 나옵니다.

[('today at 9:00pm', datetime.datetime(2019, 6, 2, 21, 0), (56, 71))]
[('today at 9pm', datetime.datetime(2019, 6, 2, 21, 0), (8, 20))]
[('October 29, 16:00', datetime.datetime(2019, 10, 29, 16, 0), (60, 77))]
[('October 29, 4 pm', datetime.datetime(2019, 10, 29, 16, 0), (60, 76))]
[('October 29, 4:00pm', datetime.datetime(2019, 10, 29, 16, 0), (60, 78))]

2. feedback learning이 안되던 문제 완화

문제

ChatterBot은 입력과 정답을 입력하여 학습하는 기능을 가지고 있습니다. 그러나 학습을 시켜도 ChatterBot이 학습한 데이터를 말하지 못하는 버그를 가지고 있었습니다.

분석

간단히 말해, 이슈 제기자가 사용한 코드에 logic을 적절하게 선택하는 코드가 없어서 그랬던 것입니다. ChatterBot이 대답을 찾을 때에는 다양한 알고리즘이 사용되는데, 아무 코드를 작성하지 않아 기본 알고리즘으로 설정한 BestMatch 알고리즘이 동작하였고, 그 결과로 엉뚱한 답을 계속 내놓고 있었던 것입니다. 일전에 버그를 수정했던 SpecificResponse 알고리즘을 사용하면 학습시킨 대로 답을 내놓을 수 있습니다. 그러나 데이터베이스를 확인해 보니, 입력한 두 값을 가지고 채울 수 있는 데이터 목록을 채우지 않는다는 것을 발견했습니다. 기본 알고리즘은 대화의 단어의 성분(부사, 명사, 동사 등…)을 가지고 대답을 찾아내는데, train 함수로 학습시킬 때와 달리 learn_response 함수로 학습시키면 단어의 성분들이 데이터베이스에 추가되지 않았던 것입니다.

해결

따라서 learn_response 함수에서도 단어의 성분들을 데이터베이스에 추가하도록 코드를 수정하였습니다. 그 결과 기대에 미치는 정도는 아니었지만 아예 학습이 되지 않던 과거와 달리 어느 정도의 학습이 되기 시작하는 모습을 보였습니다.

3. can_process 함수가 제대로 동작하도록 수정

문제

ChatterBot은 답변을 선택하는 방식을 미리 준비된 어댑터에 따라 바꿀 수 있는데, 그 중 SpecificResponseAdapter를 사용할 경우 분명 스펙대로라면 문자열이 선택되어져야 함에도 불구하고 선택되지 않았습니다.

분석

코드를 보면 문자열을 선택하는 기준을 statementinput_text를 비교하여 선택하고 하고 있습니다. 그러나 코드를 보면 statement == input_text 식으로 작성되어져 있는데, statementStatement 클래스이고 input_textstr 형식이라 비교하면 어떠한 상황에서든 False를 반환한습니다. Statement에 작성된 __str__ 함수를 보면 text 변수를 반환하도록 되어 있는데, 아마 코드 작성자가 print(statement)로 호출하면 자동으로 __str__ 함수가 호출되어 text 변수가 출력되는 것을 클래스와 문자열 비교시에도 __str__ 함수가 호출되는 것인 줄 알고 작성한 것 같습니다. 실제로 이슈에도 statementstatement.text로 바꿨더니 잘 동작했다고 하는 내용이 있습니다.

해결

문제점을 알았으니 쉽게 해결할 수 있었습니다. 이슈 제보자의 말대로 text 변수를 호출하면 되는 일이라, str 함수를 명시적으로 호출하여 해당 버그를 해결할 수 있었습니다.

example code를 사용하여 테스트해 보았습니다.

수정 이전 before

수정 이후 after