feat:添加图表数据
This commit is contained in:
@@ -192,6 +192,16 @@ class ActionNode(BaseNode):
|
||||
updated_state["validation_errors"] = [GENERATION_ERROR]
|
||||
return updated_state
|
||||
|
||||
# 模型确认现有能力无法满足需求时直接失败收尾,不进入重试
|
||||
if batch.unsupported_reason:
|
||||
updated_state.update(
|
||||
actions=[],
|
||||
validation_passed=False,
|
||||
failure_reason=f"动作生成判断无法完成:{batch.unsupported_reason}",
|
||||
status="failed",
|
||||
)
|
||||
return updated_state
|
||||
|
||||
# 保存生成的动作,实际目标、规划一致性及模拟执行结果仍由后续校验节点检查
|
||||
updated_state["actions"] = batch.actions
|
||||
|
||||
|
||||
@@ -1,31 +1,40 @@
|
||||
"""统一收尾能力不足、资源缺失和动作修正失败"""
|
||||
"""统一收尾能力不足、资源缺失和动作修正失败
|
||||
|
||||
这里只整理内部失败原因,面向用户的说明由回答节点交给模型总结,代码中不写死给用户的文案
|
||||
"""
|
||||
|
||||
from app.processor.base import BaseNode
|
||||
from app.processor.state import QueryState
|
||||
|
||||
|
||||
def failure_notice(state: QueryState) -> str:
|
||||
"""汇总上游记录的失败原因、最近一次修正分析和校验错误,作为交给模型的事实说明"""
|
||||
facts = []
|
||||
if reason := state.get("failure_reason"):
|
||||
facts.append(reason)
|
||||
plan = state.get("repair_plan")
|
||||
if plan is not None and plan.reason not in facts:
|
||||
facts.append(f"失败分析:{plan.reason}")
|
||||
if errors := state.get("validation_errors"):
|
||||
facts.append("校验错误:" + ";".join(error.message for error in errors))
|
||||
facts.append(f"已自动修正 {state.get('retry_count', 0)} 次")
|
||||
detail = "\n".join(facts) if facts else "没有记录具体原因"
|
||||
return f"本次修改请求没有执行,内部原因如下:\n{detail}"
|
||||
|
||||
|
||||
class FallbackNode(BaseNode):
|
||||
name = "fallback_node"
|
||||
|
||||
def process(self, state: QueryState) -> QueryState:
|
||||
# 浅拷贝状态
|
||||
updated_state: QueryState = state.copy()
|
||||
# 读取上游记录的具体失败原因
|
||||
reason = state.get("failure_reason")
|
||||
notice = failure_notice(state)
|
||||
self.logger.warning(notice)
|
||||
|
||||
# 如果上游没有提供有效原因,则根据校验错误补充失败说明
|
||||
if not reason:
|
||||
# 如果仍有校验错误,使用达到自动修正上限的说明,并带上已重试次数
|
||||
if state.get("validation_errors"):
|
||||
reason = f"动作校验未通过,已达到自动修正上限,共重试 {state.get('retry_count', 0)} 次"
|
||||
else:
|
||||
# 如果既没有具体原因也没有校验错误,使用通用说明,避免返回空原因
|
||||
reason = "当前需求未能完成,缺少具体失败原因"
|
||||
|
||||
# 将任务标记为最终失败,结束本轮处理
|
||||
# 将任务标记为最终失败,由回答节点向用户说明原因
|
||||
updated_state["status"] = "failed"
|
||||
# 保存上游原因或本节点补充的说明,供后续输出给用户
|
||||
updated_state["failure_reason"] = reason
|
||||
updated_state["failure_reason"] = notice
|
||||
updated_state["notice"] = notice
|
||||
# 显式标记校验未通过,防止残留动作被当作可执行结果
|
||||
updated_state["validation_passed"] = False
|
||||
# 清空旧模拟快照,避免失败结果中仍携带之前的成功快照
|
||||
|
||||
@@ -65,7 +65,7 @@ class PlannerNode(BaseNode):
|
||||
# 解析失败、引用不存在的目标等可由模型自行纠正,超过次数才失败收尾
|
||||
if attempt == PLAN_MAX_REPAIRS:
|
||||
return self.fail_on_invalid_output(
|
||||
state, error, "未能生成可用的大屏规划,请调整需求后重试"
|
||||
state, error, "规划输出多次未通过校验"
|
||||
)
|
||||
self.logger.warning(f"规划未通过校验,重新规划: {error}")
|
||||
feedback = f"上一次输出未通过校验:{error}\n请修正后重新输出完整规划。"
|
||||
|
||||
@@ -38,7 +38,7 @@ class QANode(BaseNode):
|
||||
|
||||
content = state["input_context"].model_dump_json(exclude_unset=True)
|
||||
offer = None
|
||||
# 落地策略判定请求无法执行时,附上事实说明,由本节点如实告知用户
|
||||
# 请求无法执行或执行失败时,附上事实说明,由本节点如实告知用户
|
||||
if notice := state.get("notice"):
|
||||
content += f"\n\n系统说明:{notice}"
|
||||
# 回复中的替代修改需要结构化保存,下一轮用户同意时直接执行,因此不流式输出
|
||||
@@ -59,8 +59,9 @@ class QANode(BaseNode):
|
||||
updated_state["answer"] = answer
|
||||
# 保存等待用户确认的提议,没有提议时清空
|
||||
updated_state["offer"] = offer
|
||||
# 回答生成完成,问答分支在此结束
|
||||
updated_state["status"] = "completed"
|
||||
# 回答生成完成;失败收尾后的说明保持失败状态,不交付动作
|
||||
if state.get("status") != "failed":
|
||||
updated_state["status"] = "completed"
|
||||
|
||||
# 返回包含回答和完成状态的更新结果
|
||||
return updated_state
|
||||
|
||||
@@ -15,7 +15,6 @@ from app.processor.state import QueryState
|
||||
from app.prompt.action import FIELD_QUERY_PROMPT, uses_material_fields
|
||||
from app.prompt.reflection import REFLECTION_SYSTEM_PROMPT
|
||||
from app.schemas.action_repair import ActionRepairPlan
|
||||
from app.schemas.capabilities import USER_FAILURE_MESSAGE
|
||||
|
||||
|
||||
class ReflectionNode(BaseNode):
|
||||
@@ -88,11 +87,9 @@ class ReflectionNode(BaseNode):
|
||||
updated_state["repair_plan"] = plan
|
||||
# 仅在建议允许重试时将次数加一,缺少历史计数时从零开始,不可重试时保持原次数
|
||||
updated_state["retry_count"] = state.get("retry_count", 0) + int(plan.retryable)
|
||||
# 可以重试时清空旧失败原因;不可重试时建议中的分析含内部术语,只记录日志,对用户使用统一说明
|
||||
if not plan.retryable:
|
||||
self.logger.warning("动作无法修正: {}", plan.reason)
|
||||
# 可以重试时清空旧失败原因;不可重试时保留分析,由失败收尾交给模型向用户说明
|
||||
updated_state["failure_reason"] = (
|
||||
None if plan.retryable else USER_FAILURE_MESSAGE
|
||||
None if plan.retryable else f"修正分析判断无法完成:{plan.reason}"
|
||||
)
|
||||
|
||||
# 返回修正建议、重试次数和失败原因,由后续路由决定重新生成动作还是结束
|
||||
|
||||
@@ -18,9 +18,7 @@ class RequirementNode(BaseNode):
|
||||
return await self._generate(state)
|
||||
except ValueError as error:
|
||||
# 结构化输出解析失败或引用校验不通过(含 ValidationError、OutputParserException)
|
||||
return self.fail_on_invalid_output(
|
||||
state, error, "未能可靠理解你的需求,请换个说法再试"
|
||||
)
|
||||
return self.fail_on_invalid_output(state, error, "需求理解的输出未通过校验")
|
||||
|
||||
async def _generate(self, state: QueryState) -> QueryState:
|
||||
# 浅拷贝状态
|
||||
|
||||
@@ -22,9 +22,7 @@ class ScopeNode(BaseNode):
|
||||
return await self._generate(state)
|
||||
except ValueError as error:
|
||||
# 结构化输出解析失败或引用校验不通过(含 ValidationError、OutputParserException)
|
||||
return self.fail_on_invalid_output(
|
||||
state, error, "未能确定需要修改的范围,请描述得更具体一些"
|
||||
)
|
||||
return self.fail_on_invalid_output(state, error, "修改范围的输出未通过校验")
|
||||
|
||||
async def _generate(self, state: QueryState) -> QueryState:
|
||||
# 浅拷贝状态
|
||||
|
||||
Reference in New Issue
Block a user