加载中...
  • 动态工具管理:CRUD、分页、注册表与运行时装载loading

    AI 应用后端工程化:从原型到可交付系统 · 第 09 篇 · 第二章 · 知识与工具

    从管理接口走到运行时 Provider Manager,理解配置数据如何变成模型真正能够调用的能力。

    工具能够被创建之后,系统还要回答更多日常问题:怎样列出几百个工具,如何更新而不破坏正在运行的会话,删除后旧配置怎么办,数据库中的一行记录又如何变成真正可调用的运行时对象?这些问题把工具系统分成了管理面和执行面。

    两个世界用实体协议连接

    flowchart LR
      UI[管理界面] --> API[CRUD / 分页 API]
      API --> Store[(工具配置)]
      Store --> Manager[Provider Manager]
      Manager --> Entity[运行时 ToolEntity]
      Entity --> Registry[工具注册表]
      Registry --> Agent[本轮模型调用]
    

    管理面关注名称、描述、凭证引用、启用状态和分页筛选;执行面关注可调用函数、参数 Schema、超时和错误协议。数据库模型不应该直接充当运行时工具,因为它会把 ORM 会话、敏感字段和持久化细节带入核心执行。

    ToolEntity 可以是一个不可变快照。每次运行从已发布配置构造实体,正在执行的任务不受后台编辑影响。新请求再读取新版本,这与配置发布和滚动升级的思想一致。

    CRUD 并不简单

    创建时需要验证工具名唯一、OpenAPI 合法、凭证类型匹配。更新要区分展示字段和执行契约:修改描述通常影响较小,修改参数 Schema 则可能让旧会话保存的 tool call 无法重放。删除更适合先停用,再在确认没有引用后清理。

    from dataclasses import dataclass
    
    
    @dataclass(frozen=True)
    class ToolSnapshot:
        id: str
        version: int
        name: str
        schema: dict
        enabled: bool
    
    
    def publish_tool(current: ToolSnapshot, next_schema: dict) -> ToolSnapshot:
        validate_schema(next_schema)
        return ToolSnapshot(
            id=current.id,
            version=current.version + 1,
            name=current.name,
            schema=next_schema,
            enabled=True,
        )
    

    版本号让日志能回答“这次调用使用哪份定义”。没有版本,用户在事故后编辑工具,历史记录看上去也会被新配置覆盖,排查失去证据。

    分页是稳定性功能

    后台列表早期返回全部数据似乎最方便。工具数量增长后,响应变慢、内存升高,前端也无法有效筛选。分页接口需要稳定排序,否则用户翻页期间新增一条记录,数据可能重复或遗漏。

    小规模管理页可以使用创建时间和 ID 的组合排序;变化频繁或数据很大时,游标分页比 offset 更稳定。返回结果还应包含总数或下一页游标,但不要在每页都序列化完整 OpenAPI 和加密配置。列表 DTO 与详情 DTO 可以不同。

    Manager 不只是一个字典

    Provider Manager 通常承担四项工作:按配置找到实现、注入当前调用者凭证、构造运行时实体、管理缓存与失效。若每次模型请求都重新解析大型规范会很慢,可以按配置版本缓存;后台更新后发布失效事件,让下一次请求重建。

    缓存键必须包含租户和版本。只按工具 ID 缓存,多租户凭证可能串用;只按名称缓存,重命名或复制也会产生冲突。运行时日志应记录工具 ID、版本、调用者和结果状态,而不是只记函数名。

    重构为何常发生在功能之后

    最初几个 CRUD 接口可以直接在 Service 中拼装调用函数。随着工具真正进入 Agent,管理逻辑与执行逻辑混在一起,修改分页也可能影响调用。此时抽出 Entity 和 Manager 是对已观察复杂度的回应,而不是提前设计一个抽象王国。

    重构的判断标准是依赖是否变清楚:HTTP Handler 不认识模型 SDK,Agent 不认识 ORM,Manager 不返回数据库 Session。若只是增加类名和转发层,却仍共享同一个可变字典,复杂度并没有减少。

    删除是最容易被低估的动作

    一个工具可能被应用草稿、已发布版本、历史消息和计划任务引用。物理删除数据库行会让这些对象失去解释。更安全流程是停用新调用,列出引用,阻止新配置选择,然后按保留策略清理。

    如果工具代表外部凭证,删除还要撤销密钥或清理加密材料。对用户显示“删除成功”之前,应明确是停止使用还是彻底擦除。数据生命周期要通过 API 语义表达。

    类比插件与支付渠道

    IDE 插件管理和支付渠道配置也有管理面与执行面:后台安装、升级、停用,运行时加载确定版本;历史任务保留当时快照。模型工具并不是特殊例外,只是运行时调用选择更动态。

    练习:设计一个工具更新流程,要求旧会话仍能展示历史调用,新请求使用新 Schema,停用后立即禁止执行。画出数据库记录、快照缓存和注册表之间的版本关系,并写出一次缓存失效测试。

    五次相邻提交覆盖了完整闭环

    • 8ba5d84:增加工具查询路由,管理面开始可见。
    • f753c36:加入分页与删除,资源生命周期逐渐完整。
    • 6b05b57:补充更新能力,配置开始持续演进。
    • 4a15ef0:引入 ToolEntityApiProviderManager,隔离持久化和运行时。
    • 6ed8311:重构服务以支持真实调用,管理数据最终接入执行面。

    这组演进非常适合举一反三:任何“后台可配置、前台可执行”的能力,最终都会遇到版本、快照、缓存、引用和删除语义。

    本文目录
    本文目录