文档编号 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.0 | 2026-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 缩略语与运行时枚举
下表为缩略语与关键运行时枚举速查。
| 缩略语 / 枚举 | 含义 / 取值 |
|---|---|
| RSM | Run State Machine(13 状态) |
| HITL | Human-in-the-Loop(人工介入点) |
| MCP | Model Context Protocol(工具接入协议) |
| SSE | Server-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_FAILED … SYSTEM_ERROR |
| permit 6 维 | Resource / Cost / Time / Capability / Data / Action |
| 审计表(8) | lattice_run … lattice_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_json | Planner 把 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_event | run 走到哪一步一目了然,失败能归类、能恢复 |
| 错误分类 + 恢复 | 所有失败归 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 自己挑,你不用管 |
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 的财务系统端到端实例。
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_event 与 decision_log。图 给出依赖启动顺序。
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 白名单;缺省空即拒绝一切出站 |
服务端只存 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_SECRET 与 LATTICE_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.cost 与 dimensions.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/runs(create_run)。请求体来自 CreateRunRequest(extra="forbid"),goal 必填且 min_length 1;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" }
步骤 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 签发 token | lattice keygen --role biz | 明文 lk_biz_* 仅打印一次 + sha256 供写入 env |
| 3 构造约束 | 构造 sandbox_config.dimensions.{cost,capability} | cost 三字段均 gt=0、capability allowed/forbidden 就绪 |
| 4 提交 goal | POST /api/v1/runs(Bearer + Idempotency-Key) | 201 返回 run_id / sse_stream_url / status="created" |
| 5 读结果 | GET /api/v1/runs/{id}/stream 与 GET /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_app → create_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_LIST | JSON 数组;空则 [](测试注入 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) | 打印到 stderr | 2 |
| 缺 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 → 启动
RuntimeErrorfail-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、/stream | biz |
GET /api/v1/runs/{id}(精确单条,_RUN_DETAIL_RE) | biz 或 sre(sre 只读) |
/api/v1/hitl* | hitl |
未列入的 /api/v1/* | 任意 role(先验凭据,404/405 交路由层) |
/internal/*(非 /api/v1/ 前缀) | None(中间件放行,走 LATTICE_INTERNAL_SECRET) |
7.5 401 / 403 语义
拦截顺序在 AuthMiddleware.__call__:
| 情形 | 状态码 | 信封 error_class |
|---|---|---|
| 缺失 / 形态非法 Authorization | 401 | POLICY_DENIED |
| 无效 / 已撤销 token | 401 | POLICY_DENIED |
| role 越权 | 403 | POLICY_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_config(SandboxConfig,extra="forbid"),含 dimensions(6 维,SandboxDimensions)+ 可选 hitl。
| 维 | 形态 | 强类型? |
|---|---|---|
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) | 否 |
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 申报 | R | lint 与复核、权威库维护 |
| API key 保管(biz / hitl) | R(自家 secret 管理) | 签发、哈希校验、撤销 |
| webhook 接收端(验签 / 幂等 / 对账) | R | 签名投递、重试、失败审计 |
| HITL 审批流(审批人管理) | R | hitl 端点、permit 重签、超时策略执行 |
| 前端 UI / 结果呈现 | R(Lattice 不做 UI) | — |
9 调用流程 规范性
本章给出 caller 的端到端调用路径:提交 goal → Lattice 内部生成 plan_json → 执行 → 消费结果/审计,含 happy-path、HITL 分支与失败/恢复分支。
9.1 端到端时序
| 步 | caller 动作 | 端点 |
|---|---|---|
| 1 | 提交 goal | POST /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:最小请求与响应
请求体形状来自 CreateRunRequest(extra="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。响应 HitlVerdictResponse:hitl_id / run_id / verdict / resumed(resumed = (verdict == "approved"))。approve → run 续跑、permit 复检;reject → terminated_policy(HITL_REJECTED)。同向重复裁决幂等 200;改判 → 409 POLICY_DENIED。
9.4 失败 / 恢复分支
| 触发 | error_class | RSM 终态 |
|---|---|---|
| plan 校验失败 | SCHEMA_VALIDATION_FAILED / POLICY_DENIED | failed / terminated_policy |
| 执行期 cpex deny | POLICY_DENIED | terminated_policy |
| 工具失败可重试耗尽 | TOOL_CALL_FAILED | recoverable_paused → failed |
| LLM 步失败 | MODEL_CALL_FAILED | recoverable_paused → failed |
| 超 absolute 预算 | BUDGET_EXCEEDED | terminated_budget |
| run/step 超时 | TIMEOUT | terminated(超时) |
| Evaluator 仍不达标 | EVALUATION_FAILED | failed |
失败均经 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)。
分步数据流(真实载荷)
① 提交 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 内部 + 业务通道)。s2(llm_call)据发票算账龄并标记逾期;s3 再经 ar.ledger.read 核对已收;s4(evaluation)做对账平衡校验。任一步 budget 越 absolute_usd 即 BUDGET_EXCEEDED 强停,无效 permit 即 POLICY_DENIED。
⑥ 高风险发布触发审批(输出 → 输入)。s5 为 L3: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 转 completed;reject → terminated_policy(HITL_REJECTED),对账单不发布。
⑦ 取结果与追溯(输出)。终态后财务后端 GET /api/v1/runs/{id} 取 result 与 spent_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_config | POST /api/v1/runs |
| ② | 受理回执 | Lattice → 财务(输出) | run_id / sse_stream_url / status | 201 响应 |
| ③ | 进度事件 | 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 / reject | POST /api/v1/hitl/{id}/approve |
| ⑦ | 终态结果与追溯 | Lattice → 财务(输出) | result / spent_usd / error_class;审计四路 join | GET /api/v1/runs/{id} |
10 API 参考 规范性
本章逐端点列出 consumer 可调 API:方法、路径、请求 schema、响应、状态码与 curl 示例。所有路由经 create_app() 装配。汇总见 表。
| 方法 + 路径 | role | 成功码 |
|---|---|---|
POST /api/v1/runs | biz | 201 |
GET /api/v1/runs/{id} | biz / sre | 200 |
POST /api/v1/runs/{id}/cancel | biz | 200 |
POST /api/v1/runs/{id}/resume | biz | 200 |
GET /api/v1/runs/{id}/stream | biz | 200 |
GET /api/v1/hitl/{id} | hitl | 200 |
POST /api/v1/hitl/{id}/approve | hitl | 200 |
POST /api/v1/hitl/{id}/reject | hitl | 200 |
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 → cancelled(cancel_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
请求 ResumeRunRequest:last_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
请求 HitlVerdictRequest:approver_id / reason 均可选,兼容空 body。响应 HitlVerdictResponse:hitl_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 方法)
请求 SandboxValidateRequest:permit_id / run_id / step_id / capability 必填 + 可选 args_digest;响应 SandboxValidateResponse:decision(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 落裁决。请求体 HitlVerdictRequest(extra="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_id → 404,error_class SYSTEM_ERROR。GET /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 / data,data 含 step_id;hitl_id 仅当装配了 HitlGate 时由 gate.request 填入(占位 park 路径只有 step_id)。投递经 HMAC 签名验真,机制与重试见 第 12 章。
11.3 超时策略
park 等待审批超时(HitlTimeoutError)后由 sandbox 策略据 HitlTimeoutAction(StrEnum:reject / degrade / wait)决定走向。默认超时 timeout_sec = 1800 秒。未知 action(如 escalate)→ 显式 ValueError(枚举穷尽守护,不静默落 reject)。
reject(默认,最安全):视同审核拒绝,waiting_for_hitl→terminated_policy(HITL_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 起、无空洞),是断线重连的游标地基。
断线重连时 caller 应带 Last-Event-ID: <run_id>:<seq> 头。服务端 parse_last_event_id 解析后:run_id 与路径不一致 → 400(SCHEMA_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_urlhost 必须命中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_retries | RSM 终态 | 可暂停 | caller 处置 |
|---|---|---|---|---|---|
SCHEMA_VALIDATION_FAILED | FAIL | 0 | failed | 否 | 修正 plan/请求形状后重提 |
CAPABILITY_NOT_FOUND | FAIL | 0 | failed | 否 | 注册 Tool Contract 后重提 |
POLICY_DENIED | TERMINATE | 0 | terminated_policy | 否 | 不可重试;检查 permit/risk 边界 |
TOOL_CALL_FAILED | RETRY | 3 | failed | 是 | 必要时 /resume 续跑 |
MODEL_CALL_FAILED | RETRY | 3 | failed | 是 | 必要时 /resume 续跑 |
BUDGET_EXCEEDED | TERMINATE | 0 | terminated_budget | 否 | 查 partial_result,加预算重提 |
TIMEOUT | RETRY | 1 | terminated_timeout | 否 | 不可续;调高 max_duration_sec 重提 |
HITL_REJECTED | TERMINATE | 0 | terminated_policy | 否 | 不可重试;审批被拒 |
EVALUATION_FAILED | REPLAN | 0 | failed | 否 | Orchestrator 触发 replan;超 fallback 深度落 failed |
SYSTEM_ERROR | RETRY | 1 | failed | 是 | 必要时 /resume 续跑 |
执行侧裁决(RunRecoveryCoordinator.on_step_failure):策略为 RETRY 且可暂停且 retry 未耗尽 → 转 recoverable_paused 并在 run_event.payload_json 记 pause_reason / retry_hint / step_id / retries_remaining;否则直接入策略终态。retry 计数按 step_id 累计、跨 pause/resume 持续,唯有 step 真正成功才清零。超时 watchdog 仅在 executing / waiting_for_permit 活跃态有到 terminated_timeout 的合法边,非活跃态 no-op。
Budget 三档(evaluate_budget,阈值严格大于才触发):> soft_usd → DEGRADE_MODEL(沿 fallback 链降档);> hard_usd → STOP_NEW_STEPS(停发起新 LLM 调用);> absolute_usd → TERMINATE_RUN(RSM → terminated_budget,BUDGET_EXCEEDED)。只有 absolute 越线升级为错误。
13.3 幂等键
POST /api/v1/runs 应始终携带 uuid 形态的 Idempotency-Key header(防重复扣预算第一道闸)。同 key + 同 request_hash → 返回原 run_id(201);同 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_id(ResumeRunRequest),且仅在 recoverable_paused 生效——其余状态 → 409;无 checkpoint index 或无对应 last_step_id 的 checkpoint → 409。terminated_* 系列是硬终态,不可续。
14 运行生命周期参考 资料性
本章给出 run 状态的 caller 视角参考。
14.1 13 状态
运行时枚举 RunStatus 仅 13 个值。
| # | 状态 | 含义 | 终态? |
|---|---|---|---|
| 1 | created | POST /api/v1/runs 已建 run 记录,未启动 | 否 |
| 2 | planning | Planner 子层生成 plan_json | 否 |
| 3 | waiting_for_permit | plan 已校验通过,等待 Sandbox 签发 step permit | 否 |
| 4 | executing | Executor 子层在跑某个 step | 否 |
| 5 | waiting_for_hitl | 触发 L3/L4 action,等待人工审批 | 否 |
| 6 | evaluating | Evaluator 判断结果是否满足 stop_conditions | 否 |
| 7 | completed | goal 达成,result 已落库 | 是 |
| 8 | failed | 模型/工具/schema 失败,按 Error Taxonomy 归类 | 是 |
| 9 | cancelled | 调用方主动 /cancel | 是 |
| 10 | terminated_budget | Cost 超 absolute,强制终止 | 是 |
| 11 | terminated_timeout | 超 max_duration_sec | 是 |
| 12 | terminated_policy | forbidden_capability / risk_level / HITL reject 越界 | 是 |
| 13 | recoverable_paused | 系统级可重试失败,允许 /resume | 否(唯一可恢复暂态) |
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;/resume:recoverable_paused → executing。详见 13.4。
15 配置参考 规范性
全部 env 由 build_production_app 在装配期读取;认证 env 由 AuthKeyStore.from_env 读取。必填 env 缺失则实例 fail-fast。
15.1 环境变量
| 名称 | 必填 | 默认 / 约束 | 示例 |
|---|---|---|---|
LATTICE_LLM_MODEL_GROUP | 必填 | LiteLLM 模型组名;缺失 → fail-fast | finrep-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 |
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_DENIED → terminated_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 Forbidden | role 越权(如 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 Conflict | 同 Idempotency-Key 配不同 body;或 /resume 状态非 recoverable_paused / 无对应 checkpoint | 同 key 须配同 body;仅 recoverable_paused 可 resume 且须有效 last_step_id(13.3、13.4) |
POLICY_DENIED / terminated_policy | 无有效 permit、forbidden capability、risk_level 越界 | 不可重试;检查 sandbox policy 与 permit 绑定(16.3) |
BUDGET_EXCEEDED / terminated_budget | spent 超 absolute_usd | 不可续;查 partial_result,加预算后重提(16.2) |
run 卡 waiting_for_hitl 后落终态 | 审批未在 timeout_sec(默认 1800s)内裁决 | 对齐审批 SLA < 超时;按 on_timeout 策略(reject/degrade/wait)预期走向(11.3、11.4) |
| SSE 断流 / 重连丢事件 | 未带 Last-Event-ID;或对非终态 run 期望 live-tail | 重连带 Last-Event-ID: <run_id>:<seq>;非终态用 GET /runs/{id} 对账(12.1、16.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_callstep 类型 — NOT implemented(枚举缺值 → 解析期拒)。- 第 7 维 Cross-System Calls — NOT implemented(恒空 tuple → 自然 deny)。
- Decision Log cross-system 两字段 — NOT implemented(恒 null + 源头拒非空)。
lattice_runcross-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 稳定策略。