OpenClaw工具拆解之 sessions_list+sessions_history

一、sessions_list 工具

1.1 工具概述

功能 :列出所有会话(包括子 agent 会话)
核心特性

  • 支持按类型过滤(main/group/cron/hook/node/other)
  • 支持按活跃度过滤(activeMinutes)
  • 可包含最后 N 条消息(messageLimit,最大 20)
  • 会话可见性检查(visibility guard)
  • 沙盒隔离支持

1.2 Schema 定义

位置:第 109015 行

javascript 复制代码
const SessionsListToolSchema = Type.Object({
    kinds: Type.Optional(Type.Array(Type.String())),
    limit: Type.Optional(Type.Number({ minimum: 1 })),
    activeMinutes: Type.Optional(Type.Number({ minimum: 1 })),
    messageLimit: Type.Optional(Type.Number({ minimum: 0 }))
});

1.3 完整执行代码

位置:第 109015 行

javascript 复制代码
function createSessionsListTool(opts) {
    return {
        label: "Sessions",
        name: "sessions_list",
        description: "List sessions with optional filters and last messages.",
        parameters: SessionsListToolSchema,
        execute: async (_toolCallId, args) => {
            const params = args;
            const cfg = opts?.config ?? loadConfig();
            
            // 1. 解析沙盒会话工具上下文
            const { mainKey, alias, requesterInternalKey, restrictToSpawned } = resolveSandboxedSessionToolContext({
                cfg,
                agentSessionKey: opts?.agentSessionKey,
                sandboxed: opts?.sandboxed
            });
            const effectiveRequesterKey = requesterInternalKey ?? alias;
            
            // 2. 解析会话可见性
            const visibility = resolveEffectiveSessionToolsVisibility({
                cfg,
                sandboxed: opts?.sandboxed === true
            });
            
            // 3. 解析类型过滤
            const allowedKindsList = (readStringArrayParam(params, "kinds")?.map((value) => 
                value.trim().toLowerCase()
            ) ?? []).filter((value) => [
                "main", "group", "cron", "hook", "node", "other"
            ].includes(value));
            const allowedKinds = allowedKindsList.length ? new Set(allowedKindsList) : void 0;
            
            // 4. 解析其他参数
            const limit = typeof params.limit === "number" && Number.isFinite(params.limit) ? 
                Math.max(1, Math.floor(params.limit)) : void 0;
            const activeMinutes = typeof params.activeMinutes === "number" && Number.isFinite(params.activeMinutes) ? 
                Math.max(1, Math.floor(params.activeMinutes)) : void 0;
            const messageLimitRaw = typeof params.messageLimit === "number" && Number.isFinite(params.messageLimit) ? 
                Math.max(0, Math.floor(params.messageLimit)) : 0;
            const messageLimit = Math.min(messageLimitRaw, 20);  // 最大 20 条
            
            // 5. 调用 Gateway 获取会话列表
            const gatewayCall = opts?.callGateway ?? callGateway;
            const list = await gatewayCall({
                method: "sessions.list",
                params: {
                    limit,
                    activeMinutes,
                    includeGlobal: !restrictToSpawned,
                    includeUnknown: !restrictToSpawned,
                    spawnedBy: restrictToSpawned ? effectiveRequesterKey : void 0
                }
            });
            
            const sessions = Array.isArray(list?.sessions) ? list.sessions : [];
            const storePath = typeof list?.path === "string" ? list.path : void 0;
            
            // 6. 创建可见性检查器
            const visibilityGuard = await createSessionVisibilityGuard({
                action: "list",
                requesterSessionKey: effectiveRequesterKey,
                visibility,
                a2aPolicy: createAgentToAgentPolicy(cfg)
            });
            
            const rows = [];
            const historyTargets = [];
            
            // 7. 处理每个会话
            for (const entry of sessions) {
                if (!entry || typeof entry !== "object") continue;
                const key = typeof entry.key === "string" ? entry.key : "";
                if (!key) continue;
                
                // 可见性检查
                if (!visibilityGuard.check(key).allowed) continue;
                if (key === "unknown") continue;
                if (key === "global" && alias !== "global") continue;
                
                // 分类会话类型
                const kind = classifySessionKind({
                    key,
                    gatewayKind: typeof entry.kind === "string" ? entry.kind : void 0,
                    alias,
                    mainKey
                });
                
                // 类型过滤
                if (allowedKinds && !allowedKinds.has(kind)) continue;
                
                // 解析显示键
                const displayKey = resolveDisplaySessionKey({
                    key,
                    alias,
                    mainKey
                });
                
                // 8. 解析渠道和交付上下文
                const entryChannel = typeof entry.channel === "string" ? entry.channel : void 0;
                const deliveryContext = entry.deliveryContext && typeof entry.deliveryContext === "object" ? 
                    entry.deliveryContext : void 0;
                const deliveryChannel = typeof deliveryContext?.channel === "string" ? 
                    deliveryContext.channel : void 0;
                const deliveryTo = typeof deliveryContext?.to === "string" ? deliveryContext.to : void 0;
                const deliveryAccountId = typeof deliveryContext?.accountId === "string" ? 
                    deliveryContext.accountId : void 0;
                
                const lastChannel = deliveryChannel ?? (typeof entry.lastChannel === "string" ? entry.lastChannel : void 0);
                const lastAccountId = deliveryAccountId ?? (typeof entry.lastAccountId === "string" ? entry.lastAccountId : void 0);
                
                const derivedChannel = deriveChannel({
                    key,
                    kind,
                    channel: entryChannel,
                    lastChannel
                });
                
                // 9. 解析会话元数据
                const sessionId = typeof entry.sessionId === "string" ? entry.sessionId : void 0;
                const sessionFileRaw = entry.sessionFile;
                const sessionFile = typeof sessionFileRaw === "string" ? sessionFileRaw : void 0;
                
                let transcriptPath;
                if (sessionId) {
                    try {
                        const agentId = resolveAgentIdFromSessionKey(key);
                        const trimmedStorePath = storePath?.trim();
                        let effectiveStorePath;
                        
                        if (trimmedStorePath && trimmedStorePath !== "(multiple)") {
                            if (trimmedStorePath.includes("{agentId}") || trimmedStorePath.startsWith("~")) {
                                effectiveStorePath = resolveStorePath(trimmedStorePath, { agentId });
                            } else if (path.isAbsolute(trimmedStorePath)) {
                                effectiveStorePath = trimmedStorePath;
                            }
                        }
                        
                        const filePathOpts = resolveSessionFilePathOptions({
                            agentId,
                            storePath: effectiveStorePath
                        });
                        transcriptPath = resolveSessionFilePath(sessionId, sessionFile ? { sessionFile } : void 0, filePathOpts);
                    } catch {
                        transcriptPath = void 0;
                    }
                }
                
                // 10. 构建会话行
                const row = {
                    key: displayKey,
                    kind,
                    channel: derivedChannel,
                    label: typeof entry.label === "string" ? entry.label : void 0,
                    displayName: typeof entry.displayName === "string" ? entry.displayName : void 0,
                    deliveryContext: deliveryChannel || deliveryTo || deliveryAccountId ? {
                        channel: deliveryChannel,
                        to: deliveryTo,
                        accountId: deliveryAccountId
                    } : void 0,
                    updatedAt: typeof entry.updatedAt === "number" ? entry.updatedAt : void 0,
                    sessionId,
                    model: typeof entry.model === "string" ? entry.model : void 0,
                    contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
                    totalTokens: typeof entry.totalTokens === "number" ? entry.totalTokens : void 0,
                    estimatedCostUsd: typeof entry.estimatedCostUsd === "number" ? entry.estimatedCostUsd : void 0,
                    status: typeof entry.status === "string" ? entry.status : void 0,
                    startedAt: typeof entry.startedAt === "number" ? entry.startedAt : void 0,
                    endedAt: typeof entry.endedAt === "number" ? entry.endedAt : void 0,
                    runtimeMs: typeof entry.runtimeMs === "number" ? entry.runtimeMs : void 0,
                    childSessions: Array.isArray(entry.childSessions) ? 
                        entry.childSessions.filter((value) => typeof value === "string")
                            .map((value) => resolveDisplaySessionKey({ key: value, alias, mainKey })) : void 0,
                    thinkingLevel: typeof entry.thinkingLevel === "string" ? entry.thinkingLevel : void 0,
                    verboseLevel: typeof entry.verboseLevel === "string" ? entry.verboseLevel : void 0,
                    systemSent: typeof entry.systemSent === "boolean" ? entry.systemSent : void 0,
                    abortedLastRun: typeof entry.abortedLastRun === "boolean" ? entry.abortedLastRun : void 0,
                    sendPolicy: typeof entry.sendPolicy === "string" ? entry.sendPolicy : void 0,
                    lastChannel,
                    lastTo: deliveryTo ?? (typeof entry.lastTo === "string" ? entry.lastTo : void 0),
                    lastAccountId,
                    transcriptPath
                };
                
                // 11. 如果需要消息,添加到获取队列
                if (messageLimit > 0) {
                    const resolvedKey = resolveInternalSessionKey({
                        key,
                        alias,
                        mainKey
                    });
                    historyTargets.push({ row, resolvedKey });
                }
                
                rows.push(row);
            }
            
            // 12. 并发获取消息(如果需要)
            if (messageLimit > 0 && historyTargets.length > 0) {
                const maxConcurrent = Math.min(4, historyTargets.length);
                let index = 0;
                
                const worker = async () => {
                    while (true) {
                        const next = index;
                        index += 1;
                        if (next >= historyTargets.length) return;
                        
                        const target = historyTargets[next];
                        const history = await gatewayCall({
                            method: "chat.history",
                            params: {
                                sessionKey: target.resolvedKey,
                                limit: messageLimit
                            }
                        });
                        
                        const filtered = stripToolMessages(
                            Array.isArray(history?.messages) ? history.messages : []
                        );
                        target.row.messages = filtered.length > messageLimit ? 
                            filtered.slice(-messageLimit) : filtered;
                    }
                };
                
                await Promise.all(Array.from({ length: maxConcurrent }, () => worker()));
            }
            
            return jsonResult({
                count: rows.length,
                sessions: rows
            });
        }
    };
}

1.4 会话类型分类

javascript 复制代码
function classifySessionKind(params) {
    const { key, gatewayKind, alias, mainKey } = params;
    
    // 1. Gateway 已分类
    if (gatewayKind) return gatewayKind;
    
    // 2. 根据键分类
    if (key === "global") return "other";
    if (key === mainKey) return "main";
    if (key === alias) return "main";
    
    // 3. 根据前缀分类
    if (key.startsWith("cron:")) return "cron";
    if (key.startsWith("hook:")) return "hook";
    if (key.startsWith("node:")) return "node";
    if (key.startsWith("group:")) return "group";
    
    return "other";
}

1.5 可见性检查

javascript 复制代码
const visibilityGuard = await createSessionVisibilityGuard({
    action: "list",
    requesterSessionKey: effectiveRequesterKey,
    visibility,  // session_tools.visibility 配置
    a2aPolicy: createAgentToAgentPolicy(cfg)  // Agent-to-Agent 策略
});

// 检查会话是否可见
if (!visibilityGuard.check(key).allowed) continue;

1.6 执行流程图

复制代码
sessions_list 工具调用
    ↓
1. 解析沙盒上下文
   ├─ mainKey, alias
   ├─ requesterInternalKey
   └─ restrictToSpawned
    ↓
2. 解析会话可见性配置
    ↓
