feat:前端

This commit is contained in:
2026-09-14 11:54:20 +08:00
parent 86b13c6484
commit e2e1d73b3d
46 changed files with 4477 additions and 128 deletions
+63 -67
View File
@@ -239,13 +239,13 @@ async def chat_stream(
classification = await classify_advisor_intent(
chat_request.query,
getattr(runtime, "llm_client", None),
explicit_intent=chat_request.intent,
explicit_intent=None,
)
inferred_intent = classification.intent
customer_id = chat_request.customer_id
if not chat_request.query and not chat_request.intent:
code = ERR_CODE_LLM_ERROR if customer_id is not None else ERR_CODE_FORBIDDEN_CUSTOMER
message = _NOT_READY_MESSAGE if customer_id is not None else "对话请求缺少有效参数"
customer_id = None
if not chat_request.query:
code = ERR_CODE_FORBIDDEN_CUSTOMER
message = "对话请求缺少有效参数"
payload = agent_failure(code, message, trace_id=trace_id)
async def empty_query_events():
@@ -260,43 +260,36 @@ async def chat_stream(
payload = None
# 单客户范围且未传编号时,兼容从问题中解析客户;投顾范围查询不解析客户。
if chat_request.scope == "customer" and customer_id is None:
if inferred_intent in {
AGENT_INTENT_RECOMMEND,
AGENT_INTENT_REBALANCE,
AGENT_INTENT_FUND_ANALYSIS,
AGENT_INTENT_DIALOGUE_SCRIPT,
AGENT_INTENT_DATA_QUERY,
}:
customer_id, resolve_error = await _resolve_customer_from_query(
db,
advisor_id=user.id,
query=chat_request.query,
db, advisor_id=user.id, query=chat_request.query
)
if resolve_error:
payload = agent_failure(
ERR_CODE_FORBIDDEN_CUSTOMER,
resolve_error,
trace_id=trace_id,
)
payload = agent_failure(ERR_CODE_FORBIDDEN_CUSTOMER, resolve_error, trace_id=trace_id)
async def resolve_error_events():
yield f"data: {json.dumps({'type': SSE_EVENT_TYPE_ERROR, **payload}, ensure_ascii=False)}\n\n"
return StreamingResponse(
resolve_error_events(),
media_type="text/event-stream",
headers={"X-Trace-Id": trace_id},
)
else:
payload = None
return StreamingResponse(resolve_error_events(), media_type="text/event-stream", headers={"X-Trace-Id": trace_id})
# 不带客户编号时只提供通用基金问答,不读取客户画像,也不生成个性化草稿。
if chat_request.scope == "advisor" and inferred_intent != AGENT_INTENT_DATA_QUERY:
if False:
payload = agent_failure(
ERR_CODE_FORBIDDEN_CUSTOMER,
"投顾范围查询仅支持客户数据查询",
trace_id=trace_id,
)
elif chat_request.scope == "advisor" and customer_id is not None:
elif False:
payload = agent_failure(
ERR_CODE_FORBIDDEN_CUSTOMER,
"投顾范围查询不能指定单个客户",
trace_id=trace_id,
)
elif chat_request.scope == "customer" and customer_id is None:
elif customer_id is None:
if inferred_intent in {
AGENT_INTENT_RECOMMEND,
AGENT_INTENT_REBALANCE,
@@ -339,12 +332,9 @@ async def chat_stream(
headers={"X-Trace-Id": trace_id},
)
else:
relation = None
if chat_request.scope == "customer":
customer_id = chat_request.customer_id
relation = await ensure_customer_access(
db, advisor_id=user.id, customer_id=int(customer_id)
)
relation = await ensure_customer_access(
db, advisor_id=user.id, customer_id=int(customer_id)
)
if inferred_intent == AGENT_INTENT_RECOMMEND:
memories = await _recall_advisor_memories(
request,
@@ -405,7 +395,7 @@ async def chat_stream(
db,
advisor_id=user.id,
customer_id=int(customer_id) if customer_id is not None else None,
scope=chat_request.scope,
scope="customer",
question=chat_request.query,
trace_id=trace_id,
llm_client=getattr(_advisor_runtime(request), "llm_client", None),
@@ -516,12 +506,17 @@ async def advisor_data_query(
):
"""查询当前投顾选中客户的数据,不返回 SQL,也不生成草稿。"""
trace_id = _trace_id(request)
customer_id, resolve_error = await _resolve_customer_from_query(
db, advisor_id=user.id, query=body.query
)
if resolve_error or customer_id is None:
return agent_failure(ERR_CODE_FORBIDDEN_CUSTOMER, resolve_error or "请在问题中补充客户编号或客户姓名", trace_id=trace_id)
try:
result = await execute_advisor_data_query(
db,
advisor_id=user.id,
customer_id=body.customer_id,
question=body.question,
customer_id=customer_id,
question=body.query,
trace_id=trace_id,
session_id=body.session_id,
max_rows=body.max_rows,
@@ -626,7 +621,11 @@ async def run_rebalance(
db: AsyncSession = Depends(get_db),
background_tasks: BackgroundTasks = None,
):
customer_id = body.customer_id
customer_id, resolve_error = await _resolve_customer_from_query(
db, advisor_id=user.id, query=body.query
)
if resolve_error or customer_id is None:
return agent_failure(ERR_CODE_FORBIDDEN_CUSTOMER, resolve_error or "请在问题中补充客户编号或客户姓名", trace_id=_trace_id(request))
relation = await ensure_customer_access(
db, advisor_id=user.id, customer_id=int(customer_id)
)
@@ -657,34 +656,22 @@ async def fund_analysis(
user: SysUser = Depends(audited_advisor),
db: AsyncSession = Depends(get_db),
):
memories: list[dict] = []
if body.customer_id is not None:
await ensure_customer_access(
db, advisor_id=user.id, customer_id=body.customer_id
customer_id, resolve_error = await _resolve_customer_from_query(db, advisor_id=user.id, query=body.query)
if resolve_error or customer_id is None:
return agent_failure(ERR_CODE_FORBIDDEN_CUSTOMER, resolve_error or "请在问题中补充客户编号或客户姓名", trace_id=_trace_id(request))
fund_codes = re.findall(r"[A-Za-z]{1,6}\d{3,8}", body.query.upper())
if not fund_codes:
return agent_failure(ERR_CODE_LLM_ERROR, "请在问题中补充基金代码", trace_id=_trace_id(request))
memories = await _recall_advisor_memories(request, customer_id=customer_id, query=body.query)
contexts = await load_fund_analysis_context(db, fund_codes=fund_codes)
if not contexts:
return _not_ready(request)
if len(contexts) > 1:
return agent_success(
{"items": [build_fund_analysis(item["fund"], item["performance"]) for item in contexts]},
trace_id=_trace_id(request),
)
memories = await _recall_advisor_memories(
request,
customer_id=body.customer_id,
query=f"基金分析 {','.join(body.fund_codes)}",
)
fund = body.fund
performance = body.performance
if fund is None or performance is None:
contexts = await load_fund_analysis_context(
db,
fund_codes=[str(code) for code in body.fund_codes],
)
if not contexts:
return _not_ready(request)
if len(contexts) == 1:
fund = contexts[0]["fund"]
performance = contexts[0]["performance"]
else:
return agent_success(
{"items": [build_fund_analysis(item["fund"], item["performance"]) for item in contexts]},
trace_id=_trace_id(request),
)
result = build_fund_analysis(fund, performance)
result = build_fund_analysis(contexts[0]["fund"], contexts[0]["performance"])
runtime = _advisor_runtime(request)
if getattr(runtime, "llm_client", None) is not None:
result["analysis_text"] = await generate_text(
@@ -707,16 +694,25 @@ async def generate_talk_script(
user: SysUser = Depends(audited_advisor),
db: AsyncSession = Depends(get_db),
):
await ensure_customer_access(db, advisor_id=user.id, customer_id=body.customer_id)
customer_id, resolve_error = await _resolve_customer_from_query(db, advisor_id=user.id, query=body.query)
if resolve_error or customer_id is None:
return agent_failure(ERR_CODE_FORBIDDEN_CUSTOMER, resolve_error or "请在问题中补充客户编号或客户姓名", trace_id=_trace_id(request))
scene_type = TALK_SCENE_MARKET_FLUCTUATION
if "投诉" in body.query:
scene_type = TALK_SCENE_CUSTOMER_COMPLAINT
elif "拦截" in body.query or "风控" in body.query:
scene_type = TALK_SCENE_RISK_BLOCK_ORDER
elif "调仓" in body.query or "偏离" in body.query:
scene_type = TALK_SCENE_PORTFOLIO_DIVERGENCE
memories = await _recall_advisor_memories(
request,
customer_id=body.customer_id,
query=f"沟通话术 {body.scene_type}",
customer_id=customer_id,
query=body.query,
)
try:
result = build_talk_script(
body.scene_type,
customer_name=body.customer_name,
scene_type,
customer_name="客户",
)
except ValueError as exc:
raise ApiError(ERR_CODE_LLM_ERROR, str(exc)) from exc