Files
lingma-proxy-compose/docs/architecture.zh-CN.md
2026-04-29 18:45:25 +08:00

425 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# lingma-ipc-proxy 架构文档
本文档描述 lingma-ipc-proxy 的系统架构、工作原理和核心流程。
---
## 1. 整体架构
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Claude Code │ │ OpenAI │ │ Cline │ │ Continue │ │
│ │ (Anthropic) │ │ SDK │ │ (OpenAI) │ │ (OpenAI) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼─────────────────┼─────────┘
│ │ │ │
└─────────────────┴────────┬────────┴─────────────────┘
│ HTTP
┌─────────────────────────────────────────────────────────────────────────┐
│ lingma-ipc-proxy │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ internal/httpapi │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │
│ │ │ /v1/models │ │/v1/chat/comp│ │ /v1/messages │ │ │
│ │ │ (GET) │ │ (POST) │ │ (POST) │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │ │
│ │ └─────────────────┴──────────┬──────────┘ │ │
│ │ │ normalizeRequest │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ internal/service │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌────────────────────────┐ │ │ │
│ │ │ │ Session │ │ Prompt │ │ Stream/Event │ │ │ │
│ │ │ │ Manager │ │ Builder │ │ Handler │ │ │ │
│ │ │ └────┬─────┘ └────┬─────┘ └───────────┬────────────┘ │ │ │
│ │ │ └─────────────┴──────────┬─────────┘ │ │ │
│ │ │ │ buildLingmaPrompt │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌─────────────────────────────────────────────────┐ │ │ │
│ │ │ │ internal/lingmaipc │ │ │ │
│ │ │ │ ┌──────────────┐ ┌──────────────────────────┐ │ │ │ │
│ │ │ │ │ WebSocket │ │ Named Pipe (Win) │ │ │ │ │
│ │ │ │ │ Transport │ │ Transport │ │ │ │ │
│ │ │ │ └──────┬───────┘ └───────────┬──────────────┘ │ │ │ │
│ │ │ └─────────┼──────────────────────┼────────────────┘ │ │ │
│ │ └────────────┼──────────────────────┼────────────────────┘ │ │
│ │ │ │ │ │
│ │ ┌────────────┼──────────────────────┼────────────────────┐ │ │
│ │ │ ▼ ▼ │ │ │
│ │ │ ┌─────────────────────────────────────────────────┐ │ │ │
│ │ │ │ internal/toolemulation │ │ │ │
│ │ │ │ ┌──────────────┐ ┌──────────────────────────┐ │ │ │ │
│ │ │ │ │InjectTooling │ │ ParseActionBlocks │ │ │ │ │
│ │ │ │ │ (Prompt) │ │ (Response) │ │ │ │ │
│ │ │ │ └──────────────┘ └──────────────────────────┘ │ │ │ │
│ │ │ └─────────────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ WebSocket / Named Pipe
┌─────────────────────────────────────────────────────────────────────────┐
│ Lingma 后端进程 │
│ (VS Code 插件的本地 IPC 服务) │
│ ws://127.0.0.1:8899/ws │
└─────────────────────────────────────────────────────────────────────────┘
│ HTTP API
┌─────────────────────────────────────────────────────────────────────────┐
│ 云端模型服务 │
│ (Kimi-K2.6 / Qwen3-Max / MiniMax-M2.7 等) │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 2. 模块职责
### 2.1 internal/httpapi
HTTP API 适配层,负责将外部请求转换为内部 `service.ChatRequest`
| 端点 | 协议 | 功能 |
|------|------|------|
| `GET /v1/models` | OpenAI | 返回可用模型列表 |
| `POST /v1/chat/completions` | OpenAI | 聊天补全(流式/非流式) |
| `POST /v1/messages` | Anthropic | 消息接口(流式/非流式) |
**核心函数:**
- `handleOpenAIChatCompletions()` - 处理 OpenAI 格式请求
- `handleAnthropicMessages()` - 处理 Anthropic 格式请求
- `normalizeOpenAIRequest()` / `normalizeAnthropicRequest()` - 归一化请求
**关键设计:**
- 支持 CORS 预检请求 (`OPTIONS`)
- 单请求并发控制 (`tryAcquire()` / `release()`)
- 流式响应通过 `http.Flusher` 实现 SSE
### 2.2 internal/service
业务逻辑层,负责会话管理和 Prompt 构建。
**核心结构:**
```go
type Service struct {
cfg Config
client *lingmaipc.Client
stickySessionID string
stickyModelID string
}
```
**核心函数:**
- `Generate()` - 非流式生成
- `GenerateStream()` - 流式生成(返回 `events` + `done` channel
- `buildLingmaPrompt()` - 构建 Lingma 原生 Prompt
- `runPromptLocked()` - 发送 `session/prompt` RPC 并监听 `session/update` 通知
**会话模式:**
| 模式 | 行为 |
|------|------|
| `reuse` | 复用 sticky session多轮对话保持上下文 |
| `fresh` | 每个请求新建临时 session完成后删除 |
| `auto` | 单轮请求复用;带 system/history 的请求用 fresh |
### 2.3 internal/lingmaipc
IPC 通信层,负责与 Lingma 后端进程建立连接。
**传输方式:**
| 平台 | 默认传输 | 说明 |
|------|----------|------|
| Windows | Named Pipe | `\\.\pipe\lingma-*` |
| macOS/Linux | WebSocket | `ws://127.0.0.1:{port}/ws` |
**连接发现:**
- 读取 VS Code 插件缓存:`~/.config/Lingma/SharedClientCache/.info.json`
- 获取 WebSocket 端口号
- 自动重连机制
**RPC 协议:**
- `session/new` - 创建会话
- `session/prompt` - 发送用户消息
- `session/update` - 接收流式响应通知
- `session/set_model` - 切换模型
- `chat/deleteSessionById` - 删除会话
### 2.4 internal/toolemulation
Tool 调用模拟层,将标准 `tools` 协议转换为 Prompt 层契约。
**核心流程:**
```
Client tools ──→ ExtractAnthropicTools() ──→ []Tool
InjectTooling() ──→ System Prompt + Tool 说明
模型输出 action block
ParseActionBlocks() ──→ []ToolCall
编码为 Anthropic tool_use / OpenAI tool_calls
```
**Prompt 契约格式:**
```
```json action
{"tool":"NAME","parameters":{"key":"value"}}
```
```
**支持格式:**
- `{"tool":"X","parameters":{}}` ✅ 标准格式
- `{"tool":"X","arguments":{}}` ✅ 兼容格式
- `{"tool":"X","input":{}}` ✅ 兼容格式
- `{"tool":"X","arg1":"val"}` ✅ 顶层参数(部分模型)
---
## 3. 核心流程
### 3.1 普通聊天请求流程
```mermaid
sequenceDiagram
participant C as Client
participant H as HTTP API
participant S as Service
participant L as Lingma IPC
participant B as Lingma Backend
C->>H: POST /v1/messages
H->>H: normalizeAnthropicRequest()
H->>S: GenerateStream(req)
S->>S: ensureConnected()
S->>S: resolveSession()
S->>S: buildLingmaPrompt()
S->>L: Send("session/prompt", params)
L->>B: WebSocket RPC
B->>L: session/update (agent_message_chunk)
loop 流式响应
L->>S: notification (chunk)
S->>H: events <- StreamEvent{Delta}
H->>C: SSE: content_block_delta
end
B->>L: session/update (chat_finish)
L->>S: notification (finish)
S->>H: done <- StreamResult
H->>C: SSE: message_stop
```
### 3.2 Tool 调用流程
```mermaid
sequenceDiagram
participant C as Client
participant H as HTTP API
participant T as ToolEmulation
participant S as Service
participant L as Lingma IPC
C->>H: POST /v1/messages (with tools)
H->>T: ExtractAnthropicTools()
H->>S: GenerateStream(req)
S->>T: InjectTooling(system, tools)
S->>L: session/prompt (with tool prompt)
L->>S: response (with action blocks)
S->>T: ParseActionBlocks(text)
T->>S: []ToolCall
S->>H: ChatResult{Text, ToolCalls}
H->>C: SSE: tool_use blocks
C->>H: POST /v1/messages (tool_result)
H->>T: ActionOutputPrompt(toolUseID, content)
H->>S: GenerateStream(req)
S->>L: session/prompt (with tool result)
L->>S: response
S->>H: ChatResult
H->>C: SSE: final response
```
### 3.3 图片传输流程
```mermaid
sequenceDiagram
participant C as Client
participant H as HTTP API
participant S as Service
participant L as Lingma IPC
C->>H: POST /v1/messages (with image)
H->>H: extractAnthropicImages()
H->>S: ChatRequest{Images: [...]}
S->>S: runPromptLocked()
Note over S: 1. 保存 base64 到 /tmp/lingma-img-*.ext
Note over S: 2. 构建 URI: lingma:///agent/file?path=...
S->>L: session/prompt
Note over L: prompt: [{type:"text"}, {type:"image", mimeType, uri, data}]
L->>S: response (model sees image)
S->>H: ChatResult
H->>C: SSE response
```
### 3.4 流式输出 SSE 事件序列
**Anthropic 格式(流式):**
```
event: message_start
data: {"type":"message_start","message":{...}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"你"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"好"}}
... (更多 delta)
event: content_block_stop
data: {"type":"content_block_stop","index":0}
[如有 tool_calls]
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"...","name":"Bash","input":{"command":"ls /"}}}
event: content_block_stop
data: {"type":"content_block_stop","index":1}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":5}}
event: message_stop
data: {"type":"message_stop"}
```
---
## 4. 关键技术决策
### 4.1 为什么使用 Tool Emulation 而非原生 Tool Calling
Lingma 后端模型Kimi、Qwen 等)不原生支持 OpenAI/Anthropic 的 `tools` 协议。因此代理层需要将工具定义注入到 Prompt 中,通过结构化文本输出模拟工具调用。
**优点:**
- 不依赖上游模型能力
- 兼容任何纯聊天模型
- 可精确控制 Prompt 格式
**缺点:**
- 模型需要学习特定格式
- 解析可能有容错问题
- 增加了 Prompt 长度
### 4.2 为什么使用 WebSocket/Named Pipe 而非 HTTP
Lingma 插件使用本地 IPC 与后端通信,优势:
- 低延迟(本地通信)
- 双向实时通知session/update
- 认证信息由插件管理,代理无需处理
### 4.3 图片传输的双保险策略
```
Prompt 数组 (Lingma 原生格式):
[
{"type":"text","text":"..."},
{"type":"image","mimeType":"image/png","uri":"lingma:///agent/file?path=...","data":"base64..."}
]
```
- `uri`: Lingma 后端必须验证的本地文件路径
- `data`: base64 编码的图像数据(备用)
- `mimeType`: 图像类型标识
### 4.4 单请求并发控制
Lingma IPC 一次只能处理一个请求,因此代理使用 `tryAcquire()` 机制:
```go
if !s.tryAcquire() {
writeAnthropicError(w, 429, "rate_limit_error",
"Lingma IPC proxy handles one request at a time.")
return
}
defer s.release()
```
---
## 5. 配置说明
### 5.1 配置文件结构
```json
{
"host": "127.0.0.1",
"port": 8095,
"transport": "websocket",
"mode": "agent",
"shell_type": "zsh",
"session_mode": "auto",
"timeout": 120,
"cwd": "/Users/tiancheng"
}
```
### 5.2 配置项说明
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `host` | string | `127.0.0.1` | HTTP 监听地址 |
| `port` | int | `8095` | HTTP 监听端口 |
| `transport` | string | `auto` | IPC 传输方式:`auto`/`pipe`/`websocket` |
| `mode` | string | `chat` | 模式:`chat`/`agent` |
| `shell_type` | string | `powershell` | 终端类型 |
| `session_mode` | string | `auto` | 会话模式:`reuse`/`fresh`/`auto` |
| `timeout` | int | `120` | 请求超时(秒) |
| `cwd` | string | `""` | 工作目录(传给 Lingma 后端) |
---
## 6. 扩展点
### 6.1 添加新模型
`service.go` 的模型映射中添加:
```go
func (s *Service) resolveInternalModelID(model string) string {
switch strings.ToLower(strings.TrimSpace(model)) {
case "kimi-k2.6":
return "kimi2.6"
case "qwen3-max":
return "qwen3max"
// 添加新模型映射
default:
return ""
}
}
```
### 6.2 添加新 Tool 格式支持
`toolemulation.go``parseToolCallJSON()` 中扩展参数解析逻辑。
### 6.3 添加新 API 端点
`httpapi/server.go``NewServer()` 中注册新路由。
---
*文档版本: 2025-04-25*
*对应代码版本: 当前 master*