3. 解析类型过滤(kinds)
    ↓
4. 解析其他参数(limit/activeMinutes/messageLimit)
    ↓
5. 调用 Gateway 获取会话列表
    ↓
6. 创建可见性检查器
    ↓
7. 处理每个会话
   ├─ 可见性检查
   ├─ 类型分类
   ├─ 类型过滤
   ├─ 解析渠道信息
   └─ 解析元数据
    ↓
8. 构建会话行对象
    ↓
9. 如果需要消息,添加到获取队列
    ↓
10. 并发获取消息(最多 4 个并发)
    ↓
11. 返回结果

1.7 返回结果格式

json 复制代码
{
  "count": 5,
  "sessions": [
    {
      "key": "main",
      "kind": "main",
      "channel": "feishu",
      "label": "Main Session",
      "displayName": "主会话",
      "updatedAt": 1711716000000,
      "sessionId": "abc123",
      "model": "qwen3.5-plus",
      "contextTokens": 50000,
      "totalTokens": 100000,
      "estimatedCostUsd": 0.05,
      "status": "idle",
      "startedAt": 1711710000000,
      "runtimeMs": 6000000,
      "thinkingLevel": "high",
      "messages": [
        { "role": "user", "content": "Hello" },
        { "role": "assistant", "content": "Hi there!" }
      ]
    }
  ]
}

二、sessions_history 工具

2.1 工具概述

功能 :获取指定会话的历史消息
核心特性

  • 敏感内容脱敏(redact sensitive text)
  • 文本截断(最大 4000 字符)
  • 图片省略(只保留元数据)
  • 思考过程签名删除
  • 使用/成本信息删除
  • 80KB 硬性限制

2.2 Schema 定义

位置:第 108788 行

