"""整理澄清问题,等待用户补充""" import json from langchain_core.exceptions import OutputParserException from langchain_core.messages import HumanMessage, SystemMessage from pydantic import ValidationError from app.clients.llm import get_llm from app.processor.base import BaseNode from app.processor.state import QueryState from app.prompt.clarify import CLARIFY_SYSTEM_PROMPT from app.schemas.clarification import Clarification class ClarifyNode(BaseNode): name = "clarify_node" async def process(self, state: QueryState) -> QueryState: # 浅拷贝状态,保存澄清问题和等待状态时不直接修改传入 state 的顶层字段 updated_state: QueryState = state.copy() # 获取上游确认的意图,区分意图不明确和具体需求缺少信息两种澄清场景 intent = state["intent"] # 整理意图和输入上下文,让模型结合用户原话、大屏及已有澄清记录生成问题 payload = { "intent": intent, "input_context": state["input_context"].model_dump(mode="json"), } # 如果意图已明确,补充需求节点的结果,围绕尚缺的信息提问 if intent != "unclear": # 以需求节点的问题为依据补齐选项,不重新判断意图 payload["requirement"] = state["requirement"].model_dump(mode="json") llm_client = get_llm(Clarification) # 系统消息提供稳定的澄清规则,用户消息承载本次需要分析的具体资料 messages = [ SystemMessage(content=CLARIFY_SYSTEM_PROMPT), HumanMessage(content=json.dumps(payload, ensure_ascii=False)), ] # 最多调用两次,仅在输出结构或解析失败时重试一次,其他异常继续向上抛出 for attempt in range(2): try: # 异步生成澄清问题和候选项,成功后立即退出重试循环 clarification = await llm_client.ainvoke(messages) break except (ValidationError, OutputParserException): # 如果首次生成格式不合法,追加明确的格式要求,再调用一次模型 if attempt == 0: # 要求重新生成完整问题,避免截断选项改变原意 messages.append( HumanMessage( content=( "上次输出未通过 Clarification 格式校验,请重新生成完整结果:" "question 为非空问题,options 为空或含 2–3 项," "每项包含唯一 id、非空 label 和 description,最多一个 recommended=true" ) ) ) # 进入下一次尝试,使用包含格式纠正要求的消息列表重新生成 continue # 意图不明确时不读取需求,避免使用尚未生成或不适用的需求结果 requirement = state.get("requirement") if intent != "unclear" else None # 优先使用需求节点已有的澄清问题,没有可用问题时使用通用询问 question = ( requirement.clarification_question if requirement else None ) or "你希望我对当前大屏做什么?可以描述想修改的内容,或想了解的问题" # 只构建问题,不提供候选项,让用户自由补充,避免继续依赖模型格式修正 clarification = Clarification(question=question) # 保存模型生成或兜底构建的澄清内容,供后续暂停流程展示给用户 updated_state["clarification"] = clarification # 在进入 interrupt 前保存等待状态,暂停中的节点尚不会返回更新 updated_state["status"] = "waiting_for_input" # 返回澄清内容和等待状态,实际暂停由后续调用 interrupt 的节点完成 return updated_state