feat:意图识别优化

This commit is contained in:
2026-09-30 06:49:32 -07:00
parent 16212d7138
commit c06053634f
18 changed files with 211 additions and 47 deletions
+2 -6
View File
@@ -13,6 +13,7 @@ from app.processor.state import QueryState
from app.prompt.action import build_action_prompt
from app.schemas.action_validation import ActionValidationError, schema_errors
from app.schemas.actions import ActionBatch, AddAction
from app.schemas.operations import is_direct_operation
def can_generate_actions(state: QueryState) -> bool:
@@ -47,12 +48,7 @@ def action_block_reason(state: QueryState) -> str | None:
unsupported = [
f"{change.target_scope}/{change.operation}"
for change in requirement.changes
if not (
change.target_scope == "nodes"
and change.operation in ("move", "resize", "update", "delete")
or change.target_scope == "canvas"
and change.operation in ("update", "resize", "update_canvas")
)
if not is_direct_operation(change.target_scope, change.operation)
]
# 如果存在不支持的操作,按出现顺序去重后返回原因,便于展示失败信息
+12 -12
View File
@@ -1,15 +1,13 @@
"""意图识别节点"""
from typing import cast
from app.clients.decision import close_decision_engine, get_decision_engine
from app.processor.base import BaseNode
from app.processor.intent_policy import resolve_intent
from app.processor.state import QueryState
from app.prompt.intent import build_intent_question
from app.schemas.base import JsonObject
from app.schemas.decision import DecisionRequest
from app.schemas.input_context import InputContext
from app.schemas.requirement import Intent
class IntentNode(BaseNode):
@@ -101,15 +99,17 @@ class IntentNode(BaseNode):
# 从响应中取出 intent 对应的判断结果
decision = decision_res.results["intent"]
# 如果模型无法明确意图,或置信度低于配置阈值,则转入澄清流程
if (
decision.choice == "unclear"
or decision.confidence < self.config.intent_min_confidence
):
intent: Intent = "unclear"
else:
# 置信度达标时沿用模型选项,cast 仅作类型提示,不进行运行时转换或校验
intent = cast(Intent, decision.choice)
# 用户刚点选了澄清选项时,选择本身已表达明确意图
history = context.clarification_history
# 按选中项概率、风险和澄清轮数确定最终意图,规则集中在 intent_policy
intent = resolve_intent(
decision,
action_min_probability=self.config.intent_min_probability,
qa_min_probability=self.config.intent_qa_min_probability,
clarified_rounds=len(history),
max_clarify_rounds=self.config.intent_max_clarify_rounds,
option_selected=bool(history and history[-1].get("selected_option")),
)
# 完整保留原始决策,便于查看模型选项及其置信度
updated_state["intent_decision"] = decision_res
+7 -2
View File
@@ -10,6 +10,7 @@ from app.actions.plan import plan_materials
from app.clients.llm import get_llm
from app.processor.base import BaseNode
from app.processor.state import QueryState
from app.schemas.capabilities import USER_FAILURE_MESSAGE
from app.prompt.reflection import REFLECTION_SYSTEM_PROMPT
from app.schemas.action_repair import ActionRepairPlan
@@ -71,8 +72,12 @@ class ReflectionNode(BaseNode):
updated_state["repair_plan"] = plan
# 仅在建议允许重试时将次数加一,缺少历史计数时从零开始,不可重试时保持原次数
updated_state["retry_count"] = state.get("retry_count", 0) + int(plan.retryable)
# 可以重试时清空旧失败原因,否则使用建议中的原因说明为何停止修正
updated_state["failure_reason"] = None if plan.retryable else plan.reason
# 可以重试时清空旧失败原因;不可重试时建议中的分析含内部术语,只记录日志,对用户使用统一说明
if not plan.retryable:
self.logger.warning("动作无法修正: {}", plan.reason)
updated_state["failure_reason"] = (
None if plan.retryable else USER_FAILURE_MESSAGE
)
# 返回修正建议、重试次数和失败原因,由后续路由决定重新生成动作还是结束
return updated_state
+21
View File
@@ -56,6 +56,27 @@ class RequirementNode(BaseNode):
# 如果存在无效目标,立即抛出异常,并排序输出 ID 以便定位问题
raise ValueError(f"需求引用了不存在的节点: {sorted(unknown_ids)}")
# 纯新增需求缺少位置、尺寸等信息时不澄清,采用默认值先执行,用户之后可再次提出调整
# 提示词无法保证模型每次都遵守,因此在这里兜底;含待确认目标的需求不受影响
if (
requirement.needs_clarification
and requirement.changes
and all(
change.target_scope == "screen" and change.operation == "add"
for change in requirement.changes
)
):
requirement = requirement.model_copy(
update={
"needs_clarification": False,
"clarification_question": None,
"assumptions": [
*requirement.assumptions,
"未指定的位置、尺寸和数据来源采用默认值,数据使用物料示例数据",
],
}
)
# 保存通过结构及目标校验的需求,供后续澄清或范围判断使用
updated_state["requirement"] = requirement
# 返回更新后的状态,由图继续执行后续节点
+2 -2
View File
@@ -8,6 +8,7 @@ from app.processor.base import BaseNode
from app.processor.state import QueryState
from app.schemas.action_validation import ActionValidationError, schema_errors
from app.schemas.actions import ActionBatch, AddAction, UpdateCanvasAction
from app.schemas.operations import is_canvas_operation
class ValidatorNode(BaseNode):
@@ -76,8 +77,7 @@ class ValidatorNode(BaseNode):
allowed.update(
("canvas", "", "update_canvas")
for change in requirement.changes
if change.target_scope == "canvas"
and change.operation in ("update", "resize", "update_canvas")
if is_canvas_operation(change.target_scope, change.operation)
)
# 画布没有组件 ID,使用独立作用域避免与同名组件混淆