javascript 复制代码
const SessionsHistoryToolSchema = Type.Object({
    sessionKey: Type.String(),
    limit: Type.Optional(Type.Number({ minimum: 1 })),
    includeTools: Type.Optional(Type.Boolean())
});

// 硬性限制
const SESSIONS_HISTORY_MAX_BYTES = 80 * 1024;  // 80KB
const SESSIONS_HISTORY_TEXT_MAX_CHARS = 4000;   // 4000 字符

2.3 文本截断函数

位置:第 108798 行

javascript 复制代码
function truncateHistoryText(text) {
    // 1. 敏感内容脱敏
    const sanitized = redactSensitiveText(text);
    const redacted = sanitized !== text;
    
    // 2. 检查是否超过限制
    if (sanitized.length <= SESSIONS_HISTORY_TEXT_MAX_CHARS) {
        return {
            text: sanitized,
            truncated: false,
            redacted
        };
    }
    
    // 3. 截断并添加标记
    return {
        text: `${truncateUtf16Safe(sanitized, SESSIONS_HISTORY_TEXT_MAX_CHARS)}\n...(truncated)...`,
        truncated: true,
        redacted
    };
}

2.4 内容块清理函数

位置:第 108816 行

javascript 复制代码
function sanitizeHistoryContentBlock(block) {
    if (!block || typeof block !== "object") {
        return { block, truncated: false, redacted: false };
    }
    
    const entry = { ...block };
    let truncated = false;
    let redacted = false;
    const type = typeof entry.type === "string" ? entry.type : "";
    
    // 1. 文本内容
    if (typeof entry.text === "string") {
        const res = truncateHistoryText(entry.text);
        entry.text = res.text;
        truncated ||= res.truncated;
        redacted ||= res.redacted;
    }
    
    // 2. 思考过程
    if (type === "thinking") {
        if (typeof entry.thinking === "string") {
            const res = truncateHistoryText(entry.thinking);
            entry.thinking = res.text;
            truncated ||= res.truncated;
            redacted ||= res.redacted;
        }
        // 删除思考签名(安全考虑)
        if ("thinkingSignature" in entry) {
            delete entry.thinkingSignature;
            truncated = true;
        }
    }
    
    // 3. 部分 JSON(工具调用)
    if (typeof entry.partialJson === "string") {
        const res = truncateHistoryText(entry.partialJson);
        entry.partialJson = res.text;
        truncated ||= res.truncated;
        redacted ||= res.redacted;
    }
    
    // 4. 图片(省略数据,只保留元数据)
    if (type === "image") {
        const data = typeof entry.data === "string" ? entry.data : void 0;
        const bytes = data ? data.length : void 0;
        
        if ("data" in entry) {
            delete entry.data;  // 删除 Base64 数据
            truncated = true;
        }
        
        entry.omitted = true;  // 标记为已省略
        if (bytes !== void 0) entry.bytes = bytes;  // 保留大小信息
    }
    
    return { block: entry, truncated, redacted };
}

2.5 消息清理函数

位置:第 108879 行

javascript 复制代码
function sanitizeHistoryMessage(message) {
    if (!message || typeof message !== "object") {
        return { message, truncated: false, redacted: false };
    }
    
    const entry = { ...message };
    let truncated = false;
    let redacted = false;
    
    // 1. 删除敏感元数据
    if ("details" in entry) {
        delete entry.details;
        truncated = true;
    }
    if ("usage" in entry) {
        delete entry.usage;  // token 使用量
        truncated = true;
    }
    if ("cost" in entry) {
        delete entry.cost;  // 成本
        truncated = true;
    }
    
    // 2. 处理文本内容
    if (typeof entry.content === "string") {
        const res = truncateHistoryText(entry.content);
        entry.content = res.text;
        truncated ||= res.truncated;
        redacted ||= res.redacted;
    } else if (Array.isArray(entry.content)) {
        const updated = entry.content.map((block) => sanitizeHistoryContentBlock(block));
        entry.content = updated.map((item) => item.block);
        truncated ||= updated.some((item) => item.truncated);
        redacted ||= updated.some((item) => item.redacted);
    }
    
    // 3. 处理 text 字段
    if (typeof entry.text === "string") {
        const res = truncateHistoryText(entry.text);
        entry.text = res.text;
        truncated ||= res.truncated;
        redacted ||= res.redacted;
    }
    
    return { message: entry, truncated, redacted };
}

2.6 硬性限制函数

位置:第 108921 行

