736a1b6ac6
按 Router used_quota 累计值与首次基线结算泥点 新增原子 checkpoint 事务及 llm_router_consume 钱包流水 同步额度查询校验、前端展示、生成绑定和技术文档
platform-llm 平台适配 crate
更新:2026-08-06
1. crate 职责
platform-llm 是 Rust 工作区里的大模型平台适配 crate,当前已经落地以下能力:
- 统一 Ark / DashScope / Anthropic / 其他兼容网关的文本模型配置结构
- 统一 OpenAI Chat Completions、OpenAI Responses 和 Anthropic Messages 的文本 / 原生 function tool 请求、非流式响应与 SSE 流式解析
- 统一超时、连接失败、上游错误、空响应与重试策略
- 为后续
module-ai、module-story、module-npc、module-custom-world提供可直接复用的基础 client
2. 当前能力边界
当前实现只覆盖“文本 run”主链,不提前混入媒体生成和业务编排:
- 对外抽象固定为
LlmRunRequest/LlmRunResponse,不再保留旧LlmTextRequest/LlmTextResponse类型。 OpenAiChat、OpenAiResponses与Anthropic三类 API kind 都支持 JSON 请求、非流式响应和 SSE 流式响应;默认 API kind 仍为OpenAiResponses。- 三类协议都使用统一的
function_tools/tool_choice输入和LlmRunResponse.tool_calls输出。Anthropic 请求使用顶层tools[].input_schema与对象形态tool_choice;Anthropic URL 默认在 base URL 后拼/v1/messages,如果 base URL 已以/v1结尾则只拼/messages。 - Anthropic 当前仍不支持
web_search、图片内容和纯 system 消息;至少需要一条非 system 文本消息。角色动画、图片、视频、资产轮询仍留在其他平台适配和业务模块任务里。 - 流式
on_delta只发送文本增量与完成原因;工具调用增量在 crate 内按 slot 聚合,完整调用只从最终LlmRunResponse.tool_calls读取。上下文管理、后台执行和业务状态不写回本 crate。 - 支持按 provider 打标签,但不把业务 prompt、SSE 转发和模块状态写回本 crate。
DashScope当前只通过“调用方显式提供兼容文本网关 base url”的方式接入,不复用图像 API。- 角色动画、图片、视频、资产轮询仍留在后续
platform-llm/platform-oss/ 业务模块任务里另行实现。
3. 三协议工具支持矩阵
| API kind | 请求工具形态 | tool_choice |
非流式工具响应 | 流式工具聚合 |
|---|---|---|---|---|
OpenAiChat |
tools[].type=function,函数内为 name / description / parameters / strict |
"auto" / "required" |
choices[0].message.tool_calls |
delta.tool_calls[].index;首片提供 id/name,后续拼接 arguments |
OpenAiResponses |
tools[].type=function,函数内为 name / description / parameters / strict |
"auto" / "required" |
output[].type=function_call |
output_index;output_item.added 提供身份,function_call_arguments.delta 拼接,.done 覆盖完整参数 |
Anthropic |
顶层 tools[] 为 name / description / input_schema,没有 function 包装层;只有 endpoint/model 配置显式声明支持且 schema / 请求复杂度满足 Anthropic 当前边界时才发送 strict: true。strict 传输 schema 会剥离不支持的约束,调用方原 schema 保持不变;最后一项带 ephemeral cache breakpoint |
{ "type": "auto" } / { "type": "any" };Required 映射为 any |
content[].type=tool_use,input 序列化为 arguments |
content block index;content_block_start 提供身份,input_json_delta 拼接参数;message_start/message_delta 合并 cache/input/output usage |
Responses 如果只发送 response.completed 或 response.incomplete,解析器会从其中的 response.output[] 恢复 function_call;恢复时使用 output 数组下标作为 slot。response.incomplete 表示上游没有完成本轮生成:其中的工具调用即使参数是完整 JSON 也返回 Deserialize,纯正文则保留为可用的降级结果。三种协议的并行工具调用只在平台层做 slot 聚合,不代表工具会在平台层并发执行。
4. 流式与参数契约
LlmRunRequest.max_output_tokens是协议中立的生成预算,包含可见输出与 Provider 可能使用的隐藏 reasoning token,不包含输入 token,也不保证可见正文长度。Responses 映射为max_output_tokens,Anthropic 映射为max_tokens;Chat 由LlmConfig.openai_chat_token_budget_field显式映射为当前max_completion_tokens或兼容网关旧字段max_tokens,每次只发送一个。默认保留 legacy,已验证支持新字段的 endpoint 必须显式 opt-in;禁止按模型名猜测或收到400后自动重放。LlmStreamDelta只包含accumulated_text、delta_text和finish_reason,工具调用不会进入on_delta;纯工具响应允许text为空。- 工具片段按协议索引聚合:Chat 使用
delta.tool_calls[].index,Responses 使用output_index,Anthropic 使用 content blockindex。Responses 的.done、response.completed和response.incomplete中的完整 arguments 是权威值,可以覆盖之前的分片拼接。 - 流结束固化工具调用时,缺少 id 或函数名返回
Deserialize;空参数默认保存为{};非空参数必须能反序列化为完整 JSON,截断或半截 JSON 不会交给业务层。这里是 JSON 语法完整性检查,不是针对parameters的 JSON Schema 业务校验。 - 非流式工具调用采用不同的参数边界:缺失或空白
arguments统一归一为{};Chat / Responses 的非空畸形arguments不在平台层做 JSON 校验、修复或静默丢弃,而是保留参数内容(仅按统一归一策略去除首尾空白),连同 call id 和函数名交给调用方的 repair 循环。Anthropictool_use.input缺失时同样按{}归一;调用方不能把非流式参数自动假定为统一 schema 校验通过。
5. 错误边界
Deserialize:上游 JSON、SSE 或 UTF-8 无法解析,Chat 非流式缺少choices[0],流式工具调用缺少 id / name,或流式工具参数不是完整 JSON。StreamUnavailable:仅用于流式响应已声明tool_use/tool_calls,但一个工具 slot 都没有聚合出来的协议兼容失败;调用方可以据此回退一次非流式请求,不能把已收到的解说文本当成最终回复。EmptyResponse:最终文本为空且工具调用也为空。只有文本为空但存在有效工具调用时,响应才是合法的纯工具响应。- 流尾部出现
Timeout、Connectivity、Transport或Deserialize时,只有已形成非空文本或至少一个工具调用、观察到协议完成信号、已有完成原因且工具参数完整的响应才会保留;纯工具响应即使text为空也可以保留。工具参数半截或既没有文本也没有工具调用时继续返回错误。
6. 核心导出
当前对外导出以下公共类型:
LlmProviderLlmConfigOpenAiChatTokenBudgetFieldLlmMessageRoleLlmMessageLlmRunRequestLlmApiKindLlmStreamDeltaLlmFunctionToolLlmToolChoiceLlmToolCallLlmRunResponseLlmTokenUsageLlmClientLlmError
7. 设计文档
当前文档入口
当前长期工程口径已融合到:
- ../../../docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md
- ../../../docs/【玩法创作】平台入口与玩法链路-2026-05-15.md
- ../../../docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
旧阶段设计文档不再作为实现依据。
8. 边界约束
platform-llm只承接模型平台适配,不承接业务模块状态真相与业务规则。- 业务模块只能依赖这里的统一 client / DTO / 错误模型,不能再把上游请求细节散落回各 crate。
api-server后续如果需要做 REST/SSE façade,只允许在协议层调用platform-llm,不能复制一份私有实现。
9. 验收证据边界
cargo test -p platform-llm的确定性用例把 checked-in SSE fixture 交给 parser,验证归一后的文本、工具调用、slot 聚合、Responses 仅有 completed / incomplete 终态事件时的恢复、参数 JSON 完整性和错误边界;同时包含tests/live_stream_tool_calls.rs中不依赖外部 Provider 的parse_api_kind本地解析测试。fixture 可以来源于真实端点抓包,但测试不保存原始 SSE,也不逐事件与端点报文比较,因此不能证明抓包转录无偏差。tests/live_stream_tool_calls.rs同时包含默认执行的parse_api_kind确定性测试,以及 1 个默认#[ignore]的真实端点工具调用 smoke。PLATFORM_LLM_LIVE_API_KIND支持openai_responses、openai_chat和anthropic,未设置或空白时默认openai_responses,未知非空值会直接使验收失败。真实 smoke 只验证最终归一结果中的工具名、id 和完整参数 JSON;文本增量字符数仅用于打印观测,工具调用不进入on_delta,也没有原始 SSE 录制或逐事件对比能力。- 因此验收应分别称为“固定 SSE fixture parser 覆盖及本地解析单元测试”和“真实端点归一工具调用 smoke”,不能把后者描述为原始 SSE fidelity 或转录一致性证明。