摘要:AtomCode 是一款强大的 AI 编码智能体,除了交互式 TUI 和 CLI 无头模式外,它还提供 Daemon(守护进程)模式——以 HTTP + SSE API 的形式将 AtomCode 的全部能力暴露给外部客户端。本文将全面介绍 Daemon 模式的启动方式、所有 API 端点、通信协议细节,并提供多种语言的客户端代码示例。
前言:为什么你需要 Daemon 模式?
AtomCode Daemon 模式的核心价值在于:将 AI 编码智能体转化为一个可编程的 HTTP 服务。这意味着你可以像调用普通 REST API 一样,在任何编程语言、任何平台、任何场景中驱动 AtomCode。
🚀 它能做什么?
1. 无人值守的 24/7 编码助手
Daemon 启动后便常驻后台,不限时长、不限对话次数。你可以在工作时打开一个终端跑着 Daemon,随时用脚本或 Web 页面发请求——它永远在待命。
# 启动一次,永久在线
atomcode daemon &
# 随时随地调用
curl -X POST http://127.0.0.1:13456/chat \
-H "Content-Type: application/json" \
-d '{"message":"帮我 review 代码"}'
2. GitHub CI/CD 自动化流水线
将 Daemon 接入 CI/CD 流程(GitHub Actions / GitLab CI / Jenkins),实现提交即审查、合入即测试:
| 场景 | 触发时机 | 自动执行任务 |
|---|---|---|
| PR 代码审查 | pull_request 事件 | 读取 diff → 审查代码风格、安全性、性能 → 自动评论 |
| 自动化修复 | push 到特定分支 | 运行 lint → 自动修复 → 提交修复 commit |
| 单元测试生成 | 合入前 | 分析变更代码 → 生成缺失的单元测试 → 追加到 PR |
| 文档同步 | release 事件 | 分析 API 变更 → 自动更新 README/CHANGELOG |
| 依赖审计 | 定时 (cron) | 扫描 CVE 漏洞 → 自动升级 → 生成审计报告 |
# .github/workflows/pr-review.yml 示例
on: pull_request
jobs:
atomcode-review:
runs-on: self-hosted # 需要运行 Daemon 的机器
steps:
- uses: actions/checkout@v4
- name: AI Code Review
run: |
python3 << 'EOF'
import requests
diff = open("/tmp/pr.diff").read()
resp = requests.post("http://localhost:13456/chat",
json={"message": f"请审查以下 diff,指出问题:\n{diff}"},
stream=True)
for line in resp.iter_lines():
if line: print(line.decode())
EOF
env:
ATOMCODE_DAEMON_URL: "http://localhost:13456"
3. 自动代码审查后台服务
部署一个 Webhook 服务,监听 Git 平台的审查请求,自动对每次提交/PR 进行深度代码审查。它可以在你睡觉时默默检查代码,早上上班时一份报告已经躺在邮箱里。
┌──────────┐ webhook ┌─────────────┐ POST /chat ┌──────────────┐
│ GitLab │ ──────────► │ Webhook │ ──────────────► │ atomcode- │
│ GitHub │ │ Server │ │ daemon │
│ Gitee │ │ (Flask) │ ◄────────────── │ │
└──────────┘ └─────────────┘ SSE 流式结果 └──────────────┘
│
▼
┌──────────────┐
│ 自动评论 PR │
│ 生成审查报告 │
│ 触发通知 │
└──────────────┘
4. 定时任务 & 批量处理
配合 cron / systemd timer,让 AtomCode 在凌晨自动执行代码质量巡检、生成周报、或批量处理积压的 issue。
# crontab -e
# 每天凌晨 2 点执行代码质量检查
0 2 * * * python3 /opt/scripts/quality_check.py
# 每周一早 9 点生成开发周报
0 9 * * 1 python3 /opt/scripts/weekly_report.py
5. 构建自定义 AI 工作流工具
基于 Daemon API,你可以搭建自己的 AI 工作流平台:
- Web 管理面板:用 Vue/React 写个网页,让团队通过浏览器使用 AtomCode
- Slack/钉钉 Bot:在聊天中 @机器人 → 自动调用 Daemon → 返回代码分析结果
- 多 Agent 编排:同时启动多个 Daemon 实例(不同模型/配置),协调完成复杂任务
- 内部 API 网关:给 Daemon 套上认证、限流、审计层,暴露为内部微服务
💡 一句话总结
Daemon 模式 = 把你的 AI 编码智能体变成 REST API,任何能发 HTTP 请求的系统都能用它。
无论是搭建无人值守的 CI 审查流水线,还是构建团队共享的 AI 编码平台,Daemon 模式都是 AtomCode 最强大的远程调用接口。
这里顺带推荐下atomcode和taotoken,配合taotoken的套餐,使用起来太给力啦~ 。目前已是我的主要使用神器。
如果你做过 AI 应用或自动化脚本,多半遇到过同一种疲惫:每家厂商一套账号、一套密钥、一套计费口径,想在项目里换个模型,常常不是「改一行参数」这么简单,而是「再集成一遍」。如果你想体验国外厉害的大模型能力,却总是被禁或者服务不稳定。推荐下taotoken,这个是csdn官方推出的产品,速度流畅,稳定可靠。 关键是很便宜,性价比不错。
taotoken尝鲜入口:https://taotoken.net/?u=inv_faxm8m42tg11a06f&utm_source=invite
Taotoken 的方向很直白:把「多模型」收敛成「一条统一网关」。它是 CSDN 生态里的 AI 聚合与分发能力载体——面向开发者常见的调用路径,做网关侧的路由与协议适配,让你更少折腾基建,更多时间花在产品与效果上。谐音梗“掏token”,名字起的不错。以后AI时代,token就是食粮,越来越重要了。