javascript 复制代码
function enforceSessionsHistoryHardCap(params) {
    // 1. 检查是否超过限制
    if (params.bytes <= params.maxBytes) {
        return {
            items: params.items,
            bytes: params.bytes,
            hardCapped: false
        };
    }
    
    // 2. 尝试只保留最后一条消息
    const last = params.items.at(-1);
    const lastOnly = last ? [last] : [];
    const lastBytes = jsonUtf8Bytes(lastOnly);
    
    if (lastBytes <= params.maxBytes) {
        return {
            items: lastOnly,
            bytes: lastBytes,
            hardCapped: true
        };
    }
    
    // 3. 如果最后一条也太大,返回占位符
    const placeholder = [{
        role: "assistant",
        content: "[sessions_history omitted: message too large]"
    }];
    
    return {
        items: placeholder,
        bytes: jsonUtf8Bytes(placeholder),
        hardCapped: true
    };
}

2.7 完整执行代码

位置:第 108941 行

javascript 复制代码
function createSessionsHistoryTool(opts) {
    return {
        label: "Session History",
        name: "sessions_history",
        description: "Fetch message history for a session.",
        parameters: SessionsHistoryToolSchema,
        execute: async (_toolCallId, args) => {
            const params = args;
            const gatewayCall = opts?.callGateway ?? callGateway;
            
            // 1. 解析 sessionKey(必填)
            const sessionKeyParam = readStringParam$1(params, "sessionKey", { required: true });
            const cfg = opts?.config ?? loadConfig();
            
            // 2. 解析沙盒上下文
            const { mainKey, alias, effectiveRequesterKey, restrictToSpawned } = resolveSandboxedSessionToolContext({
                cfg,
                agentSessionKey: opts?.agentSessionKey,
                sandboxed: opts?.sandboxed
            });
            
            // 3. 解析会话引用
            const resolvedSession = await resolveSessionReference({
                sessionKey: sessionKeyParam,
                alias,
                mainKey,
                requesterInternalKey: effectiveRequesterKey,
                restrictToSpawned
            });
            
            if (!resolvedSession.ok) {
                return jsonResult({
                    status: resolvedSession.status,
                    error: resolvedSession.error
                });
            }
            
            // 4. 检查可见性
            const visibleSession = await resolveVisibleSessionReference({
                resolvedSession,
                requesterSessionKey: effectiveRequesterKey,
                restrictToSpawned,
                visibilitySessionKey: sessionKeyParam
            });
            
            if (!visibleSession.ok) {
                return jsonResult({
                    status: visibleSession.status,
                    error: visibleSession.error
                });
            }
            
            const resolvedKey = visibleSession.key;
            const displayKey = visibleSession.displayKey;
            
            // 5. 创建可见性检查器
            const a2aPolicy = createAgentToAgentPolicy(cfg);
            const access = (await createSessionVisibilityGuard({
                action: "history",
                requesterSessionKey: effectiveRequesterKey,
                visibility: resolveEffectiveSessionToolsVisibility({
                    cfg,
                    sandboxed: opts?.sandboxed === true
                }),
                a2aPolicy
            })).check(resolvedKey);
            
            if (!access.allowed) {
                return jsonResult({
                    status: access.status,
                    error: access.error
                });
            }
            
            // 6. 解析参数
            const limit = typeof params.limit === "number" && Number.isFinite(params.limit) ? 
                Math.max(1, Math.floor(params.limit)) : void 0;
            const includeTools = Boolean(params.includeTools);
            
            // 7. 获取历史消息
            const result = await gatewayCall({
                method: "chat.history",
                params: {
                    sessionKey: resolvedKey,
                    limit
                }
            });
            
            const messages = Array.isArray(result?.messages) ? result.messages : [];
            
            // 8. 清理消息
            const sanitized = messages.map((msg) => sanitizeHistoryMessage(msg));
            const cleanedMessages = sanitized.map((item) => item.message);
            
            // 9. 应用硬性限制
            const totalBytes = jsonUtf8Bytes(cleanedMessages);
            const capped = enforceSessionsHistoryHardCap({
                items: cleanedMessages,
                bytes: totalBytes,
                maxBytes: SESSIONS_HISTORY_MAX_BYTES
            });
            
            return jsonResult({
                sessionKey: displayKey,
                messages: capped.items,
                hardCapped: capped.hardCapped,
                totalBytes: capped.bytes
            });
        }
    };
}

2.8 执行流程图

复制代码
sessions_history 工具调用
    ↓
1. 解析 sessionKey(必填)
    ↓
