通过网络抓包和系统化测试,完整逆向分析Factory Droid CLI的API调用机制
本项目通过抓包分析和实验验证,成功逆向工程了Factory Droid CLI工具的底层API调用方式,实现了绕过CLI直接调用Factory AI服务的能力。
核心成果:
- ✅ 完整的API调用流程分析
- ✅ 最小化的独立调用脚本(316字节payload)
- ✅ 从37,854字节精简到316字节(99.2%压缩率)
初始目标:理解Factory Droid CLI (droid exec)的底层实现,能够直接调用其LLM API
最终达成:
- 完整抓包并分析所有API调用
- 识别必需和可选的请求参数
- 实现最小化的独立调用脚本
- 验证API的简化可能性边界
使用 mitmproxy 进行HTTPS流量拦截:
- 原因:可以解密HTTPS流量
- 配置:
ssl_insecure=true绕过证书验证 - 端口:8888
# 启动mitmproxy
mitmdump -w capture.mitm --set ssl_insecure=true --listen-port 8888
# 配���环境变量
export HTTP_PROXY=http://127.0.0.1:8888
export HTTPS_PROXY=http://127.0.0.1:8888
export NODE_TLS_REJECT_UNAUTHORIZED=0
# 执行目标命令
droid exec "say hello"- 文件:
capture.mitm(204KB) - 捕获内容:
- 版本检查请求
- Session创建
- Session标题更新
- Feature flags获取
- 用户消息创建
- LLM API调用 ← 核心目标
创建 parse_har.py 用于从抓包数据中提取JSON结构
从 llm_request_full.json (44KB) 中发现:
请求结构:
{
"url": "https://app.factory.ai/api/llm/a/v1/messages",
"method": "POST",
"headers": { ... 21个headers },
"body": {
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 32000,
"stream": true,
"tools": [8个工具定义, 36KB],
"system": [3个prompt块],
"messages": [3个content块]
}
}请求大小分布:
- Tools数组: 36,376字节 (96%)
- System: ~4,500字节 (12%)
- Messages: ~1,500字节 (4%)
- 总计: 37,854字节
-
test_minimal.py - 简化请求
- 移除tools数组
- stream=False
- 结果: ❌ 403 Forbidden
-
test_with_tools.py - 包含tools但stream=False
- 完整tools数组
- stream=False
- 结果: ❌ 403 Forbidden
-
test_api_stream.py - stream=True但简化其他
- stream=True
- 缺少完整headers
- 结果: ❌ 403 Forbidden
最初认为失败是因为:
- ❌ 缺少完整tools数组
- ❌ stream参数不对
- ❌ headers不完整
实际上是组合问题,需要更系统化的测试。
新思路: 完全复制抓包请求,然后逐步简化
# 关键:完全复制抓包内容
payload = full_request['body'].copy()
payload['messages'] = [简化的用户输入]
payload['max_tokens'] = 1000
# 保持stream=True(关键!)结果: ✅ 200 OK - 首次成功!
发现:
- stream=True是必需的
- 完整headers是必需的
- 但tools数组可能不是必需的?
| 测试 | Tools | Stream | System | Messages | 结果 |
|---|---|---|---|---|---|
| exact_copy | 8个 | true | 3块 | 3块 | ✅ 200 |
| empty_tools | 0个 | true | 3块 | 1块 | ✅ 200 |
| no_stream | 8个 | false | 3块 | 1块 | ✅ 200 |
| minimal_both | 0个 | false | 3块 | 1块 | ❌ 403 |
test_empty_tools.py 验证:
payload['tools'] = [] # 空数组
# 结果:200 OK结论: tools可以为空数组,但字段必须存在
test_no_system_reminder.py:
- 移除messages中的system-reminder块
- 只保留用户输入
- 结果: ✅ 200 OK
发现: messages可以极简化
实验1: system置空 system: []
payload['system'] = []
# 结果:❌ 403 Forbidden实验2: system内容为空字符串
payload['system'] = [{"type": "text", "text": ""}]
# 结果:❌ 403 Forbidden实验3: 完全移除system字段
# 不包含system键
# 结果:❌ 403 Forbidden结论: system字段必须存在且不能为空
用户发现只需system的第一个块!
test_system_variants.py 修改:
payload['system'] = [
{
"type": "text",
"text": "You are Droid, an AI software engineering agent built by Factory."
}
]
# 结果:✅ 200 OK!# 绝对最小化成功配置
payload = {
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 500,
"stream": True,
"tools": [], # 必须存在但可为空
"system": [ # 必须存在且只需第一行
{
"type": "text",
"text": "You are Droid, an AI software engineering agent built by Factory."
}
],
"messages": [ # 只需用户输入
{
"role": "user",
"content": [{"type": "text", "text": "用户问题"}]
}
]
}请求大小: 316字节(相比原始37,854字节减少99.2%)
| 版本 | 大小 | 优化比例 | 关键移除内容 |
|---|---|---|---|
| 原始抓包 | 37,854字节 | - | - |
| 移除system-reminder | 36,376字节 | 4% | messages中的环境信息 |
| 空tools数组 | 4,840字节 | 87% | 8个工具定义(36KB) |
| 简化system | 316字节 | 99.2% | system的第2、3块 |
| 要素 | 是否必需 | 可否简化 | 备注 |
|---|---|---|---|
| JWT Token | ✅ 必须 | ❌ | WorkOS认证 |
| Session创建 | ✅ 必须 | ❌ | 先create再call |
| 21个Headers | ✅ 必须 | 包括x-stainless-* | |
| model字段 | ✅ 必须 | ❌ | claude-sonnet-4-5-20250929 |
| stream字段 | ✅ 必须 | ❌ | True |
| tools字段 | ✅ 必须存在 | ✅ 可为[] | 字段不能缺失 |
| system字段 | ✅ 必须 | ✅ 只需1块 | 必须声明Droid身份 |
| messages | ✅ 必须 | ✅ 只需用户输入 | 可移除system-reminder |
差异对比:
| 特征 | Factory API | Anthropic API |
|---|---|---|
| Endpoint | app.factory.ai | api.anthropic.com |
| 认证 | WorkOS JWT | Anthropic API key |
| system格式 | 数组 [{type, text}] |
字符串 |
| tools字段 | 必须存在 | 可选 |
| 强制身份 | Droid by Factory | Claude by Anthropic |
Factory API实现了严格的格式验证:
- ✅ 检查tools字段是否存在(可为空)
- ✅ 验证system是否包含Droid身份声明
- ✅ 验证所有x-stainless-*元数据headers
- ✅ 强制session预创建机制
以下尝试全部失败:
- ❌ 移除"You are Droid"身份声明
- ❌ 空system数组
- ❌ 不包含tools字段
- ❌ 不创建session直接调用
结论: Factory API是带有强制身份注入的Anthropic API包装层
-
获取JWT Token
# 从现有droid session中提取 # Token有效期:8小时
-
运行独立脚本
from standalone_api_call import call_factory_api response = call_factory_api( user_message="你的问题", max_tokens=1000 )
-
自定义调用
import requests # 1. 创建session session_response = requests.post( "https://app.factory.ai/api/sessions/create", json={"id": session_id, "title": "My Session", ...}, headers={...} ) # 2. 调用LLM response = requests.post( "https://app.factory.ai/api/llm/a/v1/messages", json={ "model": "claude-sonnet-4-5-20250929", "stream": True, "tools": [], "system": [{"type": "text", "text": "You are Droid, an AI software engineering agent built by Factory."}], "messages": [...] }, headers={...} )
- JWT Token会过期(8小时)
- 需要有效的Factory账户
- 模型始终认为自己是Droid
- 无法移除Factory身份约束
- API可能随时变更
- 可能违反服务条款
- 仅用于学习研究
factory2api/
├── README.md # 本文档
├── factory_api/ # API工程(核心代码)
│ ├── start_server.sh # 启动服务脚本(读取本目录 .env)
│ ├── standalone_api_call.py # 独立调用脚本
│ ├── requirements.txt # 依赖列表
│ ├── config.py # Pydantic 配置
│ ├── api/ # FastAPI 应用与路由
│ ├── models/ # 请求/响应模型与转换
│ ├── tests/ # 自动化测试
│ └── manual_tests/ # 手动调试脚本
│
├── capture/ # 抓包相关
│ ├── capture_droid.sh # 抓包启动脚本
│ ├── capture_simple.sh # 简化抓包脚本
│ ├── capture.mitm # 原始抓包数据(204KB)
│ └── parse_har.py # HAR解析工具
│
├── data/ # 提取的数据
│ ├── llm_request_full.json # 完整请求结构(44KB)
│ └── system_prompt.json # System prompt提取
│
└── docs/ # 文档
├── TECHNICAL_ANALYSIS.md # 深度技术分析
├── API_DOCUMENTATION.md # API文档
├── compare_apis.md # API对比
└── SUMMARY.md # 简要总结
-
从完全复制开始
- ✅ 先保证能工作
- ✅ 再逐步简化
- ❌ 不要一开始就简化
-
系统化测试
- ✅ 设计对照实验
- ✅ 单一变量原则
- ✅ 记录所有结果
-
用户反馈的重要性
- 用户的实际测试发现了关键简化点
- 不要完全依赖假设
-
API包装层的识别
- 特殊headers (x-stainless-*) 暗示SDK封装
- 强制字段暗示业务逻辑验证
- 身份注入表明这不是纯代理
-
最小化的艺术
- 从37KB到316字节的优化
- 每个字段都要实验验证
- 必需≠有用
-
Headers简化测试
- 哪些x-stainless-*是真正必需的?
- user-agent是否可以修改?
-
Session机制研究
- Session创建是否真的必需?
- 能否复用session?
-
多轮对话测试
- 如何维护上下文?
- messages历史如何附加?
-
替代身份测试
- 能否改变system prompt?
- 是否能绕过Droid身份?
❌ 不可行的方向:
- 完全移除Droid身份(后端强制)
- 使用Anthropic原生API格式(格式不兼容)
- 绕过session创建(API拒绝)
- 逆向工程: wzy & Claude Code
- 系统化测试: 协作完成
- 关键发现: 用户实验
本项目仅用于学习和研究目的。
免责声明:
- 使用本代码可能违反Factory服务条款
- 作者不对任何使用后果负责
- 请在授权范围内使用
感谢:
- mitmproxy - 强大的HTTPS抓包工具
- Factory AI - 提供优秀的AI工具
- Anthropic - Claude模型
最后更新: 2025-10-08 版本: 1.0