Skip to content

技术调研:为什么选这些技术?

读完这篇文档,你会理解 miniOpenClaw 用到的每一项技术选型背后的原因,以及它们的通俗含义。


一、编程语言:为什么用 Python 3.11+?

不是因为"大家都用",而是因为真的合适

选型理由说明
AI 生态最完善OpenAI SDK、tiktoken、主流 AI 库都是 Python 优先
异步编程成熟Python 3.11 的 asyncio 性能大幅提升,TaskGroup 等新特性简化并发代码
学习门槛低语法简洁,适合教学项目
类型标注完善3.11+ 的类型系统让代码更易读、IDE 提示更好

Python 3.11+ 的关键新特性(白话版)

特性白话解释在本项目的应用
StrEnum让枚举值直接就是字符串,不用每次转换MessageType.TEXT 直接就是 "text",JSON 序列化零成本
asyncio.TaskGroup一群异步任务,一个出错全部取消,更安全EventBus 并发执行多个事件处理器
asyncio.timeout给一段代码设置"最多等多久"Agent 推理循环的超时保护
异常组 ExceptionGroup多个错误可以同时汇报并发任务的错误处理

二、异步编程(asyncio):为什么不用"普通"代码?

同步 vs 异步——用餐厅服务员来理解

同步编程就像一个笨拙的服务员:

服务员走到 1 号桌 → 点菜 → 等厨房做好 → 上菜 → 才去 2 号桌

如果 1 号桌点了一道复杂的菜要等 10 分钟,2 号桌就得干等 10 分钟

异步编程就像一个机灵的服务员:

服务员走到 1 号桌 → 点菜 → 告诉厨房 → 不等了,去 2 号桌
等厨房喊"1 号桌的菜好了" → 再回来上菜

等待的时间不浪费,去服务别的客人。

为什么 AI Agent 需要异步?

AI Agent 有大量的"等待时间":

操作等待时间如果不异步
调用 LLM(大模型)1-10 秒所有用户排队干等
执行 HTTP 工具(网络请求)0.5-5 秒一个工具调用卡住整个系统
读写文件存储几十毫秒频繁 IO 累积起来很可观
WebSocket 收发消息不确定一个连接卡住影响所有连接

用异步编程,一个进程就能同时服务几十上百个用户,不需要启动很多进程或线程。

代码长什么样?

python
# 同步写法(一个一个等)
def handle_users():
    result1 = call_llm(user1_message)    # 等 3 秒
    result2 = call_llm(user2_message)    # 再等 3 秒
    # 总共 6 秒

# 异步写法(同时等)
async def handle_users():
    task1 = call_llm(user1_message)      # 发出请求,不等
    task2 = call_llm(user2_message)      # 发出请求,不等
    result1, result2 = await asyncio.gather(task1, task2)
    # 总共约 3 秒(两个请求同时在等)

三、通信协议:WebSocket vs HTTP

打电话 vs 发短信

对比WebSocket(打电话)HTTP(发短信)
连接方式一直保持连接,双方随时说话发一条回一条,每次重新连接
实时性高,服务端可以随时推送低,客户端必须主动问
适合场景聊天、实时通知、流式输出简单查询、表单提交
资源消耗长连接占用资源用完即释放

miniOpenClaw 的选择

  • Gateway 用 WebSocket:Agent 需要"实时对话"能力,服务端可能要主动推送消息(比如流式输出)
  • WebChat Channel 同时支持两种:HTTP POST 方便测试和脚本调用,WebSocket 支持实时聊天
  • Webhook Channel 用 HTTP:第三方平台回调天然就是 HTTP

这样设计兼顾了"实时性"和"便利性"。


四、数据校验:Pydantic 是干什么的?

类比:快递单格式校验

想象你寄快递,快递单上要填:收件人、电话、地址、重量。如果你漏填了电话,或者把重量写成了"很重"(应该写数字),快递员会当场拒收。

Pydantic 就是程序里的"快递单格式检查员"

python
from pydantic import BaseModel

class GatewayMessage(BaseModel):
    msg_id: str           # 必须是字符串
    msg_type: MessageType # 必须是规定的几种类型之一
    source: str           # 必须是字符串
    payload: Any          # 什么都行
    timestamp: datetime   # 必须是时间格式

如果有人传了一个 msg_type = "hahaha"(不在规定类型里),Pydantic 会立刻报错,而不是让这个"格式不对的快递"一路流转到系统深处才出问题。

为什么不用普通的 dict?

对比普通 dictPydantic Model
格式检查无,随便塞什么都行自动检查类型、必填项、取值范围
序列化手写 json.dumps内置 .model_dump_json()
IDE 提示无,msg["typ"] 写错了也不知道有,msg.msg_type 拼错会报红
文档要另外写代码本身就是文档

