Files
AgentLab/backend/app/tools/builtin/platform.py
ThearchyHelios 277982ead3 普通用户建应用(后端):
- 蓝图:bp_* 操作和逐卡白名单、蓝图检查、留痕、按蓝图判权限;五个场景模板加自由形(骨架、关节、应用骨架、定时任务),实例化与骨架比对;BlueprintApplier / TemplateApplier(只收模板声明过的关节)与自查;look 新类别口径卡;蓝图对话和填关节两个流式接口,不同组的关节并行填、出错的组串行重试;模板列表
- 试运行与发布单:四种起点(对话、模板、问数据的一轮、已发布版本);试运行是探索运行,草稿应用按这次运行解析、印章写试运行,流程类派给自己、不触发升级;发布单九步、预检只读、每步留痕、停下后接着做、核对卡片指纹和应用管理权、启动时整理中断的单;白话层覆盖全部问题编号
- 记录表与文件:平台托管只追加的记录表、按高水位固定数据、连接上的临时视图做行级可见、写入走动作(records_append)、表版本在发布时切换、草稿不影响线上、记录列表遮敏感列;文件按哈希存、两步上传、提交时绑定运行、下载按读权限、清理、file_text、按配方导入一期(table_import_period)
- 个人应用:存成个人应用(问数据的约束)、刷新(遮罩、只读、每日上限)、只有拥有者看得见、提交审核与共享副本、开关下发到认人接口;页面编辑的候选;隐藏组件不下发;三条新检查
2026-10-15 12:09:29 +08:00

72 lines
3.5 KiB
Python

"""平台内置能力的工具定义(docs/design/normal-user-apps.md N8、N9;执行在 capabilities/platform.py)。
这里只登记名字、说明和参数,让它们进能力登记(效果、治理、版本)和画布的工具清单。写入的两项(records_append、
table_import_period)效果记 write:只能放在调用工具节点里,节点按动作流程执行(先留生效记录、按幂等键写、需要时等人
确认),不会走到这里的函数——走到了说明绕过了动作流程,照实拒绝。records_lookup 是只读的查询能力。
"""
from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field
from app.files import TABLE_IMPORT_PERIOD
from app.records import RECORDS_APPEND, RECORDS_LOOKUP
from app.tools.registry import ToolContext, register
#: 写入能力被直接调用(没经动作流程)时交回的话
_ONLY_VIA_ACTION = "「{name}」是写入能力,只能放在「调用工具」节点里,由动作流程写入(留生效记录、按动作键只写一次)"
class RecordsAppendArgs(BaseModel):
table: str = Field(description="写哪张记录表:记录表的数据源名(如 rec_ab12cd),或 records_tables 的编号")
values: dict[str, Any] | str = Field(description="这一行各字段的值,键是表单字段名;也可以是一段 JSON 文字")
supersedes: str | None = Field(default=None, description="更正时填被更正那一行的编号(_id);新增一行时不填")
@register(
name=RECORDS_APPEND,
category="记录表",
description="往平台托管的记录表追加一行(填报登记)。一次动作只落一行,重放不会重复写;更正是追加一行并指向被更正的行,"
"原行不改。只能放在「调用工具」节点里",
args_schema=RecordsAppendArgs,
effect="write",
)
async def records_append(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
return {"error": _ONLY_VIA_ACTION.format(name=RECORDS_APPEND)}
class RecordsLookupArgs(BaseModel):
idempotency_key: str = Field(description="写入时用的幂等键:动作键加上第几次发送")
@register(
name=RECORDS_LOOKUP,
category="记录表",
description="按幂等键查一次记录表写入有没有落下(写入结果未知时,平台用它自动核实)",
args_schema=RecordsLookupArgs,
)
async def records_lookup(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
from app.records.actions import lookup_action
args = RecordsLookupArgs(**kwargs)
result = await lookup_action({"idempotency_key": args.idempotency_key}, environment="prod")
return {"status": result.status, **(result.body if isinstance(result.body, dict) else {})}
class TableImportPeriodArgs(BaseModel):
source: str = Field(description="导入到哪个数据源(按配方导入的上传表格,写数据源名)")
file: dict[str, Any] | str = Field(description="要导入的文件:上传后拿到的引用(来自表单的文件字段)")
@register(
name=TABLE_IMPORT_PERIOD,
category="数据",
description="把一个上传的文件按数据源已有的配方导入成新的一期(同数据页的「上传新一期」)。会换掉数据源的当前数据,"
"只能放在「调用工具」节点里,由人确认后执行",
args_schema=TableImportPeriodArgs,
effect="write",
)
async def table_import_period(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
return {"error": _ONLY_VIA_ACTION.format(name=TABLE_IMPORT_PERIOD)}