文档编号 LAT-UM-001

Lattice · 千格 使用说明书

Per-System Embedded Agentic Runtime — 集成与运维使用手册

版本v4.3-trimmed(2026-05-22 锁定)
文档状态Released
日期2026-06-21
适用范围单实例 Phase 0–4 已实现特性

摘要

本手册说明如何将 Lattice · 千格 这一 Per-System Embedded Agentic Runtime 嵌入并接入一个业务系统,覆盖部署前置条件、密钥签发、goal 提交、结果与审计消费等使用路径。读者为业务系统的宿主(host)与调用方(caller)工程师以及负责该实例的运维人员。本手册的范围严格限定为实施层 v4.3-trimmed(单实例 Phase 0–4)已实现并有测试覆盖的特性;Mesh、跨实例调用、MCP/A2A 等设计层能力不在本手册承诺范围内。

关键词

Per-System Embedded Agentic Runtime; plan_json; permit; tool contract; HITL; Run State Machine; 审计链; v4.3-trimmed

修订历史

版本日期说明
1.02026-06-21首次发布

1 引言 资料性

1.1 范围与目的

本手册描述 Lattice · 千格运行时实际已实现的使用方式,目的是让宿主与调用方工程师在不阅读源码的前提下完成实例嵌入、密钥签发、goal 提交与结果对账。

本手册的硬边界为实施层 v4.3-trimmed(2026-05-22 锁定),仅承诺单实例 Phase 0–4。下列内容明确不在本手册范围内,仅在确有必要时作为「设计保留 / 未实现」标注,不得当作已交付特性:Mesh / Federation / 跨实例调用与寻址(lattice://<system>/*)、MCP/A2A 跨实例协作、长期 Memory 写入 / pgvector RAG、multi-tenant / 中央 dashboard / K8s 部署。

1.2 读者对象

  • 宿主(host)/ 部署工程师:负责装配并启动一份 per-system 实例,注入运行时 env 与密钥,部署上游依赖容器。
  • 调用方(caller)工程师:在业务后端构造 goal 并经 REST/SSE 接口提交、消费结果与审计。
  • 运维 / SRE:关注运行稳定性、审计追踪与故障恢复。

1.3 规范性用语约定

本手册的需求级别用语含义如下,遵循 IEEE/ISO 文档惯例:

  • 必须(shall)—— 强制性要求,不满足即不符合本手册。
  • (should)—— 推荐性要求,存在正当理由时可偏离,但须评估后果。
  • (may)—— 允许性陈述,表示一种被许可的选择。
  • 不得(shall not)—— 强制性禁止,违反即不符合本手册。

1.4 如何阅读本手册

第 1–3 章为资料性内容,建立定位、术语与体系结构的共同语言;第 4–5 章为规范性内容,给出系统要求与最短接入路径,按顺序执行。后续章节(运行生命周期、API 契约、运维与审计)补全细节。

建议初次接入的读者先通读第 1–3 章建立整体认识,再按第 4–5 章的顺序动手部署与接入;已熟悉运行时的读者可直接跳到所需章节查阅。

2 术语、定义与缩略语 规范性

2.1 术语定义

下列术语含义不得扩展或重定义。代码标识符 / 协议术语保持英文,不翻译。

  • plan_json —— Lattice 内部生成的可审计执行计划,结构化 JSON。顶层模型为 PlanJson;strategy 有 4 个稳定值,step.type 有 5 种。
  • permit —— 细粒度操作许可,tool contract 的核心控制单元。Sandbox 是强制执行层而非检查器:Executor 每次执行 step 前必须持有有效 permit,无 permit 不执行任何 step。
  • tool contract —— 工具调用的强制约束规格,定义允许/禁止的操作集合。所有业务 MCP 必须注册 Tool Contract,无合约不可被规划或调用。
  • HITL —— Human-in-the-Loop,人工介入点。Risk Level L3/L4 的 step 触发 HITL,run 进入 waiting_for_hitl;HITL 为 Lattice 自建,ContextForge 无原生 HITL。
  • per-system embedded —— 每个业务系统独立嵌入一份 Lattice 实例的部署形态。每业务系统一套独立实例,不共享 runtime / 数据库 / 配额池。
  • phase —— 此术语有两义:Phase 0–4 指 v4.3-trimmed 实施层的五个交付阶段,每阶段后人工 Go/No-Go 停审;代码中另有同名的三层 phase 标签(planning / executing / evaluating),是运行时审计元数据。两者不得混用。
  • Decision Log —— 每个 LLM 调用、工具调用、决策点写入的 10 字段可复盘摘要,只存可复盘摘要,不得存 chain-of-thought。
  • Run State Machine(RSM) —— run 生命周期的 13 状态机,每次状态转换写一条 lattice_run_event(含被拒转换)。状态枚举为 RunStatus,并有一张合法转换表。

2.2 缩略语与运行时枚举

下表为缩略语与关键运行时枚举速查。

缩略语 / 枚举含义 / 取值
RSMRun State Machine(13 状态)
HITLHuman-in-the-Loop(人工介入点)
MCPModel Context Protocol(工具接入协议)
SSEServer-Sent Events(6 类事件流)
plan strategy(4)sequential_chain / multi_agent / deep_research / plan_execute_reflect
step.type(5)tool_call / sub_agent / llm_call / evaluation / hitl_gate
Risk Level(5)L0 / L1 / L2 / L3 / L4(L3/L4 触发 HITL)
error_class(10)SCHEMA_VALIDATION_FAILEDSYSTEM_ERROR
permit 6 维Resource / Cost / Time / Capability / Data / Action
审计表(8)lattice_runlattice_hitl_request
表 — 缩略语与运行时枚举速查

3 系统概述 资料性

3.1 一句话定位

Lattice · 千格的定位:

Lattice · 千格是一个 Per-System Embedded Agentic Runtime:每个业务系统内嵌一份本地拥有的 Lattice 实例,业务方只提交 goal,Lattice 在 permit-based sandbox 约束下自主规划、执行并审计全过程。

3.2 提供什么能力与功能

一句话:你把"要达成什么目标"交给 Lattice,它替你把"怎么用 AI 把这件事做完"全包了——自己规划、自己挑模型、自己调工具、全程留痕、危险动作叫人来批。专业地说,Lattice 在通用 agent 框架之上补齐了治理、沙箱、经济学、可审计计划与状态机:

Lattice = mcp-agent + 治理 + 沙箱 + 经济学 + Memory + 可审计 plan_json + 状态机

具体功能如下,每项给出技术说明通俗说明

功能技术说明通俗说明
Goal API(目标接口)业务方经 POST /api/v1/runs 只提交 goal,不接触底层编排你只递一句话目标,不用自己拼 agent 流程
自主规划 plan_jsonPlanner 把 goal 经 schema + capability + risk 三重校验,自主生成结构化、可审计的执行计划AI 自己把目标拆成步骤计划,且这份计划能读能审,不是黑盒
三层编排执行Master Orchestrator(Planner → Executor → Evaluator)规划、执行、评估,自动判定 completed / replan / failed计划 → 干活 → 验收一条龙,做不完自己重规划
permit 沙箱 + tool contract强制执行层(非检查器):每个 step 执行前必须持有效 permit,无 permit 不执行任何工具每个动作先领"许可证",没证一步都跑不了,AI 想越权也越不了
成本路由 + 三档预算按性价比选模型;Budget 三档(soft / hard / absolute),到 absolute 强停自动挑划算的模型,烧过线自动刹车,成本可控
HITL(人工介入)L3/L4 高风险 step 执行前停在 waiting_for_hitl,等人工 approve / reject危险操作(如对外发布)必须有人点头才执行
审计链 + Decision Log四路审计同 run_id 可 join;每次决策写可复盘摘要(不存 chain-of-thought)谁、为什么、做了什么、花了多少,全程留痕可追可复盘
Run State Machine(13 状态)每个 run 是一个 asyncio task,由 13 状态机驱动,每次转换写一条 run_eventrun 走到哪一步一目了然,失败能归类、能恢复
错误分类 + 恢复所有失败归 10 类 error_class,每类绑定确定恢复策略(retry / replan / fail / terminate)出错不是一团乱:分类清楚,能重试、能从断点续跑
断点续跑run-local memory + checkpoint 落库,可 POST /resume 续跑跑一半断了能从断点接着跑,不用从头再来
4 个内置编排策略sequential_chain(单链顺序)/ multi_agent(≥2 个并行 sub-agent + 汇总)/ deep_research(搜索 → 阅读 → 综合 迭代)/ plan_execute_reflect(规划 → 执行 → 反思 三阶段),由 Planner 自动选取(并行 sub-agent 个数由 plan_json 决定)内置 4 种成熟打法,AI 自己挑,你不用管
表 — Lattice 提供的功能

3.3 解决什么问题 · 实现什么目标 · 为上游做什么

要解决的问题。业务工程师在自己的业务系统里接入 Agent 能力时,反复撞到同一组痛点:

  • 多 Agent 编排每次重写,业务方写不动
  • 模型选择碎、成本不可控
  • 工具接入碎片化、协议不统一
  • AI 行为不可审计、决策无法复盘
  • 权限边界不清晰,AI 容易越界
  • 失败不可恢复、不可归类
  • 中央化平台难处理业务间巨大差异
  • 跨系统直接共享数据带来安全 / 合规 / 责任问题
  • 每个业务的 AI 升级互相牵制

核心判断:"如何完成 AI 任务"这件事应该从业务方手里接管走,但接管的载体必须嵌在业务系统内部,而不是一个外部的中央平台。

实现什么目标。

构建 Lattice · 千格——一个 Per-System Embedded Agentic Runtime:每个业务系统嵌入一份本地拥有的 Lattice 实例,业务方只提交 goal,Lattice 内部由 AI 自主生成可审计的 plan_json,在 permit-based sandbox 与 tool contract 的强制约束下执行,全程记录决策与操作,必要时由 HITL 介入。

为上游(host)程序解决什么。接入 Lattice 后,你的业务系统从"自己实现 agent 编排 / 模型路由 / 工具治理 / 审计 / 权限"降为"提交 goal + 注册业务 MCP + 接审批"。具体收益:

  • 不用再写多 Agent 编排——只递 goal,AI 自己规划。
  • 不用操心选模型、成本失控——内置成本路由 + 三档预算硬停。
  • 不用自建审计 / 权限体系——permit 沙箱 + 审计链开箱即用。
  • AI 不会越权乱来——没有 permit,任何工具一步都跑不了。
  • 危险动作有人把关——HITL 审批。
  • 出错能归类、能从断点恢复——不是黑盒崩溃。
  • 每个业务一份独立实例——互不牵制、数据不外流,AI 升级各自演进。

3.4 是什么 / 不是什么

Lattice 是嵌入式后端 Agentic Runtime,以 Python 包形式交付,按业务系统独立部署,由三层 Master Orchestrator(Planner → Executor → Evaluator)驱动的 Python 异步单进程运行时。业务方只提交 goal,内部由 AI 自主生成可审计的 plan_json,在 permit-based sandbox 与 tool contract 强制约束下执行。

Lattice 逐一否定四类常见误解(四反定位):

  • 不是 AI 中台 —— 不做平台化整合,无「一套服务全公司」。
  • 不是 SaaS —— 不对外售卖、不做多租户。
  • 不是 workflow SDK —— 业务方只给 goal,内部 plan 由 AI 自主生成,业务方不拼 capability 编排。
  • 不是 prompt 库 —— 不交付 prompt 模板 / agent demo,是可审计的执行 runtime。

3.5 部署形态与能力边界

Lattice 以 Python 包(pip install)加 Docker Compose 部署清单交付,按业务系统独立部署(per-system embedded):每业务系统一套独立实例,不共享 runtime / 数据库 / 配额池,N 业务即 N 份独立部署。对外接口仅两个:REST POST /api/v1/runs 与 SSE GET /api/v1/runs/{run_id}/stream。实例不公开对外端点(ContextForge uaid_allowed_domains=[] 加 compose 不绑公网端口)。

3.6 体系结构概览

运行时为 Python 异步单进程:执行模型为 asyncio 单进程,每个 run 是一个独立的 asyncio task,由 RSM(13 状态)驱动,每次状态转换写一条 lattice_run_event。三层 Master Orchestrator 职责分离:Planner 把 goal 经 schema + capability + risk 三重校验转为 plan_json;Executor 凭 permit 逐 step 执行;Evaluator 评估 partial result,决定 completed / replan / failed。

四类基础能力(API/SSE、模型路由、MCP 网关、Trace)全部采用上游开源件(FastAPI、LiteLLM、ContextForge、Langfuse 等);Lattice 自研只补治理、沙箱、可审计 plan_json 与状态机。关键更正:HITL 为 Lattice 自建,permit 由 Lattice cpex 插件在 ContextForge tool_pre_invoke 钩子内校验,Budget = 2 档 LiteLLM 原生信号加 1 档 Lattice 强停。 给出体系结构与外部系统的关系及数据流入 / 流出路径的概览:左侧是控制面(业务后端、审批桥、webhook 接收端与 Lattice 的 goal / 进度 / 审批 / 事件往来),右侧是数据面(业务 MCP 工具与数据源、LLM 提供方与 Lattice 的双向调用,受 permit 守门)。体系结构如何映射到一次真实交互,见 §9.5 的财务系统端到端实例。

体系结构与外部系统关系 · 数据流入 / 流出路径 Lattice 实例 per-system embedded · asyncio 单进程 API 层 · FastAPI(REST /runs · SSE /stream) Master Orchestrator(Planner → Executor → Evaluator) Planner Executor Evaluator permit Sandbox cpex @ tool_pre_invoke HITL gate L3/L4 上游件:ContextForge · LiteLLM · Langfuse · LangGraph 审计链(append-only · 4 路 join by run_id) permit 守门一切工具调用 · 全程写审计 · 不暴露公网端点 业务系统后端 host · caller 审批人 / 审批桥 host webhook 接收端 host 业务 MCP · 工具/数据源 host 拥有 读写账本 / 外部系统 LLM 提供方 经 LiteLLM 路由 1 2 3 4 5 6 7 ① goal + sandbox_config(POST /api/v1/runs)—— 输入 ② run_id · SSE 进度事件 · result(GET /api/v1/runs)—— 输出 ③ hitl_requested(SSE + webhook)—— 输出 ④ approve / reject(POST /api/v1/hitl)—— 输入 ⑤ 事件投递(HMAC 签名 webhook,至少一次)—— 输出 ⑥ 业务数据 tool_call:args → / result ←(permit + cpex 守门)—— 数据双向 ⑦ model 调用 → / 补全 ←(经 LiteLLM 路由)—— 数据双向 → 输入(外部 → Lattice) ← 输出(Lattice → 外部) ⇆ 数据双向(受 permit 守门)
图 — 体系结构与外部系统关系 · 数据流入 / 流出路径

4 系统要求与前置条件 规范性

4.1 运行环境

实例以 Python 包交付,经 build_production_app 装配,经 lattice serve 启动。启动 serve 必须安装 serve 附加依赖(uvicorn),即 pip install 'lattice[serve]',否则 lattice serve 提示并以返回码 3 退出。CLI 默认监听 --host 127.0.0.1 --port 8000。装配期为零网络:litellm.Router / create_engine / httpx.AsyncClient 均惰性,真实上游连通属运维事项。

4.2 上游依赖与启动顺序

实例依赖 6 件上游开源件,启动与接线顺序必须固定如下:ContextForge(mcpgateway) → cpex → LiteLLM → mcp-agent → Langfuse → LangGraph。其依赖约束为:先有 MCP 网关再挂 permit gate(ContextForge → cpex);先稳模型路由再接编排(LiteLLM → mcp-agent);spans 存在后打通 traceparent;最后落 checkpoint 索引(Executor → LangGraph)。外部服务一律按「可降级依赖」处理:ContextForge / Langfuse 不可用不得拖死整个实例,降级行为统一回写 run_eventdecision_log 给出依赖启动顺序。

上游依赖启动顺序 1. ContextForgeMCP 网关2. cpexpermit gate3. LiteLLM模型路由4. mcp-agent编排5. Langfusetrace/span6. LangGraphcheckpoint 先有网关再挂 permit · 先稳路由再接编排 · spans 就绪后打通 traceparent · 最后落 checkpoint 索引 外部服务按「可降级依赖」处理:ContextForge / Langfuse 不可用不得拖死实例
图 — 上游依赖启动顺序

4.3 必备密钥

全部 env 由 build_production_app 在装配期读取,认证 env 由 AuthKeyStore.from_env 读取。下列项在非 dev/test 环境为必备

env 变量必填含义
LATTICE_LLM_MODEL_GROUP必填LiteLLM 模型组名;缺失即 fail-fast
LATTICE_INSTANCE_SECRET非 dev/test 必填permit HMAC 签名根密钥,每实例独立;缺失即 fail-fast
LATTICE_API_KEYS_BIZ生产必填biz role 活跃 token 的 sha256(≤2,逗号分隔,双活轮换)
LATTICE_API_KEYS_HITL生产必填hitl role 活跃 sha256(同上)
LATTICE_API_KEYS_SRE生产必填sre role 活跃 sha256(同上)
LATTICE_DB_URL可选审计 DB;缺省 in-memory sqlite,重启即丢
LATTICE_CALLBACK_ALLOWLIST可选webhook callback host 白名单;缺省空即拒绝一切出站
表 — 实例必备与可选 env

服务端只存 token 的 sha256,永不见明文。每 role 最多 2 个哈希(双活轮换窗口);非 64 位 hex 条目即 RuntimeError。明文 token 由 lattice keygen 一次性打印(见 第 5 章)。

4.4 网络与部署约束

  • 仅 Docker Compose / per-system embedded 一种部署形态已实现;不得假设 K8s、中央 dashboard 或多租户可用。
  • 实例不暴露公网端点(uaid_allowed_domains=[] 加 compose 不绑公网端口);Compose 内网部署时,caller 拿到的 base URL 形如 http://lattice-acquisition:8000
  • callback allowlist 缺省 fail-closed:未配 LATTICE_CALLBACK_ALLOWLIST 时任何 webhook callback 都被拒。
  • 需被操作的业务能力必须先包装为 MCP 并注册 Tool Contract,无合约不可被规划或调用。

5 快速开始 规范性

5.1 五步最短路径

本节给出从部署到读取结果的最短可复制路径。所有字段取自真实 schema,不得臆造端点或字段。

步骤 1 — 部署并启动实例。准备最小 env(非 dev 另需 LATTICE_INSTANCE_SECRETLATTICE_API_KEYS_*,见 §4.3),经 lattice serve 起服:

export LATTICE_ENV=production
export LATTICE_LLM_MODEL_GROUP=finrep-default
export LATTICE_INSTANCE_SECRET=<32+ 字节随机串>
export LATTICE_DB_URL=postgresql://lattice:***@db:5432/lattice
lattice serve --host 0.0.0.0 --port 8000

步骤 2 — 签发 biz token。部署人运行 lattice keygen --role biz,明文 token 仅打印一次,同时输出 sha256 供写入 LATTICE_API_KEYS_BIZ

lattice keygen --role biz
# token  : lk_biz_a1b2c3d4_<secret>
#          (明文仅此一次,请立即登记到部署侧密管)
# sha256 : <64-hex>
# 部署:将 sha256 追加到实例 env LATTICE_API_KEYS_BIZ(≤2 个,逗号分隔)后 reload。

步骤 3 — 构造 sandbox_config。sandbox_config 必填,其中 dimensions.costdimensions.capability 强类型。cost 三字段 soft_budget_usd / max_budget_usd / absolute_usd 均须 gt=0

{
  "dimensions": {
    "cost": {"soft_budget_usd": 0.5, "max_budget_usd": 1.0, "absolute_usd": 2.0},
    "capability": {"allowed": ["lattice.acquisition.read"], "forbidden": []}
  }
}

步骤 4 — 提交 goal。Authorization: Bearer lk_biz_...POST /api/v1/runscreate_run)。请求体来自 CreateRunRequestextra="forbid"),goal 必填且 min_length 1Idempotency-Key 为 header:

POST /api/v1/runs
Authorization: Bearer lk_biz_a1b2c3d4_<secret>
Idempotency-Key: 7f3c2e10-...-uuid
Content-Type: application/json

{
  "goal": "为客户 C-1024 生成 2026 Q1 应收对账单并标记逾期项",
  "context": {"customer_id": "C-1024", "period": "2026Q1"},
  "sandbox_config": {
    "dimensions": {
      "cost": {"soft_budget_usd": 0.5, "max_budget_usd": 1.0, "absolute_usd": 2.0},
      "capability": {"allowed": ["lattice.acquisition.read"], "forbidden": []}
    }
  }
}

响应来自 CreateRunResponse

{ "run_id": "…uuid…", "sse_stream_url": "/api/v1/runs/…uuid…/stream", "status": "created" }

步骤 5 — 读结果 / 连 SSE。提交后立刻连 sse_stream_url 看进度,事件 6 类(plan_created / step_started / step_completed / hitl_requested / run_completed / run_failed);终态后经 GET /api/v1/runs/{id} 对账(get_run):

curl -N -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>" \
  http://lattice-acquisition:8000/api/v1/runs/<run_id>/stream

curl -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>" \
  http://lattice-acquisition:8000/api/v1/runs/<run_id>

5.2 步骤总表

下表汇总五步最短路径的命令/端点与预期结果。

步骤命令 / 端点预期结果
1 部署启动lattice serve --host 0.0.0.0 --port 8000实例起服于内网端口;缺必填 env 即 fail-fast(返回码 2)
2 签发 tokenlattice keygen --role biz明文 lk_biz_* 仅打印一次 + sha256 供写入 env
3 构造约束构造 sandbox_config.dimensions.{cost,capability}cost 三字段均 gt=0、capability allowed/forbidden 就绪
4 提交 goalPOST /api/v1/runs(Bearer + Idempotency-Key)201 返回 run_id / sse_stream_url / status="created"
5 读结果GET /api/v1/runs/{id}/streamGET /api/v1/runs/{id}SSE 6 类事件;终态读 status / spent_usd / result / error_class
表 — 五步最短路径

6 部署与初始化 规范性

本章规定一份 per-system embedded Lattice 实例的装配启动路径。范围限于 v4.3-trimmed / Phase 0-4 的真实代码入口;真实 LLM / ContextForge 容器 / Postgres / Langfuse 的实跑属运维职责,不在本入口内完成。

获取制品。业务系统经私有制品「下载 + 嵌入」一份实例,两种姿势(均需具 read:packages 的 GitHub PAT;装 wheel 另需仓读权限):

容器 sidecar(推荐)——拉 GHCR 镜像,用 compose 跑在内网:

echo "$CR_PAT" | docker login ghcr.io -u <user> --password-stdin
docker pull ghcr.io/uncletim-gz/lattice:0.1.0a1

pip 库(嵌入式)——从 GitHub Release 资产装 wheel:

pip install "https://$CR_PAT@github.com/UncleTIM-GZ/Lattice-V/releases/download/v0.1.0a1/lattice-0.1.0a1-py3-none-any.whl"

6.1 装配入口 build_production_appcreate_app

装配的唯一入口是 build_production_app(env, ...),语义为 env → 真实组件 → create_app。该函数必须在构造期保持零网络:litellm.Router / create_engine / httpx.AsyncClient 均惰性,不在构造期连网。

必填 env,缺失即 fail-fast:

  • LATTICE_LLM_MODEL_GROUP:缺失 → RuntimeError
  • LATTICE_INSTANCE_SECRET:当 LATTICE_ENV 非 dev/test(缺省 production)时必填,缺失 → RuntimeError

可选 env 及其缺省行为:

env缺省行为
LATTICE_DB_URL缺省 in-memory sqlite(sqlite://),用 StaticPool 单连接池
LATTICE_LLM_MODEL_LISTJSON 数组;空则 [](测试注入 router 时不用)
LATTICE_CALLBACK_ALLOWLIST逗号分隔 host 集;空则空元组(webhook fail-closed)
表 — build_production_app 可选 env

装配的真实组件:真实 audit engine(建表 Base.metadata.create_all)、LiteLLMPlanClient planner、PersistentCheckpointIndex、capability 目录。注入参数 engine / llm_router / tool_runtime / callback_http 供测试零网络注入。

6.2 启动命令 lattice serve

启动入口 run_serve 调用 build_production_app()uvicorn.run(app, host, port)。CLI 默认 --host 127.0.0.1 --port 8000。该命令 serve 依赖(uvicorn),通过 lattice[serve] 安装。

# 准备最小 env(非 dev 环境另需 LATTICE_INSTANCE_SECRET)
export LATTICE_LLM_MODEL_GROUP=finrep-default

# 启动实例(需 lattice[serve]/uvicorn)
lattice serve --host 0.0.0.0 --port 8000

启动失败语义:

失败行为返回码
装配 RuntimeError(缺必填 env)打印到 stderr2
缺 uvicorn提示 pip install 'lattice[serve]'3
表 — lattice serve 失败返回码

6.3 部署形态:Docker Compose / per-system embedded

v4.3-trimmed 仅实现一种部署形态:Docker Compose、per-system embedded、仅内网可达——没有公网端点、没有 K8s、没有中央 dashboard、没有多租户。每个业务系统内嵌一份本地拥有的实例,业务方不得把它当作远端共享服务调用。

Compose 内网部署时,业务方拿到的 base URL 形如 http://lattice-acquisition:8000,仅在 Compose 内网可达。每实例自带 instance_secret(permit HMAC 签名密钥,由 PermitStore 持有),实例之间共享 permit、互相寻址。

6.4 环境装配要点

  • 认证默认态:dev/test 且三 role 均未配 key → 认证关闭(P0 基线兼容);非 dev 缺 key → 启动 RuntimeError fail-fast。生产部署必须配齐 LATTICE_API_KEYS_<ROLE>(见 第 7 章),否则起不来。
  • 构造 ≠ 实跑build_production_app 只验装配逻辑与 fail-fast。真实 LLM / CF 容器 / Postgres / Langfuse 的连通是运维职责,不在本入口完成。
  • 持久化:DB 选型与持久化由 host 负责(LATTICE_DB_URL);Lattice 侧负责建表与四路审计写入。

7 认证与凭据管理 规范性

本章规定三 role Bearer token 模型、签发、轮换、权限矩阵与拒绝语义。

7.1 三 role 模型

角色集合 ROLES = ("biz", "hitl", "sre")

  • biz:业务工程师,提交与驱动 run。
  • hitl:审核员,行使 HITL 审批。
  • sre:站点可靠性,仅对单条 run 详情只读

命中后中间件把 caller_role 注入 scope state。日志/响应只出现 lk_<role>_<key8> 前缀,明文 secret 永不回显。

7.2 签发 lattice keygen 与令牌格式

部署人执行 lattice keygen --role <biz|hitl|sre> 签发凭据。令牌格式 lk_<role>_<key8>_<secret32>,由正则 _TOKEN_RE 校验:role 段限三值、key8 为 8 位 base62、secret32 为 32 位 base62。

lattice keygen --role biz
# token  : lk_biz_a1b2c3d4_<secret32>
#          (明文仅此一次,请立即登记到部署侧密管)
# key8   : a1b2c3d4
# sha256 : <64 hex>
# 部署:将 sha256 追加到实例 env LATTICE_API_KEYS_BIZ(≤2 个,逗号分隔)后 reload。

7.3 双活轮换

部署人把 sha256 写入 LATTICE_API_KEYS_<ROLE>(每 role ≤2 个、逗号分隔,_MAX_ACTIVE_HASHES = 2)。超过 2 个 → 启动 RuntimeError;非 64-hex 条目 → RuntimeError

  • 轮换:双活窗口内新旧 sha256 并存均可用(verify 遍历集合)。
  • 撤销:从 env 列表移除该哈希后 reload,即刻拒。

7.4 权限矩阵

端点准入由 required_roles(method, path) 实施。下表逐行对应代码分支:

方法 + 路径允许 role
POST /api/v1/runs/cancel/resume/streambiz
GET /api/v1/runs/{id}(精确单条,_RUN_DETAIL_REbiz 或 sre(sre 只读)
/api/v1/hitl*hitl
未列入的 /api/v1/*任意 role(先验凭据,404/405 交路由层)
/internal/*(非 /api/v1/ 前缀)None(中间件放行,走 LATTICE_INTERNAL_SECRET
表 — role 权限矩阵

7.5 401 / 403 语义

拦截顺序在 AuthMiddleware.__call__

情形状态码信封 error_class
缺失 / 形态非法 Authorization401POLICY_DENIED
无效 / 已撤销 token401POLICY_DENIED
role 越权403POLICY_DENIED
表 — 认证拒绝语义

401 响应附 WWW-Authenticate: Bearer 头;401/403 响应体均为 POLICY_DENIED 信封(_denied)。

8 业务能力接入 规范性

本章规定 host 把业务能力接入 Lattice 的契约:业务 MCP wrapper、Tool Contract 与 risk_level 申报、sandbox_config 6 维构造,以及 host/Lattice 责任边界。Lattice 操作业务系统的唯一通道是业务 MCP。

8.1 业务 MCP wrapper(host 自建/运维)

每个被 Lattice 操作的能力必须由 host 包装为业务 MCP wrapper 并部署到 Compose 内网。wrapper 的开发、运维、错误映射由业务模块团队负责(R=业务模块团队,A=Tech Lead),Lattice 侧只负责 permit 校验与调度。没有合规 wrapper + Tool Contract 的能力,Planner 规划不出也调不到。

8.2 Tool Contract 与 risk_level 申报

每个能力必须注册一份 Tool Contract,作为 capability 的权威真源。Tool Contract 必填字段集:fully_qualified_name / name / read_or_write / risk_level / cost_policy / timeout_sec / retry_policy

  • 未注册 capability → validator 归类 CAPABILITY_NOT_FOUND
  • 写类工具必须 idempotency_key.supported=true 且有非空 template,否则 ContractLintError,被 Planner 校验拒绝。

risk_level 申报决定 Action 维裁决:L0–L2 放行、L3/L4 触发 HITL(见 8.3 与第 13 章 permit 决策流 )。

8.3 sandbox_config 6 维构造

caller 在 POST /api/v1/runs 提交 sandbox_configSandboxConfigextra="forbid"),含 dimensions(6 维,SandboxDimensions)+ 可选 hitl

sandbox_config 6 维结构 permit HMAC-SHA256 签名 Resource 资源域 / 范围 Capability allowed / forbidden Data 数据访问面 Cost soft / max / absolute Time max_duration_sec Action risk_level L0–L4 校验三要素:签名有效(绑定 run / step / capability)+ 未过期 + 未撤销;任一不满足 → POLICY_DENIED
图 — sandbox_config 6 维结构
形态强类型?
cost{soft_budget_usd, max_budget_usd, absolute_usd}(均 gt=0
capability{allowed: [...], forbidden: [...]}
resource自由 dict否(P0 自由形态)
time自由 dict(如 max_duration_sec
data自由 dict
action自由 dict(如 max_risk_level
表 — sandbox_config 6 维

8.4 责任边界(RACI 摘要)

下表概括责任边界:host 拥有边界(部署、env、密钥、业务 MCP、审批 UI、结果 UI);Lattice 拥有内核(规划、permit、执行、审计、HITL 端点)。

事项host(你)Lattice 侧
实例部署 / 升级 / 凭据签发R(部署人)
goal / context / sandbox_config 构造R校验与拒绝非法值
业务 MCP wrapper 开发、运维、错误映射R(A=Tech Lead)permit 校验、调度、审计
Tool Contract 编写与 risk_level 申报Rlint 与复核、权威库维护
API key 保管(biz / hitl)R(自家 secret 管理)签发、哈希校验、撤销
webhook 接收端(验签 / 幂等 / 对账)R签名投递、重试、失败审计
HITL 审批流(审批人管理)Rhitl 端点、permit 重签、超时策略执行
前端 UI / 结果呈现R(Lattice 不做 UI)
表 — host / Lattice 责任边界 RACI

9 调用流程 规范性

本章给出 caller 的端到端调用路径:提交 goal → Lattice 内部生成 plan_json → 执行 → 消费结果/审计,含 happy-path、HITL 分支与失败/恢复分支。

9.1 端到端时序

端到端调用时序 CallerAPIOrchestratorSandbox/cpexAudit/HITL POST /runs (goal, sandbox_config) create run → planning plan_json + run_event 落审计 每 step 请 permit HMAC 校验 → 签发 / POLICY_DENIED executing:tool_call 经 cpex 钩子 HITL 分支(L3/L4) executing → waiting_for_hitl;approve→续跑,reject→terminated_policy evaluating → completed,result 落库 SSE 事件流(6 类)/ 201 run_id 失败 / 恢复分支 可重试类→recoverable_paused,带 last_step_id 调 /resume;absolute 超→terminated_budget GET /runs/{id} 终态对账(真相源)
图 — 端到端调用时序:submit→plan→执行→结果/审计
caller 动作端点
1提交 goalPOST /api/v1/runs (201)
2连流看进度GET /api/v1/runs/{id}/stream (SSE)
3(Lattice 内部)规划 → 执行
4轮询 / 对账GET /api/v1/runs/{id}
5(分支)桥接审批POST /api/v1/hitl/{id}/approve|reject
表 — 调用链每一步

Lattice 内部由 drive_run_integrated 驱动状态推进:created → planning → waiting_for_permit → executing → evaluating → completed。Planner 把 goal 生成 plan_json 并经三重校验;每个 tool step 经签发 permit → CF 调用 → tool_call_audit;四路审计按同 run_id 落库。

9.2 happy-path:最小请求与响应

请求体形状来自 CreateRunRequestextra="forbid"):goal 必填(min_length 1),sandbox_config 必填且 cost / capability 强类型,context / model_strategy / notification 可选。Idempotency-Key 为可选 header。

POST /api/v1/runs
Authorization: Bearer lk_biz_a1b2c3d4_<secret>
Idempotency-Key: 7f3c2e10-...-uuid
Content-Type: application/json

{
  "goal": "为客户 C-1024 生成 2026 Q1 应收对账单并标记逾期项",
  "context": {"customer_id": "C-1024", "period": "2026Q1"},
  "sandbox_config": {
    "dimensions": {
      "cost": {"soft_budget_usd": 0.5, "max_budget_usd": 1.0, "absolute_usd": 2.0},
      "capability": {"allowed": ["lattice.acquisition.read"], "forbidden": []}
    }
  }
}

响应来自 CreateRunResponse

{ "run_id": "…uuid…", "sse_stream_url": "/api/v1/runs/…uuid…/stream", "status": "created" }

提交后立刻连 sse_stream_url。SSE 事件 6 类:plan_created / step_started / step_completed / hitl_requested / run_completed / run_failed,每条带 id: <run_id>:<seq>。对账走 GET /api/v1/runs/{id}(见 9.4 字段限制)。

9.3 HITL 分支

L3/L4 step 触发 → run 转 waiting_for_hitl + 发 hitl_requested 事件。caller 用 hitl role 桥接审批:

POST /api/v1/hitl/{hitl_id}/approve
Authorization: Bearer lk_hitl_...
Content-Type: application/json

{ "approver_id": "u-42", "reason": "已核对额度" }

HitlVerdictRequest 两字段(approver_id / reason均可选,兼容空 body。响应 HitlVerdictResponsehitl_id / run_id / verdict / resumedresumed = (verdict == "approved"))。approve → run 续跑、permit 复检;reject → terminated_policy(HITL_REJECTED)。同向重复裁决幂等 200;改判 → 409 POLICY_DENIED

9.4 失败 / 恢复分支

run 数据流:goal → plan → step → 审计写入 goal+ sandbox_configplan_json4 strategy/5 stepstep 执行permit + tool_callresult+ partial_result 审计四路(append-only · 同 run_id join) run_event(每次状态转换)· plan · decision_log(10 字段摘要,无 CoT)· tool_call_audit
图 — run 数据流:goal→plan→step→audit 写入
触发error_classRSM 终态
plan 校验失败SCHEMA_VALIDATION_FAILED / POLICY_DENIEDfailed / terminated_policy
执行期 cpex denyPOLICY_DENIEDterminated_policy
工具失败可重试耗尽TOOL_CALL_FAILEDrecoverable_paused → failed
LLM 步失败MODEL_CALL_FAILEDrecoverable_paused → failed
超 absolute 预算BUDGET_EXCEEDEDterminated_budget
run/step 超时TIMEOUTterminated(超时)
Evaluator 仍不达标EVALUATION_FAILEDfailed
表 — 失败分支与 RSM 终态

失败均经 run_failed 事件 + GET 可见 error_class / partial_result。处于 recoverable_paused 的失败 POST /resume + last_step_id 续跑(仅 RECOVERABLE_PAUSED 状态生效,否则 409)。内存活态对账走 _status_payload

{
  "run_id": "…", "status": "completed", "plan_version": 1,
  "current_step_id": null, "spent_usd": 0.42, "elapsed_ms": 1180,
  "result": {"outputs": {}, "schema_version": "1"},
  "partial_result": null, "error_class": null
}

9.5 端到端实例:财务系统应收对账(输入 / 输出数据流)

本节用一个具体场景把"业务系统如何与 Lattice 交互"以及"数据如何进、如何出"讲清。场景:某财务系统(应收 AR 子系统)需为客户 C-1024 生成 2026 Q1 应收对账单,并标记逾期(> 30 天)项;其中"对外发布对账单"是高风险动作(risk L3),必须经人工审批后执行。下文给出每个边界上的真实载荷与方向。

角色与边界(谁拥有什么)

交互的前提是把业务能力与控制面分清。财务系统(host)与 Lattice 各自拥有:

财务系统(host)拥有Lattice 拥有
部署与 env、biz / hitl token把 goal 规划为 plan_json
把账本能力包装为业务 MCP:ar.invoices.read(读发票)、ar.ledger.read(读已收/分录)、ar.statement.publish(发布对账单)为每个 step 签发并校验 permit(HMAC,绑定 run/step/capability)
为每个能力编写 Tool Contract 并申报 risk_level(读 L0/L1,发布 L3执行 step(经 cpex 在 tool_pre_invoke 守门后调你的 MCP)
运营审批桥(把 hitl_requested 落到财务审批流)与 webhook 接收端HITL 端点、超时策略、审计四路记录
把最终 result 渲染给用户
表 — 财务系统接入:责任边界

数据流总览

下图给出两侧系统之间的载荷与方向:输入(财务系统 → Lattice)、输出(Lattice → 财务系统),以及受 permit 守门的业务数据双向通道(Lattice ↔ 你的 MCP)。

财务系统应收对账:输入 / 输出数据流 财务系统(host) 后端服务(caller) AR 业务 MCP(你包装 + Tool Contract) ar.invoices.read · L0 ar.ledger.read · L1 ar.statement.publish · L3 读写账本的唯一通道 审批桥 / webhook 接收端 把 hitl_requested 落到审批流 Lattice 实例 Planner(goal → plan_json) Executor + Sandboxpermit 签发 · cpex @ tool_pre_invoke Evaluator(对账平衡校验) HITL 端点 审计四路(join by run_id) goal + sandbox_config · POST /runs 1 run_id + SSE 进度事件 2 tool_call:args → / result ← permit + cpex 守门 3 hitl_requested(SSE + webhook) 4 approve / reject · POST /hitl 5 result + spent_usd + 审计 join · GET /runs 6 → 输入(财务 → Lattice) ← 输出(Lattice → 财务) ⇆ 业务数据(经你的 MCP,受 permit 守门)
图 — 财务系统应收对账:输入 / 输出数据流

分步数据流(真实载荷)

① 提交 goal(输入)。财务后端携 biz token 调 POST /api/v1/runs,capability 申明本次允许触达的三个能力:

POST /api/v1/runs
Authorization: Bearer lk_biz_a1b2c3d4_<secret>
Idempotency-Key: 7f3c2e10-...-uuid
Content-Type: application/json

{
  "goal": "为客户 C-1024 生成 2026 Q1 应收对账单并标记逾期项(>30 天)",
  "context": {"customer_id": "C-1024", "period": "2026Q1"},
  "sandbox_config": {
    "dimensions": {
      "cost": {"soft_budget_usd": 0.5, "max_budget_usd": 1.0, "absolute_usd": 2.0},
      "capability": {
        "allowed": ["ar.invoices.read", "ar.ledger.read", "ar.statement.publish"],
        "forbidden": []
      }
    }
  }
}

② 受理回执(输出)。Lattice 建 run 并立即返回,财务后端据此连 SSE:

{ "run_id": "run_9b2f…", "sse_stream_url": "/api/v1/runs/run_9b2f…/stream", "status": "created" }

③ 规划(Lattice 内部)。Planner 把 goal 转为可审计的 plan_json(此处 strategy 为 sequential_chain,5 步;发布步 risk L3):

{
  "strategy": "sequential_chain",
  "steps": [
    {"id": "s1", "type": "tool_call", "capability": "ar.invoices.read",  "risk_level": "L0"},
    {"id": "s2", "type": "llm_call",  "purpose": "账龄计算与逾期标记(>30d)", "risk_level": "L0"},
    {"id": "s3", "type": "tool_call", "capability": "ar.ledger.read",    "risk_level": "L1"},
    {"id": "s4", "type": "evaluation","purpose": "对账平衡校验",          "risk_level": "L0"},
    {"id": "s5", "type": "tool_call", "capability": "ar.statement.publish","risk_level": "L3"}
  ]
}

④ 读发票(业务数据通道)。执行 s1 前,Executor 申请绑定 (run, s1, ar.invoices.read) 的 permit;cpex 在 tool_pre_invoke 校验通过后,才调用你的 MCP。工具 I/O 双向:

// Lattice → 你的 MCP(请求)
{ "tool": "ar.invoices.read", "args": {"customer_id": "C-1024", "period": "2026Q1"} }

// 你的 MCP → Lattice(响应)
{ "invoices": [
    {"id": "INV-7781", "amount_usd": 1200.00, "due_date": "2026-02-15", "paid": false},
    {"id": "INV-7802", "amount_usd":  640.50, "due_date": "2026-03-01", "paid": true}
] }

⑤ 计算与核对(Lattice 内部 + 业务通道)。s2llm_call)据发票算账龄并标记逾期;s3 再经 ar.ledger.read 核对已收;s4evaluation)做对账平衡校验。任一步 budget 越 absolute_usdBUDGET_EXCEEDED 强停,无效 permit 即 POLICY_DENIED

⑥ 高风险发布触发审批(输出 → 输入)。s5L3:run 转 waiting_for_hitl,发 hitl_requested(SSE + webhook 到你的审批桥)。webhook 载荷示例:

{ "event": "hitl_requested", "run_id": "run_9b2f…", "seq": 7,
  "data": {"step_id": "s5", "risk_level": "L3", "capability": "ar.statement.publish"} }

财务系统审批人裁决后,经 hitl token 回送(输入):

POST /api/v1/hitl/<hitl_id>/approve
Authorization: Bearer lk_hitl_...
Content-Type: application/json

{ "approver_id": "fin-lead-7", "reason": "金额与额度已核对" }

approve → permit 复检 → 执行 ar.statement.publish(你的 MCP 返回 {statement_id, url})→ run 转 completedrejectterminated_policy(HITL_REJECTED),对账单发布。

⑦ 取结果与追溯(输出)。终态后财务后端 GET /api/v1/runs/{id}resultspent_usd;审计四路(run_event / plan / decision_log / tool_call_audit)同 run_id 可 join,供事后逐步追溯:

{
  "run_id": "run_9b2f…", "status": "completed", "spent_usd": 0.41,
  "result": {"outputs": {
     "statement_id": "AR-STMT-2026Q1-C1024",
     "overdue": [{"id": "INV-7781", "days_overdue": 41, "amount_usd": 1200.00}],
     "total_due_usd": 1200.00
  }, "schema_version": "1"},
  "partial_result": null, "error_class": null
}

输入 / 输出边界对照

#边界方向数据通道
提交 goal财务 → Lattice(输入)goal + context + sandbox_configPOST /api/v1/runs
受理回执Lattice → 财务(输出)run_id / sse_stream_url / status201 响应
进度事件Lattice → 财务(输出)plan_created / step_* / hitl_requested / run_*SSE
业务数据读写Lattice ↔ 你的 MCP(双向)tool args → / result业务 MCP(permit + cpex 守门)
审批请求Lattice → 审批桥(输出)hitl_requested(run_id / step_id / risk)SSE + webhook
审批裁决财务 → Lattice(输入)approve / rejectPOST /api/v1/hitl/{id}/approve
终态结果与追溯Lattice → 财务(输出)result / spent_usd / error_class;审计四路 joinGET /api/v1/runs/{id}
表 — 财务系统交互:输入 / 输出边界对照

10 API 参考 规范性

本章逐端点列出 consumer 可调 API:方法、路径、请求 schema、响应、状态码与 curl 示例。所有路由经 create_app() 装配。汇总见

方法 + 路径role成功码
POST /api/v1/runsbiz201
GET /api/v1/runs/{id}biz / sre200
POST /api/v1/runs/{id}/cancelbiz200
POST /api/v1/runs/{id}/resumebiz200
GET /api/v1/runs/{id}/streambiz200
GET /api/v1/hitl/{id}hitl200
POST /api/v1/hitl/{id}/approvehitl200
POST /api/v1/hitl/{id}/rejecthitl200
表 — consumer 端点汇总(端点 × role × 成功码)

10.1 POST /api/v1/runs

请求 CreateRunRequest;响应 CreateRunResponse。状态码:201 成功 / 409 Idempotency-Key 冲突 / 422 schema 或 callback SSRF 拒绝。幂等:同 Idempotency-Key + 同 body → 返回原 run_id(201);同 key + 不同 body → 409 POLICY_DENIED

curl -X POST http://lattice-acquisition:8000/api/v1/runs \
  -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>" \
  -H "Idempotency-Key: 7f3c2e10-...-uuid" \
  -H "Content-Type: application/json" \
  -d '{"goal":"生成应收对账单","sandbox_config":{"dimensions":{"cost":{"soft_budget_usd":0.5,"max_budget_usd":1.0,"absolute_usd":2.0},"capability":{"allowed":["lattice.acquisition.read"],"forbidden":[]}}}}'

10.2 GET /api/v1/runs/{id}

无请求体;响应 RunStatusResponse(部分填充)。状态码:200 / 404。取数优先级:内存 run_registry 命中 → _status_payload(活态权威);内存缺失 → DB 行 → _db_status_payload;两者皆无 → 404。

curl http://lattice-acquisition:8000/api/v1/runs/<run_id> \
  -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>"

10.3 POST /api/v1/runs/{id}/cancel

无请求体;响应 RunStatusResponse。状态码:200 / 404。终态幂等返回当前 status;非终态触发 permit 撤销 + checkpoint cleanup → cancelledcancel_run_with_cleanup)。

curl -X POST http://lattice-acquisition:8000/api/v1/runs/<run_id>/cancel \
  -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>"

10.4 POST /api/v1/runs/{id}/resume

请求 ResumeRunRequestlast_step_id 必填(min_length 1),缺失 → 422。响应 RunStatusResponse。状态码:200 / 404 / 409。仅 RECOVERABLE_PAUSED 生效,否则 409 POLICY_DENIED;无 checkpoint index 或 last_step_id 无对应 checkpoint → 409。

curl -X POST http://lattice-acquisition:8000/api/v1/runs/<run_id>/resume \
  -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>" \
  -H "Content-Type: application/json" \
  -d '{"last_step_id":"s3"}'

10.5 GET /api/v1/runs/{id}/stream

无请求体;可选 Last-Event-ID header;响应 SSE 流(media_type="text/event-stream")。状态码:200 / 400 / 404。Last-Event-ID 可解析且 run_id 与路径一致 → 自该 seq 之后按序补发;run_id 不一致 → 400 SCHEMA_VALIDATION_FAILED;不可解析 → 视同未带头、全量重放。每条事件带 id: <run_id>:<seq>

curl -N http://lattice-acquisition:8000/api/v1/runs/<run_id>/stream \
  -H "Authorization: Bearer lk_biz_a1b2c3d4_<secret>" \
  -H "Last-Event-ID: <run_id>:5"

10.6 GET /api/v1/hitl/{id}

无请求体;响应为直接 dict(非 Pydantic 模型):hitl_id / run_id / step_id / risk_level / verdict。状态码:200 / 404(未知 id → SYSTEM_ERROR 信封)。

curl http://lattice-acquisition:8000/api/v1/hitl/<hitl_id> \
  -H "Authorization: Bearer lk_hitl_a1b2c3d4_<secret>"

10.7 POST /api/v1/hitl/{id}/approve/reject

请求 HitlVerdictRequestapprover_id / reason 均可选,兼容空 body。响应 HitlVerdictResponsehitl_id / run_id / verdict / resumed。状态码:200 / 404 / 409。未知 hitl_id → 404;同向重复裁决幂等 200;改判 → 409 POLICY_DENIED

curl -X POST http://lattice-acquisition:8000/api/v1/hitl/<hitl_id>/approve \
  -H "Authorization: Bearer lk_hitl_a1b2c3d4_<secret>" \
  -H "Content-Type: application/json" \
  -d '{"approver_id":"u-42","reason":"已核对额度"}'

curl -X POST http://lattice-acquisition:8000/api/v1/hitl/<hitl_id>/reject \
  -H "Authorization: Bearer lk_hitl_a1b2c3d4_<secret>"

10.8 内部端点 POST /internal/sandbox/validate(非 caller 方法)

请求 SandboxValidateRequestpermit_id / run_id / step_id / capability 必填 + 可选 args_digest;响应 SandboxValidateResponsedecision(allow|deny)/ permit_id / violation。deny 时 violation.code 恒为 POLICY_DENIED,细分原因落 reason。该端点 deny 走错误信封,而是 200 + violation 子对象。permit 6 维裁决与校验细节见 第 13 章

11 HITL 审批接入 规范性

HITL(Human-in-the-Loop)让高风险 step(L3/L4)在执行前停下,等待人工裁决(approve / reject),并支持超时策略。审批裁决经 Lattice 自有 /api/v1/hitl/* 端点落库,run 在 Executor 协程内 park。HITL 是 Lattice 自建能力,park 点在 Executor 协程而非插件回调内(钩子内等审批会触发插件超时)。

11.1 审批 / 驳回端点

审批方必须POST /api/v1/hitl/{hitl_id}/approve/reject 落裁决。请求体 HitlVerdictRequestextra="forbid")含可选 approver_id / reason,body 缺省即空请求体。响应 HitlVerdictResponse 返回 hitl_id / run_id / verdict / resumed,其中 resumed = (verdict == "approved")

裁决落定语义(HitlStore.decide):pending → verdict;同向裁决幂等(同 verdict 重复返回原结果);改判(approve↔reject)相反裁决必须被拒,返回 409,error_class POLICY_DENIED。未知 hitl_id404,error_class SYSTEM_ERRORGET /api/v1/hitl/{hitl_id} 返回 {hitl_id, run_id, step_id, risk_level, verdict}

POST /api/v1/hitl/hitl_3f2a.../approve HTTP/1.1
Authorization: Bearer lk_hitl_***
Content-Type: application/json

{"approver_id": "alice", "reason": "figures reconciled"}

HTTP/1.1 200 OK
{"hitl_id": "hitl_3f2a...", "run_id": "run_...", "verdict": "approved", "resumed": true}

11.2 webhook 订阅

run 进入 waiting_for_hitl 发射的 hitl_requested 事件可经 HitlWebhookNotifier 桥接到订阅方 callback_url。该桥投递 hitl_requested,其余事件类型返回 None 不投递。信封含 event / run_id / seq / occurred_at / datadatastep_idhitl_id 仅当装配了 HitlGate 时由 gate.request 填入(占位 park 路径只有 step_id)。投递经 HMAC 签名验真,机制与重试见 第 12 章

11.3 超时策略

park 等待审批超时(HitlTimeoutError)后由 sandbox 策略据 HitlTimeoutActionStrEnumreject / degrade / wait)决定走向。默认超时 timeout_sec = 1800 秒。未知 action(如 escalate)→ 显式 ValueError(枚举穷尽守护,不静默落 reject)。

HITL 超时分支 waiting_for_hitl timeout_sec=1800(默认) on_timeout? HitlTimeoutAction reject(默认 / 最安全) → terminated_policy(HITL_REJECTED) degrade 放弃该高风险 step(不执行)后续跑(降级产出) wait 保持挂起,待外部审批恢复 未知 action → ValueError(不静默落 reject)
图 — HITL 超时分支:reject | degrade | wait
  • reject(默认,最安全):视同审核拒绝,waiting_for_hitlterminated_policyHITL_REJECTED)。
  • degrade:放弃该高风险 step(不执行),标记 degraded 后 run 续跑(产出降级)。
  • wait:不强制裁决,run 保持挂起于 waiting_for_hitl,待后续外部审批恢复。

11.4 审批 SLA 对齐

对齐铁律:caller 的审批流实际响应时限低于 Lattice 的 timeout_sec,并把审批单超时提醒设在 Lattice 超时之前(如 2/3 处)。否则 run 卡在 waiting_for_hitl 直到超时按 on_timeout 策略落终态。HITL 通知量测目标 ≤ 30 秒。

12 流式与异步通知 规范性

Lattice 向业务系统推送 run 进度有两条尽力通道:SSE(GET /runs/{id}/stream,交互式 UI、逐 step 进度、断线可经 Last-Event-ID 重放)与 webhook(notification.callback_url,server-to-server、HITL 审批桥接)。两者都是加速通道;轮询 GET /runs/{id} 永远是最终对账真相。

12.1 SSE 与 Last-Event-ID 重放

SSE 仅 6 类对外事件(SseEventType):plan_created / step_started / step_completed / hitl_requested / run_completed / run_failed。每条 render 为 id: <run_id>:<seq> 形态,seq 为 run 内单调序号(自 1 起、无空洞),是断线重连的游标地基。

SSE 事件流与 webhook 投递 Lattice 实例 SSE id: <run_id>:<seq>(seq 自 1 单调、无空洞) Caller(UI) Last-Event-ID 重放 断线重连:Last-Event-ID:<run_id>:<seq> → replay seq>after webhook 投递 HMAC 签名 · 仅 hitl_requested 5 次退避 0s/30s/2m/10m/30m · SSRF allowlist 订阅方 callback_url 按 seq 排序 · 按 delivery_id 去重 两者皆加速通道;轮询 GET /runs/{id} 永远是最终对账真相
图 — SSE 事件流与 webhook 投递

断线重连时 caller Last-Event-ID: <run_id>:<seq> 头。服务端 parse_last_event_id 解析后:run_id 与路径不一致 → 400SCHEMA_VALIDATION_FAILED);一致 → replay_from(after_seq) 补发 seq > after_seq 的事件;不可解析的 id(如 garbage / 空)视同未带头、全量重放;seq 超历史 → 补发为空、流正常关闭。重连后拼接 = 全量序列。

12.2 webhook 验签 / 重试 / 幂等对账

订阅注册:POST /api/v1/runs 可选 notification.callback_url,由 _callback_allowed 对照 LATTICE_CALLBACK_ALLOWLIST 校验(scheme http(s)、有 host、无 userinfo、host 命中、fail-closed);不允许 → 422,run 创建。

投递(WebhookDispatcher.deliver):

  • SSRF 防护callback_url host 必须命中 allowlist(缺省空 = 拒绝一切),且拒绝含 userinfo 的 URL;不命中 → 不发起任何出站请求,返回 DeliveryResult(blocked=True)
  • 退避重试:共 5 次尝试,delay 0s / 30s / 2m / 10m / 30m_BACKOFF_DELAYS);2xx 即成功,其余 / 超时(10s)/ 连接失败计为失败。
  • 验签X-Lattice-Signature: t=<unix_ts>,v1=<hex>v1 = HMAC-SHA256(secret, "<t>." + body)。接收端 verify_signature 校验 |now - t| ≤ 300s 防重放窗 + 常数时间比较。
  • 幂等对账:consumer 必须seq 排序、以 delivery_id 去重(至少一次投递、不保证顺序)。单次失败写 run_event.reason=notification_delivery_failed,第 5 次写 notification_delivery_exhausted

13 错误处理与恢复 规范性

Error Taxonomy 把 runtime 内所有失败归为 10 类 error_class,每类绑定一条确定的恢复策略(retry / replan / fail / terminate)与一个 RSM 终态。

13.1 错误信封

所有错误响应必须采用统一信封:

{
  "error": {
    "error_class": "POLICY_DENIED",
    "message": "permit denied for capability lattice.publish_report",
    "detail": { }
  }
}

13.2 error_class 映射与恢复

10 类 error_class → HTTP / 恢复策略 / 终态映射如下。其中可暂停(pause_before_terminal)的可重试类在 retry 未耗尽时先转 recoverable_paused 等待 /resume

error_class策略max_retriesRSM 终态可暂停caller 处置
SCHEMA_VALIDATION_FAILEDFAIL0failed修正 plan/请求形状后重提
CAPABILITY_NOT_FOUNDFAIL0failed注册 Tool Contract 后重提
POLICY_DENIEDTERMINATE0terminated_policy不可重试;检查 permit/risk 边界
TOOL_CALL_FAILEDRETRY3failed必要时 /resume 续跑
MODEL_CALL_FAILEDRETRY3failed必要时 /resume 续跑
BUDGET_EXCEEDEDTERMINATE0terminated_budgetpartial_result,加预算重提
TIMEOUTRETRY1terminated_timeout不可续;调高 max_duration_sec 重提
HITL_REJECTEDTERMINATE0terminated_policy不可重试;审批被拒
EVALUATION_FAILEDREPLAN0failedOrchestrator 触发 replan;超 fallback 深度落 failed
SYSTEM_ERRORRETRY1failed必要时 /resume 续跑
表 — error_class 映射(10 类)

执行侧裁决(RunRecoveryCoordinator.on_step_failure):策略为 RETRY 且可暂停且 retry 未耗尽 → 转 recoverable_paused 并在 run_event.payload_jsonpause_reason / retry_hint / step_id / retries_remaining;否则直接入策略终态。retry 计数按 step_id 累计、跨 pause/resume 持续,唯有 step 真正成功才清零。超时 watchdog 仅在 executing / waiting_for_permit 活跃态有到 terminated_timeout 的合法边,非活跃态 no-op。

permit 校验决策流 step 执行前 permit存在?签名有效?未过期未撤销? 允许执行 step cpex @ tool_pre_invoke 拒绝 → POLICY_DENIED → terminated_policy UNKNOWN_PERMIT · BAD_SIGNATURE · MISMATCHED_BINDING · EXPIRED · REVOKED
图 — permit 校验决策流
Budget 三档判定 spent_usd 累计 → soft_usdhard_usdabsolute_usd ≤ soft 正常执行 > soft → DEGRADE_MODEL 沿 fallback 链降档(LiteLLM) > hard → STOP_NEW_STEPS 停发起新 LLM 调用(LiteLLM) > absolute TERMINATE_RUN 阈值「严格大于」才触发 · 仅 absolute 越线升级为错误:BUDGET_EXCEEDED → terminated_budget 2 档 LiteLLM 原生信号(degrade / stop)+ 1 档 Lattice 强停(absolute)
图 — Budget 三档判定

Budget 三档(evaluate_budget,阈值严格大于才触发):> soft_usdDEGRADE_MODEL(沿 fallback 链降档);> hard_usdSTOP_NEW_STEPS(停发起新 LLM 调用);> absolute_usdTERMINATE_RUN(RSM → terminated_budgetBUDGET_EXCEEDED)。只有 absolute 越线升级为错误。

错误恢复决策树 step 失败 → error_class 恢复策略? taxonomy → recovery RETRY可暂停→pausedretry 耗尽→failedREPLANEvaluator 重规划超 fallback→failedFAIL直接落 failedschema/capabilityTERMINATEpolicy/budget/timeout→ terminated_* 硬终态 可暂停的可重试类在 retry 未耗尽时先转 recoverable_paused,等待带 last_step_id 的 /resume
图 — 错误恢复决策树

13.3 幂等键

POST /api/v1/runs 始终携带 uuid 形态的 Idempotency-Key header(防重复扣预算第一道闸)。同 key + 同 request_hash → 返回原 run_id201);同 key + 不同 body → 409 Conflict不得静默映射到旧 run。网络超时重发必须带同 key + 同 body。

13.4 取消与续跑

POST /api/v1/runs/{id}/cancel 在非终态触发 cleanup(permit 撤销 + checkpoint 清理)→ cancelled;终态幂等返回当前 status,不报错。POST /api/v1/runs/{id}/resume 必须last_step_idResumeRunRequest),且recoverable_paused 生效——其余状态 → 409;无 checkpoint index 或无对应 last_step_id 的 checkpoint → 409terminated_* 系列是硬终态,可续。

14 运行生命周期参考 资料性

本章给出 run 状态的 caller 视角参考。

14.1 13 状态

运行时枚举 RunStatus 仅 13 个值。

Run State Machine 13 状态 createdplanningwaiting_for_permitexecutingwaiting_for_hitlevaluatingcompletedfailedcancelledterminated_budgetterminated_timeoutterminated_policyrecoverable_paused L3/L4 reject→policy 完成 step 可重试失败 /resume 终态(6):无出边,仅可追加审计 活跃执行态 recoverable_paused 唯一可恢复暂态 · 第 14 态 waiting_for_cross_system 为 v5.0+ 未实现
图 — Run State Machine 13 状态
#状态含义终态?
1createdPOST /api/v1/runs 已建 run 记录,未启动
2planningPlanner 子层生成 plan_json
3waiting_for_permitplan 已校验通过,等待 Sandbox 签发 step permit
4executingExecutor 子层在跑某个 step
5waiting_for_hitl触发 L3/L4 action,等待人工审批
6evaluatingEvaluator 判断结果是否满足 stop_conditions
7completedgoal 达成,result 已落库
8failed模型/工具/schema 失败,按 Error Taxonomy 归类
9cancelled调用方主动 /cancel
10terminated_budgetCost 超 absolute,强制终止
11terminated_timeoutmax_duration_sec
12terminated_policyforbidden_capability / risk_level / HITL reject 越界
13recoverable_paused系统级可重试失败,允许 /resume否(唯一可恢复暂态)
表 — RunStatus 13 状态

14.2 终态 vs 非终态

TERMINAL_STATUSES = {completed, failed, cancelled, terminated_budget, terminated_timeout, terminated_policy}(6 个)。终态在 LEGAL_TRANSITIONS 中映射空集、无出边,只可追加审计 / decision_log。非终态为其余 7 个,其中 recoverable_paused 是唯一可恢复暂态,离开它必须/resume

14.3 caller 关心的转移

每个非终态都有 cancelled 出边。HITL park:executing → waiting_for_hitl(reason hitl_requested);reject:waiting_for_hitl → terminated_policy。可重试失败:executing → recoverable_paused/resumerecoverable_paused → executing。详见 13.4

15 配置参考 规范性

全部 env 由 build_production_app 在装配期读取;认证 env 由 AuthKeyStore.from_env 读取。必填 env 缺失则实例 fail-fast。

15.1 环境变量

名称必填默认 / 约束示例
LATTICE_LLM_MODEL_GROUP必填LiteLLM 模型组名;缺失 → fail-fastfinrep-default
LATTICE_INSTANCE_SECRET非 dev/test 必填permit HMAC 签名根密钥,每实例独立;非 dev/test 缺失 → fail-fast<32+ 字节随机串>
LATTICE_DB_URL可选审计 DB;缺省 sqlite://(in-memory,重启即丢)postgresql://lattice:***@db:5432/lattice
LATTICE_API_KEYS_BIZ生产必填biz role 活跃 token sha256(≤2,逗号分隔,双活轮换)<sha256-of-biz-token>
LATTICE_API_KEYS_HITL生产必填hitl role sha256(同上)<sha256-of-hitl-token>
LATTICE_API_KEYS_SRE生产必填sre role sha256(同上)<sha256-of-sre-token>
LATTICE_CALLBACK_ALLOWLIST可选webhook callback host 白名单(逗号分隔);缺省空 = fail-closed 拒绝一切出站app-backend.internal
LATTICE_LLM_MODEL_LIST可选JSON 数组 → litellm model_list;缺省 [][{"model_name":"..."}]
LATTICE_ENV可选环境名;dev/development/test 放宽 secret/key 强制production
表 — 生产 env 配置面
export LATTICE_ENV=production
export LATTICE_LLM_MODEL_GROUP=finrep-default
export LATTICE_INSTANCE_SECRET=<32+ 字节随机串>
export LATTICE_DB_URL=postgresql://lattice:***@db:5432/lattice
export LATTICE_API_KEYS_BIZ=<sha256-of-biz-token>
export LATTICE_API_KEYS_HITL=<sha256-of-hitl-token>
export LATTICE_API_KEYS_SRE=<sha256-of-sre-token>
export LATTICE_CALLBACK_ALLOWLIST=app-backend.internal

15.2 上游依赖启动顺序

实例依赖 6 件上游开源件,启动 / 接线顺序固定:

ContextForge(mcpgateway) → cpex → LiteLLM → mcp-agent → Langfuse → LangGraph

外部服务一律按可降级依赖处理:ContextForge / Langfuse 不可用不得拖死整个实例,降级行为统一回写 run_event + decision_log。上游容器的实际连通是运维事项,装配期不连网。

16 注意事项与限制 规范性

本章集中列出 caller 必须知道的边界与已知缺陷。scope 为 v4.3-trimmed / Phase 0-4 单实例。

16.1 单实例:无 Mesh / MCP-A2A / cross-system

cross_system_call step type 在 Pydantic parse 层即被拒——StepType 枚举只有 5 种(tool_call / sub_agent / llm_call / evaluation / hitl_gate),不含 cross_system_call,非法 step type 落 SCHEMA_VALIDATION_FAILED。Decision Log 的 cross_system_* 两字段在 trimmed 下恒 null,写非空值直接抛 CrossSystemFieldError。跨实例通知、lattice://<system>/* 寻址、MCP-A2A 协作均 NOT implemented。

16.2 预算硬停

三层 budget:soft → 降级标记后继续;hard → 停止启动新 step、优雅收口;absolute强停BUDGET_EXCEEDED + terminated_budget。absolute 是绝对硬停:超出即终止、无 replan。失败时查 partial_result 决定是否加预算重提。

16.3 permit / tool-contract 约束

无有效 permit 执行任何 step:执行期 cpex 在 tool_pre_invoke 钩子内校验 permit_id,deny → POLICY_DENIEDterminated_policy。无 Tool Contract 的能力调不到:未注册 capability 在 plan 校验期落 CAPABILITY_NOT_FOUND。permit 校验三要素:HMAC-SHA256 签名(绑定 run/step/capability)+ 未过期 + 未撤销。

16.4 append-only 审计

四路审计(run_event / plan / decision_log / tool_call_audit)同 run_id 落库、可全 join。审计为 append-only。goal 原文仅以 sha256 入审计——caller 不得假设 goal 明文可回查,也不得靠改审计纠错。

16.5 版本化

API 面锁定在 /api/v1/* 前缀下;required_roles 只认 /api/v1/ 前缀。演进规则:加法兼容、破坏既有形状;新增 /api/v1 路由必须同步更新 required_roles 矩阵,未列入的 runs 子路径缺省 biz-only fail-closed。

16.6 已知缺陷

17 故障排查 资料性

下表给出常见症状 → 原因 → 处置。处置以 caller 视角为主,错误语义见 第 13 章,约束见 第 16 章

症状可能原因处置
401 Unauthorized缺失 / 无效 token;或撤销后的旧 key检查 Authorization: Bearer;用双活窗口内的有效 key
403 Forbiddenrole 越权(如 biz key 调 HITL 端点)用对应 role 的 key(biz / hitl / sre 边界,第 15 章
422 创建 run 被拒callback_url host 不在 LATTICE_CALLBACK_ALLOWLIST 或含 userinfo把 host 加入 allowlist;去掉 URL userinfo(12.2
409 ConflictIdempotency-Key 配不同 body;或 /resume 状态非 recoverable_paused / 无对应 checkpoint同 key 须配同 body;仅 recoverable_paused 可 resume 且须有效 last_step_id13.313.4
POLICY_DENIED / terminated_policy无有效 permit、forbidden capability、risk_level 越界不可重试;检查 sandbox policy 与 permit 绑定(16.3
BUDGET_EXCEEDED / terminated_budgetspent 超 absolute_usd不可续;查 partial_result,加预算后重提(16.2
run 卡 waiting_for_hitl 后落终态审批未在 timeout_sec(默认 1800s)内裁决对齐审批 SLA < 超时;按 on_timeout 策略(reject/degrade/wait)预期走向(11.311.4
SSE 断流 / 重连丢事件未带 Last-Event-ID;或对非终态 run 期望 live-tail重连带 Last-Event-ID: <run_id>:<seq>;非终态用 GET /runs/{id} 对账(12.116.6
表 — 故障排查(症状 / 原因 / 处置)

18 附录 资料性

18.A 术语与缩略语

常用项:

  • HITL(Human-in-the-Loop):高风险 step 的人工介入点。
  • plan_json:Lattice 内部生成的可审计执行计划,结构化 JSON。
  • permit:细粒度操作许可,tool contract 的核心控制单元(HMAC 签名绑定 run/step/capability)。
  • tool contract:工具调用的强制约束规格,定义允许 / 禁止操作集合。
  • error_class:runtime 失败的 10 类归类枚举(见 13.2)。
  • RSM:Run State Machine,13 状态运行时状态机(见 14.1)。
  • per-system embedded:每个业务系统独立嵌入一份 Lattice 实例的部署形态。
  • v4.3-trimmed:实施层版本,仅承诺单实例 Phase 0-4。

18.B 设计保留 / 未实现项

下列条目均 NOT implemented,在 v4.3-trimmed 代码中仅以 inert 占位 / 保留枚举 / 恒 null 字段形态存在。caller 不得消费。

  • cross_system_call step 类型 — NOT implemented(枚举缺值 → 解析期拒)。
  • 第 7 维 Cross-System Calls — NOT implemented(恒空 tuple → 自然 deny)。
  • Decision Log cross-system 两字段 — NOT implemented(恒 null + 源头拒非空)。
  • lattice_run cross-system 列 — NOT implemented(保留列恒 null/0)。
  • waiting_for_cross_system 运行态 — NOT implemented(枚举缺值)。
  • lattice://<system>/* 寻址与 MCP/A2A Server 角色 — NOT implemented(v5.0+ 设计)。
  • 长期记忆 / pgvector RAG — NOT implemented(显式延后);仅 run-local memory 已实现。
  • Multi-tenant / K8s / 中央 dashboard — NOT implemented(范围外),仅 Docker Compose 单实例已发。
  • experimental plan 策略 — NOT implemented(不入枚举),仅 4 稳定策略。