├── __init__.py ├── knowledge_base ├── __init__.py ├── data │ ├── mention_mapping.csv │ ├── entity_type_mapping.csv │ ├── bank.csv │ ├── represented-by.csv │ ├── attribute_mapping.csv │ ├── person.csv │ ├── account.csv │ ├── card.csv │ └── contract.csv ├── insert.py ├── lookup_tables.py ├── schema.gql └── migrate.py ├── endpoints.yml ├── requirements.txt ├── config.yml ├── update_knowledge_base.py ├── data ├── lookup_tables │ ├── bank.txt │ └── person.txt ├── stories.md └── nlu.md ├── schema.py ├── .gitignore ├── domain.yml ├── LICENSE ├── README.md ├── actions.py └── graph_database.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /knowledge_base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /endpoints.yml: -------------------------------------------------------------------------------- 1 | action_endpoint: 2 | url: "http://localhost:5055/webhook" 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | rasa-sdk==1.7.0 2 | rasa==1.7.0 3 | grakn-client==1.5.3 4 | -------------------------------------------------------------------------------- /knowledge_base/data/mention_mapping.csv: -------------------------------------------------------------------------------- 1 | mapping-key,mapping-value 2 | one,0 3 | first,0 4 | 1,0 5 | two,1 6 | second,1 7 | 2,1 8 | third,2 9 | three,2 10 | 3,2 11 | last,-1 12 | final,-1 13 | fourth,4 14 | fifth,5 -------------------------------------------------------------------------------- /knowledge_base/data/entity_type_mapping.csv: -------------------------------------------------------------------------------- 1 | mapping-key,mapping-value 2 | people,person 3 | persons,person 4 | person,person 5 | banks,bank 6 | bank,bank 7 | banking,bank 8 | banking options,bank 9 | transactions,transaction 10 | transaction,transaction 11 | accounts,account 12 | account,account 13 | card,card 14 | cards,card -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | language: en 2 | pipeline: supervised_embeddings 3 | 4 | policies: 5 | - batch_size: 50 6 | epochs: 200 7 | max_training_samples: 300 8 | name: KerasPolicy 9 | - fallback_action_name: action_default_fallback 10 | name: FallbackPolicy 11 | - max_history: 5 12 | name: MemoizationPolicy 13 | - name: MappingPolicy 14 | -------------------------------------------------------------------------------- /knowledge_base/insert.py: -------------------------------------------------------------------------------- 1 | from grakn.client import GraknClient 2 | 3 | 4 | def insert(graql_insert_query): 5 | with GraknClient(uri="localhost:48555") as client: 6 | with client.session(keyspace="banking") as session: 7 | with session.transaction().write() as transaction: 8 | transaction.query(graql_insert_query) 9 | transaction.commit() 10 | 11 | 12 | if __name__ == "__main__": 13 | graql_insert_query = """ 14 | insert $b isa bank, has name 'KfW', has country 'Germany', has headquarters 'Frankfurt am Main'; 15 | """ 16 | 17 | insert(graql_insert_query) 18 | -------------------------------------------------------------------------------- /knowledge_base/data/bank.csv: -------------------------------------------------------------------------------- 1 | name,country,headquarters,free-accounts,english-customer-service,english-website,english-mobile-app,free-worldwide-withdrawals,allowed-residents 2 | N26,Germany,Berlin,true,true,true,true,true,EU residents 3 | bunq,Netherlands,Amsterdam,false,true,true,true,true,all residents 4 | Deutsche Bank,Germany,Frankfurt am Main,false,true,true,false,false,German residents 5 | Commerzbank,Germany,Frankfurt am Main,true,true,true,false,false,German residents 6 | Targobank,Germany,Düsseldorf,true,true,true,false,false,German residents 7 | DKB,Germany,Berlin,true,false,false,false,true,all residents (with difficulty) 8 | Comdirect,Germany,Quickborn,true,false,false,false,true,all residents 9 | -------------------------------------------------------------------------------- /update_knowledge_base.py: -------------------------------------------------------------------------------- 1 | from grakn.client import GraknClient 2 | 3 | 4 | def execute(graql_query): 5 | print(graql_query) 6 | 7 | with GraknClient(uri="localhost:48555") as client: 8 | with client.session(keyspace="banking") as session: 9 | with session.transaction().write() as transaction: 10 | transaction.query(graql_query) 11 | transaction.commit() 12 | 13 | 14 | if __name__ == "__main__": 15 | graql_insert_query = """ 16 | insert $b isa bank, has name 'KfW', has country 'Germany', has headquarters 'Frankfurt am Main'; 17 | """ 18 | execute(graql_insert_query) 19 | 20 | graql_delete_query = """ 21 | match $b isa bank, has name 'KfW'; delete $b; 22 | """ 23 | #execute(graql_delete_query) 24 | 25 | -------------------------------------------------------------------------------- /data/lookup_tables/bank.txt: -------------------------------------------------------------------------------- 1 | deutsche bank 2 | dab bnp paribas 3 | Landesbank Baden-Württemberg 4 | DAB BNP Paribas 5 | solarisBank 6 | Nord/LB 7 | Commerzbank 8 | Comdirect 9 | NRW.Bank 10 | portigon financial services 11 | landesbank berlin holding 12 | Portigon Financial Services 13 | landesbank baden-württemberg 14 | Deutsche Pfandbriefbank 15 | commerzbank 16 | HSH Nordbank 17 | DekaBank Deutsche Girozentrale 18 | KfW 19 | N26 20 | Landwirtschaftliche Rentenbank 21 | BayernLB 22 | dz bank 23 | Bremer Bank 24 | Deutsche Bank 25 | DKB 26 | nord/lb 27 | solarisbank 28 | bremer bank 29 | wirecard bank 30 | dekabank deutsche girozentrale 31 | bayernlb 32 | DZ Bank 33 | GLS Bank 34 | dkb 35 | consorsbank 36 | landwirtschaftliche rentenbank 37 | hsh nordbank 38 | comdirect 39 | gls bank 40 | deutsche pfandbriefbank 41 | kfw 42 | Landesbank Berlin Holding 43 | Landesbank Hessen-Thüringen 44 | n26 45 | hsh nordbank,hamburg 46 | nrw.bank 47 | Wirecard Bank 48 | Consorsbank 49 | landesbank hessen-thüringen 50 | -------------------------------------------------------------------------------- /knowledge_base/data/represented-by.csv: -------------------------------------------------------------------------------- 1 | identifier,bank-account,bank-card 2 | 1,DE82444435329779109646,70120805493 3 | 2,DE70334119137743514093,55604431442 4 | 3,DE76894768662419673111,20771854028 5 | 4,DE78690946739504309806,83707544167 6 | 5,DE50139821739297986888,42718934402 7 | 6,DE77851065666584720184,18806177349 8 | 7,DE29354508073743831293,96703007827 9 | 8,DE10985785971549145687,77564182741 10 | 9,DE93852973047302670654,31663657886 11 | 10,DE67444719107381296569,13760295902 12 | 11,DE50561242036667643899,86282228113 13 | 12,DE51728838437501118370,16099819392 14 | 13,DE22170806079195028710,60071020732 15 | 14,DE27235503909700305654,55102872139 16 | 15,DE92648558961468741310,98589286539 17 | 16,DE44305226222441557697,63746971915 18 | 17,DE50493093512418563495,34798406796 19 | 18,DE46175649932131791856,44611908990 20 | 19,DE65403466947923029055,15358949088 21 | 20,DE32132962269821718345,14141556117 22 | 21,DE33510125629974889896,98907557841 23 | 22,DE28571102327654896575,77206567451 24 | 23,DE20117978677399847042,66457300073 25 | 24,DE98578335434393251543,64657919164 26 | 25,DE28254691836878919381,33826279124 27 | 26,DE31852781254280985515,26327169045 28 | -------------------------------------------------------------------------------- /knowledge_base/data/attribute_mapping.csv: -------------------------------------------------------------------------------- 1 | mapping-key,mapping-value 2 | city,headquarters 3 | headquarter,headquarters 4 | headquarters,headquarters 5 | HQ,headquarters 6 | main office,headquarters 7 | email,email 8 | gender,gender 9 | How much money,balance 10 | how much,balance 11 | money,balance 12 | cash,balance 13 | english website,english-website 14 | english mobile app,english-mobile-app 15 | residents,allowed-residents 16 | free accounts,free-accounts 17 | free worldwide withdrawals,free-worldwide-withdrawals 18 | english customer service,english-customer-service 19 | english customer support,english-customer-service 20 | category,category 21 | execution date,execution-date 22 | amount,amount 23 | reference,reference 24 | signed,sign-date 25 | balance,balance 26 | account type,account-type 27 | opened,opening-date 28 | account-number,account-number 29 | account number,account-number 30 | name,name 31 | country,country 32 | email,email 33 | e mail,email 34 | e-mail,email 35 | last name,last-name 36 | first name,first-name 37 | gender,gender 38 | phone-number,phone-number 39 | phone number,phone-number 40 | female,gender 41 | male,gender 42 | name on card,name-on-card 43 | expiry date,expiry date 44 | expiration,expiry date 45 | created,created-date 46 | card number,card-number 47 | card-number,card-number -------------------------------------------------------------------------------- /knowledge_base/data/person.csv: -------------------------------------------------------------------------------- 1 | first-name,last-name,gender,phone-number,city,email 2 | Catalina,Sargent,female,621-620-0394,Frankfurt,catalinasargent@googlemail.com 3 | Mitchell,Gillis,male,544-132-2820,Dresden,mitchell.gillis@t-online.de 4 | Tommy,Strubbe,male,954-876-4528,Frankfurt,tommystrubbe@googlemail.com 5 | Betty,Lewis,female,800-336-5211,Stuttgart,betty.lewis@gmail.com 6 | Gerry,Vanhoose,female,885-592-1509,Düsseldorf,gerry.vanhoose@googlemail.com 7 | Robert,Farrell,male,375-226-8352,Berlin,robert.farrell@gmail.com 8 | Maxine,Liesmann,female,123-205-3687,Frankfurt,maxineliesmann@web.de 9 | Brenda,Pugh,female,168-113-6864,Leipzig,brenda.pugh@web.de 10 | Calvin,Capps,male,890-208-0554,Dresden,calvincapps@web.de 11 | Bryan,Marett,male,700-498-8237,Stuttgart,bryanmarett@gmx.de 12 | Kelly,Ward,female,200-285-3044,Munich,kellyward@t-online.de 13 | Evan,Murray,male,905-319-8507,Leipzig,evan.murray@web.de 14 | Guy,Burns,male,470-552-2004,Leipzig,guy.burns@gmx.de 15 | Jason,Garrett,male,646-502-4358,Frankfurt,jasongarrett@googlemail.com 16 | Ashley,Lanasa,female,637-468-6465,Berlin,ashleylanasa@gmail.com 17 | Patricia,Mclendon,female,669-475-4310,Frankfurt,patricia.mclendon@gmx.de 18 | Rose,Swenson,female,305-158-4879,Dresden,rose.swenson@googlemail.com 19 | Essie,Leisenring,female,514-494-7172,Berlin,essieleisenring@gmx.de 20 | Martha,Raymond,female,787-253-3055,Dresden,martha.raymond@web.de 21 | Wendi,Pasch,female,732-418-3036,Leipzig,wendipasch@web.de 22 | -------------------------------------------------------------------------------- /schema.py: -------------------------------------------------------------------------------- 1 | schema = { 2 | "transaction": { 3 | "attributes": ["category", "execution-date", "amount", "reference"], 4 | "key": "identifier", 5 | "representation": [ 6 | "execution-date", 7 | "reference", 8 | "account-of-receiver.account-number", 9 | "amount", 10 | ], 11 | }, 12 | "contract": {"attributes": ["sign-date"], "key": "identifier", "representation": ["identifier"]}, 13 | "account": { 14 | "attributes": ["balance", "account-type", "opening-date", "account-number"], 15 | "key": "account-number", 16 | "representation": ["provider.name", "account-number", "account-type"], 17 | }, 18 | "bank": { 19 | "attributes": [ 20 | "name", 21 | "headquarters", 22 | "country", 23 | "english-website", 24 | "english-mobile-app", 25 | "allowed-residents", 26 | "free-accounts", 27 | "free-worldwide-withdrawals", 28 | "english-customer-service", 29 | ], 30 | "key": "name", 31 | "representation": ["name"], 32 | }, 33 | "person": { 34 | "attributes": [ 35 | "email", 36 | "last-name", 37 | "first-name", 38 | "gender", 39 | "phone-number", 40 | "city", 41 | ], 42 | "key": "email", 43 | "representation": ["first-name", "last-name"], 44 | }, 45 | "card": { 46 | "attributes": ["name-on-card", "expiry-date", "created-date", "card-number"], 47 | "key": "card-number", 48 | "representation": ["name-on-card", "card-number"], 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /knowledge_base/lookup_tables.py: -------------------------------------------------------------------------------- 1 | import os 2 | from grakn.client import GraknClient 3 | 4 | 5 | KEYSPACE = "banking" 6 | URI = "localhost:48555" 7 | 8 | 9 | def execute_entity_query(query): 10 | with GraknClient(uri=URI) as client: 11 | with client.session(keyspace=KEYSPACE) as session: 12 | with session.transaction().read() as read_transaction: 13 | result = read_transaction.query(query) 14 | 15 | concepts = result.collect_concepts() 16 | 17 | entities = [] 18 | 19 | for c in concepts: 20 | attrs = c.attributes() 21 | entity = {"id": c.id} 22 | for each in attrs: 23 | entity[each.type().label()] = each.value() 24 | entities.append(entity) 25 | 26 | return entities 27 | 28 | 29 | def get_entities(entity_type): 30 | return execute_entity_query(f"match $x isa {entity_type}; get;") 31 | 32 | 33 | def write_to_file(file_name, entities): 34 | os.makedirs(os.path.dirname(file_name), exist_ok=True) 35 | with open(file_name, "w+", encoding="utf-8") as f: 36 | for e in entities: 37 | f.write(f"{e}\n") 38 | 39 | 40 | def run(): 41 | 42 | entities = get_entities("person") 43 | people = list(map(lambda x: x["first-name"] + " " + x["last-name"], entities)) 44 | people = people + list(map(lambda x: x["first-name"], entities)) 45 | write_to_file("lookup_person.txt", set(people)) 46 | 47 | entities = get_entities("bank") 48 | bank = list(map(lambda x: x["name"], entities)) 49 | write_to_file("lookup_bank.txt", set(bank)) 50 | 51 | 52 | if __name__ == "__main__": 53 | run() 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # rasa 107 | models 108 | rasa.db 109 | tracker.db 110 | -------------------------------------------------------------------------------- /knowledge_base/data/account.csv: -------------------------------------------------------------------------------- 1 | balance,account-number,opening-date,account-type 2 | 267.69,DE82444435329779109646,2019-01-16T10:49:31.641721,credit 3 | 2112.10,DE70334119137743514093,2017-09-12T10:49:31.641763,credit 4 | 2447.25,DE76894768662419673111,2018-05-23T10:49:31.641784,savings 5 | 6195.25,DE78690946739504309806,2016-11-17T10:49:31.641806,debit 6 | 4300.66,DE50139821739297986888,2017-07-02T10:49:31.641828,debit 7 | 1753.64,DE77851065666584720184,2018-08-07T10:49:31.641848,savings 8 | 1996.42,DE29354508073743831293,2019-06-18T10:49:31.641870,credit 9 | 7774.72,DE10985785971549145687,2019-05-20T10:49:31.641890,savings 10 | 9772.25,DE93852973047302670654,2018-05-24T10:49:31.641916,credit 11 | 4267.75,DE67444719107381296569,2017-08-10T10:49:31.641937,credit 12 | 3598.21,DE50561242036667643899,2018-12-05T10:49:31.641958,credit 13 | 9470.51,DE51728838437501118370,2017-09-18T10:49:31.641981,savings 14 | 5418.80,DE22170806079195028710,2018-11-21T10:49:31.642000,debit 15 | 6918.22,DE27235503909700305654,2018-09-21T10:49:31.642023,credit 16 | 3970.97,DE92648558961468741310,2016-10-18T10:49:31.642045,savings 17 | 8741.27,DE44305226222441557697,2019-04-24T10:49:31.642065,debit 18 | 8902.26,DE50493093512418563495,2018-04-26T10:49:31.642087,debit 19 | 2994.91,DE46175649932131791856,2018-06-22T10:49:31.642108,credit 20 | 2015.93,DE65403466947923029055,2017-09-13T10:49:31.642129,credit 21 | 4221.74,DE32132962269821718345,2016-10-28T10:49:31.642153,savings 22 | 4171.96,DE33510125629974889896,2018-01-19T10:49:31.642174,credit 23 | 196.38,DE28571102327654896575,2017-06-23T10:49:31.642193,savings 24 | 6082.94,DE20117978677399847042,2016-10-26T10:49:31.642214,savings 25 | 5827.99,DE98578335434393251543,2017-03-22T10:49:31.642235,credit 26 | 3468.22,DE28254691836878919381,2019-06-24T10:49:31.642256,credit 27 | 6800.10,DE31852781254280985515,2019-05-28T10:49:31.642279,savings 28 | -------------------------------------------------------------------------------- /knowledge_base/data/card.csv: -------------------------------------------------------------------------------- 1 | card-number,name-on-card,created-date,expiry-date 2 | 70120805493,Catalina Sargent,2019-01-17T10:49:31.641721,2029-01-14T10:49:31.641721 3 | 55604431442,Mitchell Gillis,2017-09-14T10:49:31.641763,2027-09-12T10:49:31.641763 4 | 20771854028,Mitchell Gillis,2018-05-29T10:49:31.641784,2028-05-26T10:49:31.641784 5 | 83707544167,Tommy Strubbe,2016-11-24T10:49:31.641806,2026-11-22T10:49:31.641806 6 | 42718934402,Betty Lewis,2017-07-09T10:49:31.641828,2027-07-07T10:49:31.641828 7 | 18806177349,Betty Lewis,2018-08-15T10:49:31.641848,2028-08-12T10:49:31.641848 8 | 96703007827,Gerry Vanhoose,2019-06-21T10:49:31.641870,2029-06-18T10:49:31.641870 9 | 77564182741,Robert Farrell,2019-05-22T10:49:31.641890,2029-05-19T10:49:31.641890 10 | 31663657886,Maxine Liesmann,2018-06-03T10:49:31.641916,2028-05-31T10:49:31.641916 11 | 13760295902,Brenda Pugh,2017-08-20T10:49:31.641937,2027-08-18T10:49:31.641937 12 | 86282228113,Calvin Capps,2018-12-09T10:49:31.641958,2028-12-06T10:49:31.641958 13 | 16099819392,Bryan Marett,2017-09-19T10:49:31.641981,2027-09-17T10:49:31.641981 14 | 60071020732,Bryan Marett,2018-11-28T10:49:31.642000,2028-11-25T10:49:31.642000 15 | 55102872139,Kelly Ward,2018-10-01T10:49:31.642023,2028-09-28T10:49:31.642023 16 | 98589286539,Evan Murray,2016-10-26T10:49:31.642045,2026-10-24T10:49:31.642045 17 | 63746971915,Guy Burns,2019-04-25T10:49:31.642065,2029-04-22T10:49:31.642065 18 | 34798406796,Jason Garrett,2018-05-04T10:49:31.642087,2028-05-01T10:49:31.642087 19 | 44611908990,Ashley Lanasa,2018-07-01T10:49:31.642108,2028-06-28T10:49:31.642108 20 | 15358949088,Patricia Mclendon,2017-09-22T10:49:31.642129,2027-09-20T10:49:31.642129 21 | 14141556117,Patricia Mclendon,2016-10-30T10:49:31.642153,2026-10-28T10:49:31.642153 22 | 98907557841,Rose Swenson,2018-01-29T10:49:31.642174,2028-01-27T10:49:31.642174 23 | 77206567451,Rose Swenson,2017-06-28T10:49:31.642193,2027-06-26T10:49:31.642193 24 | 66457300073,Essie Leisenring,2016-10-30T10:49:31.642214,2026-10-28T10:49:31.642214 25 | 64657919164,Martha Raymond,2017-03-31T10:49:31.642235,2027-03-29T10:49:31.642235 26 | 33826279124,Wendi Pasch,2019-06-25T10:49:31.642256,2029-06-22T10:49:31.642256 27 | 26327169045,Wendi Pasch,2019-06-07T10:49:31.642279,2029-06-04T10:49:31.642279 28 | -------------------------------------------------------------------------------- /knowledge_base/data/contract.csv: -------------------------------------------------------------------------------- 1 | identifier,sign-date,provider,customer,offer 2 | 1,2019-01-13T10:49:31.641721,Commerzbank,catalinasargent@googlemail.com,DE82444435329779109646 3 | 2,2017-09-11T10:49:31.641763,bunq,mitchell.gillis@t-online.de,DE70334119137743514093 4 | 3,2018-05-21T10:49:31.641784,Comdirect,mitchell.gillis@t-online.de,DE76894768662419673111 5 | 4,2016-11-15T10:49:31.641806,Targobank,tommystrubbe@googlemail.com,DE78690946739504309806 6 | 5,2017-06-29T10:49:31.641828,DKB,betty.lewis@gmail.com,DE50139821739297986888 7 | 6,2018-08-05T10:49:31.641848,Deutsche Bank,betty.lewis@gmail.com,DE77851065666584720184 8 | 7,2019-06-17T10:49:31.641870,N26,gerry.vanhoose@googlemail.com,DE29354508073743831293 9 | 8,2019-05-18T10:49:31.641890,DKB,robert.farrell@gmail.com,DE10985785971549145687 10 | 9,2018-05-22T10:49:31.641916,Commerzbank,maxineliesmann@web.de,DE93852973047302670654 11 | 10,2017-08-09T10:49:31.641937,Targobank,brenda.pugh@web.de,DE67444719107381296569 12 | 11,2018-12-04T10:49:31.641958,Comdirect,calvincapps@web.de,DE50561242036667643899 13 | 12,2017-09-16T10:49:31.641981,Commerzbank,bryanmarett@gmx.de,DE51728838437501118370 14 | 13,2018-11-18T10:49:31.642000,N26,bryanmarett@gmx.de,DE22170806079195028710 15 | 14,2018-09-18T10:49:31.642023,Commerzbank,kellyward@t-online.de,DE27235503909700305654 16 | 15,2016-10-17T10:49:31.642045,Commerzbank,evan.murray@web.de,DE92648558961468741310 17 | 16,2019-04-23T10:49:31.642065,DKB,guy.burns@gmx.de,DE44305226222441557697 18 | 17,2018-04-25T10:49:31.642087,Comdirect,jasongarrett@googlemail.com,DE50493093512418563495 19 | 18,2018-06-19T10:49:31.642108,Commerzbank,ashleylanasa@gmail.com,DE46175649932131791856 20 | 19,2017-09-11T10:49:31.642129,DKB,patricia.mclendon@gmx.de,DE65403466947923029055 21 | 20,2016-10-27T10:49:31.642153,bunq,patricia.mclendon@gmx.de,DE32132962269821718345 22 | 21,2018-01-16T10:49:31.642174,Commerzbank,rose.swenson@googlemail.com,DE33510125629974889896 23 | 22,2017-06-21T10:49:31.642193,Comdirect,rose.swenson@googlemail.com,DE28571102327654896575 24 | 23,2016-10-23T10:49:31.642214,Targobank,essieleisenring@gmx.de,DE20117978677399847042 25 | 24,2017-03-20T10:49:31.642235,Targobank,martha.raymond@web.de,DE98578335434393251543 26 | 25,2019-06-22T10:49:31.642256,bunq,wendipasch@web.de,DE28254691836878919381 27 | 26,2019-05-26T10:49:31.642279,Targobank,wendipasch@web.de,DE31852781254280985515 28 | -------------------------------------------------------------------------------- /domain.yml: -------------------------------------------------------------------------------- 1 | session_config: 2 | session_expiration_time: 60.0 3 | carry_over_slots_to_new_session: true 4 | intents: 5 | - affirm 6 | - bye 7 | - compare_entities 8 | - deny 9 | - greet 10 | - help 11 | - out_of_scope 12 | - query_attribute 13 | - query_entities 14 | - resolve_entity 15 | entities: 16 | - account 17 | - account_type 18 | - amount 19 | - attribute 20 | - bank 21 | - card 22 | - category 23 | - country 24 | - date 25 | - email 26 | - entities 27 | - entity_type 28 | - gender 29 | - mention 30 | - person 31 | - phone_number 32 | - reference 33 | - transaction 34 | slots: 35 | account: 36 | type: text 37 | account_number: 38 | type: text 39 | account_type: 40 | type: text 41 | allowed_residents: 42 | type: text 43 | amount: 44 | type: text 45 | attribute: 46 | type: text 47 | balance: 48 | type: text 49 | bank: 50 | type: text 51 | card: 52 | type: text 53 | card_number: 54 | type: text 55 | category: 56 | type: text 57 | country: 58 | type: text 59 | created_date: 60 | type: text 61 | date: 62 | type: text 63 | email: 64 | type: text 65 | english_customer_service: 66 | type: text 67 | english_mobile_app: 68 | type: text 69 | english_website: 70 | type: text 71 | entity_type: 72 | type: text 73 | execution_date: 74 | type: text 75 | expiry_date: 76 | type: text 77 | free_accounts: 78 | type: text 79 | free_worldwide_withdrawals: 80 | type: text 81 | gender: 82 | type: text 83 | headquarters: 84 | type: text 85 | listed_items: 86 | type: list 87 | mention: 88 | type: text 89 | name: 90 | type: text 91 | name_on_card: 92 | type: text 93 | opening_date: 94 | type: text 95 | person: 96 | type: text 97 | phone_number: 98 | type: text 99 | reference: 100 | type: text 101 | transaction: 102 | type: text 103 | templates: 104 | utter_greet: 105 | - text: Hi. I can give you some information about banks or I can give you some details 106 | about the accounts you own. What do you want to know? 107 | utter_goodbye: 108 | - text: Talk to you later! 109 | - text: Bye. 110 | utter_ok: 111 | - text: Ok 112 | utter_rephrase: 113 | - text: Can you please rephrase? 114 | - text: Sorry, I didn't get that. Can you rephrase? 115 | utter_out_of_scope: 116 | - text: Sorry, I cannot help you with that. 117 | - text: I'm not able to help you with that. 118 | utter_help: 119 | - text: I can tell you some facts about different banks. I can answer some questions 120 | about your accounts. And I can show you your recent transactions. 121 | actions: 122 | - action_compare_entities 123 | - action_query_attribute 124 | - action_query_entities 125 | - action_resolve_entity 126 | - utter_greet 127 | - utter_goodbye 128 | - utter_ok 129 | - utter_rephrase 130 | - utter_out_of_scope 131 | - utter_help 132 | -------------------------------------------------------------------------------- /knowledge_base/schema.gql: -------------------------------------------------------------------------------- 1 | define 2 | 3 | # entities 4 | 5 | person sub entity, 6 | plays customer, 7 | key email, 8 | has first-name, 9 | has last-name, 10 | has city, 11 | has phone-number, 12 | has gender; 13 | 14 | bank sub entity, 15 | plays provider, 16 | key name, 17 | has country, 18 | has headquarters, 19 | has free-accounts, 20 | has english-customer-service, 21 | has english-website, 22 | has english-mobile-app, 23 | has free-worldwide-withdrawals, 24 | has allowed-residents; 25 | 26 | account sub entity, 27 | plays bank-account, 28 | key account-number, 29 | has opening-date, 30 | has balance, 31 | has account-type, 32 | plays offer, 33 | plays account-of-receiver, 34 | plays account-of-creator; 35 | 36 | card sub entity, 37 | plays bank-card, 38 | key card-number, 39 | has name-on-card, 40 | has expiry-date, 41 | has created-date; 42 | 43 | mapping sub entity, 44 | has mapping-key, 45 | has mapping-value; 46 | 47 | mention-mapping sub mapping; 48 | entity-type-mapping sub mapping; 49 | attribute-mapping sub mapping; 50 | 51 | # relationships 52 | 53 | contract sub relation, 54 | relates customer, 55 | relates offer, 56 | relates provider, 57 | key identifier, 58 | has sign-date; 59 | 60 | transaction sub relation, 61 | relates account-of-receiver, 62 | relates account-of-creator, 63 | key identifier, 64 | has amount, 65 | has reference, 66 | has category, 67 | has execution-date; 68 | 69 | represented-by sub relation, 70 | relates bank-account, 71 | relates bank-card, 72 | key identifier; 73 | 74 | # attribute 75 | 76 | mapping-key sub attribute, 77 | datatype string; 78 | 79 | mapping-value sub attribute, 80 | datatype string; 81 | 82 | execution-date sub attribute, 83 | datatype date; 84 | 85 | created-date sub attribute, 86 | datatype date; 87 | 88 | expiry-date sub attribute, 89 | datatype date; 90 | 91 | sign-date sub attribute, 92 | datatype date; 93 | 94 | opening-date sub attribute, 95 | datatype date; 96 | 97 | email sub attribute, 98 | datatype string; 99 | 100 | name sub attribute, 101 | datatype string; 102 | 103 | first-name sub attribute, 104 | datatype string; 105 | 106 | last-name sub attribute, 107 | datatype string; 108 | 109 | city sub attribute, 110 | datatype string; 111 | 112 | headquarters sub attribute, 113 | datatype string; 114 | 115 | balance sub attribute, 116 | datatype double; 117 | 118 | account-number sub attribute, 119 | datatype string; 120 | 121 | account-type sub attribute, 122 | datatype string; 123 | 124 | card-number sub attribute, 125 | datatype long; 126 | 127 | amount sub attribute, 128 | datatype double; 129 | 130 | reference sub attribute, 131 | datatype string; 132 | 133 | category sub attribute, 134 | datatype string; 135 | 136 | phone-number sub attribute, 137 | datatype string; 138 | 139 | gender sub attribute, 140 | datatype string; 141 | 142 | country sub attribute, 143 | datatype string; 144 | 145 | allowed-residents sub attribute, 146 | datatype string; 147 | 148 | free-accounts sub attribute, 149 | datatype boolean; 150 | 151 | english-customer-service sub attribute, 152 | datatype boolean; 153 | 154 | english-website sub attribute, 155 | datatype boolean; 156 | 157 | english-mobile-app sub attribute, 158 | datatype boolean; 159 | 160 | free-worldwide-withdrawals sub attribute, 161 | datatype boolean; 162 | 163 | identifier sub attribute, 164 | datatype long; 165 | 166 | name-on-card sub attribute, 167 | datatype string; 168 | -------------------------------------------------------------------------------- /data/stories.md: -------------------------------------------------------------------------------- 1 | ## greet 2 | * greet 3 | - utter_greet 4 | 5 | ## goodbye 6 | * bye 7 | - utter_goodbye 8 | 9 | ## query entities 10 | * query_entities 11 | - action_query_entities 12 | - slot{"category": null} 13 | 14 | ## query attribute 15 | * query_attribute 16 | - action_query_attribute 17 | - slot{"mention": null} 18 | - slot{"bank": "N26"} 19 | 20 | ## resolve entity 21 | * resolve_entity 22 | - action_resolve_entity 23 | - slot{"mention": null} 24 | - slot{"bank": "N26"} 25 | - slot{"account": "DE76894768662419673111"} 26 | 27 | ## out of scope 28 | * out_of_scope 29 | - utter_out_of_scope 30 | 31 | ## help 32 | * help 33 | - utter_help 34 | 35 | ## conversation #1 36 | * greet 37 | - utter_greet 38 | * query_entities 39 | - action_query_entities 40 | - slot{"entity_type": "transaction"} 41 | - slot{"listed_items": []} 42 | - slot{"transaction": ""} 43 | * compare_entities 44 | - action_compare_entities 45 | * query_attribute 46 | - action_query_attribute 47 | - slot{"mention": null} 48 | - slot{"account": "DE89370400440532013000"} 49 | * query_attribute 50 | - action_query_attribute 51 | * bye 52 | - utter_goodbye 53 | 54 | ## conversation #2 55 | * greet 56 | - utter_greet 57 | * query_entities 58 | - action_query_entities 59 | - slot{"entity_type": "account"} 60 | - slot{"listed_items": []} 61 | - slot{"account": ""} 62 | * query_attribute 63 | - action_query_attribute 64 | - slot{"mention": null} 65 | - slot{"transaction": "123"} 66 | * bye 67 | - utter_goodbye 68 | 69 | ## conversation #3 70 | * greet 71 | - utter_greet 72 | * query_attribute 73 | - action_query_attribute 74 | - slot{"mention": null} 75 | - slot{"card": null} 76 | * bye 77 | - utter_goodbye 78 | 79 | ## conversation #4 80 | * greet 81 | - utter_greet 82 | * query_attribute 83 | - action_query_attribute 84 | - slot{"mention": null} 85 | - slot{"person": null} 86 | * query_entities 87 | - action_query_entities 88 | * compare_entities 89 | - action_compare_entities 90 | * bye 91 | - utter_goodbye 92 | 93 | ## conversation #5 94 | * greet 95 | - utter_greet 96 | * query_entities 97 | - action_query_entities 98 | - slot{"entity_type": "transaction"} 99 | - slot{"listed_items": []} 100 | - slot{"transaction": ""} 101 | * out_of_scope 102 | - utter_out_of_scope 103 | * query_attribute 104 | - action_query_attribute 105 | - slot{"mention": null} 106 | - slot{"account": "DE89370400440532013000"} 107 | * bye 108 | - utter_goodbye 109 | 110 | ## conversation #6 111 | * greet 112 | - utter_greet 113 | * out_of_scope 114 | - utter_out_of_scope 115 | * query_attribute 116 | - action_query_attribute 117 | - slot{"mention": null} 118 | - slot{"person": null} 119 | * query_entities 120 | - action_query_entities 121 | * compare_entities 122 | - action_compare_entities 123 | * out_of_scope 124 | - utter_out_of_scope 125 | * bye 126 | - utter_goodbye 127 | 128 | ## conversation #7 129 | * greet 130 | - utter_greet 131 | * query_entities 132 | - action_query_entities 133 | - slot{"entity_type": "account"} 134 | - slot{"listed_items": []} 135 | - slot{"account": ""} 136 | * query_attribute 137 | - action_query_attribute 138 | - slot{"mention": null} 139 | - slot{"transaction": "123"} 140 | * out_of_scope 141 | - utter_out_of_scope 142 | * bye 143 | - utter_goodbye 144 | 145 | ## conversation #8 146 | * greet 147 | - utter_greet 148 | * query_entities 149 | - action_query_entities 150 | - slot{"entity_type": "transaction"} 151 | - slot{"listed_items": []} 152 | - slot{"transaction": ""} 153 | * out_of_scope 154 | - utter_out_of_scope 155 | * help 156 | - utter_help 157 | * query_attribute 158 | - action_query_attribute 159 | - slot{"mention": null} 160 | - slot{"account": "DE89370400440532013000"} 161 | * query_attribute 162 | - action_query_attribute 163 | * bye 164 | - utter_goodbye 165 | 166 | ## conversation #9 167 | * greet 168 | - utter_greet 169 | * out_of_scope 170 | - utter_out_of_scope 171 | * help 172 | - utter_help 173 | * query_entities 174 | - action_query_entities 175 | - slot{"entity_type": "transaction"} 176 | - slot{"listed_items": []} 177 | - slot{"transaction": ""} 178 | * query_attribute 179 | - action_query_attribute 180 | - slot{"mention": null} 181 | - slot{"account": "DE89370400440532013000"} 182 | * bye 183 | - utter_goodbye -------------------------------------------------------------------------------- /knowledge_base/migrate.py: -------------------------------------------------------------------------------- 1 | from grakn.client import GraknClient 2 | import csv 3 | 4 | 5 | def build_banking_graph(inputs): 6 | with GraknClient(uri="localhost:48555") as client: 7 | with client.session(keyspace="banking") as session: 8 | for input in inputs: 9 | print("Loading from [" + input["data_path"] + "] into Grakn ...") 10 | load_data_into_grakn(input, session) 11 | 12 | 13 | def load_data_into_grakn(input, session): 14 | items = parse_data_to_dictionaries(input) 15 | 16 | for item in items: 17 | with session.transaction().write() as transaction: 18 | graql_insert_query = input["template"](item) 19 | transaction.query(graql_insert_query) 20 | transaction.commit() 21 | 22 | print(f"Inserted {str(len(items))} items from [{input['data_path']}] into Grakn.") 23 | 24 | 25 | def bank_template(bank): 26 | graql_insert_query = "insert $bank isa bank" 27 | graql_insert_query += ', has name "' + bank["name"] + '"' 28 | graql_insert_query += ', has headquarters "' + bank["headquarters"] + '"' 29 | graql_insert_query += ', has country "' + bank["country"] + '"' 30 | graql_insert_query += ", has free-accounts " + bank["free-accounts"] 31 | graql_insert_query += ( 32 | ", has english-customer-service " + bank["english-customer-service"] 33 | ) 34 | graql_insert_query += ", has english-website " + bank["english-website"] 35 | graql_insert_query += ", has english-mobile-app " + bank["english-mobile-app"] 36 | graql_insert_query += ( 37 | ", has free-worldwide-withdrawals " + bank["free-worldwide-withdrawals"] 38 | ) 39 | graql_insert_query += ', has allowed-residents "' + bank["allowed-residents"] + '"' 40 | graql_insert_query += ";" 41 | return graql_insert_query 42 | 43 | 44 | def person_template(person): 45 | graql_insert_query = "insert $person isa person" 46 | graql_insert_query += ', has email "' + person["email"] + '"' 47 | graql_insert_query += ', has first-name "' + person["first-name"] + '"' 48 | graql_insert_query += ', has last-name "' + person["last-name"] + '"' 49 | graql_insert_query += ', has city "' + person["city"] + '"' 50 | graql_insert_query += ', has phone-number "' + person["phone-number"] + '"' 51 | graql_insert_query += ', has gender "' + person["gender"] + '"' 52 | graql_insert_query += ";" 53 | return graql_insert_query 54 | 55 | 56 | def account_template(account): 57 | graql_insert_query = "insert $account isa account" 58 | graql_insert_query += ", has balance " + str(account["balance"]) 59 | graql_insert_query += ', has account-number "' + account["account-number"] + '"' 60 | graql_insert_query += ', has account-type "' + account["account-type"] + '"' 61 | graql_insert_query += ", has opening-date " + str(account["opening-date"]) 62 | graql_insert_query += ";" 63 | return graql_insert_query 64 | 65 | 66 | def card_template(card): 67 | graql_insert_query = "insert $card isa card" 68 | graql_insert_query += ', has name-on-card "' + card["name-on-card"] + '"' 69 | graql_insert_query += ", has card-number " + card["card-number"] 70 | graql_insert_query += ", has expiry-date " + card["expiry-date"] 71 | graql_insert_query += ", has created-date " + card["created-date"] 72 | graql_insert_query += ";" 73 | return graql_insert_query 74 | 75 | 76 | def attribute_mapping_template(mapping): 77 | graql_insert_query = "insert $mapping isa attribute-mapping" 78 | graql_insert_query += ", has mapping-key '" + mapping["mapping-key"] + "'" 79 | graql_insert_query += ", has mapping-value '" + mapping["mapping-value"] + "'" 80 | graql_insert_query += ";" 81 | return graql_insert_query 82 | 83 | 84 | def entity_type_mapping_template(mapping): 85 | graql_insert_query = "insert $mapping isa entity-type-mapping" 86 | graql_insert_query += ", has mapping-key '" + mapping["mapping-key"] + "'" 87 | graql_insert_query += ", has mapping-value '" + mapping["mapping-value"] + "'" 88 | graql_insert_query += ";" 89 | return graql_insert_query 90 | 91 | 92 | def mention_mapping_template(mapping): 93 | graql_insert_query = "insert $mapping isa mention-mapping" 94 | graql_insert_query += ", has mapping-key '" + mapping["mapping-key"] + "'" 95 | graql_insert_query += ", has mapping-value '" + mapping["mapping-value"] + "'" 96 | graql_insert_query += ";" 97 | return graql_insert_query 98 | 99 | 100 | def contract_template(contract): 101 | graql_insert_query = ( 102 | 'match $bank isa bank, has name "' + contract["provider"] + '"; ' 103 | ) 104 | graql_insert_query += ( 105 | ' $customer isa person, has email "' + contract["customer"] + '"; ' 106 | ) 107 | graql_insert_query += ( 108 | ' $account isa account, has account-number "' + contract["offer"] + '"; ' 109 | ) 110 | graql_insert_query += " insert $contract(provider: $bank, customer: $customer, offer: $account) isa contract; " 111 | graql_insert_query += "$contract has identifier " + str(contract["identifier"]) + "; " 112 | graql_insert_query += "$contract has sign-date " + contract["sign-date"] + "; " 113 | 114 | return graql_insert_query 115 | 116 | 117 | def represented_by_template(represented_by): 118 | graql_insert_query = ( 119 | 'match $account isa account, has account-number "' 120 | + represented_by["bank-account"] 121 | + '";' 122 | ) 123 | graql_insert_query += ( 124 | " $card isa card, has card-number " + represented_by["bank-card"] + "; " 125 | ) 126 | graql_insert_query += " insert $representation(bank-card: $card, bank-account: $account) isa represented-by; " 127 | graql_insert_query += "$representation has identifier " + represented_by["identifier"] + "; " 128 | return graql_insert_query 129 | 130 | 131 | def transaction_template(transaction): 132 | graql_insert_query = ( 133 | " match $account-of-receiver isa account, has account-number '" 134 | + transaction["account-of-receiver"] 135 | + "';" 136 | ) 137 | graql_insert_query += ( 138 | " $account-of-creator isa account, has account-number '" 139 | + transaction["account-of-creator"] 140 | + "';" 141 | ) 142 | graql_insert_query += ( 143 | "insert $transaction(account-of-receiver: $account-of-receiver, account-of-creator: $account-of-creator) isa transaction; " 144 | + "$transaction has identifier " 145 | + str(transaction["identifier"]) 146 | + "; " 147 | + "$transaction has amount " 148 | + str(transaction["amount"]) 149 | + "; " 150 | + "$transaction has reference '" 151 | + transaction["reference"] 152 | + "';" 153 | + "$transaction has category '" 154 | + transaction["category"] 155 | + "';" 156 | + "$transaction has execution-date " 157 | + transaction["execution-date"] 158 | + ";" 159 | ) 160 | return graql_insert_query 161 | 162 | 163 | def parse_data_to_dictionaries(input): 164 | items = [] 165 | with open(input["data_path"] + ".csv") as data: # 1 166 | for row in csv.DictReader(data, skipinitialspace=True): 167 | item = {key: value for key, value in row.items()} 168 | items.append(item) # 2 169 | return items 170 | 171 | 172 | if __name__ == "__main__": 173 | inputs = [ 174 | {"data_path": "./knowledge_base/data/person", "template": person_template}, 175 | {"data_path": "./knowledge_base/data/account", "template": account_template}, 176 | {"data_path": "./knowledge_base/data/bank", "template": bank_template}, 177 | {"data_path": "./knowledge_base/data/card", "template": card_template}, 178 | { 179 | "data_path": "./knowledge_base/data/attribute_mapping", 180 | "template": attribute_mapping_template, 181 | }, 182 | { 183 | "data_path": "./knowledge_base/data/mention_mapping", 184 | "template": mention_mapping_template, 185 | }, 186 | { 187 | "data_path": "./knowledge_base/data/entity_type_mapping", 188 | "template": entity_type_mapping_template, 189 | }, 190 | { 191 | "data_path": "./knowledge_base/data/represented-by", 192 | "template": represented_by_template, 193 | }, 194 | { 195 | "data_path": "./knowledge_base/data/transaction", 196 | "template": transaction_template, 197 | }, 198 | {"data_path": "./knowledge_base/data/contract", "template": contract_template}, 199 | ] 200 | 201 | build_banking_graph(inputs) 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /data/lookup_tables/person.txt: -------------------------------------------------------------------------------- 1 | wayne patterson 2 | manfred 3 | anita 4 | billie 5 | Sabine 6 | Uwe Arnold 7 | Tom Thomas 8 | charles 9 | Karin Keller 10 | grace 11 | Ursula 12 | greg zamora 13 | Martin Schumacher 14 | sebastian kühn 15 | jonas 16 | guy 17 | Jonas Hahn 18 | Otto Heinrich 19 | Hermann 20 | virginia davis 21 | lillian 22 | Werner 23 | Emma Schwarz 24 | edith klein 25 | erna 26 | Lukas 27 | irmgard 28 | felipe 29 | jeff 30 | william page 31 | Finn Hofmann 32 | martin 33 | gordon ibara 34 | iona 35 | Frank 36 | gisela lehmann 37 | betty 38 | leon young 39 | jessica garibay 40 | tim machado 41 | sandra torres 42 | faith 43 | heinrich 44 | katharine boss 45 | sharon marc 46 | odilia 47 | Ilse 48 | doris 49 | george prill 50 | Sebastian 51 | mauricio renfro 52 | kimberly williams 53 | billie makin 54 | jillian 55 | laci 56 | irma 57 | lynn borgmeyer 58 | Margarethe Winter 59 | stefan 60 | peter williams 61 | Peter 62 | heinrich herrmann 63 | karl günther 64 | anita moree 65 | Renate 66 | Martha 67 | sara 68 | Hannah Schulze 69 | Hildegard 70 | basil grant 71 | Günter 72 | walter wolff 73 | king 74 | michael smith 75 | Michael 76 | kurt winkler 77 | april butler 78 | lena 79 | Martha Martin 80 | marie 81 | laura 82 | debbie 83 | aaron terp 84 | daniel 85 | Klaus 86 | lisa 87 | allison 88 | geoffrey 89 | petra schreiber 90 | ursula voigt 91 | Ernst Krüger 92 | elva 93 | jan weiß 94 | christian hoffmann 95 | pauline bauder 96 | barbara velasquez 97 | randy doctor 98 | debra pagan 99 | emerson 100 | evelyn mendoza 101 | charles sanders 102 | joe 103 | Lara Roth 104 | Heinz 105 | walter diaz 106 | Niklas 107 | Jannik Jung 108 | joy 109 | linda hylton 110 | frieda 111 | ilse 112 | friedrich schmitt 113 | Niklas Seidel 114 | aaron 115 | lea lorenz 116 | andy 117 | heike köhler 118 | marsha 119 | helene 120 | kandis petaway 121 | trenton 122 | Hans Maier 123 | gerhard 124 | horst peters 125 | william solorzano 126 | Martina 127 | Sabine Schulte 128 | elfriede wolf 129 | daniel koch 130 | Martina Krämer 131 | stella becerra 132 | george harvey 133 | Bertha Wagner 134 | scott lee 135 | carl 136 | eric 137 | Gertrud 138 | Walter Wolff 139 | brian beals 140 | hannah schulze 141 | dwight 142 | petra 143 | Maria Schmatloch 144 | alice rogers 145 | frank hartmann 146 | Christina Schäfer 147 | kandis 148 | elaine sturm 149 | jillian hall 150 | stefan pohl 151 | phillip 152 | ingrid 153 | karen 154 | glenn 155 | hildegard 156 | Heike Köhler 157 | william harris 158 | gordon lyford 159 | Jonas 160 | Ingrid 161 | stefanie 162 | helmut mayer 163 | dieter 164 | Ilse Lang 165 | friedrich 166 | christina schäfer 167 | Michelle 168 | finn hofmann 169 | Melanie Vogt 170 | helga walter 171 | marsha hammond 172 | Hannah 173 | margarethe winter 174 | stefanie engel 175 | ruth dwyer 176 | steven smith 177 | kyle mason 178 | dayna sweeny 179 | Hermann Kaiser 180 | michael laws 181 | cheryl huddleston 182 | danny reny 183 | gertrude wehling 184 | todd 185 | Uwe 186 | Friedrich 187 | zachary 188 | ernst 189 | tiffany sandford 190 | Gerda Schmitz 191 | Birgit Becker 192 | eric brown 193 | richard sieck 194 | joshua gonzalez 195 | jewell 196 | Angelika Fischer 197 | michael stein 198 | allison walker 199 | dwight martinson 200 | angie 201 | Marie 202 | Thomas 203 | andreas schneider 204 | christa schulz 205 | micheal strubbe 206 | william hill 207 | walter joseph 208 | gloria 209 | elfriede 210 | demetrice loh 211 | tom thomas 212 | Melanie 213 | christian 214 | sandra 215 | monika otto 216 | Walter 217 | goldie yacko 218 | hector hall 219 | Alex 220 | frank 221 | paul 222 | sherry chase 223 | nicole groß 224 | kimberly 225 | scott 226 | heike 227 | lieselotte 228 | Monika Otto 229 | john oglesby 230 | joe soukup 231 | carl billinghurst 232 | diana heath 233 | dennis peccia 234 | carol sabin 235 | birgit 236 | katrin 237 | darlene jackson 238 | danny 239 | erika 240 | allyson herman 241 | Ernst 242 | germaine reaves 243 | Andreas 244 | nadine 245 | christina 246 | lila 247 | donald 248 | marjorie 249 | anna vasquez 250 | Edith Klein 251 | arthur fuller 252 | Nicole 253 | robert lee 254 | Marie Kraus 255 | alan 256 | linnie 257 | Wolfgang 258 | irene joyce 259 | Edith 260 | helga 261 | basil 262 | gerhard krause 263 | alan johnson 264 | joseph vega 265 | Laura 266 | Petra Schreiber 267 | maria marchant 268 | linnie brown 269 | luca 270 | günter schmid 271 | katharine 272 | jonas hahn 273 | deangelo 274 | Jörg Schubert 275 | shelby williams 276 | Karl 277 | glenn cain 278 | george fravel 279 | Irmgard Möller 280 | jerry prater 281 | Anna 282 | Lieselotte 283 | bertha townson 284 | Lena 285 | hans 286 | esther gomez 287 | jack gabriel 288 | katrin frank 289 | larry 290 | patricia 291 | tiffany 292 | barbara 293 | gary 294 | michael rapp 295 | Michael Stein 296 | donald hall 297 | anna meyer 298 | kaye poteat 299 | Lea 300 | Andrea Schmidt 301 | kayleen morales 302 | rachel merchant 303 | Hans 304 | julia 305 | Christian Hoffmann 306 | Gisela Lehmann 307 | george tada 308 | Susanne 309 | gordon 310 | julie 311 | iona pierson 312 | lisa buckley 313 | edwin espada 314 | lynda wilson 315 | Jan Weiß 316 | lukas ludwig 317 | dayna 318 | Nadine 319 | mauricio 320 | uwe arnold 321 | helen costales 322 | Sabrina 323 | lasonya dorow 324 | george obrien 325 | Gabriele Werner 326 | helen russ 327 | cheryl 328 | frankie 329 | Andreas Schneider 330 | james smith 331 | alice luer 332 | josephine 333 | josephine mcdonald 334 | frankie lynch 335 | leon franke 336 | odilia murray 337 | Günter Schmid 338 | finn 339 | shirley kelly 340 | nancy james 341 | Werner Pfeiffer 342 | jessica 343 | tim 344 | frederick 345 | randy 346 | alexander 347 | akela 348 | michelle 349 | Laura Beck 350 | murray porter 351 | ilse lang 352 | julia vogel 353 | paul brandt 354 | jack 355 | manfred böhm 356 | hermann 357 | Bertha 358 | Luca Simon 359 | shirley 360 | thomas reyes 361 | robert 362 | imogene 363 | Elke Neumann 364 | Klaus Berger 365 | rebecca 366 | Katrin 367 | reginald 368 | pedro york 369 | melissa holman 370 | Lena Baumann 371 | Sandra 372 | april 373 | william rains 374 | tania armstrong 375 | bob 376 | edith 377 | jan 378 | angelika 379 | emma 380 | melanie vogt 381 | laura beck 382 | Christian 383 | ruth 384 | sidney myers 385 | elisabeth schröder 386 | mary megown 387 | erika zimmermann 388 | helen butler 389 | john 390 | george mayfield 391 | donna lee 392 | alice 393 | jerry 394 | carol 395 | Heinrich 396 | angela 397 | Nadine Sommer 398 | catherine 399 | Elisabeth Schröder 400 | guy geary 401 | nola lamus 402 | sara kuhn 403 | irma arons 404 | bob smith 405 | sara fielden 406 | Anja Weber 407 | lasonya 408 | larry becerra 409 | christopher 410 | william 411 | martin schumacher 412 | Birgit 413 | Sebastian Kühn 414 | Jannik 415 | elke 416 | helene kircher 417 | maria crowner 418 | Gerhard 419 | bertha 420 | tom 421 | angie moore 422 | reginald steins 423 | kyle douglas 424 | hermann kaiser 425 | ursula 426 | cary scholler 427 | jeffrey rowe 428 | ronald renfro 429 | debbie desalvo 430 | phillip kearns 431 | Helga 432 | wilhelm gaertner 433 | jeffrey 434 | luz 435 | goldie 436 | Erika Zimmermann 437 | sabrina dietrich 438 | grace linney 439 | Manfred Böhm 440 | Daniel 441 | emma schwarz 442 | helen gannett 443 | sabine 444 | kevin 445 | herman 446 | Petra 447 | robert fitts 448 | lara roth 449 | lucille brady 450 | kenneth 451 | Udo 452 | tracey hall 453 | linda landon 454 | Wilhelm Gaertner 455 | hans maier 456 | wolfgang 457 | Finn 458 | hildegard fuchs 459 | heinz 460 | jasmine lamay 461 | jasmine 462 | gertrud meier 463 | dennis 464 | charles polanco 465 | frederick kerns 466 | gabriele werner 467 | todd steffes 468 | catherine pitre 469 | Erna 470 | betty pruchnik 471 | steven fairman 472 | gloria weston 473 | nadine sommer 474 | Maria 475 | zachary spaulding 476 | kevin maurer 477 | jennifer chirinos 478 | Ingrid Scholz 479 | ronald 480 | Stefan Pohl 481 | Jörg 482 | dieter richter 483 | Helmut Mayer 484 | irmgard möller 485 | ute sauer 486 | francisca 487 | Dieter 488 | joseph 489 | roy 490 | karin 491 | tania 492 | debra 493 | pedro 494 | tommy hayden 495 | candice goodwin 496 | dennis bauer 497 | gary smithson 498 | douglas 499 | nola 500 | Akela 501 | Alexander 502 | lea 503 | monika 504 | lieselotte albrecht 505 | roy silver 506 | vida hahn 507 | Friedrich Schmitt 508 | gerda schmitz 509 | ola watkins 510 | lena baumann 511 | Angelika 512 | richard deboard 513 | kyle 514 | douglas hundley 515 | esther 516 | richard 517 | mary sauer 518 | Erika 519 | ellen meadows 520 | karin keller 521 | Kurt 522 | sharon 523 | jerry wolstenholme 524 | martha martin 525 | Gabriele 526 | Jürgen 527 | Dieter Richter 528 | Horst Peters 529 | Elfriede 530 | werner pfeiffer 531 | joseph kelley 532 | Manfred 533 | wilhelm 534 | christopher dalton 535 | otto 536 | jeff harris 537 | Gisela 538 | ricardo patterson 539 | christopher swindle 540 | tim scheulen 541 | linda 542 | ingrid scholz 543 | pamela acree 544 | joy molina 545 | james white 546 | thomas knight 547 | Elke 548 | Heike 549 | greg 550 | Jürgen Friedrich 551 | susanne horn 552 | delbert 553 | sebastian 554 | wayne 555 | gabriele 556 | ellen 557 | jannik 558 | steven 559 | rachel 560 | andrew rucker 561 | andrea 562 | rodney 563 | Paul Brandt 564 | kathy 565 | Heinrich Herrmann 566 | Elfriede Wolf 567 | Ute 568 | deangelo gates 569 | mary bennett 570 | Frank Hartmann 571 | angela webb 572 | peter 573 | Peter Haas 574 | Gerhard Krause 575 | linda greenman 576 | roger 577 | marjorie fossum 578 | mary munsey 579 | Paul 580 | delbert carasco 581 | Christa Schulz 582 | donna bellah 583 | Sabrina Dietrich 584 | marie kraus 585 | Gertrud Meier 586 | allison lane 587 | andreas 588 | karl 589 | greg chism 590 | irene 591 | otto heinrich 592 | Gerda 593 | Katrin Frank 594 | Lieselotte Albrecht 595 | james 596 | christa 597 | Michelle Jäger 598 | bertha wagner 599 | alexander müller 600 | sabine schulte 601 | Hildegard Fuchs 602 | francisca cass 603 | Frieda Lange 604 | Helga Walter 605 | helen 606 | andrew 607 | frieda lange 608 | patricia palmer 609 | Anja 610 | renee 611 | Alexander Müller 612 | Sara Kuhn 613 | renee maupin 614 | horst 615 | heinz könig 616 | pamela 617 | donna 618 | eddie jefferson 619 | Dennis 620 | robert napolitano 621 | nicole 622 | johnnie carns 623 | roger bryant 624 | renate 625 | joshua 626 | Stefanie Engel 627 | Horst 628 | Sandra Ziegler 629 | tim bergmann 630 | lynn 631 | Lukas Ludwig 632 | jürgen friedrich 633 | sabrina 634 | Leon 635 | kathy rubin 636 | lisa schuster 637 | Karin 638 | kenneth wenthold 639 | renate graf 640 | Renate Graf 641 | jörg schubert 642 | sherry 643 | robert ramirez 644 | susanne 645 | trenton cochran 646 | andy stone 647 | virginia 648 | Wilhelm 649 | arthur 650 | timothy pilgrim 651 | elisabeth 652 | michael birkey 653 | Lisa 654 | robert patterson 655 | jürgen 656 | Tim Bergmann 657 | lucille 658 | birgit becker 659 | candice 660 | thomas busch 661 | steven burres 662 | ricardo 663 | pauline 664 | niklas seidel 665 | Daniel Koch 666 | tommy 667 | mary highley 668 | vida 669 | martina krämer 670 | lloyd mccall 671 | roy martinez 672 | hector 673 | Kurt Winkler 674 | Thomas Busch 675 | sandra ziegler 676 | peter haas 677 | Frieda 678 | ute 679 | anja weber 680 | Julia 681 | thomas 682 | Susanne Horn 683 | frank burton 684 | günter 685 | ernst krüger 686 | julie johnson 687 | werner 688 | judson fronczak 689 | Luca 690 | eddie 691 | Ursula Voigt 692 | andrea essex 693 | kurt 694 | kayleen 695 | klaus 696 | anja 697 | maria 698 | edward hall 699 | rebecca marshall 700 | mary 701 | Andrea 702 | angelika fischer 703 | lynda 704 | Martin 705 | Heinz König 706 | karen koth 707 | Erna Braun 708 | sharon holzman 709 | Monika 710 | george 711 | hannah 712 | Julia Vogel 713 | brenda collins 714 | micheal 715 | diana 716 | Lara 717 | lara 718 | tracey 719 | anna 720 | martha 721 | maria schmatloch 722 | danny gallegos 723 | shelby 724 | felipe hines 725 | cary 726 | edward 727 | gerda 728 | Tom 729 | martina 730 | lisa loiacono 731 | michael 732 | kaye 733 | rodney milton 734 | allyson 735 | walter 736 | Emma 737 | Karl Günther 738 | jewell wong 739 | judson 740 | gertrude 741 | leon 742 | nicole taylor 743 | herbert huber 744 | king wilcox 745 | germaine 746 | gertrud 747 | melanie 748 | Otto 749 | nancy 750 | Margarethe 751 | Leon Franke 752 | elaine 753 | timothy labarge 754 | edwin 755 | demetrice 756 | ray 757 | Ute Sauer 758 | jörg 759 | carl hicks 760 | margarethe 761 | Juste 762 | ola 763 | Jan 764 | Dennis Bauer 765 | emerson ramsay 766 | Stefanie 767 | Lea Lorenz 768 | herbert 769 | kenneth peterson 770 | jannik jung 771 | timothy 772 | roger kobashigawa 773 | clara torres 774 | jennifer weathers 775 | brenda 776 | aaron webb 777 | eric laney 778 | doris salinas 779 | helmut 780 | brian 781 | jennifer 782 | Christina 783 | james williams 784 | Irmgard 785 | darlene 786 | lloyd 787 | murray 788 | Christa 789 | kevin dowden 790 | johnnie 791 | andrea schmidt 792 | doris webber 793 | diane 794 | erna braun 795 | william mangina 796 | michelle jäger 797 | daniel fernandez 798 | gisela 799 | lillian pearce 800 | alex 801 | Elisabeth 802 | stella 803 | Herbert Huber 804 | Stefan 805 | klaus berger 806 | clara 807 | elva levin 808 | patricia trainor 809 | melissa 810 | lila arlington 811 | Vladimir 812 | laci austin 813 | diane dietz 814 | ray straw 815 | lukas 816 | niklas 817 | ruth blackmon 818 | faith runnels 819 | Lisa Schuster 820 | sidney 821 | Herbert 822 | imogene hammett 823 | Tim 824 | geoffrey visser 825 | uwe 826 | evelyn 827 | Anna Meyer 828 | Helmut 829 | luca simon 830 | rodney wilkins 831 | herman esparza 832 | debra rivera 833 | luz gomez 834 | Sara 835 | elke neumann 836 | donna cabral 837 | Nicole Groß 838 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial Knowledge Base 2 | 3 | This repository contains the code that is referred to in the tutorial 4 | [Integrating Rasa with knowledge bases](https://blog.rasa.com/integrating-rasa-with-knowledge-bases/). 5 | 6 | 7 | **UPDATE**: 8 | We added [Knowledge Base Actions](https://rasa.com/docs/rasa/core/knowledge-bases/) to Rasa. 9 | [Knowledge Base Actions](https://rasa.com/docs/rasa/core/knowledge-bases/) help you to connect your knowledge base to Rasa more quickly. 10 | Try it out and share your feedback on the [Rasa Community Forum](https://forum.rasa.com/). 11 | 12 | 13 | ## Outline 14 | 15 | 1. [Requirements](#Requirements) 16 | * [Setting up the Graph Database](#Setting-up-the-Graph-Database) 17 | * [Alternative to Graph Databases](#Alternative-to-Graph-Databases) 18 | 2. [Chat with the Bot](#Chat-with-the-Bot) 19 | 3. [Limitations of Knowledge Bases](#Limitations-of-Knowledge-Bases) 20 | 4. [Feedback](#Feedback) 21 | 22 | 23 | ## Requirements 24 | 25 | Install requirements: 26 | ``` 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | ### Setting up the Graph Database 31 | 32 | Our knowledge base is represented by a graph database. 33 | In this repository [Grakn](https://grakn.ai/) is used as a graph database. 34 | However, you can also use any other graph database or use an alternative (see below). 35 | 36 | In order to use this code example, you need to install [Grakn](https://grakn.ai/). 37 | To be able to run this bot, you need version 1.5.7. 38 | Please check the [installation instruction](https://dev.grakn.ai/docs/running-grakn/install-and-run) 39 | of Grakn in order to install it. 40 | Once you installed Grakn, you need to start the Grakn server by executing 41 | ```bash 42 | grakn server start 43 | ``` 44 | You can stop the server by running `grakn server stop`. 45 | 46 | In order to get some data into the graph database you need to execute the following steps: 47 | 1. Create the schema by executing 48 | ```bash 49 | grakn console --keyspace banking --file 50 | ``` 51 | This will create a keyspace `banking` in your Grakn graph database with the schema defined in `knowledge_base/schema.gql`. 52 | 2. Load data into your schema by running 53 | ```bash 54 | python knowledge_base/migrate.py 55 | ``` 56 | Grakn recommends you to write a `migrate.py` script 57 | (see [migration-python](https://dev.grakn.ai/docs/examples/phone-calls-migration-python)) 58 | to load data from csv files into your graph database. 59 | Our migration script loads the data located in `knowledge_base/data` into the keyspace `banking`. 60 | 61 | The graph database is set up and ready to be used. 62 | 63 | ### Alternative to Graph Databases 64 | 65 | If you just have a small knowledge base and you don't want to install and set up a graph database, such as Grakn, 66 | you can also encode your domain knowledge in a data structure, such as a python dictionary. 67 | You can find an example in the file `graph_database.py`. 68 | The file contains an implementation that uses a graph database (class `GraphDatabase`) and an implementation 69 | that simply uses a python dictionary as domain knowledge (class `InMemoryGraph`). 70 | If you want to use the `InMemoryGraph` instead of the `GraphDatabase` in the bot, you need to exchange the 71 | initialization of the graph database in `actions.py`. 72 | But be aware of the fact, that the `InMemoryGraph` does not cover the same knowledge as the `GraphDatabase`. 73 | It just knows about banks and their attributes. 74 | 75 | 76 | ## Chat with the Bot 77 | 78 | Make sure you installed all requirements and your grakn server is running. 79 | 80 | If you want to chat with the bot, execute the following steps: 81 | 1. Train the bot using `rasa train`. 82 | 2. Start the action server with `rasa run actions` in a separate terminal. 83 | 3. Chat with the bot on the command line by executing `rasa shell`. 84 | 85 | If you want to see what slots are set and how confident the bot is in predicting the next action, you should run 86 | the bot in debug mode: `rasa shell --debug`. 87 | 88 | Here are some example questions you can ask the bot: 89 | - “What are my bank options?” 90 | - “What is the headquarter of the first bank?” 91 | - “What accounts do I have?” 92 | - “What is my balance on the second account?” 93 | - “What are my recent transactions?” 94 | 95 | 96 | ## Limitations of Knowledge Bases 97 | 98 | Before we look at the limitations of knowledge bases, let's first take a look, in what way an entity can be referenced: 99 | 100 | | Type | Example | Description | 101 | |---------------------------------|-------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 102 | | direct mention | _Rasa_ is located in _Berlin_. | An entity was mentioned by its name in the text, such as _Rasa_ or _Berlin_. | 103 | | ordinal mention | Where is the headquarters of the _third_ company? | The user was confronted to a list of entities. The user refers to an entity of that list by its position. | 104 | | mention by synonym | What's my _balance_? vs. How much _cash_ do I have? | The user refers to the same entity with different names. | 105 | | ambiguous mention | I want to transfer money to _John_? | The bot can find multiple entities with the referenced name (e.g. _John_). The entity needs to be specified. | 106 | | mention by pronoun | _Rasa_ is located in _Berlin_. _It_ is an open source company. | Reference to a previous mentioned entity by its pronoun, such as _it_. | 107 | | mention by hypernyms & hyponyms | Here are some recent transactions: Raynair 99.95€, Spotify 9.99€. Which do you want to dispute? | The user can answer many things, such as "the subscription", or "the plane ticket". The bot needs to understand what entity the user refers to by those answers. | 108 | | mention by attribute(s) | Where is the headquarters of the _open source_ company that _builds chatbots_? | Find an entity in the knowledge base that fits the mentioned criteria. | 109 | | mention by attribute comparison | Your last transactions: Amazon 99.95€, Netflix 4.99€. Which do you want to dispute? | If the user says, for example, "the bigger one", the bot needs to first resolve the comparison before he can detect the entity the user is referring to. | 110 | 111 | 112 | The bot in this repo can handle some but not all of the cases above. 113 | Let's go over them one by one and take a closer look at what is possible and what are current limitations: 114 | 115 | **direct mention** 116 | 117 | The direct mention is handled by the NER of Rasa. 118 | No knowledge base is needed to recognize an entity in a text. 119 | However, your knowledge base can be used to create [lookup tables](https://rasa.com/docs/rasa/nlu/training-data-format/#lookup-tables), that can then be used to improve the NER. 120 | 121 | **mention by pronoun** 122 | 123 | If an entity is referred to by its pronoun, the bot cannot detect it. 124 | Let's look at the example "My sister has a dog. She loves him." 125 | In the example "him" is referring to "dog" and "she" refers to "sister". 126 | However, for the bot it is hard to figure that out. 127 | Typically, coreference resolution models are used to solve this kind of mention. 128 | Here are some links to repositories: 129 | * [huggingface](https://github.com/huggingface/neuralcoref) 130 | * [allennlp](https://demo.allennlp.org/coreference-resolution/OTM5MjM3) 131 | * [standford nlp](https://nlp.stanford.edu/projects/coref.shtml) 132 | 133 | **ordinal mention** 134 | 135 | With the smart use of slots, your bot is able to resolve an ordinal mention to its real-world entity (see [code snippet](https://github.com/RasaHQ/tutorial-knowledge-base/blob/master/actions.py#L11)). 136 | As soon as multiple entities from the knowledge base are listed, the bot stores those in a specific slot (`listed_items`). 137 | The recognized ordinal mention needs to be mapped to an index and the entity can be picked up from the list of entities using the identified index. 138 | 139 | **mention by attribute(s)** 140 | 141 | Theoretically, your bot can find any entity by its attributes in its knowledge base. 142 | However, if you requested, for example, to name the transaction you did last month to Max, multiple nodes and 143 | relations in your graph database are involved. 144 | The query to fetch the requested entity becomes quite complex. 145 | The bot is currently not able to handle such complex requests. 146 | But, if you simply ask for a specific entity that just involves a node and its attributes in the graph database, 147 | the bot can answer your request. 148 | 149 | **ambiguous mention** 150 | 151 | Your bot should be able to help you resolve an ambiguous mention. 152 | For example, if you want to transfer money to John, but you have transferred money to John Doe and John Mustermann in the past, 153 | the bot needs you to confirm which exact John you meant. 154 | In order to archive that, the bot looks up the ambiguous entity in the knowledge base. 155 | If multiple entities are found, you will be confronted with the list of entities. 156 | You can then specify the entity you meant by, for example, using an ordinal mention. 157 | 158 | **mention by synonym** 159 | 160 | The bot uses mapping tables in the knowledge base to resolve synonyms, e.g. mapping `cash` and `balance` to the same entity. 161 | However, this is limited to the names you defined in those mapping tables. 162 | 163 | **mention by hypernyms & hyponyms** 164 | 165 | The bot cannot handle mentions by hypernyms & hyponyms at the moment. 166 | Knowledge about, for example, Rynair selling plane tickets is missing. 167 | If such knowledge can be encoded in your knowledge base, more complex queries could be used to retrieve the information of interest. 168 | 169 | **mention by attribute comparison** 170 | 171 | If you say something like "Please, dispute the biggest of the just listed transactions.", the bot first needs to 172 | compare multiple entities by certain attributes before it can pick the entity of interest. 173 | As the bot currently cannot perform a comparison, this kind of mention cannot be handled by the bot at the moment. 174 | 175 | 176 | Apart from the limitation already listed per mention type, the bot has some further limitations: 177 | * _Comparing entities is still limited_: 178 | The bot is not able to detect the comparison operator and can therefore not compare multiple entities in a proper way. 179 | For example, if the bot listed your accounts and you are asking "On what account do I currently have more money?". 180 | The bot needs to recognize not only that you are interested in the balance of the listed accounts, but also that 181 | "more money" means that the accounts need to be compared by their balance and you only want to know 182 | about the account with the highest balance. 183 | So far, the bot just lists the requested attribute for all entities and you have to "compare" yourself. 184 | 185 | * _Executing complex queries_: 186 | The bot tries to handle requests in a generic way. 187 | However, some user requests, such as "How much money did I transfer to Max from my N26 account in the last month.", require complex queries. 188 | The example request involves the nodes "person", "bank", and "account" as well as the relations "transaction" and "contract". 189 | As those requests are very specific and the resulting query is complex, the bot needs to handle them separately. 190 | However, this special treatment is currently not implemented, so that the bot is not able to answer complex queries that involve multiple nodes and relations. 191 | 192 | ## Feedback 193 | 194 | If you have any questions about the tutorial or this repository, feel free to share them on [Rasa Community Forum](https://forum.rasa.com/). 195 | -------------------------------------------------------------------------------- /data/nlu.md: -------------------------------------------------------------------------------- 1 | ## intent:affirm 2 | - yes 3 | - of course 4 | - sure 5 | - yeah 6 | - ok 7 | - cool 8 | - go for it 9 | - yep 10 | - yep, will do thank you 11 | - I'm sure I will! 12 | - oh awesome! 13 | - Yes 14 | - accept 15 | - I accept 16 | - i accept 17 | - ok i accept 18 | - I changed my mind. I want to accept it 19 | - ok cool 20 | - alright 21 | - i will! 22 | - ok, I behave now 23 | - yop 24 | - oki doki 25 | - yes please 26 | - yes please! 27 | - jo 28 | - yep if i have to 29 | - amayzing 30 | - confirm 31 | - nice 32 | - coolio 33 | - definitely yes without a doubt 34 | - yas 35 | - yup 36 | - perfect 37 | - sure thing 38 | - absolutely 39 | - Oh, ok 40 | - Sure 41 | - hm, i'd like that 42 | - ja 43 | - sure! 44 | - yes i accept 45 | - Sweet 46 | - amazing! 47 | - how nice! 48 | - cool! 49 | - yay 50 | - yes accept please 51 | - great 52 | - oh cool 53 | - yes 54 | - fine 55 | 56 | ## intent:bye 57 | - goodbye 58 | - goodnight 59 | - good bye 60 | - good night 61 | - see ya 62 | - toodle-oo 63 | - bye bye 64 | - gotta go 65 | - farewell 66 | - catch you later 67 | - bye for now 68 | - bye 69 | - bye was nice talking to you 70 | - bye udo 71 | - bye bye bot 72 | - bye bot 73 | - k byyye #slay 74 | - tlak to you later 75 | - ciao 76 | - Bye bye 77 | - then bye 78 | - tschüssikowski 79 | - bye! 80 | 81 | ## intent:compare_entities 82 | - On which of those accounts do I currently have more [money](attribute)? 83 | - Where do I have more [money](attribute)? 84 | - Which one has more [cash](attribute) on it? 85 | - Which is the one with more [money](attribute)? 86 | - How much [money](attribute) do I have on those accounts? 87 | - on which account do i have more [money](attribute) 88 | - on which of those do i have more [money](attribute)? 89 | - how much [money](attribute) do i have on them? 90 | - what is my [balance](attribute) on those? 91 | - what about the [balance](attribute) on those accounts 92 | 93 | ## intent:deny 94 | - no 95 | - definitely not 96 | - never 97 | - absolutely not 98 | - i don't think so 99 | - i'm afraid not 100 | - no sir 101 | - no ma'am 102 | - no way 103 | - no sorry 104 | - No, not really. 105 | - nah not for me 106 | - nah 107 | - no and no again 108 | - no go 109 | - no thanks 110 | - decline 111 | - deny 112 | - i decline 113 | - never mind 114 | - I'm not giving you my email address 115 | - no I haven't decided yet if I want to sign up 116 | - I don't want to give it to you 117 | - I'm not going to give it to you 118 | - no i don't accept 119 | - no!!!! 120 | - no you did it wrong 121 | - no i can't 122 | - i'm not sure 123 | - NEIN 124 | - nein 125 | - not really 126 | - i guess it means - no 127 | - i don't want to 128 | - i don't want either of those 129 | - nah thanks 130 | - neither of these 131 | 132 | ## intent:greet 133 | - Hi 134 | - Hey 135 | - Hi bot 136 | - Hey bot 137 | - Hello 138 | - Good morning 139 | - hi again 140 | - hi folks 141 | - hi Mister 142 | - hi pal! 143 | - hi there 144 | - greetings 145 | - hello everybody 146 | - hello is anybody there 147 | - hello robot 148 | - hallo 149 | - heeey 150 | - hi hi 151 | - hey 152 | - hey hey 153 | - hello there 154 | - hi 155 | - hello 156 | - yo 157 | - hola 158 | - hi? 159 | - hey bot! 160 | - hello friend 161 | - good morning 162 | - hii 163 | - hello sweet boy 164 | - yoo 165 | - hey there 166 | - hiihihi 167 | - hello sweatheart 168 | - hellooo 169 | - helloooo 170 | - heyo 171 | - ayyyy whaddup 172 | - hello? 173 | - Hallo 174 | - heya 175 | - hey bot 176 | - howdy 177 | - Hellllooooooo 178 | - whats up 179 | - Hei 180 | - Well hello there ;) 181 | - I said, helllllloooooO!!!! 182 | - Heya 183 | - Whats up my bot 184 | - hiii 185 | - heyho 186 | - hey, let's talk 187 | - hey let's talk 188 | - jojojo 189 | - hey dude 190 | - hello it is me again 191 | - what up 192 | - hi there 193 | - hi 194 | - jop 195 | - hi friend 196 | - hi there it's me 197 | - good evening 198 | - good morning 199 | - good afternoon 200 | - HI 201 | - Hello? 202 | 203 | ## intent:help 204 | - What can you do? 205 | - What should I ask you? 206 | - how can you help me 207 | - I need help 208 | - help me 209 | - I'm stuck 210 | - i need help 211 | - i don't know what to do 212 | 213 | ## intent:out_of_scope 214 | - Can I transfer money? 215 | - Can I open a bank [account](entity_type)? 216 | - Can you help me with transferring money? 217 | - I want to open a bank [account](entity_type) 218 | - I need a new bank [account](entity_type) 219 | - i want to transfer money 220 | - I want to transfer money 221 | - can you name the [owner](attribute) of the [account](entity_type) [DE76894768662419673111](account) 222 | - do i also have an account on [N26](bank) 223 | - What are [transactions](entity_type) I made to account [DE51728838437501118370](account) 224 | 225 | ## intent:query_attribute 226 | - What is the [gender](attribute) of [Hans Maier](person)? 227 | - What is the [email](attribute) of [Jannik Jung](person)? 228 | - In what [city](attribute) does [christa schulz](person) live? 229 | - In what [city](attribute) is [N26](bank) located? 230 | - Has [Wirecard Bank](bank) an [english website](attribute)? 231 | - Does the [first](mention) offer [free accounts](attribute)? 232 | - What kind of [residents](attribute) can open an account at [DKB](bank)? 233 | - Does [N26] allow [free worldwide withdrawals](attribute)? 234 | - Where is the [headquarter](attribute) of [Deutsche Bank](bank)? 235 | - Does the [Deutsche Bank](attribute) have an [english customer service](attribute)? 236 | - Can you tell me if [KfW](bank) offers [free worldwide withdrawals](attribute)? 237 | - What is the [city](attribute) of the [second](mention) one? 238 | - What is the [gender](attribute) of the [last](mention) one? 239 | - How much [money](attribute) did I spend on [food](category)? 240 | - [How much](attribute) did I spent on [cloth](category)? 241 | - What did I [pay](attribute) for [food](category) 242 | - [How much](attribute) did I spent on [traveling](category)? 243 | - How much [money](attribute) do I have on that account? 244 | - What is my [balance](attribute) on that account? 245 | - what is my [balance](attribute) 246 | - how much [money](attribute) do i have on that [account](entity_type) 247 | - What is the [headquarter](attribute) of the [forth](mention) one? 248 | - What is the [headquarters](attribute) of [Comdirect](bank)? 249 | - Does the [second](mention) one has an [english website](attribute) 250 | - Does [DKB](bank) has an [english customer support](attribute)? 251 | - how much [money](attribute) did I spent on [food](category)? 252 | - [how much](attribute) did I spent on [food](category)? 253 | - what [types](attribute) of [account](entity_type) do you offer? 254 | - In which [city](attribute) is the [second](mention) one located in? 255 | - what is the [expiry date](attribute) of the [first](mention) one 256 | - what is the [expiration date](attribute) of the [last](mention) one 257 | - what is the [category](attribute) of the [second](mention) one 258 | - how much [money](attribute) did i spend on [food](category) 259 | - does [N26](bank) have an [english website](attribute) 260 | - what [residents](attribute) are allowed to open an account on [N26](bank) 261 | - what about the [headquarters](attribute) of the [last](mention) one 262 | - what is the [headquarter](attribute) of the [third](mention) one 263 | - what is the [headquarters](attribute) of the [second](mention) one 264 | - what about the [third](mention) one 265 | - what is the [balance](attribute) of the [first](mention) one 266 | - how much [money](attribute) do i have on my account 267 | - how much [money](attribute) do i have on the [second](mention) one 268 | - [How much](attribute) do I have in my [savings](account_type) account? 269 | - How much [money](attribute) is in my [savings](account_type) account? 270 | - What is my [balance](attribute) for [savings](account_type)? 271 | - How much [money](attribute) do I have in my [credit](account_type) account? 272 | - What is my [savings](account_type) account [balance](attribute)? 273 | - what is the [balance](attribute) of my [credit](account_type) [account](entity_type) 274 | - what is the [name](attribute) of the [account](entity_type) owner of [DE70334119137743514093](account) 275 | - what is the [name](attribute) of the [account](entity_type) [DE70334119137743514093](account) 276 | - what is the [category](attribute) of the [last](mention) [transaction](entity_type) 277 | - does it has an [english customer service](attribute)? 278 | - Does it has an [english customer support](attribute) 279 | - Does [Deutsche Bank](bank) offer [free accounts](attribute)? 280 | - what about the [last](mention) one 281 | - What is the [HQ](attribute) of the [third](mention) one 282 | - what is the [headquarters](attribute) of the [first](mention) one 283 | 284 | ## intent:query_entities 285 | - What options for [banks](entity_type) do I have? 286 | - Show me all [banks](entity_type) 287 | - List [banks](entity_type) 288 | - Show me [banks](entity_type) 289 | - What are my [banks](entity_type) options? 290 | - List all [banks](entity_type) in [Germany](country) 291 | - Can you show me all [banks](entity_type) located in [Germany](country)? 292 | - Can you list all [people](entity_type)? 293 | - What [accounts](entity_type) do I currently have? 294 | - Can you list all [accounts](entity_type) for me please? 295 | - What [transactions](entity_type) can you find for my account [DE89370400440532013000](account) 296 | - List all my [transactions](entity_type) 297 | - What [accounts](entity_type) do I own? 298 | - What are my recent [transactions](entity_type)? 299 | - List all [transactions](entity_type) on that account 300 | - Are any of those [transactions](entity_type) [food](category) related 301 | - what [accounts](entity_type) do i have with that [bank](entity_type) 302 | - list all [banks](entity_type) 303 | - what [bank](entity_type) options do i have 304 | - what are my recent [transactions](entity_type) on that account 305 | - what [accounts](entity_type) do i have 306 | - can you list my [accounts](entity_type) 307 | - can you list my [accounts](entity_type)? 308 | - what are my [bank](entity_type) options? 309 | - what are my recent [transactions](entity_type) on that account? 310 | - what are my recent [transaction](entity_type) on that account? 311 | - can you list my [accounts](entity_type), please? 312 | - what [cards](entity_type) do i have? 313 | - tell me about [accounts](entity_type) 314 | - I want to know different [banking](entity_type) options 315 | - what [accounts](entity_type) do I have? 316 | - tell me about my [accounts](entity_type) 317 | - what [banks](entity_type) do you have? 318 | - What [bank](entity_type) options do I have? 319 | - What [banking](entity_type) options do I have? 320 | - what are my [accounts](entity_type) 321 | - can you list may [cards](entity_type) 322 | - what [cards](entity_type) do i have 323 | - can you list my [transactions](entity_type) 324 | - what are my recent [transactions](entity_type) on [DE76894768662419673111](account) 325 | - can you list some [banks](entity_type) for me 326 | - what are my [bank](entity_type) options 327 | - can you list some [transactions](entity_type) of the [second](mention) one 328 | - what are my [transactions](entity_type) on [DE82444435329779109646](account) 329 | - what are my recent [transaction](entity_type) on [food](category) 330 | - i want to check on my [accounts](entity_type) 331 | - what are my [banking](entity_type) options?> 332 | - what are my [banking](entity_type) options? 333 | - What are my [accounts](entity_type)? 334 | - What [accounts](entity_type) do I have? 335 | - what are my recent [transactions](entity_type) on the [first](mention) one 336 | - which [accounts](entity_type) do I have? 337 | - Details about [accounts](entity_type) 338 | - [Accounts](entity_type) 339 | - can you list my [accoutns](entity_type) 340 | - what are my recent [transactions](entity_type) on that account 341 | - list my [accounts](entity_type) 342 | - what [accounts](entity_type) do i owe 343 | - can you list [transactions](entity_type) for the [second](mention) one 344 | - can you list my [transaction](entity_type) on [food](category) 345 | - [how much](attribute) did i spend on [food](category) 346 | - what are my [transactions](entity_type) on that account 347 | - how much [money](attribute) did i spent on [food](category) 348 | - what are [transactions](entity_type) I made on [food](category) 349 | - and what are [transactions](entity_type) from my account [DE76894768662419673111](account) 350 | - can you only show me [transactions](entity_type) from my account [DE70334119137743514093](account) 351 | - can you show me some of my recent [transactions](entity_type) 352 | - what are [banks](entity_type) you can recommend 353 | - what [banks](entity_type) do you know 354 | 355 | ## intent:resolve_entity 356 | - [1](mention) 357 | - [3](mention) 358 | - [2](mention) 359 | - [4](mention) 360 | - [5](mention) 361 | - The [first](mention) one 362 | - The [last](mention) one 363 | - The [second](mention) one 364 | - The [third](mention) person 365 | - I meant the [first](mention) one 366 | - Take the [last](mention) person 367 | - I meant [Gerda Schmitz](person) 368 | - [Bertha Wagner](person) 369 | - [dennis bauer](person) 370 | - The [first](mention) [nicole](person) 371 | - Take the [first](mention) one 372 | - Take the [second](mention) one 373 | - [N26](bank) 374 | - take the [first](mention) [bank](entity_type) 375 | - I select the [first](mention) [bank](entity_type) 376 | - The [latter](mention) 377 | - take the [second](mention) one 378 | 379 | ## regex:account 380 | - DE[0-9]{20} 381 | 382 | ## lookup:bank 383 | data/lookup_tables/bank.txt 384 | 385 | ## lookup:person 386 | data/lookup_tables/person.txt 387 | -------------------------------------------------------------------------------- /actions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Text, Dict, Any, List, Union 3 | 4 | from rasa_sdk.events import SlotSet 5 | from rasa_sdk import Action, Tracker 6 | 7 | from schema import schema 8 | from graph_database import GraphDatabase 9 | 10 | 11 | def resolve_mention(tracker: Tracker) -> Text: 12 | """ 13 | Resolves a mention of an entity, such as first, to the actual entity. 14 | If multiple entities are listed during the conversation, the entities 15 | are stored in the slot 'listed_items' as a list. We resolve the mention, 16 | such as first, to the list index and retrieve the actual entity. 17 | 18 | :param tracker: tracker 19 | :return: name of the actually entity 20 | """ 21 | graph_database = GraphDatabase() 22 | 23 | mention = tracker.get_slot("mention") 24 | listed_items = tracker.get_slot("listed_items") 25 | 26 | if mention is not None and listed_items is not None: 27 | idx = int(graph_database.map("mention-mapping", mention)) 28 | 29 | if type(idx) is int and idx < len(listed_items): 30 | return listed_items[idx] 31 | 32 | 33 | def get_entity_type(tracker: Tracker) -> Text: 34 | """ 35 | Get the entity type mentioned by the user. As the user may speak of an 36 | entity type in plural, we need to map the mentioned entity type to the 37 | type used in the knowledge base. 38 | 39 | :param tracker: tracker 40 | :return: entity type (same type as used in the knowledge base) 41 | """ 42 | graph_database = GraphDatabase() 43 | entity_type = tracker.get_slot("entity_type") 44 | return graph_database.map("entity-type-mapping", entity_type) 45 | 46 | 47 | def get_attribute(tracker: Tracker) -> Text: 48 | """ 49 | Get the attribute mentioned by the user. As the user may use a synonym for 50 | an attribute, we need to map the mentioned attribute to the 51 | attribute name used in the knowledge base. 52 | 53 | :param tracker: tracker 54 | :return: attribute (same type as used in the knowledge base) 55 | """ 56 | graph_database = GraphDatabase() 57 | attribute = tracker.get_slot("attribute") 58 | return graph_database.map("attribute-mapping", attribute) 59 | 60 | 61 | def get_entity_name(tracker: Tracker, entity_type: Text): 62 | """ 63 | Get the name of the entity the user referred to. Either the NER detected the 64 | entity and stored its name in the corresponding slot or the user referred to 65 | the entity by an ordinal number, such as first or last, or the user refers to 66 | an entity by its attributes. 67 | 68 | :param tracker: Tracker 69 | :param entity_type: the entity type 70 | 71 | :return: the name of the actual entity (value of key attribute in the knowledge base) 72 | """ 73 | 74 | # user referred to an entity by an ordinal number 75 | mention = tracker.get_slot("mention") 76 | if mention is not None: 77 | return resolve_mention(tracker) 78 | 79 | # user named the entity 80 | entity_name = tracker.get_slot(entity_type) 81 | if entity_name: 82 | return entity_name 83 | 84 | # user referred to an entity by its attributes 85 | listed_items = tracker.get_slot("listed_items") 86 | attributes = get_attributes_of_entity(entity_type, tracker) 87 | 88 | if listed_items and attributes: 89 | # filter the listed_items by the set attributes 90 | graph_database = GraphDatabase() 91 | for entity in listed_items: 92 | key_attr = schema[entity_type]["key"] 93 | result = graph_database.validate_entity( 94 | entity_type, entity, key_attr, attributes 95 | ) 96 | if result is not None: 97 | return to_str(result, key_attr) 98 | 99 | return None 100 | 101 | 102 | def get_attributes_of_entity(entity_type, tracker): 103 | # check what attributes the NER found for entity type 104 | attributes = [] 105 | if entity_type in schema: 106 | for attr in schema[entity_type]["attributes"]: 107 | attr_val = tracker.get_slot(attr.replace("-", "_")) 108 | if attr_val is not None: 109 | attributes.append({"key": attr, "value": attr_val}) 110 | return attributes 111 | 112 | 113 | def reset_attribute_slots(slots, entity_type, tracker): 114 | # check what attributes the NER found for entity type 115 | if entity_type in schema: 116 | for attr in schema[entity_type]["attributes"]: 117 | attr = attr.replace("-", "_") 118 | attr_val = tracker.get_slot(attr) 119 | if attr_val is not None: 120 | slots.append(SlotSet(attr, None)) 121 | return slots 122 | 123 | 124 | def to_str(entity: Dict[Text, Any], entity_keys: Union[Text, List[Text]]) -> Text: 125 | """ 126 | Converts an entity to a string by concatenating the values of the provided 127 | entity keys. 128 | 129 | :param entity: the entity with all its attributes 130 | :param entity_keys: the name of the key attributes 131 | :return: a string that represents the entity 132 | """ 133 | if isinstance(entity_keys, str): 134 | entity_keys = [entity_keys] 135 | 136 | v_list = [] 137 | for key in entity_keys: 138 | _e = entity 139 | for k in key.split("."): 140 | _e = _e[k] 141 | 142 | if "balance" in key or "amount" in key: 143 | v_list.append(f"{str(_e)} €") 144 | elif "date" in key: 145 | v_list.append(_e.strftime("%d.%m.%Y (%H:%M:%S)")) 146 | else: 147 | v_list.append(str(_e)) 148 | return ", ".join(v_list) 149 | 150 | 151 | class ActionQueryEntities(Action): 152 | """Action for listing entities. 153 | The entities might be filtered by specific attributes.""" 154 | 155 | def name(self): 156 | return "action_query_entities" 157 | 158 | def run(self, dispatcher, tracker, domain): 159 | graph_database = GraphDatabase() 160 | 161 | # first need to know the entity type we are looking for 162 | entity_type = get_entity_type(tracker) 163 | 164 | if entity_type is None: 165 | dispatcher.utter_template("utter_rephrase", tracker) 166 | return [] 167 | 168 | # check what attributes the NER found for entity type 169 | attributes = get_attributes_of_entity(entity_type, tracker) 170 | 171 | # query knowledge base 172 | entities = graph_database.get_entities(entity_type, attributes) 173 | 174 | # filter out transactions that do not belong the set account (if any) 175 | if entity_type == "transaction": 176 | account_number = tracker.get_slot("account") 177 | entities = self._filter_transaction_entities(entities, account_number) 178 | 179 | if not entities: 180 | dispatcher.utter_template( 181 | "I could not find any entities for '{}'.".format(entity_type), tracker 182 | ) 183 | return [] 184 | 185 | # utter a response that contains all found entities 186 | # use the 'representation' attributes to print an entity 187 | entity_representation = schema[entity_type]["representation"] 188 | 189 | dispatcher.utter_message( 190 | "Found the following '{}' entities:".format(entity_type) 191 | ) 192 | sorted_entities = sorted([to_str(e, entity_representation) for e in entities]) 193 | for i, e in enumerate(sorted_entities): 194 | dispatcher.utter_message(f"{i + 1}: {e}") 195 | 196 | # set slots 197 | # set the entities slot in order to resolve references to one of the found 198 | # entites later on 199 | entity_key = schema[entity_type]["key"] 200 | 201 | slots = [ 202 | SlotSet("entity_type", entity_type), 203 | SlotSet("listed_items", list(map(lambda x: to_str(x, entity_key), entities))), 204 | ] 205 | 206 | # if only one entity was found, that the slot of that entity type to the 207 | # found entity 208 | if len(entities) == 1: 209 | slots.append(SlotSet(entity_type, to_str(entities[0], entity_key))) 210 | 211 | reset_attribute_slots(slots, entity_type, tracker) 212 | 213 | return slots 214 | 215 | def _filter_transaction_entities( 216 | self, entities: List[Dict[Text, Any]], account_number: Text 217 | ) -> List[Dict[Text, Any]]: 218 | """ 219 | Filter out all transactions that do not belong to the provided account number. 220 | 221 | :param entities: list of transactions 222 | :param account_number: account number 223 | :return: list of filtered transactions with max. 5 entries 224 | """ 225 | if account_number is not None: 226 | filtered_entities = [] 227 | for entity in entities: 228 | if entity["account-of-creator"]["account-number"] == account_number: 229 | filtered_entities.append(entity) 230 | return filtered_entities[:5] 231 | 232 | return entities[:5] 233 | 234 | 235 | class ActionQueryAttribute(Action): 236 | """Action for querying a specific attribute of an entity.""" 237 | 238 | def name(self): 239 | return "action_query_attribute" 240 | 241 | def run(self, dispatcher, tracker, domain): 242 | graph_database = GraphDatabase() 243 | 244 | # get entity type of entity 245 | entity_type = get_entity_type(tracker) 246 | 247 | if entity_type is None: 248 | dispatcher.utter_template("utter_rephrase", tracker) 249 | return [] 250 | 251 | # get name of entity and attribute of interest 252 | name = get_entity_name(tracker, entity_type) 253 | attribute = get_attribute(tracker) 254 | 255 | if name is None or attribute is None: 256 | dispatcher.utter_template("utter_rephrase", tracker) 257 | slots = [SlotSet("mention", None)] 258 | reset_attribute_slots(slots, entity_type, tracker) 259 | return slots 260 | 261 | # query knowledge base 262 | key_attribute = schema[entity_type]["key"] 263 | value = graph_database.get_attribute_of( 264 | entity_type, key_attribute, name, attribute 265 | ) 266 | 267 | # utter response 268 | if value is not None and len(value) == 1: 269 | dispatcher.utter_message( 270 | f"{name} has the value '{value[0]}' for attribute '{attribute}'." 271 | ) 272 | else: 273 | dispatcher.utter_message( 274 | f"Did not found a valid value for attribute {attribute} for entity '{name}'." 275 | ) 276 | 277 | slots = [SlotSet("mention", None), SlotSet(entity_type, name)] 278 | reset_attribute_slots(slots, entity_type, tracker) 279 | return slots 280 | 281 | 282 | class ActionCompareEntities(Action): 283 | """Action for comparing multiple entities.""" 284 | 285 | def name(self): 286 | return "action_compare_entities" 287 | 288 | def run(self, dispatcher, tracker, domain): 289 | graph = GraphDatabase() 290 | 291 | # get entities to compare and their entity type 292 | listed_items = tracker.get_slot("listed_items") 293 | entity_type = get_entity_type(tracker) 294 | 295 | if listed_items is None or entity_type is None: 296 | dispatcher.utter_template("utter_rephrase", tracker) 297 | return [] 298 | 299 | # get attribute of interest 300 | attribute = get_attribute(tracker) 301 | 302 | if attribute is None: 303 | dispatcher.utter_template("utter_rephrase", tracker) 304 | return [] 305 | 306 | # utter response for every entity that shows the value of the attribute 307 | for e in listed_items: 308 | key_attribute = schema[entity_type]["key"] 309 | value = graph.get_attribute_of(entity_type, key_attribute, e, attribute) 310 | 311 | if value is not None and len(value) == 1: 312 | dispatcher.utter_message( 313 | f"{e} has the value '{value[0]}' for attribute '{attribute}'." 314 | ) 315 | 316 | return [] 317 | 318 | 319 | class ActionResolveEntity(Action): 320 | """Action for resolving a mention.""" 321 | 322 | def name(self): 323 | return "action_resolve_entity" 324 | 325 | def run(self, dispatcher, tracker, domain): 326 | entity_type = tracker.get_slot("entity_type") 327 | listed_items = tracker.get_slot("listed_items") 328 | 329 | if entity_type is None: 330 | dispatcher.utter_template("utter_rephrase", tracker) 331 | return [] 332 | 333 | # Check if entity was mentioned as 'first', 'second', etc. 334 | mention = tracker.get_slot("mention") 335 | if mention is not None: 336 | value = resolve_mention(tracker) 337 | if value is not None: 338 | return [SlotSet(entity_type, value), SlotSet("mention", None)] 339 | 340 | # Check if NER recognized entity directly 341 | # (e.g. bank name was mentioned and recognized as 'bank') 342 | value = tracker.get_slot(entity_type) 343 | if value is not None and value in listed_items: 344 | return [SlotSet(entity_type, value), SlotSet("mention", None)] 345 | 346 | dispatcher.utter_template("utter_rephrase", tracker) 347 | return [SlotSet(entity_type, None), SlotSet("mention", None)] 348 | -------------------------------------------------------------------------------- /graph_database.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import List, Dict, Any, Optional, Text 3 | 4 | from grakn.client import GraknClient 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class KnowledgeBase(object): 10 | 11 | def get_entities( 12 | self, 13 | entity_type: Text, 14 | attributes: Optional[List[Dict[Text, Text]]] = None, 15 | limit: int = 5, 16 | ) -> List[Dict[Text, Any]]: 17 | 18 | raise NotImplementedError("Method is not implemented.") 19 | 20 | def get_attribute_of( 21 | self, entity_type: Text, key_attribute: Text, entity: Text, attribute: Text 22 | ) -> List[Any]: 23 | 24 | raise NotImplementedError("Method is not implemented.") 25 | 26 | def validate_entity( 27 | self, entity_type, entity, key_attribute, attributes 28 | ) -> Optional[Dict[Text, Any]]: 29 | 30 | raise NotImplementedError("Method is not implemented.") 31 | 32 | def map(self, mapping_type: Text, mapping_key: Text) -> Text: 33 | 34 | raise NotImplementedError("Method is not implemented.") 35 | 36 | 37 | class GraphDatabase(KnowledgeBase): 38 | """ 39 | GraphDatabase uses a grakn graph database to encode your domain knowledege. Make 40 | sure to have the graph database set up and the grakn server running. 41 | """ 42 | 43 | def __init__(self, uri: Text = "localhost:48555", keyspace: Text = "banking"): 44 | self.uri = uri 45 | self.keyspace = keyspace 46 | self.me = "mitchell.gillis@t-online.de" 47 | 48 | def _thing_to_dict(self, thing): 49 | """ 50 | Converts a thing (a grakn object) to a dict for easy retrieval of the thing's 51 | attributes. 52 | """ 53 | entity = {"id": thing.id, "type": thing.type().label()} 54 | for each in thing.attributes(): 55 | entity[each.type().label()] = each.value() 56 | return entity 57 | 58 | def _execute_entity_query(self, query: Text) -> List[Dict[Text, Any]]: 59 | """ 60 | Executes a query that returns a list of entities with all their attributes. 61 | """ 62 | with GraknClient(uri=self.uri) as client: 63 | with client.session(keyspace=self.keyspace) as session: 64 | with session.transaction().read() as tx: 65 | logger.debug("Executing Graql Query: " + query) 66 | result_iter = tx.query(query) 67 | concepts = result_iter.collect_concepts() 68 | entities = [] 69 | for c in concepts: 70 | entities.append(self._thing_to_dict(c)) 71 | return entities 72 | 73 | def _execute_attribute_query(self, query: Text) -> List[Any]: 74 | """ 75 | Executes a query that returns the value(s) an entity has for a specific 76 | attribute. 77 | """ 78 | with GraknClient(uri=self.uri) as client: 79 | with client.session(keyspace=self.keyspace) as session: 80 | with session.transaction().read() as tx: 81 | print("Executing Graql Query: " + query) 82 | result_iter = tx.query(query) 83 | concepts = result_iter.collect_concepts() 84 | return [c.value() for c in concepts] 85 | 86 | def _execute_relation_query( 87 | self, query: Text, relation_name: Text 88 | ) -> List[Dict[Text, Any]]: 89 | """ 90 | Execute a query that queries for a relation. All attributes of the relation and 91 | all entities participating in the relation are part of the result. 92 | """ 93 | with GraknClient(uri=self.uri) as client: 94 | with client.session(keyspace=self.keyspace) as session: 95 | with session.transaction().read() as tx: 96 | print("Executing Graql Query: " + query) 97 | result_iter = tx.query(query) 98 | 99 | relations = [] 100 | 101 | for concept in result_iter: 102 | relation_entity = concept.map().get(relation_name) 103 | relation = self._thing_to_dict(relation_entity) 104 | 105 | for ( 106 | role_entity, 107 | entity_set, 108 | ) in relation_entity.role_players_map().items(): 109 | role_label = role_entity.label() 110 | thing = entity_set.pop() 111 | relation[role_label] = self._thing_to_dict(thing) 112 | 113 | relations.append(relation) 114 | 115 | return relations 116 | 117 | def _get_me_clause(self, entity_type: Text) -> Text: 118 | """ 119 | Construct the me clause. Needed to only list, for example, accounts that are 120 | related to me. 121 | 122 | :param entity_type: entity type 123 | 124 | :return: me clause as string 125 | """ 126 | 127 | clause = "" 128 | 129 | # do not add the me clause to a query asking for banks or people as they are 130 | # independent of the accounts related to me 131 | if entity_type not in ["person", "bank"]: 132 | clause = ( 133 | f"$person isa person, has email '{self.me}';" 134 | f"$contract(customer: $person, offer: $account, provider: $bank) isa contract;" 135 | ) 136 | return clause 137 | 138 | def _get_attribute_clause( 139 | self, attributes: Optional[List[Dict[Text, Text]]] = None 140 | ) -> Text: 141 | """ 142 | Construct the attribute clause. 143 | 144 | :param attributes: attributes 145 | 146 | :return: attribute clause as string 147 | """ 148 | 149 | clause = "" 150 | 151 | if attributes: 152 | clause = ",".join([f"has {a['key']} '{a['value']}'" for a in attributes]) 153 | clause = ", " + clause 154 | 155 | return clause 156 | 157 | def get_attribute_of( 158 | self, entity_type: Text, key_attribute: Text, entity: Text, attribute: Text 159 | ) -> List[Any]: 160 | """ 161 | Get the value of the given attribute for the provided entity. 162 | 163 | :param entity_type: entity type 164 | :param key_attribute: key attribute of entity 165 | :param entity: name of the entity 166 | :param attribute: attribute of interest 167 | 168 | :return: the value of the attribute 169 | """ 170 | me_clause = self._get_me_clause(entity_type) 171 | 172 | return self._execute_attribute_query( 173 | f""" 174 | match 175 | {me_clause} 176 | ${entity_type} isa {entity_type}, 177 | has {key_attribute} '{entity}', 178 | has {attribute} $a; 179 | get $a; 180 | """ 181 | ) 182 | 183 | def _get_transaction_entities( 184 | self, attributes: Optional[List[Dict[Text, Text]]] = None 185 | ) -> List[Dict[Text, Any]]: 186 | """ 187 | Query the graph database for transactions. Restrict the transactions 188 | by the provided attributes, if any attributes are given. 189 | As transaction is a relation, query also the related account entities. 190 | 191 | :param attributes: list of attributes 192 | 193 | :return: list of transactions 194 | """ 195 | 196 | attribute_clause = self._get_attribute_clause(attributes) 197 | me_clause = self._get_me_clause("transaction") 198 | 199 | return self._execute_relation_query( 200 | f"match " 201 | f"{me_clause} " 202 | f"$transaction(account-of-receiver: $x, account-of-creator: $account) " 203 | f"isa transaction{attribute_clause}; " 204 | f"get $transaction;", 205 | "transaction", 206 | ) 207 | 208 | def _get_card_entities( 209 | self, attributes: Optional[List[Dict[Text, Text]]] = None, limit: int = 5 210 | ) -> List[Dict[Text, Any]]: 211 | """ 212 | Query the graph database for cards. Restrict the cards 213 | by the provided attributes, if any attributes are given. 214 | 215 | :param attributes: list of attributes 216 | :param limit: maximum number of cards to return 217 | 218 | :return: list of cards 219 | """ 220 | 221 | attribute_clause = self._get_attribute_clause(attributes) 222 | me_clause = self._get_me_clause("card") 223 | 224 | return self._execute_entity_query( 225 | f"match " 226 | f"{me_clause} " 227 | f"$represented-by(bank-account: $account, bank-card: $card) " 228 | f"isa represented-by;" 229 | f"$card isa card{attribute_clause}; " 230 | f"get $card;" 231 | )[:limit] 232 | 233 | def _get_account_entities( 234 | self, attributes: Optional[List[Dict[Text, Text]]] = None, limit: int = 5 235 | ) -> List[Dict[Text, Any]]: 236 | """ 237 | Query the graph database for accounts. Restrict the accounts 238 | by the provided attributes, if any attributes are given. 239 | Query the related relation contract, to obtain additional information 240 | about the bank and the person who owns the account. 241 | 242 | :param attributes: list of attributes 243 | :param limit: maximum number of accounts to return 244 | 245 | :return: list of accounts 246 | """ 247 | 248 | attribute_clause = self._get_attribute_clause(attributes) 249 | me_clause = self._get_me_clause("account") 250 | 251 | entities = self._execute_relation_query( 252 | f""" 253 | match 254 | $account isa account{attribute_clause}; 255 | {me_clause} 256 | get $contract; 257 | """, 258 | "contract", 259 | )[:limit] 260 | 261 | for entity in entities: 262 | for k, v in entity["offer"].items(): 263 | entity[k] = v 264 | entity.pop("offer") 265 | 266 | return entities 267 | 268 | def get_entities( 269 | self, 270 | entity_type: Text, 271 | attributes: Optional[List[Dict[Text, Text]]] = None, 272 | limit: int = 10, 273 | ) -> List[Dict[Text, Any]]: 274 | """ 275 | Query the graph database for entities of the given type. Restrict the entities 276 | by the provided attributes, if any attributes are given. 277 | 278 | :param entity_type: the entity type 279 | :param attributes: list of attributes 280 | :param limit: maximum number of entities to return 281 | 282 | :return: list of entities 283 | """ 284 | 285 | if entity_type == "transaction": 286 | return self._get_transaction_entities(attributes) 287 | if entity_type == "account": 288 | return self._get_account_entities(attributes, limit) 289 | if entity_type == "card": 290 | return self._get_card_entities(attributes, limit) 291 | 292 | me_clause = self._get_me_clause(entity_type) 293 | attribute_clause = self._get_attribute_clause(attributes) 294 | 295 | return self._execute_entity_query( 296 | f"match " 297 | f"{me_clause} " 298 | f"${entity_type} isa {entity_type}{attribute_clause}; " 299 | f"get ${entity_type};" 300 | )[:limit] 301 | 302 | def map(self, mapping_type: Text, mapping_key: Text) -> Text: 303 | """ 304 | Query the given mapping table for the provided key. 305 | 306 | :param mapping_type: the name of the mapping table 307 | :param mapping_key: the mapping key 308 | 309 | :return: the mapping value 310 | """ 311 | 312 | value = self._execute_attribute_query( 313 | f"match " 314 | f"$mapping isa {mapping_type}, " 315 | f"has mapping-key '{mapping_key}', " 316 | f"has mapping-value $v;" 317 | f"get $v;" 318 | ) 319 | 320 | if value and len(value) == 1: 321 | return value[0] 322 | 323 | def validate_entity( 324 | self, entity_type, entity, key_attribute, attributes 325 | ) -> Dict[Text, Any]: 326 | """ 327 | Validates if the given entity has all provided attribute values. 328 | 329 | :param entity_type: entity type 330 | :param entity: name of the entity 331 | :param key_attribute: key attribute of entity 332 | :param attributes: attributes 333 | 334 | :return: the found entity 335 | """ 336 | attribute_clause = self._get_attribute_clause(attributes) 337 | 338 | value = self._execute_entity_query( 339 | f"match " 340 | f"${entity_type} isa {entity_type}{attribute_clause}, " 341 | f"has {key_attribute} '{entity}'; " 342 | f"get ${entity_type};" 343 | ) 344 | 345 | if value and len(value) == 1: 346 | return value[0] 347 | 348 | 349 | class InMemoryGraph(KnowledgeBase): 350 | """ 351 | If you don't want to use a graph database and you just have a few data points, you 352 | can also store your domain knowledge, for example, in a dictionary. 353 | This class is an example class that uses a python dictionary to encode some domain 354 | knowledge about banks. 355 | """ 356 | 357 | def __init__(self): 358 | self.graph = { 359 | "bank": [ 360 | { 361 | "name": "N26", 362 | "headquarters": "Berlin", 363 | "country": "Germany", 364 | "free-accounts": "true", 365 | }, 366 | { 367 | "name": "bunq", 368 | "headquarters": "Amsterdam", 369 | "country": "Netherlands", 370 | "free-accounts": "false", 371 | }, 372 | { 373 | "name": "Deutsche Bank", 374 | "headquarters": "Frankfurt am Main", 375 | "country": "Germany", 376 | "free-accounts": "false", 377 | }, 378 | { 379 | "name": "Commerzbank", 380 | "headquarters": "Frankfurt am Main", 381 | "country": "Germany", 382 | "free-accounts": "true", 383 | }, 384 | { 385 | "name": "Targobank", 386 | "headquarters": "Düsseldorf", 387 | "country": "Germany", 388 | "free-accounts": "true", 389 | }, 390 | { 391 | "name": "DKB", 392 | "headquarters": "Berlin", 393 | "country": "Germany", 394 | "free-accounts": "true", 395 | }, 396 | { 397 | "name": "Comdirect", 398 | "headquarters": "Quickborn", 399 | "country": "Germany", 400 | "free-accounts": "true", 401 | }, 402 | ] 403 | } 404 | 405 | self.attribute_mapping = { 406 | "headquarters": "headquarters", 407 | "HQ": "headquarters", 408 | "main office": "headquarters", 409 | "city": "headquarters", 410 | "name": "name", 411 | "country": "country", 412 | "free-accounts": "free-accounts", 413 | "free accounts": "free-accounts", 414 | } 415 | self.entity_type_mapping = {"banks": "bank", "bank": "bank"} 416 | 417 | def get_entities( 418 | self, 419 | entity_type: Text, 420 | attributes: Optional[List[Dict[Text, Text]]] = None, 421 | limit: int = 5, 422 | ) -> List[Dict[Text, Any]]: 423 | """ 424 | Query the graph database for entities of the given type. Restrict the entities 425 | by the provided attributes, if any attributes are given. 426 | 427 | :param entity_type: the entity type 428 | :param attributes: list of attributes 429 | :param limit: maximum number of entities to return 430 | 431 | :return: list of entities 432 | """ 433 | if entity_type not in self.graph: 434 | return [] 435 | 436 | entities = self.graph[entity_type] 437 | 438 | # filter entities by attributes 439 | if attributes: 440 | entities = list( 441 | filter( 442 | lambda e: [e[a["key"]] == a["value"] for a in attributes].count( 443 | False 444 | ) 445 | == 0, 446 | entities, 447 | ) 448 | ) 449 | 450 | return entities[:limit] 451 | 452 | def get_attribute_of( 453 | self, entity_type: Text, key_attribute: Text, entity: Text, attribute: Text 454 | ) -> List[Any]: 455 | """ 456 | Get the value of the given attribute for the provided entity. 457 | 458 | :param entity_type: entity type 459 | :param key_attribute: key attribute of entity 460 | :param entity: name of the entity 461 | :param attribute: attribute of interest 462 | 463 | :return: the value of the attribute 464 | """ 465 | if entity_type not in self.graph: 466 | return [] 467 | 468 | entities = self.graph[entity_type] 469 | 470 | entity_of_interest = list( 471 | filter(lambda e: e[key_attribute] == entity, entities) 472 | ) 473 | 474 | if not entity_of_interest or len(entity_of_interest) > 1: 475 | return [] 476 | 477 | return [entity_of_interest[0][attribute]] 478 | 479 | def validate_entity( 480 | self, entity_type, entity, key_attribute, attributes 481 | ) -> Optional[Dict[Text, Any]]: 482 | """ 483 | Validates if the given entity has all provided attribute values. 484 | 485 | :param entity_type: entity type 486 | :param entity: name of the entity 487 | :param key_attribute: key attribute of entity 488 | :param attributes: attributes 489 | 490 | :return: the found entity 491 | """ 492 | if entity_type not in self.graph: 493 | return None 494 | 495 | entities = self.graph[entity_type] 496 | 497 | entity_of_interest = list( 498 | filter(lambda e: e[key_attribute] == entity, entities) 499 | ) 500 | 501 | if not entity_of_interest or len(entity_of_interest) > 1: 502 | return None 503 | 504 | entity_of_interest = entity_of_interest[0] 505 | 506 | for a in attributes: 507 | if entity_of_interest[a["key"]] != a["value"]: 508 | return None 509 | 510 | return entity_of_interest 511 | 512 | def map(self, mapping_type: Text, mapping_key: Text) -> Text: 513 | """ 514 | Query the given mapping table for the provided key. 515 | 516 | :param mapping_type: the name of the mapping table 517 | :param mapping_key: the mapping key 518 | 519 | :return: the mapping value 520 | """ 521 | 522 | if ( 523 | mapping_type == "attribute-mapping" 524 | and mapping_key in self.attribute_mapping 525 | ): 526 | return self.attribute_mapping[mapping_key] 527 | 528 | if ( 529 | mapping_type == "entity-type-mapping" 530 | and mapping_key in self.entity_type_mapping 531 | ): 532 | return self.entity_type_mapping[mapping_key] 533 | --------------------------------------------------------------------------------