feat:新增代码注释

This commit is contained in:
2026-09-29 19:21:29 +08:00
parent e332259b07
commit ff8adba01b
13 changed files with 293 additions and 20 deletions
+29 -2
View File
@@ -15,10 +15,13 @@ class PlannerNode(BaseNode):
name = "planner_node"
async def process(self, state: QueryState) -> QueryState:
# 浅拷贝状态
updated_state: QueryState = state.copy()
# 获取已提取的需求和范围,作为规划内容及修改边界的依据
requirement = state["requirement"]
scope = state["change_scope"]
# 如果需求仍需澄清、不是新建或修改大屏,或无需规划,则拒绝进入本节点
if (
requirement.needs_clarification
or requirement.intent
@@ -30,20 +33,28 @@ class PlannerNode(BaseNode):
):
raise ValueError("PlannerNode 只处理信息完整且需要规划的新建或修改需求")
# 获取当前大屏和用户输入,供模型结合已有内容安排布局与组件
context = state["input_context"]
# 获取可选的组件查询结果
resources = state.get("component_resources")
# 如果已有查询结果但目录未就绪,则停止规划,避免忽略资源查询失败
if resources is not None and resources.status != "resolved":
raise ValueError("组件目录尚未就绪,不能继续规划")
result = await get_llm(ScreenPlan).ainvoke(
llm_client = get_llm(ScreenPlan)
result = await llm_client.ainvoke(
[
# 系统消息提供稳定的规划规则和输出边界
SystemMessage(content=PLANNER_SYSTEM_PROMPT),
# 用户消息承载本次上下文、需求、范围及组件能力摘要
HumanMessage(
content=json.dumps(
{
"input_context": context.model_dump(mode="json"),
"requirement": requirement.model_dump(mode="json"),
"change_scope": scope.model_dump(mode="json"),
# 仅提供规划需要的组件摘要,省略完整默认配置,没有目录时传入 None
"component_resources": resources.to_planner_context()
if resources is not None
else None,
@@ -54,13 +65,18 @@ class PlannerNode(BaseNode):
]
)
# 将模型结果转成字典后重新校验,确保规划字段及组件关系符合输出协议
plan = ScreenPlan.model_validate(result.model_dump())
# 如果提供了组件目录,则额外校验规划中的物料类型是否真实受支持
if resources is not None:
# 提取目录支持的前端组件类型,使用集合进行成员检查
supported_types = {component.type for component in resources.components}
for component in plan.components:
# 如果新增组件没有指定类型,无法选择对应物料,立即报错
if component.disposition == "add" and component.component_type is None:
raise ValueError(f"新增规划组件未指定物料类型: {component.plan_id}")
# 新增或修改组件时,只要明确指定了类型,就必须在目录中存在
if (
component.disposition in ("add", "modify")
and component.component_type is not None
@@ -70,39 +86,50 @@ class PlannerNode(BaseNode):
f"规划使用了目录中不存在的组件类型: {component.component_type}"
)
# 收集当前大屏中真实存在的节点 ID,用于检查规划引用的已有目标
existing_ids = {node.id for node in context.screen.nodes}
# 汇总规划中明确引用的目标 ID,新增组件没有已有目标时不计入
planned_ids = {
component.target_id
for component in plan.components
if component.target_id is not None
}
# 如果规划引用了不存在的组件,则抛出异常并列出无效 ID
if unknown := planned_ids - existing_ids:
raise ValueError(f"规划引用了不存在的组件: {sorted(unknown)}")
# 汇总需求明确指定的目标 ID,避免规划遗漏用户提及的组件
required_ids = {
target for change in requirement.changes for target in change.target_ids
}
# 如果需求目标未在规划中出现,则停止处理并指出遗漏
if missing := required_ids - planned_ids:
raise ValueError(f"规划遗漏了需求中的组件: {sorted(missing)}")
# 如果是局部规划,还需保证对已有组件的修改和删除不超出指定范围
if scope.scope == "local":
# 只统计会改变已有组件的目标,保留和新增组件不纳入此项检查
changed_ids = {
component.target_id
for component in plan.components
if component.disposition in ("modify", "remove")
}
# 如果实际修改或删除的目标不在允许范围内,则拒绝扩大修改范围
if outside := changed_ids - set(scope.target_ids):
raise ValueError(f"局部规划修改了范围外的组件: {sorted(outside)}")
# 原始约束和假设不能在规划阶段被无声丢弃
# 合并需求约束和规划约束,按首次出现顺序去重,保留需求约束在前
plan.constraints = list(
dict.fromkeys([*requirement.constraints, *plan.constraints])
)
# 同样合并原始假设与规划补充的假设,避免重复记录或丢失前置条件
plan.assumptions = list(
dict.fromkeys([*requirement.assumptions, *plan.assumptions])
)
# 保存通过校验的规划,供后续动作生成使用
updated_state["screen_plan"] = plan
# 返回更新后的状态,由图继续处理规划对应的动作
return updated_state