31 lines
1.8 KiB
Python
31 lines
1.8 KiB
Python
"""Conservative independent intent gate for instructions without confirmed slots.
|
|||
|
|
Only registered aliases and explicit quantities are accepted. Other language asks
|
||
|
|
for structured clarification; this parser is not claimed to cover arbitrary NLP.
|
||
|
|
"""
|
||
|
|
import re
|
||
|
|
COUNTS={'一':1,'二':2,'两':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10,'one':1,'two':2,'three':3}
|
||
|
|
def matches(instruction,registry,aliases):
|
||
|
|
candidates=[]
|
||
|
|
for key in registry:
|
||
|
|
for term in [key]+list(aliases.get(key,[])):
|
||
|
|
if not term:continue
|
||
|
|
for m in re.finditer(re.escape(term),instruction):candidates.append((m.start(),m.end(),key))
|
||
|
|
chosen=[]
|
||
|
|
for item in sorted(candidates,key=lambda x:(-(x[1]-x[0]),x[0])):
|
||
|
|
if not any(item[0]<q[1] and q[0]<item[1] for q in chosen):chosen.append(item)
|
||
|
|
return sorted(chosen)
|
||
|
|
def extract(instruction,site):
|
||
|
|
names=site.get('object_locations',{}) or site.get('object_aliases',{})
|
||
|
|
objects=matches(instruction,names,site.get('object_aliases',{}))
|
||
|
|
sources=matches(instruction,site.get('sources',{}),site.get('source_aliases',{}))
|
||
|
|
destinations=matches(instruction,site.get('destinations',{}),site.get('destination_aliases',{}))
|
||
|
|
if not objects or len({x[2] for x in sources})!=1 or len({x[2] for x in destinations})!=1:return None
|
||
|
|
items=[]
|
||
|
|
for start,end,name in objects:
|
||
|
|
m=re.search(r'(\d+|一|二|两|三|四|五|六|七|八|九|十|one|two|three)\s*(?:瓶|个|件|盒|袋)?\s*$',instruction[:start])
|
||
|
|
if not m:return None
|
||
|
|
count=COUNTS.get(m[1],int(m[1]) if m[1].isdigit() else 0)
|
||
|
|
if not 1<=count<=20:return None
|
||
|
|
items.append(dict(target_name=name,quantity=count,source_location=sources[0][2]))
|
||
|
|
return dict(items=items,destination=destinations[0][2])
|