Files
AgentLab/backend/app/plain.py
T
ThearchyHelios d2b398563b 写回卡片加「结果不明时谁去核实」:写回的目标能力没登记查询时,结果不明(请求发出去了、没拿到答复)平台查不了、也不自动重发,要有人去目标系统里核实;以前发布单报「写入结果不明时没人核实」「有一步还没指定谁来做」却在卡片上找不到地方改。
- 蓝图格式:写回卡片多一项 reconciler(一组人或组),旧蓝图没有这一项照旧可读;只收用户的操作(同「谁能看」),助手整张换写回卡片时留着用户选好的人
- 是否需要(templates/_common.needs_reconciler,和应用骨架加核实岗位同一个判断):模板用到写回卡片、选了目标能力、能力没登记查询;交给前端的蓝图带 reconciler_needed,界面不自己判
- 蓝图检查 bp.reconciler_unbound:试运行只提醒(待办派给试运行的人自己),发布挡住;发布单上应用检查说同一件事的 app.reconciler_unbound 和核实岗位的 app.slot_unbound 不再重复列
- 岗位绑定:选了的人绑到应用的核实岗位;能力补登了查询就不再要、也不再绑,卡片上留着的人照样摆出来、写明用不上
- 卡片:需要时摆这一项,标「缺」、写明为什么要人核实,选人控件只能选人或组;「缺」只挡发布;蓝图助手的提示词写明这一项由用户定
- 示例蓝图(核对写回)补上核实人,审批、核对写回的卡片说明写上这一项;测试和检查脚本跟上
2026-10-15 17:31:20 +08:00