2. 解析沙盒上下文
    ↓
3. 解析会话引用
   ├─ 检查会话是否存在
   └─ 解析内部键
    ↓
4. 检查可见性
   ├─ 可见性配置
   ├─ A2A 策略
   └─ 访问检查
    ↓
5. 解析参数(limit/includeTools)
    ↓
6. 调用 Gateway 获取历史消息
    ↓
7. 清理消息
   ├─ 删除敏感元数据(usage/cost/details)
   ├─ 文本截断(4000 字符)
   ├─ 思考签名删除
   └─ 图片数据省略
    ↓
8. 应用硬性限制(80KB)
   ├─ 未超限 → 返回全部
   ├─ 超限 → 只保留最后一条
   └─ 最后一条也超限 → 返回占位符
    ↓
9. 返回结果

2.9 返回结果格式

正常

json 复制代码
{
  "sessionKey": "main",
  "messages": [
    {
      "role": "user",
      "content": "Hello",
      "timestamp": 1711716000000
    },
    {
      "role": "assistant",
      "content": "Hi there!",
      "timestamp": 1711716001000
    }
  ],
  "hardCapped": false,
  "totalBytes": 5000
}

被截断

json 复制代码
{
  "sessionKey": "main",
  "messages": [...],
  "hardCapped": true,
  "totalBytes": 81920
}

图片消息

json 复制代码
{
  "role": "user",
  "content": [{
    "type": "image",
    "omitted": true,
    "bytes": 1048576
  }],
  "timestamp": 1711716000000
}

三、关键机制对比

3.1 过滤能力

特性 sessions_list sessions_history
类型过滤 kinds 数组 不支持
活跃度过滤 activeMinutes 不支持
数量限制 limit limit
消息获取 messageLimit(最大 20) 全部消息

3.2 安全限制

限制类型 sessions_list sessions_history
可见性检查 visibilityGuard visibilityGuard
沙盒隔离 restrictToSpawned restrictToSpawned
内容脱敏 不需要 redactSensitiveText
硬性限制 80KB

3.3 并发处理

特性 sessions_list sessions_history
并发获取 最多 4 个并发 单次获取
并发目标 messageLimit > 0 的会话 不适用

四、使用示例

4.1 sessions_list 工具调用

用户列出最近 10 分钟活跃的主会话

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sessions_list",
    "arguments": { 
      "kinds": ["main"],
      "activeMinutes": 10,
      "limit": 10
    }
  }
}

执行结果

json 复制代码
{
  "count": 1,
  "sessions": [{
    "key": "main",
    "kind": "main",
    "channel": "feishu",
    "updatedAt": 1711716000000,
    "status": "idle"
  }]
}

4.2 sessions_history 工具调用

用户获取主会话的最后 20 条消息

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sessions_history",
    "arguments": { 
      "sessionKey": "main",
      "limit": 20
    }
  }
}

执行结果

json 复制代码
{
  "sessionKey": "main",
  "messages": [
    { "role": "user", "content": "Hello" },
    { "role": "assistant", "content": "Hi there!" }
  ],
  "hardCapped": false,
  "totalBytes": 5000
}
相关推荐
Ulyanov2 小时前
打造现代化雷达电子对抗仿真界面 第三篇:综合电子战指挥控制台——多视图协同与插件化架构
python·架构·系统仿真·雷达电子战
Westward-sun.2 小时前
OpenCV 疲劳检测实战:用 dlib 计算眼睛纵横比 (EAR)
人工智能·opencv·计算机视觉·视觉检测
AI周红伟2 小时前
Hermes Agent 工具-周红伟
linux·网络·人工智能·腾讯云·openclaw
杜子不疼.2 小时前
Python + AI 实战:用 LangChain 搭建企业级 RAG 知识库
人工智能·python·langchain
阿杰学AI2 小时前
AI核心知识118—大语言模型之 Software 2.0 (简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·aigc·编程·software 2.0
天天进步20152 小时前
[核心篇] 视频一致性算法:Toonflow 是如何处理视频闪烁问题的?
人工智能
中科岩创2 小时前
数字传感护华为数字能源大厦,控制加固施工安全风险!
人工智能·科技·物联网
无敌昊哥战神2 小时前
【算法与数据结构】深入浅出回溯算法:理论基础与核心模板(C/C++与Python三语解析)
c语言·数据结构·c++·笔记·python·算法
甄心爱学习2 小时前
【项目实训(个人3)】
vue.js·人工智能·python·个人开发