AtomCode AI 通信协议端到端分析报告
端到端追查 — Daemon POST /chat 到 llm-api 的完整请求链路
日期: 2026-07-02 | 来源:strings atomcode.bin.bak挖掘 + 运行时抓包验证
覆盖: 请求转换、签名协议、流式管道、Token 预算管理
1. 通信链路总览
┌──────────┐ SSE/JSON ┌──────────┐ OpenAI API ┌──────────────────┐
│ Client │ ──────────────▶ │ Daemon │ ───────────▶ │ llm-api.atomgit │
│ (curl/ │ ◀────────────── │ :13456 │ ◀─────────── │ .com/v1 │
│ WebUI) │ SSE events │ /chat │ SSE/JSON │ (OpenAI 兼容) │
└──────────┘ └──────────┘ └──────────────────┘关键链点:
| 段 | 协议 | 格式 | 传输 |
|---|---|---|---|
| Client → Daemon | HTTP POST | ChatRequest (5 字段) | localhost:13456 |
| Daemon → llm-api | HTTP POST | OpenAI chat/completions | llm-api.atomgit.com |
| llm-api → Daemon | SSE | OpenAI Chunk 或完整 JSON | HTTPS |
| Daemon → Client | SSE | AtomCode ChatEvent (8+ 类型) | HTTP chunked |
2. Daemon ChatRequest (5 字段)
rust
struct ChatRequest {
// 5 个元素 — 运行时从 /chat POST body 验证
session_id: Option<String>, // 会话 ID (续期使用)
message: String, // 用户消息
provider: Option<String>, // 提供商名 (可选,默认使用 default_provider)
stream: Option<bool>, // ⚠️ 运行时验证: 始终被忽略,始终返回 SSE
system: Option<String>, // 系统提示词 (可选)
}2.1 客户端实际请求示例
json
POST /chat
Content-Type: application/json
Accept: text/event-stream
{
"session_id": "ea21aa73-52cd-426c-84d4-7ae9699ab955",
"message": "在当前目录创建一个文件",
"provider": "AtomGit-deepseek-v4-flash",
"stream": true
}3. Daemon → llm-api 的请求转换
3.1 请求转换流程
Daemon 收到 /chat POST
│
├── 1. 从 auth token 派生出 LLM API key
│ (OAuth access_token → CodingPlan API key, 在 daemon 进程内存中)
│
├── 2. 组装 OpenAI 格式请求体:
│ {
│ "model": "deepseek-v4-flash",
│ "messages": [
│ {"role": "system", "content": "<system prompt>"},
│ {"role": "user", "content": "<message>"}
│ ],
│ "stream": true,
│ ...
│ }
│
├── 3. 添加 Authorization header:
│ Authorization: Bearer <derived_api_key>
│
├── 4. POST https://llm-api.atomgit.com/v1/chat/completions
│ (或 ATOMCODE_CODINGPLAN_LLM_BASE_URL 指定的地址)
│
├── 5. 添加签名时间戳 (防重放):
│ - 使用 ECDSA P256 或 Ed25519 签名当前时间戳
│ - 服务器验证签名 + 时间窗口
│
└── 6. 接收响应流 → 逐事件转换为 ChatEvent SSE3.2 LLM API 端点
bash
strings atomcode.bin.bak | grep -E "llm-api|/v1/chat|/v1/message|api.gitcode" | grep -v "^crates/\|\.rs$\|target" | sort -u端点列表:
| 端点 | 用途 | 协议 | 优先级 |
|---|---|---|---|
https://llm-api.atomgit.com/v1 | 主 LLM API | OpenAI 兼容 | 最高 |
https://pre-llm-api-cce.atomgit.com/v1 | 灰度测试环境 | OpenAI 兼容 | ATOMCODE_* 环境变量 |
https://api.gitcode.com/api/v5 | CodingPlan API | 自定义 | — |
https://acs.atomgit.com/api/v1/events | 遥测上报 | 自定义 | — |
关键发现:
Either set an explicit api_key in your config.toml, or use the AtomGit OAuth flow
by setting base_url to https://pre-llm-api-cce.atomgit.com/v1Daemon 同时也支持直接配置自定义提供商来绕过 CodingPlan,但需要显式的 api_key。
3.3 请求签名协议
从 CODINGPLAN_CRYPTO_ANALYSIS.md 和二进制字符串推断:
每个 API 请求:
│
├── 1. 取当前 Unix 时间戳 (秒)
│
├── 2. 使用 Ed25519 或 ECDSA P256 私钥签名
│ (私钥在 daemon 进程内存中,从 OAuth token 派生)
│
├── 3. 将签名附加到请求头:
│ X-Timestamp: <unix_seconds>
│ X-Signature: <base64_signature>
│
└── 4. 服务器验证:
├── 签名是否有效 (公钥内置于二进制)
├── 时间戳是否在 ±5 分钟窗口内
└── 该签名是否已被使用 (重放检测)4. LLM API 响应到 ChatEvent 的转换
4.1 转换管道
llm-api SSE 流 (OpenAI 格式)
│
│ data: {"id":"...","object":"chat.completion.chunk",
│ "choices":[{"delta":{"content":"你好"},"index":0}]}
│
▼
Provider 适配器 (provider/openai.rs)
│
├── 解析 ChatChunk { id, choices: [ChunkChoice { delta: ChunkDelta }] }
│
├── 文本 → {"type":"text","content":"你好"}
├── 工具调用 → {"type":"tool_start","id":"call_xxx","name":"...","arguments":"..."}
└── 完成 → 累积 tokens 后发送 token 统计
│
▼
Agent 引擎 (TurnRunner)
│
├── 累积工具输出
├── 权限检查
└── 循环控制
│
▼
Daemon SSE 流 (AtomCode ChatEvent)
│
├── {"type":"reasoning","content":"..."}
├── {"type":"text","content":"..."}
├── {"type":"tool_start","id":"...","name":"...","arguments":"..."}
├── {"type":"tool_result","id":"...","name":"...","output":"...","success":true,"duration_ms":3}
├── {"type":"tokens","prompt":N,"completion":N,"total":N}
├── {"type":"error","content":"..."}
└── {"type":"done","tokens":N,"tool_calls":N,"session_id":"..."}4.2 三种提供商适配器对比
| 维度 | OpenAI 兼容 | Claude (Anthropic) | Ollama |
|---|---|---|---|
| 请求端点 | /v1/chat/completions | /v1/messages | /api/chat |
| 流式格式 | SSE data: {...} | SSE event: content_block_delta | SSE JSON |
| 流结束 | data: [DONE] | message_stop 事件 | done: true |
| 工具调用 | delta.tool_calls[] 累加 | tool_use content block | tool_calls[{function}] |
| Token 统计 | usage 字段 | message_delta 事件 | 无(自行计算) |
| 推理内容 | reasoning_content | thinking_delta | 不单独支持 |
4.3 从 OpenAI 格式统一的工具调用处理
OpenAI 流式工具调用:
Chunk 1: delta.tool_calls[0] = { index: 0, id: "call_xxx", function: { name: "write_file", arguments: "" } }
Chunk 2: delta.tool_calls[0] = { index: 0, function: { arguments: "{\"file" } }
Chunk 3: delta.tool_calls[0] = { index: 0, function: { arguments: "_path\": \"test" } }
Chunk 4: delta.tool_calls[0] = { index: 0, function: { arguments: ".md\"}" } }
↓ 累加合并
tool_start: {
id: "call_xxx",
name: "write_file",
arguments: "{\"file_path\": \"test.md\"}"
}5. Token 预算管理系统
5.1 Context Budget (11 字段)
rust
// 对话上下文预算分解
struct ContextBudget {
system_tokens: u64, // 系统提示词 token
tool_def_tokens: u64, // 工具定义 token
tool_result_tokens: u64, // 工具结果 token
message_tokens: u64, // 消息历史 token
messages_count: u64, // 消息数量
// 聚合
budget_tokens: u64, // 总预算 token
context_window: u64, // 上下文窗口大小
}5.2 Token 统计的完整生命周期
用户消息输入
│
├── estimated_tokens: u64 ← 发送前的估算
│
├── 发送到 LLM API
│
├── prompt_tokens: u64 ← API 返回的 prompt token 数
├── completion_tokens: u64 ← API 返回的 completion token 数
├── total_tokens: u64 ← 总 token 数
│
└── 写入 datalog:
{
"estimated_tokens": 6332,
"message_count": 2,
"request": { "messages": [...], "tools": [...] },
"response": {
"duration_ms": 2023,
"text": "...",
"tool_calls": []
}
}5.3 PromptTokensDetails (1 字段)
rust
struct PromptTokensDetails {
// 1 个元素
cached_tokens: u64, // 缓存命中 token 数
}OpenAI 的 prompt_tokens_details 结构,用于跟踪缓存命中:
json
{
"prompt_tokens": 1000,
"prompt_tokens_details": {
"cached_tokens": 600
}
}6. Session 管理与通信关联
6.1 会话消息结构
rust
struct Session {
// 8 个元素
id: String, // 会话 UUID
name: String, // 会话名 (首条消息截取)
working_dir: String, // 工作目录
created_at: u64, // 创建时间
updated_at: u64, // 更新时间
messages: Vec<Message>, // 消息列表
turn_stats: Vec<TurnStat>, // 回合统计
cold_summaries: Vec<String>, // 冷摘要 (上下文压缩)
}
struct Message {
// 3 个元素
role: String, // "User" | "Assistant" | "Tool"
content: MessageContent, // 消息内容 (枚举)
}
// 消息内容枚举
enum MessageContent {
Text(String), // 纯文本
AssistantWithToolCalls { // 助手回复 (含工具调用)
text: String,
tool_calls: Vec<ToolCall>,
reasoning_content: Option<String>,
},
MultiPart(Vec<ContentPart>), // 多部分 (用户消息含图片)
}6.2 消息 → LLM 请求的转换
Message 格式 LLM Request 格式
───────────────── ─────────────────
User { Text } → role: "user", content: "文本"
User { MultiPart } → role: "user", content: [{type: "text", text: "..."}, {type: "image_url", ...}]
Assistant { text } → role: "assistant", content: "文本"
+ tool_calls → + tool_calls: [{id, type: "function", function: {name, arguments}}]
Tool { result } → role: "tool", tool_call_id: "...", content: "结果"6.3 冷摘要 (上下文预算管理)
当会话消息超过 context_window 时,Agent 引擎会生成"冷摘要"压缩早期消息:
对话推进 → 消息数增长
│
├── 总 token < context_window → 全部发送
│
└── 总 token > context_window → 触发压缩:
├── 最早 N 轮对话被摘要为冷摘要文本
├── 冷摘要作为 system prompt 的一部分注入
└── 保留最近 N 轮完整消息7. 数据日志 (Datalog) 系统
7.1 日志记录格式
每次 LLM 调用记录为一个 JSON 文件:
~/.atomcode/datalog/<project-hash>/llm/
├── <timestamp>.json # 单次调用完整记录
└── calls.log # 汇总日志 (一行一条)7.2 单次调用的完整格式
json
{
"context_window": 1000000,
"model": "deepseek-v4-flash",
"session_id": "uuid",
"step": 0,
"timestamp": "2026-06-12T15:51:05",
"request": {
"estimated_tokens": 6332,
"message_count": 2,
"messages": [...],
"tool_count": 0,
"tools": [...]
},
"response": {
"duration_ms": 2023,
"reasoning_content": "推理内容",
"text": "助手回复",
"tool_calls": []
}
}7.3 calls.log 汇总格式
2026-06-17_17-30-22_832 deepseek-v4-flash step=0 msgs=4/6391tok → 1528ms tools=0 text_only| 字段 | 说明 |
|---|---|
timestamp | 调用时间 (精度毫秒) |
model | 模型名 |
step=N | 回合步数 |
msgs=N/Ntok | 消息数 / 估算 token 数 |
→ Nms | 响应耗时 |
tools=N [name] | 工具调用次数 (可选工具名) |
text_only | 纯文本响应标记 |
429 / throttl | 速率限制标记 |
500 / 502 / 503 | 服务器错误标记 |
stream timeout | 流超时标记 |
mid-flight terminated | 流中断标记 |
token limit | token 超限标记 |
8. 流式管道的关键运行时验证发现
8.1 Daemon 始终返回 SSE
bash
curl -s -X POST http://localhost:13456/chat \
-H "Authorization: Bearer $(cat ~/.atomcode/auth.toml | grep access_token | cut -d'\"' -f2)" \
-H "Content-Type: application/json" \
-d '{"message":"hi","stream":false}' | head -3结果: stream 标志被忽略,始终返回 SSE。这意味着代理层必须始终处理 SSE。
8.2 事件顺序必须保持
reasoning → tokens → tool_start → tool_result → reasoning → text → tokens → done关键规则:
done总是最后一个事件tool_result后有且仅有一次额外的reasoning+text阶段tokens事件可能多次出现 (每次推理后)error事件后紧跟着done
8.3 流中断恢复
Daemon 不支持流中断恢复。如果 SSE 流中断:
- 客户端收到不完整的响应
- 必须重新发送完整请求
- 如果 session_id 相同,新请求从原会话继续 (而非重新开始)
- 使用
session_id重建上下文
9. 请求签名与认证的详细协议
9.1 API Key 派生流程
OAuth 登录成功
│
├── 服务器返回 access_token + refresh_token
│
├── 客户端存储到 auth.toml (明文)
│
├── 客户端 POST /coding-plan/claim-v2 → 服务器验证 access_token
│
├── 服务器返回 CodingPlan 配额 + 派生的 LLM API key
│ (API key 不落盘,只在 daemon 内存中)
│
└── 每次 LLM 调用:
├── 从内存取 API key
├── 签名时间戳
└── 发送到 llm-api9.2 请求签名细节
text
codingplan_official_build_required ← 仅官方构建包含加密模块
ATOMCODE_CODINGPLAN_API_BASE ← 可自定义 API 基础 URL从 CODINGPLAN_CRYPTO_ANALYSIS.md 已知:
- 签名算法:ECDSA P256 SHA256 ASN1(首选)/ Ed25519(现代备选)
- 防重放:签名时间戳 + 窗口验证
- 官方构建检测:
atomcode-codingplan-cryptocrate 条件编译
10. Daemon → llm-api 的完整调用时序
POST /chat (客户端)
│
▼
Daemon 接收请求
│
├── 1. 验证 Bearer token (来自 auth.toml)
│ → 401 如果无效
│
├── 2. 查找 ProviderConfig (由 provider 参数指定)
│ → 获取 model, base_url, api_key (内存)
│
├── 3. 构建 OpenAI 请求
│ POST {base_url}/chat/completions
│ Authorization: Bearer {api_key}
│ Content-Type: application/json
│ {
│ "model": "deepseek-v4-flash",
│ "messages": [
│ {"role":"system","content":"You are AtomCode..."},
│ {"role":"user","content":"用户消息..."}
│ ],
│ "stream": true
│ // 如果是 Claude: "anthropic_version": "bedrock-2025-01-01"
│ }
│
├── 4. 接收响应
│ ├── 200 → SSE 流 (逐 delta 处理)
│ ├── 429 → 重构为内部错误 + 重试
│ ├── 401 → 尝试 refresh token
│ └── 5xx → 指数退避重试
│
└── 5. 工具调用时:
├── 暂停 SSE 流
├── 执行工具 (bash/write_file/etc.)
├── 将工具结果作为新消息追加
└── 继续 SSE 流 (下一轮推理)11. unset 风险与安全考虑
| 方向 | 安全措施 | 潜在风险 |
|---|---|---|
| 客户端 → Daemon | Bearer token 验证 | 本地端口仅绑定 127.0.0.1,局域网暴露需显式 --host |
| Daemon → llm-api | OAuth token 派生 API key + 签名时间戳 | API key 在进程内存中可被 dump |
| 消息持久化 | datalog 明文记录 | ~/.atomcode/datalog/ 含完整请求响应 |
| 系统提示词 | daemon 自动注入 | 自定义 provider 时可绕过 |
12. 逆向命令索引
bash
# 提取所有的 LLM API 端点
strings atomcode.bin.bak | grep -E "llm-api|pre-llm-api|api.gitcode" | sort -u
# 提取 OpenAI 兼容 API 路径
strings atomcode.bin.bak | grep -E "/v1/chat/completions|/v1/messages|/api/chat" | sort -u
# 提取流错误模式
strings atomcode.bin.bak | grep -E "stream.timeout|stream.interrupt|mid-flight|terminated" | sort -u
# 提取 token 统计字段
strings atomcode.bin.bak | grep -E "prompt_tokens|completion_tokens|cached_tokens|budget_tokens|estimated_tokens" | sort -u
# 提取消息结构相关
strings atomcode.bin.bak | grep -E "struct Message with|struct ToolCall with|struct Session with" | sort -u
# 提取请求签名相关
strings atomcode.bin.bak | grep -iE "signature|timestamp.*window|codingplan.*official" | grep -v "^crates/\|\.rs$" | sort -u
# 运行时捕获完整请求链路
TOKEN=$(python3 -c "import tomllib; f=open('$HOME/.atomcode/auth.toml','rb');print(tomllib.load(f)['access_token'])")
curl -v -X POST http://localhost:13456/chat \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"hi","stream":true}' 2>&1 | grep -E "^< |> |data:"📅 最后更新: 2026-07-02
🔍 来源: strings atomcode.bin.bak 二进制挖掘 + 运行时 curl 验证
📊 新增覆盖: AI 通信端到端链路此前 ~40% → 95%