409 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""白话层(docs/design/normal-user-apps.md N10):问题编号 → 一句话、为什么要紧、谁来改、怎么办。
覆盖工作流发布门禁(contract.* / governed.* / action.* / process.*)、应用检查(app.*)、蓝图(bp.*)、模板(tpl.*)、
个人应用(personal.*)、发布单步骤失败(plan.*)。没登记白话的照旧显示技术说法;检查脚本报「有问题编号没有白话」。
who:you(你来定)/ assistant(助手可以修)/ admin(要找管理员),按自动修复的 kind、APP_HUMAN_ONLY、要管理员的编号判。
框架在这里;内容由包 B2 补齐(B1、B3、B4 新增的编号各自补进来)。
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
WHO = ("you", "assistant", "admin")
@dataclass(frozen=True)
class Plain:
title: str
why: str = ""
who: str = "you"
action: str = ""
_Y, _A, _M = "you", "assistant", "admin"
#: 问题编号 → 白话。前缀匹配用 PLAIN_PREFIX(如 "subgraph_version_" 一类)
PLAIN: dict[str, Plain] = {
# ---------------------------------------------------------------- 出具契约(engine/governance.py)
"contract.not_object": Plain(
"成果的出具规则格式不对", "出具规则读不出来,系统就判不了这份成果能不能正式交出去。", _A,
"交给助手按出具规则的格式重写,或在成果节点上重新设置。"),
"contract.metrics_from_missing": Plain(
"没说指标从哪张口径卡来", "报告里的数对不上任何指标,核对不了出处。", _A,
"在成果节点的出具规则里选上提供指标的口径卡。"),
"contract.metrics_from_invalid": Plain(
"出具规则选的口径卡不存在", "选错了口径卡,核对时找不到指标。", _A, "改选工作流里实际存在的口径卡。"),
"contract.report_from_missing": Plain(
"没说核对哪一份报告", "不指明报告,报告里的数就没人核对。", _A, "在出具规则里选上写报告的那个节点。"),
"contract.report_from_invalid": Plain(
"出具规则选的报告节点不对", "选的不是写报告的节点,或者不在成果的上游,核对会落空。", _A,
"改选成果上游的报告撰写节点。"),
"contract.required_missing": Plain(
"没列出必须有的指标", "不列出必需指标,缺了关键的数也会交出去。", _Y,
"选出这份成果里必须有的指标(哪些缺了就不交出去由你定)。"),
"contract.cells_undeclared": Plain(
"报告直接引用了查询结果的格子,却没声明", "受管级别不认没声明的引用,正式运行时报告会写不出来。", _Y,
"选择允许直接引用查询结果,或者加一张口径卡把数登记成指标。"),
"contract.strict_off": Plain(
"出具规则没开严格模式", "不开的话,说不清出处的数只会让成果降档,不会被拦下。", _A, "打开严格模式。"),
"contract.claims_ignored": Plain(
"「没附依据的结论句」那一项设了也不生效", "受管级别仍会把没附依据的结论句算进缺口,这项设置只会误导人。", _A,
"改成降档或不予出具。"),
# ---------------------------------------------------------------- 受管级门禁(engine/governance.py)
"governed.supervisor": Plain(
"用了由模型临场分工的节点", "分工和轮数在运行时才定,事先没法审核,受管级别不收。", _Y,
"改用配置固定的 Agent 或「模型调用」节点(结构要拆成哪几步由你定)。"),
"governed.subgraph_unpinned": Plain(
"引用的工作流没固定版本", "上游一改,这边的口径跟着变,没人知道。", _Y, "选定一个已发布的版本。"),
"governed.agent_approval_never": Plain(
"Agent 调危险工具不用审批", "受管级别要求危险工具至少经人审批。", _A, "把审批策略改为「仅危险工具需要审批」。"),
"governed.default_approval_never": Plain(
"全图默认不用审批", "跟着全图默认的 Agent 调危险工具时没人把关。", _A, "把全图默认的审批策略改为需要审批。"),
"governed.agent_no_tools": Plain(
"Agent 没配工具", "不调工具的 Agent 只是在写文字,用「模型调用」节点更合适。", _Y, "改用「模型调用」节点,或给它配上工具。"),
"governed.agent_tools_wide": Plain(
"Agent 的工具太多", "授权范围越宽,越难说清它会做什么。", _Y, "只留下完成任务要用的工具。"),
"governed.code_network": Plain(
"代码节点开了联网", "在代码里取的数不留快照,事后查不了出处。", _Y, "把取数改到「调用工具」节点。"),
"governed.no_contract": Plain(
"成果没有出具规则", "没有出具规则,系统判不了这份成果完不完整。", _A, "给成果节点加上出具规则。"),
"governed.metric_untraced": Plain(
"有指标追不到原始数据", "按指标出具时,指标的出处就是全部依据;追不到的那一个,「完整出具」就没有根据。", _A,
"在口径卡里按格取数,或让整形节点原样解析查询结果。"),
"governed.report_from_required": Plain(
"出具规则核对的是叙述,不是报告", "按数值找出处可能对上好几个地方,受管级别要求每个数只有一个出处。", _A,
"把核对对象改为上游的报告撰写节点。"),
"governed.exit_text_source": Plain(
"成果里的文字不是报告写的", "别的节点写的文字说不清出处。", _A, "成果字段改取报告撰写节点的正文。"),
"governed.text_bypass": Plain(
"模型写的文字绕过了报告直接进了成果", "这些文字里的数和表名都没法追溯。", _A, "改由报告撰写节点来写,成果取报告正文。"),
"governed.report_policy": Plain(
"报告的核对规则不够严", "受管级别要求报告用最严的核对规则。", _A, "把报告撰写节点的核对规则调到最严。"),
"governed.judge_budget": Plain(
"模型裁判没设金额上限", "裁判的花费不能沿用默认值,要事先说清。", _Y, "写一个每份报告的裁判金额上限,或明确选择不限。"),
"governed.caliber_compute_input": Plain(
"口径卡的数来自代码计算", "沙箱里算出来的数追溯不了出处。", _Y,
"若这一步是在取数,把它标成取数;若是在计算,把计算挪到口径卡的表达式里(是哪一种由你定)。"),
"governed.caliber_agent_schema": Plain(
"给口径卡供数的 Agent 交的是自由文本", "口径卡读到的数没法核对到查询结果。", _A,
"给 Agent 配上结构化输出,并打开按出处核对字段。"),
"governed.caliber_agent_cite_fields": Plain(
"给口径卡供数的 Agent 没核对字段出处", "口径卡读到的仍是模型给的数。", _A, "打开按出处核对字段。"),
"governed.caliber_model_input": Plain(
"口径卡的数来自「模型调用」节点", "这种节点不调工具,写出的数没有出处。", _A, "改由「调用工具」节点或配好结构化输出的 Agent 取数。"),
# ---------------------------------------------------------------- 写外部系统(engine/lint_capabilities.py)
"action.write_in_agent": Plain(
"Agent 的工具里有会写外部系统的能力", "写入只能放在「调用工具」节点里,先确认再发送、留生效记录。", _Y,
"从 Agent 的工具清单里移除它,改用「调用工具」节点。"),
"action.unattended_write": Plain(
"写入外部系统不经确认,而这项能力不允许无人值守", "没人确认就写进外部系统,出了错没人拦。", _Y,
"把这一步改为需要确认;确实要无人值守的,请管理员在能力登记里放开。"),
"action.prod_missing": Plain(
"写入能力没有生产环境", "正式运行要写的时候无处可发。", _M, "请管理员在「工具 → 端点」里补上生产环境。"),
"action.reconcile_unplanned": Plain(
"写入能力没有登记查询", "写入结果不明时没法自动核实,只能转人工核实。", _M, "请管理员在能力登记里给它指定查询能力。"),
# ---------------------------------------------------------------- 按流程使用(engine/lint_process.py)
"process.wait_mid_agent": Plain(
"Agent 中途可能停下来等审批", "按流程使用时,停在 Agent 中途的断点在系统升级后恢复不了。", _Y,
"把需要审批的调用挪到「调用工具」节点。"),
"process.human_in_subgraph": Plain(
"人工步骤放在了引用的工作流里", "引用的工作流不保存断点,恢复时整段从头再来,可能重复办。", _Y,
"把人工步骤挪到外层工作流。"),
"process.code_network": Plain(
"代码节点联网,可能在写外部系统", "按流程使用时,写外部系统要先留生效记录,结果不明时才能核实。", _Y,
"把对外部系统的写入挪到「调用工具」节点。"),
# ---------------------------------------------------------------- 应用检查(apps/lint.py、apps/resolve.py)
"app.format": Plain(
"应用描述的格式不对", "格式不对的应用发布不了。", _A, "交给助手按应用格式修好。"),
"app.pin_unpublished": Plain(
"应用用的工作流版本还没发布", "应用只能用已发布的版本,正式运行只从已发布的版本发起。", _Y,
"先发布这一版,或换成一个已发布的版本。"),
"app.pin_retired": Plain(
"应用用的工作流版本已退役", "退役的版本不再接受新的正式运行。", _Y, "升级到一个在用的发布版本。"),
"app.pin_hash": Plain(
"应用记下的版本和库里的对不上", "按内容指纹认版本,对不上就说不清运行的是哪张图。", _Y, "重新选择这一版。"),
"app.upgrade_policy": Plain(
"口径变了,还没选以前的数怎么办", "换版本可能改了口径,新旧数放在一起比之前要先说清。", _Y,
"选一种处置:按新口径重算、新旧并列,或注明前后不可比。"),
"app.binding_target_missing": Plain(
"页面上有一块绑的东西不存在", "这一块会显示不出来。", _A, "改绑到存在的视图、指标或节点,或删掉这一块。"),
"app.view_source_empty": Plain(
"这个视图暂时还没有数", "页面会写明还没有数据,等第一次正式运行完成就有了。", _Y, "不用改;想马上有数,发布后运行一次。"),
"app.on_demand_ttl": Plain(
"按需查看的缓存时效没设好", "查活库的视图不设时效,同样的条件会一直给旧结果。", _A, "设一个缓存时效。"),
"app.component_unknown": Plain(
"用了组件目录里没有的组件", "界面画不出来不认识的组件。", _A, "换成组件目录里有的组件。"),
"app.props_invalid": Plain(
"组件的设置不对", "设置不对,这一块可能显示错或显示不出来。", _A, "按组件的说明改设置。"),
"app.text_number": Plain(
"页面的固定文字里写了数字", "写死的数不会随数据更新,也说不清出处。", _A, "改成绑定到指标或查询结果。"),
"app.binding_not_evidenced": Plain(
"成果字段里的数不是报告里的原文", "说不清出处的数不展示。", _A, "改绑报告,或让成果字段逐字取自报告。"),
"app.metric_untraced": Plain(
"页面上的指标追不到原始数据", "这个数点开看不到出处。", _A, "换一个能追到原始数据的指标,或先改口径卡。"),
"app.expr_arith": Plain(
"显示条件里做了计算", "页面层不做算术,数要在工作流里算好。", _A, "把计算挪到口径卡,条件里只做比较。"),
"app.expr_invalid": Plain(
"显示条件写法不对", "条件算不出来时这一块按不显示处理。", _A, "改正条件的写法。"),
"app.expr_root": Plain(
"显示条件用了不认识的名字", "条件算不出来时这一块按不显示处理。", _A, "改用条件里能用的名字。"),
"app.entry_form_mismatch": Plain(
"发起表单和工作流的输入对不上", "填的东西传不到工作流里,或者传错位置。", _A, "让表单字段和工作流的输入一一对应。"),
"app.entry_unmapped_required": Plain(
"工作流必填的输入,发起表单没给", "发起时会因为缺输入而失败。", _A, "在发起表单里加上这一项,或给它一个固定值。"),
"app.slot_unbound": Plain(
"有一步还没指定谁来做", "没人的那一步会派给任何人,审批、升级都会悄悄失效。", _Y, "在「谁来做」里给这一步选人或组。"),
"app.slot_principal_unknown": Plain(
"「谁来做」里选的人或组在名录里对不上", "派给这个名字的待办没有人能处理。", _Y, "从候选中重新选择,或移除。"),
"app.reconciler_unbound": Plain(
"写入结果不明时没人核实", "写外部系统的结果不明时,要有人去外部系统里查。", _Y, "指定负责核实写入结果的人或组。"),
"app.process_unsafe": Plain(
"这个工作流按流程使用不安全", "流程要能停下等人、断了能接着走,这张图有一处做不到。", _Y,
"按问题里说的改工作流,再发布新版本。"),
"app.hook_entry_missing": Plain(
"外部系统发起用的入口不存在", "外部系统发来的请求会被拒收。", _A, "改选存在的入口。"),
"app.notify_channel_missing": Plain(
"订阅的通知渠道不存在", "这条通知发不出去。", _Y, "换一个存在的渠道,或请管理员建好它。"),
"app.acl_empty": Plain(
"还没给任何人用", "开了登录时,除了管理员和搭建者谁都打不开。", _Y, "在「谁能看」里给要用的人或组授权。"),
"app.acl_principal_unknown": Plain(
"授权给的人或组在名录里对不上", "以后建了同名的人会直接拿到这一档权限,或者停用的人重新启用后又能用。", _Y,
"从候选中重新选择,或移除。"),
"app.polish_refused": Plain(
"助手想改的地方超出了它能改的范围", "助手只能改页面的呈现,改数据、改流程要人来。", _Y, "确实要改的,由你在对应的地方改。"),
# ---------------------------------------------------------------- 发布单(blueprints/publish_plan.py)
"plan.not_instantiated": Plain(
"蓝图还没生成工作流", "没有图就没有东西可以发布。", _Y, "先把卡片补齐,用真数试运行一次。"),
"plan.graph_changed": Plain(
"出单之后蓝图又改过", "要发布的已经不是这份发布单看过的那张图。", _Y, "重新出一份发布单。"),
"plan.trial_failed": Plain(
"最近一次试运行失败了", "用真数运行出了错,这样发布出去,每次出数都会是同一个错。", _Y,
"回到试运行看出错的原因(可以交给助手修),修好后再用真数试一次。"),
"plan.untrialed": Plain(
"还没用真数试运行过", "没看过真数就发布,可能不是你要的结果。", _Y, "先试运行一次,看过结果再发布。"),
"plan.trial_differs": Plain(
"发布的内容和最近一次试运行不同", "试运行之后又改过,发布的这一版没用真数看过。", _Y, "建议再试运行一次。"),
"plan.admin_required": Plain(
"受管级别只有管理员能发布", "受管级别的工作流按最严的规则出具,发布由管理员签字。", _M, "请管理员来发布,或改按已发布级别发布。"),
"plan.downgrade_admin": Plain(
"这会把受管工作流降为已发布", "降级之后正式运行不再过受管级别的检查,要管理员决定。", _M, "请管理员来发布,或改按受管级别发布。"),
"plan.preflight": Plain(
"发布前检查没通过", "有问题没解决,这时发布会留下半截状态,所以什么都没写。", _Y, "按下面列出的问题逐条处理,再接着做。"),
"plan.workflow_publish": Plain(
"工作流没能发布", "工作流不发布,就不会有正式运行。", _Y, "按问题处理后接着做。"),
"plan.schedule_invalid": Plain(
"定时设置不对", "按这个设置建不了定时任务。", _Y, "回到「多久更新」卡片改好,再接着做。"),
"plan.app_publish": Plain(
"应用没能发布", "工作流已经发布了,应用还没有;使用者暂时看不到它。", _Y, "按问题处理后接着做。"),
"plan.app_archived": Plain(
"这个蓝图对应的应用已归档", "归档的应用不再改草稿、不再发布。", _Y, "如需继续,请新建一个应用。"),
"plan.app_forbidden": Plain(
"你对这个蓝图发布成的应用没有管理权限", "发布单会改这个应用的页面、发布新的一版、替换它的授权,这些要管理权限。", _M,
"请应用的管理者或管理员来发布,或请他们在应用的授权里给你管理权限。"),
"plan.grant_refused": Plain(
"「谁能看」里有人或组在名录里对不上", "授权只能给名录里在用的人和组。", _Y, "回到「谁能看」卡片重新选择。"),
"plan.first_run": Plain(
"第一次运行没能发起", "应用已经发布好了,只是打开时还没有数。", _Y, "稍后在应用里运行一次,或等定时任务出数。"),
"plan.object_missing": Plain(
"前面某一步建好的东西不见了", "接着做之前要确认前面几步的结果还在。", _Y, "重新出一份发布单。"),
"plan.interrupted": Plain(
"执行到一半服务重启了", "这一步可能只做了一半,后面的步骤都没有做。", _Y,
"接着做:已经存好的版本、建好的应用会按原样复用,不会另建一份。"),
"plan.step_failed": Plain(
"这一步出了意外的错", "这一步没做完,后面的步骤都没有做。", _Y, "稍后接着做;一直不行请把错误信息交给管理员。"),
}
# ---------------------------------------------------------------- 个人应用与页面编辑(包 B4:blueprints/personal.py、
# apps/lint.py 的 _lint_personal / _lint_views / _lint_hidden、api/personal.py)
PLAIN.update({
"app.view_source_personal": Plain(
"视图的数据来源和应用的类型对不上", "探索运行的结果没经过发布审核,只能给个人应用的拥有者自己看;个人应用又只有"
"探索运行,取别的来源永远是空的。", _Y,
"共享应用的视图改成取正式运行(最近一次出数、按条件查看或本实例);个人应用的视图改回取它自己的刷新结果。"),
"app.hidden_bound": Plain(
"隐藏的这块是唯一显示这组数的地方", "隐藏之后,页面上一处都看不到这组数了。", _Y,
"确实不要了可以删掉它;还要看的话取消隐藏。"),
"personal.policy": Plain(
"个人应用里有使用者不能用的东西", "个人应用只能读数据、整理、撰写,不能审批、不能写入别的系统,也不能执行代码,"
"这样它的结果才能只给你自己看。", _Y, "回到蓝图换一种做法;需要审批或写回的,请提交给搭建者做成正式应用。"),
"personal.off": Plain(
"管理员还没有开放个人应用", "个人应用由管理员决定开不开放。", _M, "请管理员在设置里打开,或请搭建者做成应用。"),
"personal.not_trialed": Plain(
"还没有用真数试过", "没看过真数就存,可能不是你要的结果。", _Y, "先试运行一次,看过结果再存成你的应用。"),
"personal.graph_changed": Plain(
"蓝图改过,还没重新存成你的应用", "刷新按存下的那张图运行,改过的蓝图还没生效。", _Y,
"回到蓝图「存成我的应用」,再刷新。"),
"personal.daily_limit": Plain(
"今天的刷新次数用完了", "每人每天能刷新的次数有上限,控制运行的花费。", _M, "明天再试,或请管理员调高上限。"),
"personal.no_run": Plain(
"还没有成功刷新过", "审核的人要看到这个应用用真数出的结果。", _Y, "先刷新一次,成功之后再提交审核。"),
"personal.in_review": Plain(
"已经提交过审核", "同一个应用同时只能有一份在审核。", _Y, "等搭建者发布或退回之后再提交。"),
"personal.review_target": Plain(
"审核要交给搭建者", "只有搭建者能把它做成正式应用。", _Y, "从候选里选一位搭建者,或者交给一个组。"),
"personal.review_forbidden": Plain(
"审核个人应用要搭建者来做", "审核的人要在副本上试运行、发布成正式应用,这些只有搭建者能做。", _M,
"请一位搭建者处理这条审核,或请管理员调整你的角色。"),
"personal.not_personal": Plain(
"这不是个人应用,或者它出自的蓝图已经不在了", "刷新、提交审核只对还能找到蓝图的个人应用。", _Y,
"共享应用请用它自己的刷新;蓝图删了的个人应用可以归档。"),
"personal.not_shareable": Plain(
"个人应用不能发布或授权给别人", "个人应用的结果没经过发布审核,只给拥有者自己看。", _Y,
"想给别人用,请提交审核,由搭建者发布成正式应用。"),
})
# ---------------------------------------------------------------- 记录表与文件(包 B3:records/、files/、api/files.py、
# apps/lint.py 的 _lint_records_rows)。records.* 是写记录表被拒的原因,files.* 是上传、提交、下载文件被拒的原因
PLAIN.update({
"app.records_rows_exposed": Plain(
"看板上的表格把记录一条条都列出来了", "看板的数是用全部记录算的,这样每个能用应用的人都能看到别人提交的记录。", _Y,
"要看自己提交了什么,请用「记录列表」组件(按看的人过滤);看板上的表格改成汇总。确实要公开给所有人看的可以不改。"),
"records.column_invalid": Plain(
"表单里有字段存不进记录表", "字段名重复、以下划线开头或者类型不认识,记录表建不起来。", _Y,
"回到「填什么」卡片改字段名或类型。"),
"records.column_unknown": Plain(
"要写入的字段记录表里没有", "表单改过,应用还在用改之前的那一版。", _Y, "重新发布应用,让表单和记录表对上。"),
"records.required_missing": Plain(
"必填的字段没有值", "必填的字段空着,这条记录就是不完整的。", _Y, "把必填的字段填上再提交。"),
"records.value_invalid": Plain(
"有字段的值和类型对不上", "比如数字字段填了文字,存进去以后汇总会算错。", _Y, "按字段要求的类型重新填写。"),
"records.supersede_missing": Plain(
"要更正的那条记录找不到", "更正要指向这张表里已有的一条记录。", _Y, "从记录列表里选要更正的那一条。"),
"records.supersede_twice": Plain(
"这条记录已经被更正过了", "同一条记录只能更正一次,后面的修改要改在最新的那一条上。", _Y, "在最新的那一条上更正。"),
"records.table_retired": Plain(
"这一版记录表已经停用", "表单改过之后出了新版本,旧记录照原样保留、只能查看。", _Y, "重新发布应用,写到新版本里。"),
"records.table_foreign": Plain(
"写的记录表不属于这个应用", "正式运行只能写自己应用的记录表,不然别的应用的记录会被混进来、被不该看的人看到。", _Y,
"回到写记录表那一步,改成这个应用自己的记录表,再重新发布。"),
"records.table_missing": Plain(
"记录表的数据文件不见了", "数据文件被移走或删掉,记录写不进去、也查不了。", _M, "请管理员检查数据目录。"),
"records.file_exists": Plain(
"记录表的数据文件已经有数据,不能重建", "为了不覆盖已有的记录,系统拒绝了重建。", _M, "请管理员检查数据目录。"),
"records.action_key_missing": Plain(
"写记录表没有经过动作流程", "只有经过动作流程,同一次提交才保证只写一条。", _A,
"把写入放在「调用工具」节点里,不要放进 Agent。"),
"files.missing": Plain("没有选文件", "", _Y, "选择要上传的文件。"),
"files.empty": Plain("文件是空的", "空文件里没有可用的内容。", _Y, "检查后重新选择文件。"),
"files.type_not_allowed": Plain(
"这种类型的文件不能上传", "只收常见的文档、表格、图片和文字文件,别的类型可能带来风险。", _Y,
"另存为 PDF、Word、Excel、CSV、图片或文字文件再上传。"),
"files.type_not_accepted": Plain(
"这里不收这种文件", "表单的这一项限定了文件类型或大小。", _Y, "按提示换一个文件。"),
"files.content_mismatch": Plain(
"文件内容和扩展名对不上", "改过扩展名的文件,系统没法按它说的类型读。", _Y, "用原始文件重新上传。"),
"files.context_invalid": Plain(
"没说清文件是给哪里用的", "上传要对应一个入口、一条待办或一个蓝图,系统才知道谁能用它。", _A,
"从表单里的文件控件上传。"),
"files.forbidden": Plain(
"没有权限", "只有能用这个入口、处理这条待办、看得到这次运行或这条记录的人,才能上传或下载。", _M,
"请应用的管理者给你授权。"),
"files.ref_invalid": Plain(
"文件没有上传好", "提交的是文件的引用,要先上传、拿到引用。", _Y, "重新上传这个文件再提交。"),
"files.not_found": Plain(
"文件不见了", "上传后 24 小时还没提交的文件会被清理。", _Y, "重新上传再提交。"),
"files.not_yours": Plain(
"这个文件不是你上传的", "只能提交自己上传的文件。", _Y, "自己上传一份再提交。"),
"files.already_bound": Plain(
"这个文件已经提交过一次", "一次上传只能随一次提交进运行,这样每次运行用的是哪份文件说得清。", _Y,
"再提交的话请重新上传。"),
"files.context_mismatch": Plain(
"文件是在别的地方上传的", "文件只能用在上传时说明的那个入口或那条待办上。", _Y, "在这里重新上传。"),
"files.bound": Plain(
"已经提交的文件不能删除", "它是那次运行的证据,删掉就说不清当时用的是什么。", _Y, "不需要处理。"),
"files.content_lost": Plain(
"文件内容不在了或被改动过", "下载到的内容如果和当时登记的不一样,就不能当证据用。", _M, "请管理员检查数据目录。"),
})
# ---------------------------------------------------------------- 蓝图与模板(包 B1:blueprints/ops.py 的 bp.*、
# blueprints/instantiate.py、blueprints/generation.py 的 tpl.*)
PLAIN.update({
"bp.missing": Plain(
"蓝图上还有没填的卡片", "缺了这一项,模板没法生成能运行的工作流。", _Y, "在卡片上补上,或者告诉助手这一项是什么。"),
"bp.unconfirmed": Plain(
"有助手或模板提的项还没确认", "没确认的项是猜的,发布出去就成了正式的口径和规则。", _Y, "逐项看过,对的点确认,不对的直接改。"),
"bp.data_unmapped": Plain(
"有要看的数还没对到表或口径", "对不到数据,就算不出这个数。", _A,
"请助手按数据目录对到表(或复用已发布的口径),对到之后由你确认。"),
"bp.data_source_unknown": Plain(
"有要看的数对到的数据源不存在", "试运行和发布时找不到这个数据源,这个数算不出来。", _Y,
"在「看哪些数」里点「改」重新选数据源;填报登记汇总本应用的记录,选「本应用的记录表」。"),
"bp.data_table_unknown": Plain(
"有要看的数对到的表不在那个数据源里", "查不到这张表,这个数算不出来。", _Y,
"在「看哪些数」里点「改」重新选表;表刚建的话,先到「数据」页读取一次表结构。"),
"bp.people_unbound": Plain(
"有一步还没定谁来处理", "流程走到这一步就没人接手。", _Y, "在「谁来做」里选人、组或岗位。"),
"bp.reconciler_unbound": Plain(
"写回的结果不明时没人核实", "这项写入能力没登记查询:结果不明时平台查不了,也不会自动重发,要有人去目标系统里看写进去没有。",
_Y, "在「写回哪里」卡片的「结果不明时谁去核实」里选人或组。"),
"bp.template_mismatch": Plain(
"卡片上写的超出了这个场景能做的", "超出的那部分不会生效,做出来的应用和你说的对不上。", _Y,
"按提示删掉或改小,或者换一个场景。"),
"bp.refused": Plain(
"这处修改没有采纳", "改动不符合这张卡片的写法。", _Y, "按提示的写法重新改。"),
"tpl.skeleton_changed": Plain(
"改动超出了模板留出的位置", "模板的结构是锁住的,审批、写入、出具这些地方不能被顺手改掉。", _A,
"只改模板留给助手的取数和指标;结构要变,请换一个场景或回画布。"),
"tpl.joint_unfilled": Plain(
"还有取数或指标没写好", "缺了它,试运行算不出数。", _A, "请助手填上;填不上时补充说明数从哪来、怎么算。"),
"tpl.joint_refused": Plain(
"助手想改模板锁住的地方,没有采纳", "模板锁住的结构不归助手改。", _A, "不需要处理;要改结构请回到卡片或换一个场景。"),
"tpl.metric_expr": Plain(
"指标的算法写得不合规矩", "指标只能从取数结果里按格取数再算,这样每个数都能追到出处。", _A, "请助手按规矩重写这个指标。"),
"tpl.metric_column": Plain(
"指标要用的列,取数的 SQL 里没有", "运行时这个指标会算不出来。", _A, "请助手让取数的 SQL 交出这一列,或改指标。"),
"tpl.sql_rejected": Plain(
"取数的 SQL 不是只读的一条查询", "取数只能读,不能改数据,也只能是一条语句。", _A, "请助手重写成一条只读的查询。"),
})
PLAIN_PREFIX: dict[str, Plain] = {
# 子工作流、口径卡固定的版本不是「已发布且未退役」的(apps/versions.py)
"subgraph_version_": Plain(
"引用的工作流版本没发布或已退役", "正式运行发起时会被拒绝。", _Y, "在那个工作流里发布新版本,再把这里的引用换过去。"),
"caliber_version_": Plain(
"引用的口径卡版本没发布或已退役", "正式运行发起时会被拒绝。", _Y, "在那个工作流里发布新版本,再把这里的引用换过去。"),
}
#: 要管理员处理的编号:端点、能力登记归管理员管(受管级发布、降级的权限在发布单里另报 plan.admin_required、
#: plan.downgrade_admin)
ADMIN_CODES = frozenset(code for code, plain in PLAIN.items() if plain.who == _M)
def plain_of(code: str | None) -> Plain | None:
if not code:
return None
if code in PLAIN:
return PLAIN[code]
for prefix, plain in PLAIN_PREFIX.items():
if code.startswith(prefix):
return plain
return None
def who_of(issue: dict[str, Any]) -> str:
"""这条问题归谁处理(you / assistant / admin)。依次看:要管理员的编号;自动修复给的修复种类(fix_kind:choice 要人
选,auto、assist 助手能做);应用检查只有人能处理的(api/copilot_gen.APP_HUMAN_ONLY);白话里登记的那一档(应用检查
其余的登记成「助手可以修」:助手的自查会修它们);没登记的应用检查算「助手可以修」,别的算「你来定」。"""
from app.api.copilot_gen import APP_HUMAN_ONLY
code = str(issue.get("code") or "")
if code in ADMIN_CODES:
return _M
kind = issue.get("fix_kind")
if kind == "choice":
return _Y
if kind in ("auto", "assist"):
return _A
if code in APP_HUMAN_ONLY:
return _Y
plain = plain_of(code)
if plain is not None:
return plain.who
return _A if code.startswith("app.") else _Y
def with_plain(issue: dict[str, Any]) -> dict[str, Any]:
"""给一条问题补上 plain = {title, why, who, action};没登记的不补(照旧显示技术说法)。who 按 who_of 判。"""
plain = plain_of(issue.get("code"))
if plain is None:
return issue
return {**issue, "plain": {"title": plain.title, "why": plain.why, "who": who_of(issue), "action": plain.action}}