109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""整理澄清问题,等待用户补充"""
|
||
|
||
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:
|
||
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"
|
||
return updated_state
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import asyncio
|
||
import json
|
||
|
||
from pydantic import BaseModel
|
||
|
||
from app.clients.decision import close_decision_engine
|
||
from app.observability.tracing import ainvoke_screen, flush_tracing
|
||
from app.processor.graph import query_app
|
||
|
||
async def main():
|
||
# 修改请求,通过完整流程测试澄清分支
|
||
state: QueryState = {
|
||
"request": {
|
||
"prompt": "这个图不太对",
|
||
"screen": {"canvas": {"width": 1920, "height": 1080}, "nodes": []},
|
||
}
|
||
}
|
||
try:
|
||
final_state = await ainvoke_screen(
|
||
query_app, state, thread_id="clarify-test"
|
||
)
|
||
finally:
|
||
await close_decision_engine()
|
||
flush_tracing()
|
||
print(
|
||
json.dumps(
|
||
{
|
||
key: value.model_dump(mode="json")
|
||
if isinstance(value, BaseModel)
|
||
else value
|
||
for key, value in final_state.items()
|
||
},
|
||
ensure_ascii=False,
|
||
indent=2,
|
||
default=str,
|
||
)
|
||
)
|
||
|
||
asyncio.run(main())
|