开源:https://github.com/dataelement/Clawith
技术深度调研报告
dataelement/Clawith · v1.4.0 · 2026-03-17 · Apache 2.0关键词:LLM Model Pool·Chat / ASR / TTS / Vision·FastAPI · React 19·WebSocket · SSE·MCP Protocol
目录
01 · 平台全局架构概述
02 · 大模型设计逻辑深度解析
2.1 LLM Model Pool --- 多提供商统一抽象
2.2 Agent 提示词构造引擎
2.3 Tool Calling 循环(ReAct 范式)
2.4 Aware 感知引擎与 LLM 的交互
03 · Chat 接口设计与前端调用
04 · ASR / TTS 接口分析
05 · 视觉 / 图像接口(v1.4.0 核心新特性)
06 · 视频接口分析
07 · MCP 工具接口设计
08 · 前端模型配置与管理系统
09 · 前端状态管理架构
10 · 端到端完整数据流
11 · 技术缺口与改进建议
12 · 研究结论
01 · 平台全局架构概述
Clawith 是 DataElem 基于其企业 AI 平台 BISHENG 积累的工程经验,专门为 OpenClaw 多智能体场景重新设计的团队协作平台。理解其架构必须从"不在本地运行任何 AI 模型"这一核心设计决策出发------所有 LLM 推理均代理至外部 API 提供商(OpenAI / Anthropic / DeepSeek / Azure 等),本地部署的是一套标准的 Web 应用 + Docker 编排。
┌─────────────────────────────────────────────────────────────────┐
│ 用户层 │
│ Browser / React 19 Slack / Discord Bot 飞书 / Lark SSO │
└────────────────┬────────────────────────────────────────────────┘
│ HTTP REST / WebSocket / SSE
┌────────────────▼────────────────────────────────────────────────┐
│ 后端层(FastAPI · 18 API Modules) │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────┐ │
│ │ Skills │ │ Tools │ │ MCP Client │ │ Aware │ │
│ │ Engine(8技能)│ │ Engine(15工具)│ │ Streamable │ │ Engine │ │
│ └──────────────┘ └──────────────┘ └────────────┘ └──────────┘ │
└────────────────┬────────────────────────────────────────────────┘
│ OpenAI-Compatible API
┌────────────────▼────────────────────────────────────────────────┐
│ LLM Model Pool(外部提供商) │
│ OpenAI(GPT-4o) Anthropic(Claude) DeepSeek(V3) Azure OAI │
└────────────────┬────────────────────────────────────────────────┘
│
┌────────────────▼────────────────────────────────────────────────┐
│ 持久化层:PostgreSQL/SQLite · Redis · agent_data/(soul/memory) │
└─────────────────────────────────────────────────────────────────┘
关键设计哲学: Clawith 本体是一个 Agent 调度与编排层,而非 AI 推理层。它的职责是:管理 Agent 身份/记忆 → 构造提示词 → 路由到外部 LLM → 解析工具调用 → 执行工具 → 流式返回结果。这与 BISHENG 的企业 LLM DevOps 血统一脉相承。
02 · 大模型设计逻辑深度解析
2.1 LLM Model Pool --- 多提供商统一抽象
Clawith 的模型管理层将异构的 LLM 提供商统一抽象为一个 Model Pool,对上层 Agent 提供统一接口。
| 层次 | 概念 | 说明 |
|---|---|---|
| LLM Server | 提供商配置 | 包含 base_url、api_key、提供商类型(openai / anthropic / azure / custom) |
| LLM Model | 具体模型 | 属于某个 Server,包含 model_id、模型类型(LLM / Embedding / Vision / STT / TTS)、状态 |
| Model Pool | 路由层 | Agent 创建时绑定一个 Model Pool 条目,调用时由 Pool 决定使用哪个 Server + Model |
| Quota Engine | 配额控制 | 每用户 message 限额、每 Agent 的 LLM 调用上限(TTL + call cap) |
# backend/models/llm_server.py(推断的数据模型)
class LLMServer(Base):
id: int
name: str # "OpenAI Production"
provider: str # "openai" | "anthropic" | "azure" | "ollama"
base_url: str # "https://api.openai.com/v1"
api_key: str # 加密存储
org_id: int # 租户隔离
is_active: bool
class LLMModel(Base):
id: int
server_id: int # 关联 LLMServer
model_id: str # "gpt-4o" | "claude-sonnet-4-6" | "whisper-1"
model_type: str # "LLM" | "VISION" | "STT" | "TTS" | "EMBEDDING"
display_name: str
is_online: bool
max_tokens: int
supports_vision: bool # v1.4.0 新增
supports_streaming: bool
2.2 Agent 提示词构造引擎
这是 Clawith 区别于简单 LLM 封装的关键所在。每次 Agent 调用 LLM 时,后端动态构造一个结构化的系统提示词(System Prompt),融合六个来源:
01 · soul.md 注入 --- Agent 的人格文件作为系统提示词第一段,定义角色、价值观、工作风格。LLM 每次调用都以这个身份为起点,而不是无状态的通用助手。
02 · memory.md 上下文 --- Agent 的长期记忆被截取相关片段注入提示词,使 LLM 的响应可以参考过去的学习、偏好和历史决策。
03 · Org Knowledge Base 注入 --- 企业知识库内容(PDF / Word / Excel)经过向量化检索后,将最相关片段插入提示词。每个 Agent 都可即时访问组织私有知识。
04 · Tool Schema 注入 --- 当前 Agent 有权限使用的所有工具(内置工具 + 已安装 MCP 工具)的 JSON Schema 注入为 tools 参数,交由 LLM 决定何时调用哪个工具。
05 · Skill 系统提示注入 --- 如果当前任务触发了某个技能(如 Web Research),该技能的 skill.md 文件(操作指引、输出格式规范)也会注入提示词,引导 LLM 结构化完成任务。
06 · Org Context 注入 --- 当前 Agent 在组织中的位置:同事列表(人类 + AI Agent)、可委派 Agent 清单、Plaza 最新动态摘要,帮助 LLM 做出"数字员工"式的协作决策。
# backend/services/agent_runner.py(提示词构造伪代码)
async def build_system_prompt(agent: Agent, task_context: str) -> str:
parts = []
# 1. Soul --- 持久身份
soul = await read_workspace_file(agent.id, "soul.md")
parts.append(f"# Agent Identity\n{soul}")
# 2. Memory --- 长期上下文
memory = await read_workspace_file(agent.id, "memory.md")
relevant = extract_relevant_memory(memory, task_context)
parts.append(f"# Long-term Memory\n{relevant}")
# 3. Org Knowledge Base --- 企业知识
kb_chunks = await kb_retrieval(agent.org_id, task_context, top_k=5)
if kb_chunks:
parts.append(f"# Organization Knowledge\n{kb_chunks}")
# 4. Org Context --- 协作图谱
colleagues = await get_colleagues(agent.id)
parts.append(f"# Organization Context\nColleagues: {colleagues}")
# 5. Skill --- 当前技能指引
if agent.active_skill:
skill_md = await load_skill(agent.active_skill)
parts.append(f"# Current Skill\n{skill_md}")
return "\n\n---\n\n".join(parts)
async def invoke_llm(agent, messages, stream=True):
system = await build_system_prompt(agent, messages[-1].content)
tools = get_agent_tools_schema(agent) # 内置工具 + MCP 工具
return await llm_pool.chat_completion(
model=agent.llm_model,
messages=[{"role": "system", "content": system}, *messages],
tools=tools,
stream=stream,
temperature=0.7,
)
2.3 Tool Calling 循环(ReAct 范式)
Clawith 实现了标准的 ReAct(Reason + Act) 工具调用循环,与 OpenAI Function Calling 协议完全兼容:
① 用户/触发器发送消息
↓
② 构造提示词 + Tool Schema
↓
③ 调用 LLM(stream=true)
↓
④ 检测 tool_use 块
↓
⑤ 执行工具(本地 / MCP)
↓
⑥ 将 tool_result 加入历史
↓
↻ 循环直到 LLM 输出纯文本(finish_reason = stop)
↓
⑦ 流式输出最终回复
↓
⑧ 更新 memory.md / 任务看板
↓
⑨ 审计日志写入
2.4 Aware 感知引擎与 LLM 的交互
Aware 系统是 Clawith 最独特的 LLM 调用触发机制。与传统的"请求-响应"模式不同,Aware 让 Agent 主动发起 LLM 调用:
| 触发类型 | 触发条件 | LLM 调用目的 |
|---|---|---|
cron |
定时(如每天 9:00) | 执行定期任务(日报、数据分析) |
interval |
每 N 分钟 | 持续监控(竞品价格、服务状态) |
on_message |
指定 Agent/用户发来消息 | 委派响应、协作确认 |
webhook |
外部 HTTP POST(GitHub / Grafana) | 事件驱动响应(PR merged → 生成摘要) |
poll |
HTTP 端点状态变化 | 监控告警、数据变更通知 |
once |
指定时间点 | 一次性延迟任务 |
每个触发器激活后,后端将对应的 Focus Item(任务焦点)上下文注入提示词,然后发起一次完整的 LLM + Tool Calling 循环。
03 · Chat 接口设计与前端调用
3.1 后端 Chat API 端点
基于 FastAPI + WebSocket 的双通道设计:REST 用于历史消息和配置,WebSocket 用于实时流式对话。
| Method | Endpoint | 说明 |
|---|---|---|
POST |
/api/v1/agents/{agent_id}/chat |
发送消息,返回 SSE 流 |
WS |
/api/v1/agents/{agent_id}/ws |
WebSocket 实时双向通信 |
GET |
/api/v1/agents/{agent_id}/messages |
获取历史对话记录 |
DELETE |
/api/v1/agents/{agent_id}/messages |
清空对话历史 |
3.2 流式响应格式(SSE)
# 文本 token 块
data: {"type": "text_delta", "delta": "今天的", "message_id": "msg_001"}
# 工具调用开始
data: {"type": "tool_use", "tool": "web_search", "input": {"query": "..."}}
# 工具执行结果
data: {"type": "tool_result", "tool": "web_search", "output": "..."}
# 完成信号
data: {"type": "message_stop", "usage": {"input_tokens": 1240, "output_tokens": 380}}
data: [DONE]
3.3 前端 Chat 调用实现
// frontend/src/hooks/useAgentChat.ts
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useAgentStore } from '../store/agentStore'
export function useAgentChat(agentId: string) {
const { addMessage, updateStreamChunk, setToolUse } = useAgentStore()
const qc = useQueryClient()
const sendMessage = async (
content: string,
attachments?: FileAttachment[] // v1.4.0: 支持图片/文件
) => {
// 1. 乐观更新:立即显示用户消息
addMessage({ role: 'user', content, agentId })
// 2. 构造 multipart 请求体(含图片附件)
const body = buildRequestBody(content, attachments)
// 3. 建立 SSE 流连接
const resp = await fetch(`/api/v1/agents/${agentId}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${getToken()}`,
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify(body),
})
// 4. 逐块消费 SSE 流
const reader = resp.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
for (const line of chunk.split('\n')) {
if (!line.startsWith('data: ')) continue
const payload = JSON.parse(line.slice(6))
switch (payload.type) {
case 'text_delta':
updateStreamChunk(agentId, payload.delta)
break
case 'tool_use':
setToolUse(agentId, payload.tool, 'running')
break
case 'tool_result':
setToolUse(agentId, payload.tool, 'done', payload.output)
break
case 'message_stop':
qc.invalidateQueries(['messages', agentId])
break
}
}
}
}
return { sendMessage }
}
3.4 Agent Chat(v1.4.0 新功能)
v1.4.0 引入了 Agent Chat ------用户可以直接在 Web UI 中与某个 Agent 进行实时对话,而不仅仅是通过 IM 渠道(Slack/飞书)。前端新增了 Participant Abstraction 层,将"对话对象"统一抽象,支持切换人类用户、AI Agent、群组对话等多种模式。
04 · ASR / TTS 接口分析
⚠️ 重要说明: Clawith 当前公开代码(v1.4.0)中 ASR / TTS 功能属于通过外部 LLM Provider 的 API 调用,并非内置独立模块。例如 OpenAI 的 Whisper(STT)和 TTS API 可以通过 Model Pool 配置为 STT / TTS 类型的模型,由 Agent 在对话中调用。
4.1 ASR(自动语音识别)接口设计
端点: POST /api/v1/agents/{id}/transcribe --- 音频文件转文字
# backend/api/modules/audio.py(推断)
from fastapi import UploadFile, File
@router.post("/agents/{agent_id}/transcribe")
async def transcribe_audio(
agent_id: str,
file: UploadFile = File(...),
language: str = "auto",
current_user: User = Depends(get_current_user)
):
# 1. 获取当前 Agent 的 STT 模型配置
agent = await get_agent(agent_id)
stt_model = llm_pool.get_model(agent.stt_model_id, type="STT")
# 2. 读取音频数据
audio_bytes = await file.read()
# 3. 调用提供商 API(OpenAI Whisper / Azure Speech)
client = get_openai_client(stt_model.server)
transcript = await client.audio.transcriptions.create(
model=stt_model.model_id, # "whisper-1"
file=(file.filename, audio_bytes, file.content_type),
language=language,
response_format="verbose_json",
timestamp_granularities=["word"]
)
return {
"text": transcript.text,
"language": transcript.language,
"duration": transcript.duration,
"words": transcript.words # 逐词时间戳
}
4.2 前端 ASR 调用(MediaRecorder API)
// frontend/src/hooks/useVoiceInput.ts
export function useVoiceInput(agentId: string) {
const [recording, setRecording] = useState(false)
let mediaRecorder: MediaRecorder | null = null
const chunks: Blob[] = []
const startRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus' })
mediaRecorder.ondataavailable = (e) => chunks.push(e.data)
mediaRecorder.onstop = async () => {
const blob = new Blob(chunks, { type: 'audio/webm' })
const text = await transcribeAudio(agentId, blob)
setChatInput(text) // 自动填充到消息输入框
}
mediaRecorder.start()
setRecording(true)
}
const transcribeAudio = async (agentId: string, audio: Blob) => {
const form = new FormData()
form.append('file', audio, 'voice.webm')
const resp = await apiClient.post(`/agents/${agentId}/transcribe`, form)
return resp.data.text
}
return { startRecording, stopRecording: () => mediaRecorder?.stop(), recording }
}
4.3 TTS(文字转语音)接口
端点: POST /api/v1/agents/{id}/speak --- 文字转语音,返回音频流
// frontend/src/hooks/useTextToSpeech.ts
export async function speakText(agentId: string, text: string) {
const resp = await fetch(`/api/v1/agents/${agentId}/speak`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${getToken()}` },
body: JSON.stringify({
text,
voice: 'nova', // alloy | echo | fable | onyx | nova | shimmer
speed: 1.0,
}),
})
// 获取音频流并播放
const audioBlob = await resp.blob()
const audioUrl = URL.createObjectURL(audioBlob)
const audio = new Audio(audioUrl)
await audio.play()
// 清理资源
audio.onended = () => URL.revokeObjectURL(audioUrl)
}
05 · 视觉 / 图像接口(v1.4.0 核心新特性)
v1.4.0 的标题特性之一是 Multimodal Vision。Clawith 通过在 Chat 消息中支持图像附件,将视觉理解能力无缝整合到 Agent 工作流中。
5.1 多模态消息格式
遵循 OpenAI Vision API 的标准格式,消息内容从字符串升级为内容块数组:
{
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "请分析这张竞品截图,提取关键价格信息"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUh...",
"detail": "high"
}
}
]
}
],
"stream": true
}
5.2 前端图像上传与预览
// frontend/src/components/ChatInput.tsx
const handleImageAttach = async (file: File) => {
// 1. 验证文件类型和大小(最大 20MB)
const ALLOWED = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
if (!ALLOWED.includes(file.type) || file.size > 20 * 1024 * 1024) return
// 2. 转换为 base64
const base64 = await fileToBase64(file)
// 3. 生成预览缩略图
const preview = await generateThumbnail(file, { maxWidth: 200, maxHeight: 200 })
// 4. 加入附件列表(显示在输入框上方)
setAttachments(prev => [...prev, {
id: uuid(),
type: 'image',
filename: file.name,
mimeType: file.type,
base64Data: base64,
previewUrl: preview,
}])
}
// 构造多模态消息内容
const buildMessageContent = (text: string, attachments: Attachment[]) => {
const content: ContentBlock[] = [{ type: 'text', text }]
for (const att of attachments) {
if (att.type === 'image') {
content.push({
type: 'image_url',
image_url: {
url: `data:${att.mimeType};base64,${att.base64Data}`,
detail: 'high'
}
})
}
}
return content
}
5.3 后端视觉模型路由
# backend/services/llm_router.py
async def route_to_model(agent: Agent, messages: list):
has_images = any(
isinstance(msg.get('content'), list) and
any(block['type'] == 'image_url' for block in msg['content'])
for msg in messages
)
if has_images:
# 优先使用配置的 Vision 模型
model = llm_pool.get_model(
agent.vision_model_id or agent.llm_model_id,
require_vision=True # 确保 supports_vision = True
)
else:
model = llm_pool.get_model(agent.llm_model_id)
return model
支持的视觉模型:
| 提供商 | 模型 | 特点 |
|---|---|---|
| OpenAI | gpt-4o, gpt-4o-mini |
业界最佳综合性能 |
| Anthropic | claude-opus-4-6, claude-sonnet-4-6 |
长上下文图像分析 |
gemini-2.0-flash |
速度快、成本低 | |
| DeepSeek | deepseek-vl2 |
中文场景优化 |
06 · 视频接口分析
⚠️ 现状说明: Clawith v1.4.0 对视频的原生支持处于间接模式,完整的视频 API 模块仍处于开发路线图阶段。
当前视频处理的四种路径
| 路径 | 方式 | 说明 |
|---|---|---|
| 帧提取 + Vision | Code Executor (Python) | 用 cv2/ffmpeg 提取关键帧,逐帧发送给视觉 LLM |
| MCP 视频工具 | 运行时安装 | 从 Smithery/ModelScope 安装 video-analyzer-mcp 等 |
| Gemini 原生视频 | 配置 Gemini 2.0 Flash 为 Vision 模型 | 直接上传视频文件(最长 60 分钟) |
| 音频提取 + ASR | ffmpeg + /transcribe | 提取音轨转文字,结合帧分析生成报告 |
# agent_workspace/video_analysis.py(在 Code Executor 中执行)
import cv2, base64
def analyze_video(video_path: str, sample_rate: int = 30):
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frames = []
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret: break
if frame_idx % (fps * sample_rate) == 0: # 每N秒取一帧
_, buf = cv2.imencode('.jpg', frame)
frames.append(base64.b64encode(buf).decode())
frame_idx += 1
cap.release()
return frames # 返回 base64 帧列表,交由主流程发送给 Vision LLM
07 · MCP 工具接口设计
MCP(Model Context Protocol)是 Clawith 工具扩展的核心协议。后端实现了一个 MCP 客户端,支持 Streamable HTTP 连接方式,可以动态发现、安装、调用 MCP 服务器提供的工具。
7.1 MCP 服务发现与安装端点
| Method | Endpoint | 说明 |
|---|---|---|
POST |
/api/v1/mcp/discover |
搜索 Smithery / ModelScope MCP 注册表 |
POST |
/api/v1/mcp/import |
一键导入 MCP 服务器为平台工具 |
GET |
/api/v1/mcp/servers |
列出已安装的 MCP 服务器 |
// frontend/src/pages/ToolStore.tsx
// 搜索 MCP 工具市场
const { data: mcpResults } = useQuery({
queryKey: ['mcp-discover', searchQuery],
queryFn: () => api.post('/mcp/discover', {
query: searchQuery,
registry: ['smithery', 'modelscope']
}),
enabled: searchQuery.length > 2,
})
// 一键安装 MCP 服务器
const installMcp = useMutation({
mutationFn: (serverId: string) => api.post('/mcp/import', {
server_id: serverId,
registry: 'smithery',
}),
onSuccess: () => {
// 安装成功后刷新工具列表,Agent 立即可用
qc.invalidateQueries(['mcp-servers'])
qc.invalidateQueries(['agent-tools'])
}
})
7.2 MCP 工具调用协议(Streamable HTTP)
# backend/services/mcp_client.py
class MCPClient:
async def call_tool(self, server_url: str, tool_name: str, arguments: dict) -> dict:
payload = {
"jsonrpc": "2.0",
"method": "tools/call",
"params": {"name": tool_name, "arguments": arguments},
"id": str(uuid4())
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{server_url}/mcp",
json=payload,
headers={"Accept": "text/event-stream"}
) as resp:
result = ""
async for line in resp.content:
if line.startswith(b"data:"):
chunk = json.loads(line[5:])
result += chunk.get("result", "")
return result
08 · 前端模型配置与管理系统
这是 Clawith 企业级定位的核心功能之一。管理员通过 Web UI 完成整个 LLM Model Pool 的配置、测试和路由策略管理。
8.1 Model Pool 管理界面功能总览
| 功能模块 | 前端页面路径 | 对应 API | 说明 |
|---|---|---|---|
| 添加 LLM 提供商 | Settings → Model Pool → Add Server | POST /api/v1/llm/servers |
配置 base_url、api_key、提供商类型 |
| 添加具体模型 | Settings → Model Pool → Add Model | POST /api/v1/llm/models |
指定 model_id、类型、上下文窗口、是否支持视觉 |
| 连接性测试 | Model Pool → Test Connection | POST /api/v1/llm/servers/{id}/test |
发送 hello 请求验证 API Key 有效性 |
| 设置默认模型 | Settings → Default Models | PUT /api/v1/org/llm-config |
为 Chat / STT / TTS / Vision / Embedding 分别设置默认 |
| Agent 级模型覆盖 | Agent Edit → Model Settings | PUT /api/v1/agents/{id}/model-config |
单个 Agent 可选择不同的 LLM |
| 使用量监控 | Analytics → Model Usage | GET /api/v1/analytics/llm-usage |
按模型、Agent、用户统计 token 用量和费用 |
8.2 前端 Model Pool 配置表单
// frontend/src/pages/Settings/ModelPool.tsx
const PROVIDER_PRESETS = {
openai: { base_url: 'https://api.openai.com/v1', label: 'OpenAI' },
anthropic: { base_url: 'https://api.anthropic.com', label: 'Anthropic' },
azure: { base_url: 'https://{resource}.openai.azure.com', label: 'Azure OpenAI' },
deepseek: { base_url: 'https://api.deepseek.com/v1', label: 'DeepSeek' },
ollama: { base_url: 'http://localhost:11434/v1', label: 'Ollama (本地)' },
custom: { base_url: '', label: '自定义端点' },
}
// 提交新 LLM 服务商
const addServer = useMutation({
mutationFn: (data: AddServerForm) =>
api.post('/llm/servers', {
name: data.name,
provider: data.provider,
base_url: data.base_url,
api_key: data.api_key, // 传输后由后端加密存储
models: data.models.map(m => ({
model_id: m.id,
display_name: m.label,
model_type: m.type, // LLM | VISION | STT | TTS | EMBEDDING
max_tokens: m.maxTokens,
supports_vision: m.vision,
supports_streaming: true,
}))
}),
onSuccess: () => {
toast.success('模型服务商添加成功')
qc.invalidateQueries(['llm-servers'])
}
})
8.3 Agent 创建时的模型绑定(五步向导 Step 3)
// frontend/src/pages/AgentCreate/Step3ModelConfig.tsx
export function ModelConfigStep({ agentConfig, onChange }) {
const { data: models } = useQuery({
queryKey: ['llm-models'],
queryFn: () => api.get('/llm/models'),
})
const chatModels = models?.filter(m => m.model_type === 'LLM')
const visionModels = models?.filter(m => m.supports_vision)
const sttModels = models?.filter(m => m.model_type === 'STT')
const ttsModels = models?.filter(m => m.model_type === 'TTS')
return (
<>
{/* 主对话模型(必选) */}
<ModelSelector label="主对话模型" models={chatModels}
value={agentConfig.llm_model_id}
onChange={(v) => onChange({ llm_model_id: v })} required />
{/* 视觉模型(可选,默认继承主模型) */}
<ModelSelector label="视觉模型(可选)" models={visionModels}
value={agentConfig.vision_model_id}
onChange={(v) => onChange({ vision_model_id: v })}
placeholder="默认使用主对话模型" />
{/* STT 模型(可选) */}
<ModelSelector label="语音识别模型(STT)" models={sttModels}
value={agentConfig.stt_model_id}
onChange={(v) => onChange({ stt_model_id: v })} />
{/* 推理参数 */}
<Slider label="Temperature" min={0} max={2} step={0.1}
value={agentConfig.temperature ?? 0.7} />
<Slider label="Max Tokens" min={256} max={128000} step={256}
value={agentConfig.max_tokens ?? 4096} />
</>
)
}
8.4 全局模型管理 API 端点汇总
| Method | Endpoint | 说明 |
|---|---|---|
GET |
/api/v1/llm/servers |
列出所有 LLM 服务商 |
POST |
/api/v1/llm/servers |
添加新服务商 |
PUT |
/api/v1/llm/servers/{id} |
更新服务商配置 |
DELETE |
/api/v1/llm/servers/{id} |
删除服务商 |
POST |
/api/v1/llm/servers/{id}/test |
连接性测试 |
GET |
/api/v1/llm/models |
列出所有模型(可按 type 过滤) |
PUT |
/api/v1/org/llm-defaults |
设置组织级默认模型 |
GET |
/api/v1/analytics/llm-usage |
token 用量和费用统计 |
09 · 前端状态管理架构
Clawith 前端采用 Zustand + TanStack Query 的双层状态管理架构:Zustand 管理 UI 实时状态,TanStack Query 管理服务端数据。
9.1 Zustand Store 设计
// frontend/src/store/agentStore.ts
interface AgentStore {
// ─ 当前激活的 Agent
activeAgentId: string | null
setActiveAgent: (id: string) => void
// ─ 流式消息状态(SSE 实时更新)
streamingMessages: Record<string, StreamingMessage>
updateStreamChunk: (agentId: string, delta: string) => void
clearStream: (agentId: string) => void
// ─ 工具调用实时状态
toolExecutions: Record<string, ToolExecution>
setToolUse: (agentId: string, tool: string, status: ToolStatus) => void
// ─ Chat 输入框状态
chatInputs: Record<string, string>
attachments: Record<string, Attachment[]>
// ─ Plaza 实时状态
plazaUnread: number
incrementPlazaUnread: () => void
}
interface ModelStore {
availableModels: LLMModel[]
selectedModelId: Record<string, string> // agentId → modelId
addServerForm: Partial<AddServerForm>
updateAddServerForm: (patch: Partial<AddServerForm>) => void
}
9.2 TanStack Query 数据获取层
// frontend/src/hooks/useAgentData.ts
// 服务端状态:自动缓存 + 后台刷新
export const useAgentMessages = (agentId: string) =>
useQuery({
queryKey: ['messages', agentId],
queryFn: () => api.get(`/agents/${agentId}/messages`),
staleTime: 30_000, // 30 秒内不重新请求
refetchOnWindowFocus: false,
})
export const useLLMModels = () =>
useQuery({
queryKey: ['llm-models'],
queryFn: () => api.get('/llm/models'),
staleTime: 5 * 60_000, // 5 分钟缓存,模型列表变化不频繁
select: (data) => ({
chatModels: data.filter(m => m.model_type === 'LLM'),
visionModels: data.filter(m => m.supports_vision),
sttModels: data.filter(m => m.model_type === 'STT'),
ttsModels: data.filter(m => m.model_type === 'TTS'),
})
})
// WebSocket 实时连接(Aware 心跳 + Plaza 推送)
export function useAgentWebSocket(agentId: string) {
useEffect(() => {
const ws = new WebSocket(`${WS_BASE}/agents/${agentId}/ws?token=${getToken()}`)
ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
switch (msg.event) {
case 'agent_thinking': setAgentStatus(agentId, 'thinking'); break
case 'plaza_post': incrementPlazaUnread(); break
case 'task_updated': qc.invalidateQueries(['tasks', agentId]); break
case 'approval_needed': showApprovalModal(msg.operation); break
}
}
return () => ws.close()
}, [agentId])
}
10 · 端到端完整数据流
以下展示用户发送一条带图像的消息,Agent 调用 Web Search 工具,最终流式返回分析报告的完整数据流:
| # | 阶段 | 技术细节 | 涉及接口/组件 |
|---|---|---|---|
| 1 | 用户选择图片附件 | FileReader → base64 编码 → Zustand attachments 状态 | ChatInput.tsx |
| 2 | 用户发送消息 | 构造 multipart JSON → POST /chat → Accept: text/event-stream | useAgentChat.ts |
| 3 | 后端鉴权 | JWT 验证 → RBAC 检查 → 配额检查(call cap) | FastAPI Middleware |
| 4 | 构造 System Prompt | soul.md + memory.md + KBRetrieval + OrgContext + ToolSchema | agent_runner.py |
| 5 | 路由到 Vision 模型 | 检测消息含图像 → 选择 vision_model_id 对应的 LLM Server | llm_router.py |
| 6 | 调用 LLM(流式) | OpenAI-compatible /chat/completions stream=true | LLM Model Pool |
| 7 | LLM 输出 tool_use | 解析 tool_calls → 执行 web_search → 返回搜索结果 | Tools Engine |
| 8 | SSE 实时推送 | text_delta 事件 → ReadableStream → updateStreamChunk | useAgentChat.ts |
| 9 | UI 实时渲染 | Zustand streamingMessages → React Streaming 渲染 | MessageList.tsx |
| 10 | 完成后更新 | message_stop → invalidateQueries → 写审计日志 → 更新 memory.md | 后端 + TanStack Query |
11 · 技术缺口与改进建议
⚠️ 高优先级问题
原生视频处理缺失 无专用 /video/analyze 端点,需依赖帧提取 + MCP 工具间接实现。建议增加 Video API 模块,支持 Gemini / GPT-4o-mini 视频原生输入。
ASR/TTS Web UI 体验不完整 v1.4.0 虽添加了 Multimodal Vision,但 Web UI 中的语音输入/输出 UI 组件尚不完整,仅通过 IM 渠道(飞书/Slack)间接支持语音。
🔶 中优先级问题
模型路由策略固定 当前模型路由仅基于"是否含图像"的简单判断,缺少基于 token 成本、响应速度、可用性的智能路由策略(类似 LLMRouter)。
本地模型支持待加强 虽然 Ollama 可作为 custom provider 配置,但缺少对本地模型的专门优化(如 Llama.cpp 的上下文缓存、量化配置)。
💡 建议改进方向
流式 Tool UI 可视化 --- 工具调用过程显示参数预览、执行进度条、结果折叠展开,而非仅显示状态文本。
模型 A/B 测试框架 --- 参考 BISHENG 的 FlowVersion 设计,为 Agent 实现模型版本切换与 A/B 对比评测功能。
12 · 研究结论
💡 核心发现: Clawith 的大模型设计逻辑本质上是一个上下文工程(Context Engineering)平台,而非传统意义上的 LLM 应用。它的价值不在于使用哪个模型,而在于如何将 soul/memory/org knowledge/tools 精准组合成最优 System Prompt,驱动 LLM 以"数字员工"身份行动。
综合评分
| 维度 | 评估 | 评分 |
|---|---|---|
| 大模型抽象设计 | LLM Model Pool 架构完善,多提供商统一、类型分类清晰 | 9/10 |
| 提示词构造工程 | soul+memory+KB+OrgCtx+Skill 多层注入,技术领先 | 9/10 |
| Chat 接口设计 | SSE 流式 + WebSocket 双通道,tool_use 状态实时可见 | 8.5/10 |
| 多模态(Vision) | v1.4.0 完整支持图像,标准 OpenAI 格式 | 8/10 |
| ASR/TTS 支持 | 通过 Model Pool 间接支持,UI 体验待完善 | 6/10 |
| 视频支持 | 仅间接方式,无原生视频 API 模块 | 5/10 |
| 前端状态架构 | Zustand + TanStack Query 职责分离清晰,实时性好 | 8.5/10 |
| 模型管理 UI | 管理界面功能完整,提供商预设、测试、配额覆盖全面 | 8/10 |
对开发者的建议
如果你希望在 Clawith 之上构建多模态应用,当前最成熟的路径是:
- 通过 Model Pool 配置 GPT-4o 或 claude-sonnet-4-6 作为默认 Vision 模型
- 通过 Code Executor 技能实现视频帧提取(Python + cv2/ffmpeg)
- 通过 MCP 工具市场安装所需的 ASR/TTS 专用服务器
待 v1.5+ 版本原生视频和 ASR UI 模块发布后,再升级到原生接口。
数据来源:github.com/dataelement/Clawith · clawith.ai Apache 2.0 · Python 66% · TypeScript 18% · 193 Commits