文章目录
一、概述
1.1 什么是 AtomCode?
AtomCode 是一款开源的 AI 编码智能体,由 AtomGit 开发并开源。它运行在你的终端中,能够自主理解代码库、执行多步任务、调用各种工具(bash、文件读写、代码搜索、Web 访问等),像一个真正的开发者一样完成编码工作。
核心特性:
| 特性 | 说明 |
|---|---|
| 多模型支持 | 对接 100+ 大模型(DeepSeek、OpenAI、Claude、Gemini 等),支持自定义 Provider |
| 自主多步执行 | 自动拆解复杂任务,循环「思考→执行→观察」直到完成 |
| 完整工具链 | bash、文件读写、代码搜索、grep、glob、Web 搜索/抓取等 20+ 工具 |
| 会话持久化 | 对话历史保存在本地,跨启动可继续上下文 |
| 三种运行模式 | TUI 交互式、CLI 无头式、HTTP Daemon 后台服务 |
| 100% 开源 | Apache 2.0 协议,代码完全公开 |
官方资源:
| 资源 | 地址 |
|---|---|
| 官方网站 & 文档 | https://atomcode.atomgit.com |
| 源代码仓库 | https://atomgit.com/atomgit_atomcode/atomcode |
| GitHub 镜像 | https://github.com/atomgit/atomcode |
| 安装方式 | pip install atomcode 或 npm install -g @atomgit/atomcode |
| 交流群 | AtomCode 官方社区 |
1.2 三种运行模式
| 模式 | 命令 | 特点 |
|---|---|---|
| 交互式 TUI | atomcode | 终端交互界面,适合日常开发 |
| 无头 CLI | atomcode -p "任务" | 单次执行,输出到 stdout,适合 CI/脚本 |
| Daemon 模式 | atomcode daemon | 后台 HTTP 服务,供外部客户端驱动 |
Daemon 模式与传统 Daemon 不同——它不是一个「定时任务守护」,而是一个 HTTP + SSE API 服务器。你可以把它理解为 AtomCode 的「远程过程调用层」:启动后,任何能发 HTTP 请求的客户端(Python 脚本、VS Code 插件、Web 前端、桌面应用)都可以通过 API 与 AtomCode 交互。
它的架构如下:
┌──────────────────────────────────────────────────┐
│ 客户端 │
│ (Python 脚本 / JS 前端 / IDE 插件 / 桌面应用) │
└──────────────────┬───────────────────────────────┘
│ HTTP REST + SSE (JSON)
▼
┌──────────────────────────────────────────────────┐
│ atomcode-daemon │
│ (HTTP 服务层,端口 13456) │
└──────────────────┬───────────────────────────────┘
│ 内部调用
▼
┌──────────────────────────────────────────────────┐
│ atomcode-core │
│ (AI 智能体核心: LLM + 工具循环 + 会话管理) │
└──────────────────────────────────────────────────┘
Daemon 与 CLI 共享同一份配置(~/.atomcode/config.toml),登录状态、Provider 配置、会话历史全部通用。
二、启动 Daemon
2.1 基本启动
# 最简单的方式
atomcode daemon
输出类似:
[2026-07-04T10:00:00Z INFO atomcode_daemon] Listening on http://127.0.0.1:13456
[2026-07-04T10:00:00Z INFO atomcode_daemon] Idle timeout: 1800s
2.2 带参数启动
# 指定端口和客户端标识
atomcode daemon --port 13456 --client vscode
# 直接调用 daemon 二进制(更多底层选项)
atomcode-daemon --host 127.0.0.1 --port 13456 \
--client atomcode-air \
--idle-timeout 3600
2.3 所有启动参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--host HOST | 127.0.0.1 | 监听地址。非 loopback 地址会触发安全警告 |
--port PORT | 13456 | 监听端口 |
--client NAME | — | 客户端身份标识:vscode / atomcode-air / 其他 |
--idle-timeout SECS | 1800 (30分钟) | 空闲超时;0 禁用;最小 60 秒 |
--no-telemetry | — | 禁用遥测 |
--idle-timeout 也支持环境变量 ATOMCODE_DAEMON_IDLE_TIMEOUT。
2.4 验证是否启动成功
# 健康检查
curl http://127.0.0.1:13456/health
# 响应示例
{"status":"ok"}
三、API 端点全面览
所有端点按功能分组如下。
3.1 健康与生命周期
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /health | 健康检查。返回 {"status":"ok"} |
POST | /shutdown | 优雅关闭 Daemon(客户端退出时调用) |
3.2 会话与项目管理
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /sessions | 列出所有历史会话 |
POST | /sessions | 创建新会话 |
GET | /sessions/search | 按关键词搜索历史会话 |
GET | /project | 当前工作目录的项目状态 |
POST | /cd | 切换工作目录。请求体:{"path": "/new/path"} |
GET | /projects | 列出所有有过会话的项目 |
GET | /projects/:hash/sessions | 某项目下的会话列表 |
GET | /projects/:hash/sessions/:id | 某会话的详情 |
DELETE | /projects/:hash/sessions/:id | 删除某会话 |
PATCH | /projects/:hash/sessions/:id/rename | 重命名某会话。请求体:{"name": "新名称"} |
3.3 聊天与推理——核心接口
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /models | 列出可用 Provider 和模型 |
POST | /chat | 发送提示词,返回 SSE 流(最核心的接口) |
POST | /chat/stop | 停止正在进行的 /chat 请求 |
3.4 配置管理
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /config | 读取当前 ~/.atomcode/config.toml |
POST | /config/reload | 重新加载配置文件 |
GET | /providers | 列出所有已配置的 Provider |
POST | /providers | 添加一个新 Provider |
PATCH | /providers/:name | 更新指定 Provider 的配置 |
DELETE | /providers/:name | 删除指定 Provider |
POST | /providers/:name/default | 将指定 Provider 设为默认 |
PATCH | /providers/:name/thinking | 开关/调整指定 Provider 的思考模式 |
3.5 MCP 集成
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /mcp/status | 查看 MCP 服务器连接状态 |
POST | /mcp/reload | 重新加载 .mcp.json 配置 |
3.6 认证与授权
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /auth/status | 当前登录状态 |
POST | /auth/login/start | 启动 AtomGit OAuth 登录流程 |
POST | /auth/login/:id/poll | 轮询登录结果 |
DELETE | /auth/login/:id | 取消正在进行的登录 |
POST | /auth/logout | 登出 |
POST | /codingplan/setup | 认领 CodingPlan 并写入 Provider 配置 |
四、核心接口详解:POST /chat
这是 Daemon 的核心端点——它接收用户的任务描述,驱动 AtomCode 智能体执行,并通过 SSE 流实时返回结果。
4.1 请求格式
POST /chat HTTP/1.1
Host: 127.0.0.1:13456
Content-Type: application/json
{
"message": "请帮我分析这个项目的目录结构,并给出优化建议"
}
4.2 响应格式——SSE 事件流
响应是一个 Server-Sent Events (SSE) 流,Content-Type 为 text/event-stream。每个事件的 data 字段包含一个 JSON 对象。
SSE 流的基本格式:
data: {"type":"text","content":"这个项目采用"}
data: {"type":"text","content":"MVC架构,"}
data: {"type":"tool_call_start","tool_name":"read_file","args":{"path":"src/main.rs"}}
data: {"type":"tool_call_end","tool_name":"read_file","result":"..."}
data: {"type":"text","content":"主要模块包括..."}
每个事件 JSON 的通用结构:
{
"type": "<事件类型>",
// ... 事件类型相关字段
}
4.3 请求流程示意
五、SSE 事件类型全表
POST /chat 返回的 SSE 流中可能包含以下事件类型(来自 crates/atomcode-core/src/turn/event.rs):
5.1 文本输出
// AI 回复文本
{"type":"text","content":"这是一段生成的"}
// 完整文本块(有些场景直接发完整片段)
{"type":"text","content":"完整的回复内容"}
5.2 推理过程
// LLM 的内部推理过程(think tags 的内容)
{"type":"reasoning","content":"首先我需要理解这个函数的作用..."}
5.3 工具调用
// 工具调用开始
{
"type": "tool_call_start",
"tool_name": "read_file",
"args": {"path": "src/main.rs"},
"call_id": "call_xxx"
}
// 工具调用结束(含结果)
{
"type": "tool_call_end",
"tool_name": "read_file",
"result": "文件内容...",
"call_id": "call_xxx"
}
5.4 状态与统计
// Token 用量
{
"type": "tokens",
"prompt": 1523,
"completion": 456,
"total": 1979
}
// 状态更新
{"type":"status","status":"processing"}
// 错误事件
{"type":"error","error":"调用 API 超时,请检查网络连接"}
5.5 完整事件类型速查表
type 值 | 含义 | 关键字段 |
|---|---|---|
text | AI 回复文本 | content: str |
reasoning | 推理过程 | content: str |
tool_call_start | 工具开始调用 | tool_name, args, call_id |
tool_call_end | 工具调用完成 | tool_name, result, call_id |
tokens | Token 用量统计 | prompt: int, completion: int, total: int |
status | 状态更新 | status: str |
error | 错误通知 | error: str |
done | 对话完成信号 | session_id: str |
六、Python 客户端示例
6.1 基础版:流式打印
import json
import requests
def chat_with_atomcode(prompt: str, base_url="http://127.0.0.1:13456"):
"""最简实现:发送任务并流式打印到终端"""
resp = requests.post(
f"{base_url}/chat",
json={"message": prompt},
stream=True,
)
resp.raise_for_status()
for line in resp.iter_lines():
if not line:
continue
decoded = line.decode("utf-8")
if decoded.startswith("data: "):
event = json.loads(decoded[6:])
t = event.get("type")
if t == "text":
print(event.get("content", ""), end="", flush=True)
elif t == "reasoning":
# 灰色显示推理过程,content 字段
print(f"\033[90m{event.get('content', '')}\033[0m", end="", flush=True)
elif t == "tool_call_start":
print(f"\n\033[33m🔧 调用工具 [{event.get('tool_name', '?')}]\033[0m")
elif t == "tool_call_end":
print(f"\033[32m✔ 完成\033[0m")
elif t == "error":
print(f"\n\033[31m❌ 错误: {event.get('error', '未知')}\033[0m")
elif t == "tokens":
print(f"\n\033[90m[Token: 输入={event.get('prompt', '?')} 输出={event.get('completion', '?')}]\033[0m")
if __name__ == "__main__":
chat_with_atomcode("分析当前项目的目录结构")
6.2 使用 SSE 客户端库
pip install sseclient-py
import json
import requests
import sseclient
def chat_sse(prompt: str, base_url="http://127.0.0.1:13456"):
"""使用 sseclient 库消费事件流"""
resp = requests.post(
f"{base_url}/chat",
json={"message": prompt},
stream=True,
)
client = sseclient.SSEClient(resp)
for event in client.events():
data = json.loads(event.data)
match data.get("type"):
case "text":
print(data.get("content", ""), end="", flush=True)
case "reasoning":
print(f"\033[90m{data.get('content', '')}\033[0m", end="", flush=True)
case "tool_call_start":
print(f"\n🛠 [{data['tool_name']}] {data.get('args', {})}")
case "tool_call_end":
print(f"\n✅ 完成")
case "error":
print(f"\n❌ {data['error']}")
6.3 完整封装:面向对象的客户端
import json
import requests
from typing import Callable, Optional
from sseclient import SSEClient
class AtomCodeDaemonClient:
"""
AtomCode Daemon 的 Python 客户端封装
用法:
client = AtomCodeDaemonClient()
client.chat("帮我看下这个项目的结构",
on_text=lambda d: print(d, end=""),
on_tool_start=lambda n,a: print(f"\\n[调用 {n}]"),
)
"""
def __init__(self, base_url: str = "http://127.0.0.1:13456"):
self.base_url = base_url.rstrip("/")
# ── 工具方法 ──
def health(self) -> bool:
"""检查 Daemon 是否在运行"""
try:
resp = requests.get(f"{self.base_url}/health", timeout=3)
return resp.status_code == 200
except requests.ConnectionError:
return False
def list_models(self) -> list:
"""列出可用 Provider 和 Model"""
return requests.get(f"{self.base_url}/models").json()
def list_sessions(self) -> list:
"""列出所有历史会话"""
return requests.get(f"{self.base_url}/sessions").json()
def new_session(self) -> dict:
"""创建一个新会话,返回会话信息"""
return requests.post(f"{self.base_url}/sessions").json()
def delete_session(self, session_id: str):
"""删除指定会话"""
# 先查 session 所属项目
sessions = self.list_sessions()
for s in sessions:
if s.get("id") == session_id:
project_hash = s.get("project_hash")
if project_hash:
requests.delete(
f"{self.base_url}/projects/{project_hash}/sessions/{session_id}"
)
return
# ── 核心:流式聊天 ──
def chat(
self,
prompt: str,
on_text: Optional[Callable[[str], None]] = None,
on_reasoning: Optional[Callable[[str], None]] = None,
on_tool_start: Optional[Callable[[str, dict], None]] = None,
on_tool_end: Optional[Callable[[str, str], None]] = None,
on_token_usage: Optional[Callable[[dict], None]] = None,
on_error: Optional[Callable[[str], None]] = None,
on_status: Optional[Callable[[str], None]] = None,
) -> str:
"""
发送提示词并消费 SSE 事件流
参数:
prompt: 用户提示词
各种 on_xx 回调,收到对应事件时被调用
返回:
拼接后的完整回复文本
"""
full_text: list[str] = []
resp = requests.post(
f"{self.base_url}/chat",
json={"message": prompt},
stream=True,
)
resp.raise_for_status()
client = SSEClient(resp)
for event in client.events():
data = json.loads(event.data)
t = data.get("type")
if t == "text":
content = data.get("content", "")
full_text.append(content)
if on_text:
on_text(content)
elif t == "reasoning":
if on_reasoning:
on_reasoning(data.get("content", ""))
elif t == "tool_call_start":
if on_tool_start:
on_tool_start(data.get("tool_name"), data.get("args", {}))
elif t == "tool_call_end":
if on_tool_end:
on_tool_end(data.get("tool_name"), data.get("result", ""))
elif t == "tokens":
if on_token_usage:
on_token_usage(data)
elif t == "error":
if on_error:
on_error(data.get("error", "未知错误"))
elif t == "status":
if on_status:
on_status(data.get("status", ""))
return "".join(full_text)
def chat_sync(self, prompt: str) -> str:
"""同步模式:等待完整回复后返回纯文本"""
return self.chat(prompt)
# ── 控制 ──
def stop(self):
"""停止正在进行的 chat 请求"""
try:
requests.post(f"{self.base_url}/chat/stop", timeout=3)
except requests.RequestException:
pass
def shutdown(self):
"""关闭 Daemon"""
try:
requests.post(f"{self.base_url}/shutdown", timeout=3)
except requests.RequestException:
pass
# ── 使用示例 ──
if __name__ == "__main__":
import sys
client = AtomCodeDaemonClient()
# 先检查 Daemon 是否运行
if not client.health():
print("❌ AtomCode Daemon 未运行!请先执行: atomcode daemon")
sys.exit(1)
print("✅ 已连接 AtomCode Daemon")
print(f"📋 可用模型: {client.list_models()}\n")
# 流式对话
final = client.chat(
prompt="分析当前项目的目录结构和代码组成",
on_text=lambda d: print(d, end="", flush=True),
on_reasoning=lambda d: print(f"\033[90m{d}\033[0m", end="", flush=True),
on_tool_start=lambda n, a: print(f"\n\033[33m🔧 [{n}]\033[0m"),
on_tool_end=lambda n, r: print(f"\033[32m✔ [{n}]\033[0m"),
on_token_usage=lambda u: print(f"\n\033[90m[Token: {u}]\033[0m"),
on_error=lambda e: print(f"\n\033[31m❌ {e}\033[0m"),
)
print(f"\n\n📝 最终回复长度: {len(final)} 字符")
6.4 完整测试脚本:一站式验证所有 API
项目附带的 test_atomcode_daemon.py 是一个可独立运行的测试脚本,覆盖了 Daemon API 的全部核心功能:
| 测试项 | 函数 | 作用 |
|---|---|---|
| 健康检查 | test_health() | 确认 Daemon 是否在运行 |
| 模型列表 | test_models() | 列出可用 Provider 和模型 |
| 会话列表 | test_sessions() | 查看历史会话 |
| 流式对话 | test_chat_stream() | 核心测试:发送提示词,实时打印 reasoning/text/tool 事件 |
| 同步模式 | test_chat_sync() | 等待完整回复后纯文本输出 |
#!/usr/bin/env python3
"""
AtomCode Daemon 交互测试脚本
测试流程:
1. 检查 Daemon 是否运行
2. 获取可用模型列表
3. 发送测试提示词,流式接收回复
4. 显示工具调用和 token 用量
5. 测试同步模式(非流式)
用法:
python3 test_atomcode_daemon.py # 默认地址
python3 test_atomcode_daemon.py --url http://localhost:13456
python3 test_atomcode_daemon.py --prompt "帮我写个快排" --sync
"""
import argparse
import json
import sys
import time
from typing import Optional
try:
import requests
except ImportError:
print("❌ 需要安装 requests 库: pip install requests")
sys.exit(1)
# ──────────────────────────────────────────────
# 客户端
# ──────────────────────────────────────────────
class AtomCodeDaemonClient:
"""AtomCode Daemon 的 HTTP 客户端"""
def __init__(self, base_url: str = "http://127.0.0.1:13456"):
self.base_url = base_url.rstrip("/")
self._session = requests.Session()
# ── 健康检查 ──
def health(self) -> bool:
try:
r = self._session.get(f"{self.base_url}/health", timeout=5)
return r.status_code == 200
except requests.ConnectionError:
return False
# ── 模型列表 ──
def list_models(self) -> Optional[list]:
try:
r = self._session.get(f"{self.base_url}/models", timeout=5)
r.raise_for_status()
return r.json()
except Exception as e:
print(f" ⚠ 获取模型列表失败: {e}")
return None
# ── 会话列表 ──
def list_sessions(self) -> Optional[list]:
try:
r = self._session.get(f"{self.base_url}/sessions", timeout=5)
r.raise_for_status()
return r.json()
except Exception as e:
print(f" ⚠ 获取会话列表失败: {e}")
return None
# ── 流式聊天 ──
def chat_stream(self, prompt: str):
"""
流式聊天,逐个事件 yield 返回。
Yields:
dict: SSE 事件 JSON
"""
resp = self._session.post(
f"{self.base_url}/chat",
json={"message": prompt},
stream=True,
)
resp.raise_for_status()
for line in resp.iter_lines():
if not line:
continue
decoded = line.decode("utf-8")
if decoded.startswith("data: "):
yield json.loads(decoded[6:])
# ── 同步聊天(等待完整回复)──
def chat_sync(self, prompt: str) -> str:
full_text: list[str] = []
for event in self.chat_stream(prompt):
t = event.get("type")
if t in ("text", "text_delta"):
full_text.append(event.get("content") or event.get("delta", ""))
return "".join(full_text)
# ── 停止 ──
def stop(self):
try:
self._session.post(f"{self.base_url}/chat/stop", timeout=3)
except Exception:
pass
# ── 关闭 daemon ──
def shutdown(self):
try:
self._session.post(f"{self.base_url}/shutdown", timeout=3)
except Exception:
pass
# ──────────────────────────────────────────────
# 测试用例
# ──────────────────────────────────────────────
def test_health(client: AtomCodeDaemonClient) -> bool:
"""测试 1: 健康检查"""
print("\n" + "=" * 60)
print("📡 测试 1: 健康检查")
print("=" * 60)
ok = client.health()
if ok:
print(" ✅ Daemon 正在运行")
else:
print(" ❌ Daemon 未响应!请先启动: atomcode daemon")
return ok
def test_models(client: AtomCodeDaemonClient):
"""测试 2: 获取模型列表"""
print("\n" + "=" * 60)
print("📋 测试 2: 获取可用模型")
print("=" * 60)
models = client.list_models()
if models:
if isinstance(models, list):
for m in models:
name = m.get("name", m.get("model", str(m)))
provider = m.get("provider", "")
print(f" ✅ {name}" + (f" ({provider})" if provider else ""))
else:
print(f" ✅ {models}")
else:
print(" ⚠ 无法获取模型列表(非致命)")
def test_sessions(client: AtomCodeDaemonClient):
"""测试 3: 会话列表"""
print("\n" + "=" * 60)
print("💬 测试 3: 查看历史会话")
print("=" * 60)
sessions = client.list_sessions()
if sessions:
if isinstance(sessions, list):
if sessions:
print(f" ✅ 共有 {len(sessions)} 个历史会话")
for i, s in enumerate(sessions[:3]): # 最多显示 3 个
sid = s.get("id", s.get("session_id", "?"))
print(f" #{i+1} session: {sid}")
if len(sessions) > 3:
print(f" ... 还有 {len(sessions)-3} 个")
else:
print(" ℹ 暂无历史会话")
else:
print(" ⚠ 无法获取会话列表(非致命)")
def test_chat_stream(client: AtomCodeDaemonClient, prompt: str):
"""测试 4: 流式对话"""
print("\n" + "=" * 60)
print("🗣 测试 4: 流式对话")
print(f' 提示词: "{prompt}"')
print("=" * 60)
print()
full_text: list[str] = []
tool_count = 0
token_info = {}
start_time = time.time()
try:
for event in client.chat_stream(prompt):
t = event.get("type")
if t == "reasoning":
# 推理过程用灰色 + 斜体显示
print(f"\033[90m\033[3m{event.get('content', '')}\033[0m", end="", flush=True)
elif t == "text":
# 完整文本块,直接打印
content = event.get("content", "")
full_text.append(content)
print(content, end="", flush=True)
elif t == "tool_call_start":
tool_count += 1
name = event.get("tool_name", "?")
args = event.get("args", {})
args_str = json.dumps(args, ensure_ascii=False)
print(f"\n ── \033[33m🔧 工具调用 #{tool_count}: {name}\033[0m")
# 只显示参数前 200 字符
if len(args_str) > 200:
args_str = args_str[:200] + "..."
print(f" 参数: {args_str}")
elif t == "tool_call_end":
name = event.get("tool_name", "?")
result = event.get("result", "")
result_str = str(result)
# 只显示结果摘要
summary = result_str[:150].replace("\n", " ")
if len(result_str) > 150:
summary += "..."
print(f" ── \033[32m✔ {name} 完成\033[0m")
print(f" 结果: {summary}")
elif t == "tokens":
token_info = event
total = event.get("total", "?")
inp = event.get("prompt", "?")
out = event.get("completion", "?")
print(f"\n ── \033[90m📊 Token: 输入={inp} 输出={out} 总计={total}\033[0m")
elif t == "status":
status = event.get("status", "")
if status:
print(f"\n ── \033[90m[状态: {status}]\033[0m")
elif t == "error":
print(f"\n ── \033[31m❌ 错误: {event.get('error', '未知')}\033[0m")
except KeyboardInterrupt:
print("\n ⏹ 用户中断,正在停止...")
client.stop()
except requests.RequestException as e:
print(f"\n ❌ 网络错误: {e}")
elapsed = time.time() - start_time
print()
print("-" * 60)
print(f" ✅ 回复完成 | 耗时: {elapsed:.1f}s | 字符数: {sum(len(t) for t in full_text)}")
if tool_count:
print(f" 🔧 工具调用次数: {tool_count}")
if token_info:
print(f" 📊 Token: {token_info.get('total_tokens', '?')}")
def test_chat_sync(client: AtomCodeDaemonClient, prompt: str):
"""测试 5: 同步模式"""
print("\n" + "=" * 60)
print("📝 测试 5: 同步模式(等待完整回复)")
print(f' 提示词: "{prompt}"')
print("=" * 60)
print()
start = time.time()
try:
result = client.chat_sync(prompt)
elapsed = time.time() - start
print(result)
print()
print("-" * 60)
print(f" ✅ 完成 | 耗时: {elapsed:.1f}s | 字符数: {len(result)}")
except Exception as e:
print(f" ❌ 出错: {e}")
# ──────────────────────────────────────────────
# 主入口
# ──────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="AtomCode Daemon 交互测试脚本",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"示例:\n"
" %(prog)s # 运行全部测试\n"
" %(prog)s --url http://localhost:13456\n"
" %(prog)s --prompt \"写个二分查找\" # 自定义提示词\n"
" %(prog)s --sync # 仅测试同步模式\n"
" %(prog)s --stream # 仅测试流式模式\n"
" %(prog)s --quick # 快速模式(跳过部分测试)"
),
)
parser.add_argument("--url", default="http://127.0.0.1:13456",
help="Daemon 地址 (默认: http://127.0.0.1:13456)")
parser.add_argument("--prompt", default="请简要介绍一下这个项目的目录结构",
help="测试用的提示词")
parser.add_argument("--stream", action="store_true",
help="仅测试流式模式")
parser.add_argument("--sync", action="store_true",
help="仅测试同步模式")
parser.add_argument("--quick", action="store_true",
help="快速模式:跳过模型列表和会话列表")
args = parser.parse_args()
client = AtomCodeDaemonClient(args.url)
print()
print("╔" + "═" * 58 + "╗")
print("║ AtomCode Daemon 交互测试脚本 ║")
print("║" + " " * 58 + "║")
print(f"║ 目标: {args.url:<46}║")
print("╚" + "═" * 58 + "╝")
# 步骤 1:健康检查(必须通过)
if not test_health(client):
print()
print("💡 提示:请先启动 Daemon:")
print(" atomcode daemon")
print("或者指定正确的地址:")
print(f" {sys.argv[0]} --url http://your-host:port")
sys.exit(1)
# 根据参数决定运行哪些测试
run_all = not (args.stream or args.sync)
if run_all or not args.quick:
# 正常模式:运行多数测试
if run_all or args.stream:
if not args.quick:
test_models(client)
test_sessions(client)
test_chat_stream(client, args.prompt)
if run_all or args.sync:
test_chat_sync(client, args.prompt)
elif args.quick:
# 快速模式:只跑核心流式测试
print("\n ⚡ 快速模式")
test_chat_stream(client, args.prompt)
print()
print("=" * 60)
print("✅ 全部测试完成")
print("=" * 60)
print()
if __name__ == "__main__":
main()
用法
# 运行全部测试(健康检查 → 模型列表 → 会话列表 → 流式对话 → 同步模式)
python3 test_atomcode_daemon.py
# 仅测试流式模式
python3 test_atomcode_daemon.py --stream
# 仅测试同步模式
python3 test_atomcode_daemon.py --sync
# 快速模式:跳过模型/会话列表,只跑核心对话
python3 test_atomcode_daemon.py --quick
# 自定义提示词和 Daemon 地址
python3 test_atomcode_daemon.py \
--url http://localhost:13456 \
--prompt "帮我把这个 Rust 项目结构画成 ASCII 目录树"
脚本核心架构
脚本包含两个核心组件:
① AtomCodeDaemonClient 类 — 轻量级 HTTP 客户端封装:
class AtomCodeDaemonClient:
def __init__(self, base_url="http://127.0.0.1:13456"):
self.base_url = base_url.rstrip("/")
self._session = requests.Session()
def health(self) -> bool # GET /health
def list_models(self) -> list # GET /models
def list_sessions(self) -> list # GET /sessions
def chat_stream(self, prompt) # POST /chat (SSE)
def chat_sync(self, prompt) -> str # POST /chat (等待完整回复)
def stop(self) # POST /chat/stop
def shutdown(self) # POST /shutdown
注意 chat_stream() 是生成器(yield 每个 SSE 事件),chat_sync() 在其基础上聚合文本。调用方可以灵活选用。
② 测试函数 — 五项独立测试用例,每项有清晰的输出格式:
# 流式对话测试(核心)
def test_chat_stream(client, prompt):
for event in client.chat_stream(prompt):
t = event.get("type")
if t == "reasoning": # 推理过程(灰色斜体)
print(f"\033[90m\033[3m{event['content']}\033[0m")
elif t == "text": # 文本输出
print(event["content"], end="", flush=True)
elif t == "tool_call_start":# 工具开始调用
print(f"\n 🔧 {event['tool_name']}: {event['args']}")
elif t == "tool_call_end": # 工具完成
print(f" ✔ {event['tool_name']} 完成")
elif t == "tokens": # Token 统计
print(f"📊 Token: {event}")
运行效果示例
╔══════════════════════════════════════════════════════════╗
║ AtomCode Daemon 交互测试脚本 ║
║ 目标: http://127.0.0.1:13456 ║
╚══════════════════════════════════════════════════════════╝
============================================================
📡 测试 1: 健康检查
============================================================
✅ Daemon 正在运行
============================================================
📋 测试 2: 获取可用模型
============================================================
✅ deepseek-v3 (deepseek)
✅ deepseek-v4-flash (deepseek)
============================================================
💬 测试 3: 查看历史会话
============================================================
✅ 共有 5 个历史会话
============================================================
🗣 测试 4: 流式对话
提示词: "请简要介绍一下这个项目的目录结构"
============================================================
## 项目目录结构简介
这是一个 **OpenHarmony** 项目...
── 📊 Token: 输入=7106 输出=47 总计=7153
------------------------------------------------------------
✅ 回复完成 | 耗时: 14.9s | 字符数: 1432
============================================================
📝 测试 5: 同步模式(等待完整回复)
提示词: "请简要介绍一下这个项目的目录结构"
============================================================
...(输出同上)...
------------------------------------------------------------
✅ 完成 | 耗时: 14.9s | 字符数: 1432
============================================================
✅ 全部测试完成
============================================================
test_atomcode_daemon.py 脚本相当于一个可运行的 API 参考实现——你可以把它作为起点,裁剪出适合自己场景的客户端代码。具体文件见项目目录下的 test_atomcode_daemon.py。
七、其他语言客户端
7.1 Node.js / TypeScript
// 最简单的 HTTP + SSE 客户端
const response = await fetch("http://127.0.0.1:13456/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: "分析这个项目",
}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
for (const line of text.split("\n")) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
if (event.type === "text") {
process.stdout.write(event.content);
}
}
}
}
7.2 Go
package main
import (
"bufio"
"encoding/json"
"fmt"
"net/http"
"strings"
)
func main() {
// 发起 POST /chat 请求
body := `{"message":"分析这个项目"}`
resp, _ := http.Post("http://127.0.0.1:13456/chat",
"application/json",
strings.NewReader(body))
defer resp.Body.Close()
// 逐行读取 SSE 流
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "data: ") {
var event map[string]interface{}
json.Unmarshal([]byte(line[6:]), &event)
if event["type"] == "text" {
fmt.Print(event["content"])
}
}
}
}
7.3 Rust
use reqwest::Client;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let body = serde_json::json!({
"message": "分析这个项目"
});
let mut stream = client
.post("http://127.0.0.1:13456/chat")
.json(&body)
.send()
.await?
.bytes_stream();
use futures_util::StreamExt;
let mut buf = String::new();
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
buf.push_str(&String::from_utf8_lossy(&chunk));
while let Some(newline) = buf.find('\n') {
let line = buf[..newline].trim().to_string();
buf = buf[newline + 1..].to_string();
if let Some(data) = line.strip_prefix("data: ") {
if let Ok(event) = serde_json::from_str::<Value>(data) {
if event["type"] == "text" {
print!("{}", event["content"].as_str().unwrap_or(""));
use std::io::Write;
std::io::stdout().flush()?;
}
}
}
}
}
Ok(())
}
7.4 cURL(测试用)
# 基本用法:流式输出到终端
curl -N -X POST http://127.0.0.1:13456/chat \
-H "Content-Type: application/json" \
-d '{"message":"分析项目结构"}'
# 用 jq 格式化事件
curl -N -s -X POST http://127.0.0.1:13456/chat \
-H "Content-Type: application/json" \
-d '{"message":"Hello"}' | \
while IFS= read -r line; do
[[ "$line" = data:* ]] && echo "$line" | sed 's/^data: //' | jq .
done
八、安全注意事项
⚠️ 无内置认证
Daemon 没有任何内置的身份认证。默认绑定 127.0.0.1 就是唯一的安全措施——只有本机能访问。
# ✅ 安全:只监听本机
atomcode daemon --host 127.0.0.1
# ❌ 危险!暴露到局域网或公网
atomcode daemon --host 0.0.0.0
🔒 为什么不能暴露
成功调用 /chat 的客户端可以获得完整的工具执行权限,包括:
bash— 执行任意shell命令write— 写入任意文件edit— 修改任意文件web_fetch— 发起网络请求web_search— 搜索互联网
这相当于完全控制你的开发机器。
🔐 远程访问方案
如果确实需要远程访问,建议使用反向代理:
客户端 ──HTTPS/TLS──► 反向代理 ──http──► atomcode-daemon
(nginx/caddy)
├── 认证 (OAuth/Basic Auth)
├── TLS 加密
└── 来源限制
🛡️ CORS 策略
Daemon 配置了 CORS,只允许 loopback 来源(localhost、127.0.0.1、::1,任意端口和协议)。即使通过 DNS 重绑定攻击,CORS 层也会拦截。
九、与主流协议的对比
AtomCode Daemon 的协议是自定义 HTTP + SSE,可以与当前主流的两大 AI Agent 协议进行对比:
9.1 与 IBM ACP (Agent Communication Protocol) 对比
IBM 的 ACP 是智能体间通信协议(REST,智能体 ↔ 智能体):
文档地址:https://www.ibm.com/cn-zh/think/topics/agent-communication-protocol
| 维度 | AtomCode Daemon | IBM ACP |
|---|---|---|
| 设计目的 | 客户端 ↔ 智能体 | 智能体 ↔ 智能体 |
| 传输层 | HTTP + SSE | HTTP REST |
| Agent 发现 | GET /models | GET /agents |
| 执行接口 | POST /chat (SSE) | POST /runs (JSON/SSE) |
| 消息格式 | 自定义 TurnEvent (JSON) | 标准 Message/Part |
| 会话管理 | REST CRUD 端点 | runs / threads |
| 认证 | 无(仅 loopback) | 可插拔 SecurityScheme |
| SDK | 无需,纯 HTTP | pip install acp-sdk |
9.2 与 JetBrains ACP (Agent Client Protocol) 对比
JetBrains/Zed 的 ACP 是编辑器与编码智能体间的协议(stdio JSON-RPC):
| 维度 | AtomCode Daemon | JetBrains ACP |
|---|---|---|
| 设计目的 | 通用客户端驱动 | 编辑器 ↔ 编码智能体 |
| 传输层 | HTTP + SSE | stdio JSON-RPC 2.0 |
| 启动方式 | 独立 HTTP 服务 | 编辑器 spawn 子进程 |
| 核心方法 | POST /chat | session/prompt |
| 流式方式 | SSE (text/event-stream) | session/update 通知 |
| 工具回调 | SSE 事件 | session/update + permission_request |
| 适用客户端 | 任何 HTTP 客户端 | 编辑器(Zed, JetBrains, VS Code) |
9.3 总体来说
AtomCode Daemon = 自定协议 (HTTP + SSE)
─ 最简单直接,任何语言都能对接
─ 专为 AtomCode 自身设计
IBM ACP = 智能体间通信标准 (REST)
─ 跨框架/跨组织智能体协作
─ 需要双方都实现 ACP
JetBrains ACP = 编辑器侧编码智能体标准 (stdio JSON-RPC)
─ 编辑器与编码智能体连线
─ 需要实现子进程通信
十、典型应用场景
10.1 IDE 插件集成
VS Code 扩展、JetBrains 插件通过 Daemon API 连接到 AtomCode,实现编辑器内 AI 编码辅助。
编辑器中点"AI 助手" → 发 POST /chat → SSE 流式展示代码生成
10.2 自动化脚本与 CI/CD
# 在 CI 中启动 daemon
atomcode daemon &
sleep 3 # 等启动
# 批量执行任务
python client.py "修复所有 lint 错误"
python client.py "生成单元测试"
python client.py "更新 README"
# 关闭 daemon
curl -X POST http://127.0.0.1:13456/shutdown
10.3 自定义 Web UI
# AtomCode 自带 WebUI
atomcode webui
# 或在浏览器中访问自建前端 → 发 POST /chat
10.4 多 Agent 编排
# 一个脚本来编排多个任务
client = AtomCodeDaemonClient()
tasks = [
"分析 src/ 目录中的代码质量",
"为 database.rs 生成单元测试",
"重构 utils.rs 中的重复代码",
]
for task in tasks:
print(f"\n{'='*60}")
print(f"任务: {task}")
print(f"{'='*60}")
result = client.chat_sync(task)
print(f"\n完成,结果长度: {len(result)} 字符")
10.5 长期会话保持
Daemon 的会话与 CLI 共享,跨启动持久化:
# 第一次对话
client.chat("帮我理解这个项目", on_text=print)
# 第二天继续——会话还在
client = AtomCodeDaemonClient()
sessions = client.list_sessions()
print(f"历史会话数: {len(sessions)}")
附:快速参考卡片
启动
atomcode daemon # 默认端口 13456
atomcode daemon --port 12345 # 自定义端口
检查
curl http://127.0.0.1:13456/health
curl http://127.0.0.1:13456/models
调用
curl -N -X POST http://127.0.0.1:13456/chat \
-H "Content-Type: application/json" \
-d '{"message":"你好"}'
停止
curl -X POST http://127.0.0.1:13456/chat/stop # 停止当前对话
curl -X POST http://127.0.0.1:13456/shutdown # 关闭 daemon
文档版本: v1.1 | 适用 AtomCode 版本: v4.25.9+
参考: AtomCode 官方文档 - Headless & Daemon
仓库: https://atomgit.com/atomgit_atomcode/atomcode
690

被折叠的 条评论
为什么被折叠?



