11 KiB
TauriTavern Agent LLM Gateway
本文档定义 Agent Runtime 与现有 LLM 调用链之间的边界。
结论:Agent 内部使用 provider-agnostic canonical model IR;provider 边界继续复用现有 ChatCompletionService;provider-native metadata 作为 opaque state 保留和回放,不成为 Agent 业务语义。
1. Ground of Truth
当前后端 LLM 事实:
- Provider source 定义在
ChatCompletionSource。见src-tauri/src/domain/repositories/chat_completion_repository.rs。 - Payload builder 由
application/services/chat_completion_service/payload/mod.rs按 provider 分发。 ChatCompletionService负责 source 解析、iOS policy、endpoint override、feature policy、settings、secret、prompt caching、payload build、generate/generate_stream/cancel。- LLM API 日志依赖
LoggingChatCompletionRepositorywrapper。 - Custom Native API 文档强调 tool call id 透明性与 native metadata 保真。见
docs/CurrentState/NativeApiFormats.md。
Agent 不维护第二套 provider registry,不直接调用 HttpChatCompletionRepository,不自行读取 secret,不绕过现有 policy/logging/prompt cache。
2. 当前调用链
当前调用链:
AgentRuntimeService
-> AgentModelGateway.generate_with_cancel(AgentModelRequest)
-> encode_chat_completion_request()
-> ChatCompletionService.generate_exchange_with_cancel(ChatCompletionGenerateRequestDto)
-> payload builder
-> ChatCompletionRepository
-> LoggingChatCompletionRepository
-> HttpChatCompletionRepository
-> decode_chat_completion_response()
-> AgentModelResponse
当前只实现非 streaming Agent tool loop。ChatCompletionStreamEvent::Chunk 仍只是 provider SSE bridge,不是 Agent timeline event。
当前代码布局:
agent_model_gateway/mod.rs:AgentModelGatewaytrait、AgentModelExchange、ChatCompletionAgentModelGatewaywrapper。encode.rs/decode.rs:canonicalAgentModelRequest/AgentModelResponse与 normalized ChatCompletion exchange 的双向转换。format.rs:根据 request payload 解析ChatCompletionSource与ChatCompletionProviderFormat。schema.rs:按 provider adapter 清洗 tool JSON Schema。provider_state.rs:写入内部_tauritavern_provider_state,并生成下一轮 run-scoped continuation。providers/*:OpenAI-compatible、OpenAI Responses、Claude、Gemini / Gemini Interactions 的 provider-specific 规则。
3. Canonical Model IR
Agent runtime 只消费 canonical IR:
AgentModelRequest {
payload,
messages,
tools,
tool_choice,
provider_state,
}
AgentModelResponse {
message,
tool_calls,
text,
provider_metadata,
raw_response,
}
AgentModelContentPart 当前支持:
Text
Reasoning
ToolCall
ToolResult
Media
ResourceRef
Native
设计原则:
Text/ToolCall/ToolResult是可迁移语义。Reasoning保存 provider 返回的可见/摘要化 reasoning;没有公开 reasoning 文本的 provider 可以为空。Native保存 provider-private blocks,不解析、不清洗、不改写。payload仍承载现有 ChatCompletionService 需要的 source/model/settings 字段。provider_state是当前已落地的 run-scoped continuation state,详见docs/CurrentState/AgentProviderState.md。
4. Provider Format
当前 gateway 根据 request payload 判断 provider format:
- OpenAI-compatible
- OpenAI Responses
- Claude Messages
- Gemini / Makersuite / Vertex AI
- Gemini Interactions
转换方向:
canonical AgentModelRequest
-> OpenAI-compatible messages/tools shape
-> existing provider payload builder
-> provider native payload
响应方向:
provider native response
-> existing normalizer / repository
-> OpenAI-compatible normalized response with message.native
-> AgentModelResponse
这个过渡结构保留了现有 ChatCompletionService 投资,同时让 Agent runtime 摆脱 OpenAI-shaped raw JSON。
5. Native Metadata Contract
Provider native metadata 必须当作 opaque continuation state。
已落地保留:
| Provider format | 保留内容 | 用途 |
|---|---|---|
| Claude Messages | assistant content blocks,包括 thinking、tool_use、signature |
同 provider 续接时原样回放 |
| Gemini | response content.parts 与 thoughtSignature |
同 provider 续接时原样回放 |
| Gemini Interactions | raw outputs |
同 provider 续接时原样回放 |
| OpenAI Responses | raw output items 与 responseId |
function call output / reasoning continuation |
禁止:
- 重写 provider 返回的 tool call id。
- 缺失 id 时自动生成 fallback id。
- 丢弃 Claude / Gemini / Responses 的 native tool metadata。
- 把 Gemini thought signature 或 Claude thinking signature 压成普通文本。
- 把 OpenAI encrypted reasoning 当作可解释内容。
同 provider continuation 所需 native state 丢失时,应 fail-fast 或测试失败。跨 provider switch 只能迁移 canonical 语义,不能伪装 provider-private state 已迁移。
UI 展示契约:
- Provider normalizer 只把可见文本或 provider 明确给出的摘要提升为 normalized
message.reasoning_content。 - Claude / Gemini 的 signature、Gemini thoughtSignature、OpenAI encrypted reasoning 与 Responses continuation item 仍留在
Native/provider_state,不进入显示 DTO。 - Agent timeline 读取模型回合详情时使用
api.agent.readModelTurn({ runId, round });该 DTO 是AgentModelResponse的白名单投影,不是 raw LLM API log。
5.1 Provider State Contract
provider_state 由 Agent runtime 初始化,由 AgentModelGateway 在每轮模型调用后更新。
当前契约:
- 初始状态只包含
sessionId = runId。 - gateway 会把该状态以内部字段
_tauritavern_provider_state写入 ChatCompletion payload。 - LLM API log 与真正发往 provider 的 payload 都必须剥离
_tauritavern_provider_state。 - 每轮成功后,gateway 返回
sessionId、chatCompletionSource、providerFormat、messageCursor、lastResponseId。 - OpenAI Responses 会额外返回
transport: "responses_websocket"与previousResponseId。 - OpenAI Responses 续接时,gateway 根据
messageCursor只发送新消息,并注入previous_response_id。 - Claude / Gemini / OpenAI Responses / Gemini Interactions 会记录
nativeContinuation;tool call 存在但 native metadata 丢失时 fail-fast。
OpenAI Responses Agent 路径使用 persistent WebSocket session。session 由 sessionId 复用,建连复用 HttpClientPool 的 ChatCompletion WebSocket profile;run 完成、失败或取消后异步关闭,关闭动作不得阻塞 run 最终状态落盘。
6. Tool Schema
Tool registry 只产 canonical AgentToolSpec。
Gateway/payload adapter 在发送前渲染 provider-facing schema:
AgentToolSpec
-> provider-specific schema sanitizer
-> OpenAI-compatible function tool shape
-> existing provider payload builder maps to native shape
当前 sanitizer 策略:
- OpenAI-compatible / OpenAI Responses:保留完整 canonical schema。
- Claude Messages:移除轻量元字段,如
$schema、$id。 - Gemini / Gemini Interactions:移除当前不兼容的 JSON Schema 关键字,如
$schema、$defs、additionalProperties、组合 schema、const、default、title等。
Canonical schema 不应为某个 provider 被永久降级。
7. Tool Call Contract
Tool call id 必须是不透明字符串:
Provider tool call id
-> AgentToolCall.id
-> AgentToolResult.call_id
-> provider tool result id
缺失 id 是 model.invalid_tool_call,不得静默生成 tool_call_{index}。
Tool result 当前会编码为 JSON 字符串,包含:
{
"ok": true,
"content": "...",
"structured": {},
"errorCode": null,
"resourceRefs": []
}
前 5 轮 workspace.write_file / workspace.apply_patch 成功结果会被 runtime hydrate:下一轮模型看到完整文件内容,journal/workspace 真相不变。
8. Policy
Gateway 必须遵守:
- iOS policy source allowlist。
- iOS policy endpoint override。
- web search/request image capability。
- settings 中的 provider 配置。
- secret 暴露策略。
- prompt caching opt-in/opt-out。
- model/profile tool support 声明。
Policy denied 必须 fail-fast 并写 journal,不允许静默降级为另一个 provider、另一个模型或空工具集。
9. Prompt Cache
Prompt cache 是 provider 能力,不是 Agent 自己随意拼 header。
Agent 可以在 canonical request 中表达 cache 意图,但具体是否启用、如何写 header、是否需要 beta header,必须由现有 provider logic 或正式 adapter 决定。
Custom Claude Messages 的 header 兼容策略尤其不能被 Agent 硬编码覆盖。
10. Streaming 边界
未来需要 ModelDelta,但当前 Agent tool loop 仍是非 streaming。
两种事件流不能混:
Provider stream
来自 ChatCompletionService/Repository 的 SSE data 或 normalized chunk。
Agent run event stream
AgentRunEvent:model_delta、tool_call_requested、checkpoint_created 等语义事件。
Agent UI 必须订阅 api.agent.subscribe(runId, handler) 的 run event,不直接消费 provider raw stream。
11. Error Contract
Gateway 错误建议:
model.provider_denied
model.upstream_invalid_response
model.unsupported_tool_call
model.invalid_tool_call
model.request_build_failed
model.request_failed
model.stream_failed
model.cancelled
model.native_metadata_lost
Agent runtime 会按 Profile run.modelRetry 重试 429 rate limit 与 transient transport/provider availability 错误。model.upstream_invalid_response 专用于上游响应体不可读或不是合法 provider JSON 的暂态异常,可自动重试;payload build、policy denied、provider 明确拒绝、response schema decode、tool call id、native metadata 等本地或 provider 明确契约错误不重试。
model.native_metadata_lost 不应静默降级。
12. Tests
当前已覆盖:
- canonical response decode。
- missing tool call id fail-fast。
- Gemini schema sanitizer。
- Agent loop 通过 canonical response 推进。
- 前 5 轮 workspace write result hydration。
- OpenAI Responses native output items 回放。
- OpenAI Responses
provider_state.previousResponseId注入与messageCursor增量输入。 - Claude / Gemini native continuation 计数与缺失 fail-fast。
- same-provider native metadata loss fail-fast。
- cross-provider switch 不迁移 provider-private state。
- LLM API log 剥离
_tauritavern_provider_state。 - Claude native content blocks 回放。
- normalizer 保留 Claude/Gemini/OpenAI Responses/Gemini Interactions native metadata。
- Agent model retry 只覆盖 rate limit / transient transport-provider 错误,非瞬时契约错误不重试。
后续最低补齐:
- prompt cache 与 provider-native state 共存。
- stream
ModelDelta不泄漏 raw provider event。 - persistent provider session close 不阻塞 Agent 最终状态。