五、各依赖库的选择理由

5.1 websockets —— WebSocket 服务端

为什么选它说明
官方推荐Python 生态中最成熟的 WebSocket 库
纯异步天然支持 asyncio,不需要额外的适配层
轻量不像 aiohttp 那样是个全功能 Web 框架,只做 WebSocket 一件事

类比:如果 aiohttp 是一把瑞士军刀,websockets 就是一把专业螺丝刀——做一件事,做到最好。

5.2 aiohttp —— HTTP 服务端

为什么选它说明
异步原生从设计之初就是异步的,不是后来改的
功能完整路由、中间件、WebSocket(服务端)、静态文件——Web 服务该有的都有
与 asyncio 一致和项目的异步风格完全匹配

用在哪:WebChat Channel(HTTP /chat 接口)、Webhook Channel(接收外部平台回调)。

为什么不用 Flask/FastAPI?

  • Flask 是同步的,和我们的异步设计冲突
  • FastAPI 功能更强但更重(依赖 Starlette + Uvicorn),对教学项目来说太厚

5.3 httpx —— HTTP 客户端

为什么选它说明
同时支持同步和异步httpx.AsyncClient 完美配合 asyncio
现代 APIrequests 更现代,类型标注完善
HTTP/2 支持面向未来

用在哪:内置的 http_request 工具——让 Agent 能发 HTTP 请求获取信息。

类比:如果说 requests 是功能手机(经典好用),httpx 就是智能手机(经典功能都有,还多了很多新功能)。

5.4 openai —— LLM 调用客户端

为什么选它说明
官方 SDK与 OpenAI API 100% 对齐,类型完善
兼容性好大量第三方 LLM 服务(Azure、国内模型等)都兼容 OpenAI API 格式
异步支持AsyncOpenAI 完美配合我们的异步设计
Function Calling内置支持 tools/function_calling,和我们的工具系统无缝对接

5.5 tiktoken —— Token 计数

什么是 Token?

LLM 不是按"字"来处理文本的,而是按 "Token" 来的。Token 是 LLM 的"最小处理单位":

文本大约 Token 数
"你好"约 2 个
"Hello"1 个
"Hello, World!"约 4 个

为什么要计数? 因为 LLM 有上下文长度限制(比如最多 128K token)。如果对话太长,需要截断旧消息。tiktoken 帮我们精确计算消息占了多少 Token。

类比:就像打电话有通话时长限制,tiktoken 帮你看"还剩多少分钟"。

5.6 python-dotenv —— 环境变量管理

解决什么问题? API Key 这种敏感信息不能写在代码里(万一提交到 GitHub 就暴露了)。python-dotenv 让你把敏感信息写在 .env 文件里,代码自动读取。

bash
# .env 文件(不提交到 Git)
OPENAI_API_KEY=sk-你的密钥
python
# 代码里自动读取
import os
api_key = os.getenv("OPENAI_API_KEY")

类比:就像密码不写在便利贴上,而是放在保险箱里——需要的时候去取。


六、为什么不用 LangChain?—— 详细对比

LangChain 是目前最流行的 AI Agent 框架,很多人的第一反应是"直接用 LangChain 不就好了"。我们来做一个诚实的对比:

LangChain 帮你做了什么

功能LangChain 做法miniOpenClaw 做法
LLM 调用封装好了,一行代码自己用 OpenAI SDK,理解每个参数
工具调用大量内置工具,直接用自己写 Tool 基类和 @tool 装饰器
记忆管理多种 Memory 类型开箱即用自己实现短期/长期记忆
链式调用Chain/LCEL 声明式编排自己写 ReAct 循环
向量检索集成了几十种向量库简单关键词匹配(教学足够)

但 LangChain 也有代价

问题说明
抽象层太多一个简单的 LLM 调用要经过 5-6 层封装,出错了很难定位
版本变动大API 经常大改,今天写的代码明天可能就跑不了
学会了"用框架"而不是"理解原理"离开 LangChain 就不会写了
依赖树庞大装一个 LangChain 带进来几百个依赖

总结一句话

LangChain 适合"快速做产品",miniOpenClaw 适合"深入学原理"。 先理解了原理,再用框架才能用得明明白白。


七、测试工具

工具用途白话解释
pytest运行测试自动检查"代码是否正确"的工具
pytest-asyncio测试异步代码让 pytest 能测 async def 函数
pytest-cov覆盖率统计告诉你"有多少代码被测试覆盖了"
ruff代码检查自动检查代码风格问题(缩进、命名、导入顺序等)

接下来读什么?

你已经了解了整个项目的需求、架构和技术选型。现在可以开始动手了: