feat: 新增需求理解并实现human in loop
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
"""暂停图并接收用户回答,不在恢复时重复调用模型"""
|
||||||
|
|
||||||
|
from langgraph.types import interrupt
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
|
from app.processor.base import BaseNode
|
||||||
|
from app.processor.state import QueryState
|
||||||
|
from app.schemas.clarification_reply import ClarificationReply
|
||||||
|
|
||||||
|
|
||||||
|
class WaitForUserNode(BaseNode):
|
||||||
|
name = "wait_for_user_node"
|
||||||
|
|
||||||
|
def process(self, state: QueryState) -> QueryState:
|
||||||
|
payload = state["clarification"].model_dump(mode="json")
|
||||||
|
while True:
|
||||||
|
# interrupt 的控制异常必须交给 LangGraph 处理
|
||||||
|
value = interrupt(payload)
|
||||||
|
try:
|
||||||
|
reply = ClarificationReply.model_validate(
|
||||||
|
{"answer": value} if isinstance(value, str) else value
|
||||||
|
)
|
||||||
|
if not reply.cancel and reply.option_id is not None:
|
||||||
|
if reply.option_id not in {
|
||||||
|
option.id for option in state["clarification"].options
|
||||||
|
}:
|
||||||
|
raise ValueError("无效选项")
|
||||||
|
break
|
||||||
|
except (ValidationError, ValueError):
|
||||||
|
payload = {
|
||||||
|
**payload,
|
||||||
|
"error": "请选择本题有效选项或填写回答,并提供有效的大屏数据;也可设置 cancel=true 取消",
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
**state,
|
||||||
|
"clarification_reply": reply,
|
||||||
|
"status": "cancelled" if reply.cancel else "running",
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"""用户恢复澄清流程时提交的内容"""
|
||||||
|
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
from pydantic import model_validator
|
||||||
|
|
||||||
|
from app.schemas.base import ExtensibleModel, NonBlankString
|
||||||
|
from app.schemas.request import RequestContext
|
||||||
|
from app.schemas.screen import Screen
|
||||||
|
|
||||||
|
|
||||||
|
class ClarificationReply(ExtensibleModel):
|
||||||
|
answer: NonBlankString | None = None
|
||||||
|
option_id: NonBlankString | None = None
|
||||||
|
cancel: bool = False
|
||||||
|
# 不传时沿用暂停前的快照,传入时完整替换对应字段
|
||||||
|
screen: Screen | None = None
|
||||||
|
context: RequestContext | None = None
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def require_answer(self) -> Self:
|
||||||
|
if not self.cancel and self.answer is None and self.option_id is None:
|
||||||
|
raise ValueError("请提供回答,或设置 cancel=true 取消")
|
||||||
|
return self
|
||||||
Reference in New Issue
Block a user