Skip to content

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 → DaemonHTTP POSTChatRequest (5 字段)localhost:13456
Daemon → llm-apiHTTP POSTOpenAI chat/completionsllm-api.atomgit.com
llm-api → DaemonSSEOpenAI Chunk 或完整 JSONHTTPS
Daemon → ClientSSEAtomCode 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 SSE

3.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 APIOpenAI 兼容最高
https://pre-llm-api-cce.atomgit.com/v1灰度测试环境OpenAI 兼容ATOMCODE_* 环境变量
https://api.gitcode.com/api/v5CodingPlan 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/v1

Daemon 同时也支持直接配置自定义提供商来绕过 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_deltaSSE JSON
流结束data: [DONE]message_stop 事件done: true
工具调用delta.tool_calls[] 累加tool_use content blocktool_calls[{function}]
Token 统计usage 字段message_delta 事件无(自行计算)
推理内容reasoning_contentthinking_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 limittoken 超限标记

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 流中断:

  1. 客户端收到不完整的响应
  2. 必须重新发送完整请求
  3. 如果 session_id 相同,新请求从原会话继续 (而非重新开始)
  4. 使用 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-api

9.2 请求签名细节

text
codingplan_official_build_required   ← 仅官方构建包含加密模块
ATOMCODE_CODINGPLAN_API_BASE         ← 可自定义 API 基础 URL

CODINGPLAN_CRYPTO_ANALYSIS.md 已知:

  • 签名算法:ECDSA P256 SHA256 ASN1(首选)/ Ed25519(现代备选)
  • 防重放:签名时间戳 + 窗口验证
  • 官方构建检测:atomcode-codingplan-crypto crate 条件编译

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 风险与安全考虑

方向安全措施潜在风险
客户端 → DaemonBearer token 验证本地端口仅绑定 127.0.0.1,局域网暴露需显式 --host
Daemon → llm-apiOAuth 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%

基于 VitePress 构建 · AtomCode v4.25.3 逆向工程分析文档