101 lines
5.8 KiB
Python
101 lines
5.8 KiB
Python
"""Resolve bounded list references and subset questions from scoped saved replies."""
|
||
|
||
import re
|
||
import sqlite3
|
||
|
||
from .db import connect
|
||
from .order_query import COUNT,NOUN,CONDITION,parse_query
|
||
|
||
|
||
ORDINALS={word:index for index,word in enumerate(
|
||
'一 二 三 四 五 六 七 八 九 十 十一 十二 十三 十四 十五 十六 十七 十八 十九 二十'.split(),1)}
|
||
RELATIVE=r'^(?:这其中|其中|这些(?:订单|运单)?(?:中|里)?|这几(?:票|条)(?:中|里)?|里面|当中)'
|
||
|
||
|
||
def is_relative_question(message):
|
||
return (bool(re.match(RELATIVE,message))
|
||
and bool(re.search(COUNT+'|'+NOUN+'|'+CONDITION+r'|轨迹|节点|阶段|到哪|在哪',message,re.I))
|
||
and not re.search(r'报价|海运|海派|海卡|空运|运费|费用|价格|KG|公斤|千克|CBM|立方',message,re.I))
|
||
|
||
|
||
def ordinal_request(message):
|
||
from .order_data import is_order_followup
|
||
text=re.sub(r'[\s,,。.!!??]','',message)
|
||
match=re.fullmatch(r'(?:请|帮我|看看|看一下|查询)?第(\d{1,5}|[一二三四五六七八九十]{1,3})'
|
||
r'(?:个|票|条|笔)(?:订单|运单)?(?:的)?(.*)',text)
|
||
if not match:return None
|
||
detail=match[2]
|
||
if detail not in ('','呢','详情') and not is_order_followup(detail):return None
|
||
number=int(match[1]) if match[1].isdigit() else ORDINALS.get(match[1],0)
|
||
return number
|
||
|
||
|
||
def shown_references(connection,conv_id,state):
|
||
if 'shown' in state:
|
||
return [(item['inbound_code'] or '未登记',item['order_no']) for item in state['shown']]
|
||
row=connection.execute("SELECT content FROM messages WHERE conv_id=? AND id=? AND role='assistant'",
|
||
(conv_id,state['message_id'])).fetchone()
|
||
# Older states stored only the reply ID. Accept its fixed, unambiguous row format.
|
||
lines=[line for line in (row[0] if row else '').splitlines() if line.startswith('- 入仓码:')]
|
||
result=[]
|
||
for line in lines:
|
||
match=re.match(r'^- 入仓码:([^\r\n;]+);订单号:([^\r\n;]+);(?:业务状态|异常原因):',line)
|
||
if not match or any(line.count(label)>1 for label in (';订单号:',';业务状态:',';异常原因:')):
|
||
return []
|
||
result.append(match.groups())
|
||
return result
|
||
|
||
|
||
def answer_followup(message,tenants,conv_id):
|
||
from .customer_service import order_login_response
|
||
from .order_listing import load_state,answer_summary
|
||
|
||
text=message.strip(' ,,。.!!??')
|
||
ordinal=ordinal_request(text)
|
||
relative=is_relative_question(text)
|
||
reason=bool(re.fullmatch(r'(?:那|这些|这几票|它们|上述|上面)?(?:订单|运单)?'
|
||
r'(?:为什么异常|为什么有异常|异常的?原因(?:是什么|有哪些|呢)?|异常是怎么回事)',text))
|
||
query_text=re.sub(RELATIVE,'',text)
|
||
query_text=re.sub(r'^(?:那么|那)','',query_text)
|
||
intent=parse_query(query_text+'订单有多少') if not (re.search(NOUN,query_text,re.I) or re.search(COUNT,query_text,re.I)) else parse_query(query_text)
|
||
if relative and intent is None and re.search(COUNT,query_text,re.I):intent=parse_query(query_text+'订单')
|
||
short_count=(not re.search(NOUN,text,re.I) and not re.search(COUNT,text,re.I)
|
||
and intent is not None and not intent['unsupported']
|
||
and any(intent.get(key) for key in ('status','stage','exception','grouped')))
|
||
if ordinal is None and not relative and not reason and not short_count:return None
|
||
if not tenants:return order_login_response()
|
||
try:
|
||
previous=load_state(conv_id)
|
||
if ordinal is not None:
|
||
if not previous:return {'t':'请先查看需要查询的订单列表,或直接提供入仓码。'}
|
||
connection=connect()
|
||
try:
|
||
shown=shown_references(connection,conv_id,previous)
|
||
finally:connection.close()
|
||
if not shown:return {'t':'当前列表没有可查询的订单,请重新指定筛选条件,或直接提供入仓码。'}
|
||
if not 1<=ordinal<=len(shown):
|
||
return {'t':f'刚才的列表展示了{len(shown)}票,请选择其中一票,或回复“查看全部”展开列表后再查询。'}
|
||
inbound,number=shown[ordinal-1]
|
||
return {'_order_reference':number if inbound=='未登记' else inbound}
|
||
if short_count and not relative and (not previous or previous.get('selected')):return None
|
||
if reason:
|
||
if not previous or previous.get('selected'):return None
|
||
intent={**previous['query'],'exception':True,'unsupported':False}
|
||
elif relative:
|
||
if not previous:return {'t':'请先查询要筛选的订单范围,再继续问“其中”的数量。'}
|
||
if previous.get('selected'):
|
||
return {'t':'您想统计刚才列表中的订单,还是查询当前这一票?请说明订单范围或提供入仓码。'}
|
||
if intent is None or intent['unsupported'] or 'summaries' in intent:
|
||
return {'t':'请明确要在当前订单范围内筛选的一个条件,例如“其中异常的有多少票”。'}
|
||
for key in ('status','stage'):
|
||
if previous['query'][key] and intent.get(key) and previous['query'][key]!=intent[key]:
|
||
return {'t':'当前范围与这次条件对应不同状态或节点,请确认要切换筛选条件,还是继续查看原范围。'}
|
||
intent={**intent,**{key:intent.get(key) or value for key,value in previous['query'].items()}}
|
||
elif intent.get('grouped'):
|
||
if not previous or previous.get('selected'):return None
|
||
intent={**intent,**previous['query']}
|
||
if intent is None or intent['unsupported']:return None
|
||
return answer_summary(intent,tenants,conv_id,reasons=reason)
|
||
except sqlite3.Error:
|
||
return {'t':'订单信息暂时无法读取,请稍后重试。'}
|