From d9a5daca0a4a78d0a9f8428c1b41c0586ecd8944 Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Fri, 20 Mar 2026 02:10:10 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=90=8C=E6=AD=A5:=202026-03?= =?UTF-8?q?-20=2002:10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agents/haonan/sessions/sessions.json | 239 ++++++------- .../workspace/memory/2026-03-19-1500.md | 164 +++++++++ .../workspace/memory/2026-03-19-1612.md | 67 ++++ .../workspace/memory/2026-03-19.md | 8 + agents/ui-designer/sessions/sessions.json | 140 +++++++- cron/jobs.json | 55 +-- cron/jobs.json.bak | 52 +-- .../bc3f3807-8735-4bef-bb42-3efd115c3f7e.json | 21 ++ feishu/dedup/feishu-art.json | 32 +- feishu/dedup/feishu-worker.json | 24 +- identity/device-auth.json | 2 +- logs/commands.log | 2 + skills/kb_workflow/.env.example | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 364 -> 364 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 361 -> 361 bytes .../__pycache__/cli.cpython-312.pyc | Bin 9608 -> 9691 bytes .../auth/__pycache__/__init__.cpython-312.pyc | Bin 386 -> 386 bytes .../__pycache__/zhihu_login.cpython-312.pyc | Bin 11582 -> 11582 bytes skills/kb_workflow/cli.py | 9 +- .../__pycache__/__init__.cpython-312.pyc | Bin 820 -> 820 bytes .../__pycache__/config.cpython-312.pyc | Bin 6012 -> 6012 bytes .../__pycache__/models.cpython-312.pyc | Bin 7616 -> 7616 bytes .../__pycache__/proxy_pool.cpython-312.pyc | Bin 9868 -> 9868 bytes .../__pycache__/zhihu_crawler.cpython-312.pyc | Bin 26112 -> 26112 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 418 -> 418 bytes .../stealth_config.cpython-312.pyc | Bin 5516 -> 5516 bytes skills/kb_workflow/deep_analyzer/__init__.py | 36 +- .../__pycache__/__init__.cpython-312.pyc | Bin 28172 -> 28545 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 433 -> 433 bytes .../bilibili_extractor.cpython-312.pyc | Bin 32331 -> 32331 bytes .../markdown_converter.cpython-312.pyc | Bin 2833 -> 2833 bytes .../zhihu_extractor.cpython-312.pyc | Bin 19539 -> 19539 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 1441 -> 1441 bytes .../__pycache__/config.cpython-312.pyc | Bin 3080 -> 3224 bytes .../__pycache__/errors.cpython-312.pyc | Bin 6314 -> 6314 bytes .../__pycache__/idempotency.cpython-312.pyc | Bin 2930 -> 2930 bytes .../__pycache__/link_parser.cpython-312.pyc | Bin 5842 -> 5842 bytes .../__pycache__/pipeline.cpython-312.pyc | Bin 13738 -> 15199 bytes .../__pycache__/retry.cpython-312.pyc | Bin 881 -> 881 bytes .../__pycache__/schemas.cpython-312.pyc | Bin 21810 -> 23514 bytes .../__pycache__/state.cpython-312.pyc | Bin 6052 -> 6052 bytes skills/kb_workflow/pipeline/config.py | 5 + skills/kb_workflow/pipeline/pipeline.py | 51 ++- .../__pycache__/__init__.cpython-312.pyc | Bin 878 -> 878 bytes .../__pycache__/base.cpython-312.pyc | Bin 4822 -> 4822 bytes .../__pycache__/bilibili.cpython-312.pyc | Bin 19884 -> 19884 bytes .../__pycache__/zhihu.cpython-312.pyc | Bin 20761 -> 20761 bytes .../pipeline/processors/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 1204 -> 1358 bytes .../__pycache__/analyze.cpython-312.pyc | Bin 11426 -> 11426 bytes .../__pycache__/archive.cpython-312.pyc | Bin 6744 -> 7498 bytes .../__pycache__/asr.cpython-312.pyc | Bin 26963 -> 26963 bytes .../__pycache__/base.cpython-312.pyc | Bin 2356 -> 2356 bytes .../__pycache__/download.cpython-312.pyc | Bin 13319 -> 13319 bytes .../__pycache__/parse.cpython-312.pyc | Bin 3092 -> 3092 bytes .../__pycache__/srt.cpython-312.pyc | Bin 0 -> 13168 bytes .../pipeline/processors/archive.py | 19 +- skills/kb_workflow/pipeline/processors/srt.py | 333 ++++++++++++++++++ skills/kb_workflow/pipeline/schemas.py | 39 ++ skills/kb_workflow/srt/__init__.py | 27 ++ .../srt/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 946 bytes .../srt/__pycache__/corrector.cpython-312.pyc | Bin 0 -> 5393 bytes .../srt/__pycache__/parser.cpython-312.pyc | Bin 0 -> 4718 bytes .../srt/__pycache__/segmenter.cpython-312.pyc | Bin 0 -> 3091 bytes skills/kb_workflow/srt/corrector.py | 157 +++++++++ skills/kb_workflow/srt/parser.py | 161 +++++++++ skills/kb_workflow/srt/segmenter.py | 97 +++++ 67 files changed, 1526 insertions(+), 220 deletions(-) create mode 100644 agents/knowledge-base/workspace/memory/2026-03-19-1500.md create mode 100644 agents/knowledge-base/workspace/memory/2026-03-19-1612.md create mode 100644 agents/knowledge-base/workspace/memory/2026-03-19.md create mode 100644 delivery-queue/bc3f3807-8735-4bef-bb42-3efd115c3f7e.json create mode 100644 skills/kb_workflow/pipeline/processors/__pycache__/srt.cpython-312.pyc create mode 100644 skills/kb_workflow/pipeline/processors/srt.py create mode 100644 skills/kb_workflow/srt/__init__.py create mode 100644 skills/kb_workflow/srt/__pycache__/__init__.cpython-312.pyc create mode 100644 skills/kb_workflow/srt/__pycache__/corrector.cpython-312.pyc create mode 100644 skills/kb_workflow/srt/__pycache__/parser.cpython-312.pyc create mode 100644 skills/kb_workflow/srt/__pycache__/segmenter.cpython-312.pyc create mode 100644 skills/kb_workflow/srt/corrector.py create mode 100644 skills/kb_workflow/srt/parser.py create mode 100644 skills/kb_workflow/srt/segmenter.py diff --git a/agents/haonan/sessions/sessions.json b/agents/haonan/sessions/sessions.json index d1dfcbe..11de632 100644 --- a/agents/haonan/sessions/sessions.json +++ b/agents/haonan/sessions/sessions.json @@ -715,8 +715,8 @@ "memoryFlushCompactionCount": 0 }, "agent:haonan:cron:41396aeb-6e3a-47b2-9bd3-9156a348c1dc": { - "sessionId": "13cf89c7-ae35-4b61-935d-4b76542f1c28", - "updatedAt": 1773856800006, + "sessionId": "971b07ad-12bb-4041-879c-97906de4bf18", + "updatedAt": 1773943200005, "systemSent": true, "model": "glm-5", "contextTokens": 200000, @@ -757,11 +757,11 @@ "version": 0 }, "modelProvider": "zhipu", - "inputTokens": 10815, - "outputTokens": 343, - "totalTokens": 16891, + "inputTokens": 13549, + "outputTokens": 371, + "totalTokens": 16884, "totalTokensFresh": true, - "cacheRead": 39360, + "cacheRead": 19840, "cacheWrite": 0 }, "agent:haonan:cron:f03b184f-5f1d-4093-ae1b-fc9fe0b59eef": { @@ -807,9 +807,9 @@ "version": 0 }, "modelProvider": "zhipu", - "inputTokens": 42108, - "outputTokens": 2001, - "totalTokens": 24584, + "inputTokens": 55959, + "outputTokens": 3146, + "totalTokens": 156912, "deliveryContext": { "channel": "webchat" }, @@ -818,7 +818,7 @@ "sessionFile": "/home/starmen1265/.openclaw/agents/haonan/sessions/ccc40ca7-7876-4d82-ab84-7b790feddc19.jsonl", "abortedLastRun": false, "totalTokensFresh": true, - "cacheRead": 39613, + "cacheRead": 100953, "cacheWrite": 0 }, "agent:haonan:cron:3245fca7-045b-4977-8fab-020f6df2e453": { @@ -861,8 +861,8 @@ "cacheWrite": 0 }, "agent:haonan:cron:aa4d2de9-79be-4262-83ee-af8824793bfc": { - "sessionId": "26e8d6da-1d38-4fc7-9ddb-a1c14c23fde4", - "updatedAt": 1773857400007, + "sessionId": "3659e428-8612-437c-9c2b-1ce2f4a0b33d", + "updatedAt": 1773943800004, "systemSent": true, "label": "Cron: openclaw-git-sync", "skillsSnapshot": { @@ -910,113 +910,6 @@ "cacheRead": 32768, "cacheWrite": 0 }, - "agent:haonan:cron:f03b184f-5f1d-4093-ae1b-fc9fe0b59eef:run:742f547d-e84f-486c-99fe-66589180c4a7": { - "sessionId": "742f547d-e84f-486c-99fe-66589180c4a7", - "updatedAt": 1773771600017, - "systemSent": true, - "model": "glm-4.7-FlashX", - "contextTokens": 200000, - "label": "Cron: capability-evolver-daily", - "skillsSnapshot": { - "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n \n clawlist\n MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.\n ~/.openclaw/skills/clawlist/SKILL.md\n \n", - "skills": [ - { - "name": "capability-evolver" - }, - { - "name": "clawlist" - } - ], - "skillFilter": [ - "clawlist", - "capability-evolver", - "self-improving-agent" - ], - "resolvedSkills": [ - { - "name": "capability-evolver", - "description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.", - "filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md", - "baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver", - "source": "openclaw-managed", - "disableModelInvocation": false - }, - { - "name": "clawlist", - "description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.", - "filePath": "/home/starmen1265/.openclaw/skills/clawlist/SKILL.md", - "baseDir": "/home/starmen1265/.openclaw/skills/clawlist", - "source": "openclaw-managed", - "disableModelInvocation": false - } - ], - "version": 0 - }, - "modelProvider": "zhipu", - "inputTokens": 42108, - "outputTokens": 2001, - "totalTokens": 24584, - "deliveryContext": { - "channel": "webchat" - }, - "lastChannel": "webchat", - "channel": "webchat", - "sessionFile": "/home/starmen1265/.openclaw/agents/haonan/sessions/ccc40ca7-7876-4d82-ab84-7b790feddc19.jsonl", - "abortedLastRun": false, - "totalTokensFresh": true, - "cacheRead": 39613, - "cacheWrite": 0 - }, - "agent:haonan:cron:41396aeb-6e3a-47b2-9bd3-9156a348c1dc:run:13cf89c7-ae35-4b61-935d-4b76542f1c28": { - "sessionId": "13cf89c7-ae35-4b61-935d-4b76542f1c28", - "updatedAt": 1773856800006, - "systemSent": true, - "model": "glm-5", - "contextTokens": 200000, - "label": "Cron: backup-openclaw-json", - "skillsSnapshot": { - "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n \n clawlist\n MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.\n ~/.openclaw/skills/clawlist/SKILL.md\n \n", - "skills": [ - { - "name": "capability-evolver" - }, - { - "name": "clawlist" - } - ], - "skillFilter": [ - "clawlist", - "capability-evolver", - "self-improving-agent" - ], - "resolvedSkills": [ - { - "name": "capability-evolver", - "description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.", - "filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md", - "baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver", - "source": "openclaw-managed", - "disableModelInvocation": false - }, - { - "name": "clawlist", - "description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.", - "filePath": "/home/starmen1265/.openclaw/skills/clawlist/SKILL.md", - "baseDir": "/home/starmen1265/.openclaw/skills/clawlist", - "source": "openclaw-managed", - "disableModelInvocation": false - } - ], - "version": 0 - }, - "modelProvider": "zhipu", - "inputTokens": 10815, - "outputTokens": 343, - "totalTokens": 16891, - "totalTokensFresh": true, - "cacheRead": 39360, - "cacheWrite": 0 - }, "agent:haonan:cron:aa4d2de9-79be-4262-83ee-af8824793bfc:run:26e8d6da-1d38-4fc7-9ddb-a1c14c23fde4": { "sessionId": "26e8d6da-1d38-4fc7-9ddb-a1c14c23fde4", "updatedAt": 1773857400007, @@ -1110,9 +1003,9 @@ "version": 0 }, "modelProvider": "zhipu", - "inputTokens": 42108, - "outputTokens": 2001, - "totalTokens": 24584, + "inputTokens": 55959, + "outputTokens": 3146, + "totalTokens": 156912, "deliveryContext": { "channel": "webchat" }, @@ -1121,7 +1014,107 @@ "sessionFile": "/home/starmen1265/.openclaw/agents/haonan/sessions/ccc40ca7-7876-4d82-ab84-7b790feddc19.jsonl", "abortedLastRun": false, "totalTokensFresh": true, - "cacheRead": 39613, + "cacheRead": 100953, + "cacheWrite": 0 + }, + "agent:haonan:cron:41396aeb-6e3a-47b2-9bd3-9156a348c1dc:run:971b07ad-12bb-4041-879c-97906de4bf18": { + "sessionId": "971b07ad-12bb-4041-879c-97906de4bf18", + "updatedAt": 1773943200005, + "systemSent": true, + "model": "glm-5", + "contextTokens": 200000, + "label": "Cron: backup-openclaw-json", + "skillsSnapshot": { + "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n \n clawlist\n MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.\n ~/.openclaw/skills/clawlist/SKILL.md\n \n", + "skills": [ + { + "name": "capability-evolver" + }, + { + "name": "clawlist" + } + ], + "skillFilter": [ + "clawlist", + "capability-evolver", + "self-improving-agent" + ], + "resolvedSkills": [ + { + "name": "capability-evolver", + "description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.", + "filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md", + "baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver", + "source": "openclaw-managed", + "disableModelInvocation": false + }, + { + "name": "clawlist", + "description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.", + "filePath": "/home/starmen1265/.openclaw/skills/clawlist/SKILL.md", + "baseDir": "/home/starmen1265/.openclaw/skills/clawlist", + "source": "openclaw-managed", + "disableModelInvocation": false + } + ], + "version": 0 + }, + "modelProvider": "zhipu", + "inputTokens": 13549, + "outputTokens": 371, + "totalTokens": 16884, + "totalTokensFresh": true, + "cacheRead": 19840, + "cacheWrite": 0 + }, + "agent:haonan:cron:aa4d2de9-79be-4262-83ee-af8824793bfc:run:3659e428-8612-437c-9c2b-1ce2f4a0b33d": { + "sessionId": "3659e428-8612-437c-9c2b-1ce2f4a0b33d", + "updatedAt": 1773943800004, + "systemSent": true, + "label": "Cron: openclaw-git-sync", + "skillsSnapshot": { + "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n \n clawlist\n MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.\n ~/.openclaw/skills/clawlist/SKILL.md\n \n", + "skills": [ + { + "name": "capability-evolver" + }, + { + "name": "clawlist" + } + ], + "skillFilter": [ + "clawlist", + "capability-evolver", + "self-improving-agent" + ], + "resolvedSkills": [ + { + "name": "capability-evolver", + "description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.", + "filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md", + "baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver", + "source": "openclaw-managed", + "disableModelInvocation": false + }, + { + "name": "clawlist", + "description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.", + "filePath": "/home/starmen1265/.openclaw/skills/clawlist/SKILL.md", + "baseDir": "/home/starmen1265/.openclaw/skills/clawlist", + "source": "openclaw-managed", + "disableModelInvocation": false + } + ], + "version": 0 + }, + "modelProvider": "zhipu", + "model": "glm-5", + "contextTokens": 200000, + "inputTokens": 1734, + "outputTokens": 413, + "totalTokens": 18003, + "totalTokensFresh": true, + "cacheRead": 32768, "cacheWrite": 0 } } \ No newline at end of file diff --git a/agents/knowledge-base/workspace/memory/2026-03-19-1500.md b/agents/knowledge-base/workspace/memory/2026-03-19-1500.md new file mode 100644 index 0000000..6f778cb --- /dev/null +++ b/agents/knowledge-base/workspace/memory/2026-03-19-1500.md @@ -0,0 +1,164 @@ +# Session: 2026-03-19 15:00:36 UTC + +- **Session Key**: agent:knowledge-base:main +- **Session ID**: 731aff7c-67da-45ff-957f-7940849d0aae +- **Source**: feishu + +## Conversation Summary + +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548be4a02ca4b33d43c7a3208be", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:31 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548be4a02ca4b33d43c7a3208be] +张青: 记一下,可可想要一个里面有小人的公交车 +assistant: 已记录到速记本:**可可想要一个里面有小人的公交车** (2026-03-19 21:31) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548be25df0b0b22cdf708250b00", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:31 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548be25df0b0b22cdf708250b00] +张青: 还要带语音控制的 +assistant: 已更新:**可可想要一个里面有小人的公交车(带语音控制)** (2026-03-19 21:31) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548be03aaca4b393638a734446f", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:32 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548be03aaca4b393638a734446f] +张青: 还要能放音乐的 +assistant: 已更新:**可可想要一个里面有小人的公交车(带语音控制、能放音乐)** (2026-03-19 21:31) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548bfc0824a4b364c8c0c8977d1", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:33 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548bfc0824a4b364c8c0c8977d1] +张青: 乐乐想要一个库洛米城堡,里面有很多摆件,这些摆件呢,可以动 +assistant: 已记录:**乐乐想要一个库洛米城堡,里面有很多摆件,这些摆件可以动** (2026-03-19 21:33) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548bfd70e48cb22d0cbc00050f8", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:33 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548bfd70e48cb22d0cbc00050f8] +张青: 通过按按钮来控制里面的家具或者家电呃运动 +assistant: 已更新:**乐乐想要一个库洛米城堡,里面有很多摆件,通过按按钮控制家具或家电运动** (2026-03-19 21:33) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548bf82dbca0b2c751a1b709843", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:34 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548bf82dbca0b2c751a1b709843] +张青: 可可还想要一个可以涂颜色的石膏娃娃 +assistant: 已记录:**可可想要一个可以涂颜色的石膏娃娃** (2026-03-19 21:34) +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b548b8e22dcacb2d8d587bb7b14e", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 21:37 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b548b8e22dcacb2d8d587bb7b14e] +张青: 嗯,可可想要一个空的城堡,里面有个绿色按钮。 + +绿色按钮按一下,城堡的顶上就会出现一个美乐蒂,再按一下按钮美乐蒂就会变成一朵花 +assistant: 已记录:**可可想要一个空的城堡,里面有个绿色按钮:按一下城堡顶上出现美乐蒂,再按一下美乐蒂变成一朵花** (2026-03-19 21:37) diff --git a/agents/knowledge-base/workspace/memory/2026-03-19-1612.md b/agents/knowledge-base/workspace/memory/2026-03-19-1612.md new file mode 100644 index 0000000..b7439a1 --- /dev/null +++ b/agents/knowledge-base/workspace/memory/2026-03-19-1612.md @@ -0,0 +1,67 @@ +# Session: 2026-03-19 16:12:12 UTC + +- **Session Key**: agent:knowledge-base:main +- **Session ID**: b11228d2-b3ca-444f-837c-c0a787d3a0cb +- **Source**: feishu + +## Conversation Summary + +assistant: ✅ New session started · model: zhipu/glm-5 +user: A new session was started via /new or /reset. Execute your Session Startup sequence now - read the required files before responding to the user. Then greet the user in your configured persona, if one is provided. Be yourself - use your defined voice, mannerisms, and mood. Keep it to 1-3 sentences and ask what they want to do. If the runtime model differs from default_model in the system prompt, mention the default model. Do not mention internal steps, files, tools, or reasoning. +assistant: 📚 蒋先生您好,我是达康书记。已为您准备好知识库与工作区维护服务。 + +今天有什么需要我帮您处理的? +user: Conversation info (untrusted metadata): +```json +{ + "message_id": "om_x100b54f5454c14a4c42e9b6c6ea2cd3", + "sender_id": "ou_9351c287f42069fa30366ac641d24df7", + "sender": "张青", + "timestamp": "Thu 2026-03-19 23:05 GMT+8" +} +``` + +Sender (untrusted metadata): +```json +{ + "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)", + "id": "ou_9351c287f42069fa30366ac641d24df7", + "name": "张青" +} +``` + +[message_id: om_x100b54f5454c14a4c42e9b6c6ea2cd3] +张青: https://www.bilibili.com/video/BV1NwwqzeE55/?t=6&spm_id_from=333.1007.tianma.1-1-1.click&vd_source=1deaab00c5de8885422a7a7690170eba 添加知识库 +assistant: 收到,蒋先生。我立即按照知识库工作流技能处理这个B站视频链接。 +assistant: 好的,蒋先生。我按照知识库工作流技能处理这个B站视频链接。 + +**步骤 1:检查环境变量** +assistant: 好的,蒋先生。工作流已经开始处理这个B站视频。我看到它正在: + +1. ✅ 解析 URL +2. ⚠️ 字幕获取失败(可能视频没有字幕) +3. 🔄 正在进行 AI 深度分析... + +让我继续监控进度: +assistant: ✅ **处理完成!** + +**视频信息:** +- **标题:** 爱因斯坦又对了!科学家首次观测到物质从真空中诞生,【科学快报】265期 +- **作者:** 科学透镜 +- **BV号:** BV1NwwqzeE55 + +**处理流程:** +- ✅ Parse - URL 解析成功 +- ✅ Download - 下载音频和元数据 +- ✅ ASR - 语音识别完成(Whisper) +- ✅ Analyze - AI 深度分析完成(耗时 110.78秒) +- ✅ Archive - 归档成功 + +**核心内容摘要:** +本视频报道了三项颠覆性科学发现: +1. **物质从真空中诞生** - 布鲁克海文国家实验室首次观测到Lambda超子与反粒子的自旋纠缠,证实虚粒子可转化为真实物质 +2. **电子运动延迟** - ETH测得电子响应原子核运动存在30飞秒延迟,打破波恩-奥本海默近似 +3. **半莫比乌斯分子** - 人类首次合成并精准操控具有拓扑结构的分子 + +**归档位置:** +`~/work/zq/30.资源/AI分析/bilibili/20260319_BV1NwwqzeE55_analysis.md` diff --git a/agents/knowledge-base/workspace/memory/2026-03-19.md b/agents/knowledge-base/workspace/memory/2026-03-19.md new file mode 100644 index 0000000..af51648 --- /dev/null +++ b/agents/knowledge-base/workspace/memory/2026-03-19.md @@ -0,0 +1,8 @@ +# 2026-03-19 每日记录 + +## 定时任务执行 + +### 02:30 - 每日回顾整理(daily-review-cleanup) +- 执行脚本:`~/.openclaw/cron/scripts/daily-review.sh` +- 处理日期:2026-03-18 +- 结果:从 NAS 拉取更新完成,无已完成任务需要归档 diff --git a/agents/ui-designer/sessions/sessions.json b/agents/ui-designer/sessions/sessions.json index 6f5d6f0..5ea9fca 100644 --- a/agents/ui-designer/sessions/sessions.json +++ b/agents/ui-designer/sessions/sessions.json @@ -1,7 +1,7 @@ { "agent:ui-designer:main": { - "sessionId": "4caf7900-b5bc-4f76-a39d-80e506bcea84", - "updatedAt": 1773758631721, + "sessionId": "60827bbf-d985-4a29-bd62-1783159bb38e", + "updatedAt": 1773937164358, "systemSent": true, "abortedLastRun": false, "chatType": "direct", @@ -22,7 +22,7 @@ "to": "user:ou_9847ce84b960736c14554730b41e4957", "accountId": "feishu-art" }, - "sessionFile": "/home/starmen1265/.openclaw/agents/ui-designer/sessions/4caf7900-b5bc-4f76-a39d-80e506bcea84.jsonl", + "sessionFile": "/home/starmen1265/.openclaw/agents/ui-designer/sessions/60827bbf-d985-4a29-bd62-1783159bb38e.jsonl", "compactionCount": 0, "skillsSnapshot": { "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n", @@ -51,8 +51,8 @@ "model": "glm-5", "systemPromptReport": { "source": "run", - "generatedAt": 1773758614151, - "sessionId": "4caf7900-b5bc-4f76-a39d-80e506bcea84", + "generatedAt": 1773937122951, + "sessionId": "60827bbf-d985-4a29-bd62-1783159bb38e", "sessionKey": "agent:ui-designer:main", "provider": "zhipu", "model": "glm-5", @@ -64,8 +64,8 @@ "sandboxed": false }, "systemPrompt": { - "chars": 24499, - "projectContextChars": 12014, + "chars": 36876, + "projectContextChars": 24391, "nonProjectContextChars": 12485 }, "injectedWorkspaceFiles": [ @@ -73,8 +73,8 @@ "name": "AGENTS.md", "path": "/home/starmen1265/.openclaw/agents/ui-designer/workspace/AGENTS.md", "missing": false, - "rawChars": 2617, - "injectedChars": 2617, + "rawChars": 2663, + "injectedChars": 2663, "truncated": false }, { @@ -187,6 +187,120 @@ "injectedChars": 616, "truncated": false }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, + { + "path": "SELF_IMPROVEMENT_REMINDER.md", + "rawChars": 616, + "injectedChars": 616, + "truncated": false + }, { "path": "SELF_IMPROVEMENT_REMINDER.md", "rawChars": 616, @@ -414,13 +528,13 @@ ] } }, - "cacheRead": 9408, + "cacheRead": 23872, "cacheWrite": 0, "totalTokensFresh": true, "contextTokens": 200000, - "inputTokens": 7026, - "outputTokens": 464, - "totalTokens": 16434 + "inputTokens": 311770, + "outputTokens": 332, + "totalTokens": 170338 }, "agent:ui-designer:cron:78b64c2f-7f91-47ff-9250-d13f546eb21f": { "sessionId": "03e44b46-c707-4a9f-bf3d-1818aafdcde2", diff --git a/cron/jobs.json b/cron/jobs.json index 09b9348..08a3464 100644 --- a/cron/jobs.json +++ b/cron/jobs.json @@ -7,7 +7,7 @@ "name": "backup-openclaw-json", "enabled": true, "createdAtMs": 1772261581796, - "updatedAtMs": 1773856817101, + "updatedAtMs": 1773943214562, "schedule": { "kind": "cron", "expr": "0 2 * * *", @@ -23,10 +23,10 @@ "mode": "announce" }, "state": { - "nextRunAtMs": 1773943200000, - "lastRunAtMs": 1773856800004, + "nextRunAtMs": 1774029600000, + "lastRunAtMs": 1773943200004, "lastStatus": "ok", - "lastDurationMs": 17097, + "lastDurationMs": 14558, "consecutiveErrors": 0, "lastRunStatus": "ok", "lastDelivered": true, @@ -59,7 +59,8 @@ "consecutiveErrors": 0, "lastRunStatus": "ok", "lastDeliveryStatus": "delivered", - "lastDelivered": true + "lastDelivered": true, + "runningAtMs": 1773943800002 }, "delivery": { "mode": "announce" @@ -71,7 +72,7 @@ "name": "capability-evolver-daily", "enabled": true, "createdAtMs": 1772550743528, - "updatedAtMs": 1773771643135, + "updatedAtMs": 1773858232095, "schedule": { "kind": "cron", "expr": "20 2 * * *", @@ -92,16 +93,16 @@ "to": "feishu-pm" }, "state": { - "nextRunAtMs": 1773858000000, - "lastRunAtMs": 1773771600014, + "nextRunAtMs": 1773944400000, + "lastRunAtMs": 1773858000004, "lastStatus": "error", - "lastDurationMs": 43121, - "consecutiveErrors": 5, + "lastDurationMs": 232091, + "consecutiveErrors": 6, "lastRunStatus": "error", "lastDeliveryStatus": "not-delivered", "lastDelivered": false, - "lastDeliveryError": "⚠️ ✉️ Message: 蒋先生 failed", - "runningAtMs": 1773858000003 + "lastDeliveryError": "⚠️ ✉️ Message failed", + "lastError": "⚠️ ✉️ Message failed" } }, { @@ -111,7 +112,7 @@ "description": "每天凌晨2:30整理昨天的已完成任务到每日回顾", "enabled": true, "createdAtMs": 1741484880000, - "updatedAtMs": 1773772248827, + "updatedAtMs": 1773858626341, "schedule": { "kind": "cron", "expr": "30 2 * * *", @@ -129,11 +130,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773858600000, - "lastRunAtMs": 1773772200010, + "nextRunAtMs": 1773945000000, + "lastRunAtMs": 1773858600004, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 48817, + "lastDurationMs": 26337, "lastDelivered": true, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0 @@ -145,7 +146,7 @@ "name": "zq_workspace_sync", "enabled": true, "createdAtMs": 1772551807455, - "updatedAtMs": 1773772800011, + "updatedAtMs": 1773859200006, "schedule": { "expr": "40 2 * * *", "kind": "cron", @@ -158,8 +159,8 @@ "text": "执行 zq 工作区同步:bash ~/.openclaw/cron/scripts/zq_workspace_sync.sh" }, "state": { - "nextRunAtMs": 1773859200000, - "lastRunAtMs": 1773772800010, + "nextRunAtMs": 1773945600000, + "lastRunAtMs": 1773859200005, "lastStatus": "skipped", "lastDurationMs": 1, "consecutiveErrors": 0, @@ -175,7 +176,7 @@ "description": "每天早上8点整理与各个 AI 代理的对话记录", "enabled": true, "createdAtMs": 1772776369645, - "updatedAtMs": 1773792029087, + "updatedAtMs": 1773878422623, "schedule": { "kind": "cron", "expr": "0 8 * * *", @@ -193,11 +194,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773878400000, - "lastRunAtMs": 1773792000007, + "nextRunAtMs": 1773964800000, + "lastRunAtMs": 1773878400004, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 29080, + "lastDurationMs": 22619, "lastDelivered": true, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0 @@ -210,7 +211,7 @@ "description": "每天7点同步提醒到CalDAV日历", "enabled": true, "createdAtMs": 1772791528528, - "updatedAtMs": 1773788430000, + "updatedAtMs": 1773874820139, "schedule": { "kind": "cron", "expr": "0 7 * * *", @@ -228,11 +229,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773874800000, - "lastRunAtMs": 1773788400005, + "nextRunAtMs": 1773961200000, + "lastRunAtMs": 1773874800006, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 29995, + "lastDurationMs": 20133, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0, "lastDelivered": true diff --git a/cron/jobs.json.bak b/cron/jobs.json.bak index 753feea..2d8376d 100644 --- a/cron/jobs.json.bak +++ b/cron/jobs.json.bak @@ -7,7 +7,7 @@ "name": "backup-openclaw-json", "enabled": true, "createdAtMs": 1772261581796, - "updatedAtMs": 1773856817101, + "updatedAtMs": 1773943214562, "schedule": { "kind": "cron", "expr": "0 2 * * *", @@ -23,10 +23,10 @@ "mode": "announce" }, "state": { - "nextRunAtMs": 1773943200000, - "lastRunAtMs": 1773856800004, + "nextRunAtMs": 1774029600000, + "lastRunAtMs": 1773943200004, "lastStatus": "ok", - "lastDurationMs": 17097, + "lastDurationMs": 14558, "consecutiveErrors": 0, "lastRunStatus": "ok", "lastDelivered": true, @@ -71,7 +71,7 @@ "name": "capability-evolver-daily", "enabled": true, "createdAtMs": 1772550743528, - "updatedAtMs": 1773771643135, + "updatedAtMs": 1773858232095, "schedule": { "kind": "cron", "expr": "20 2 * * *", @@ -92,16 +92,16 @@ "to": "feishu-pm" }, "state": { - "nextRunAtMs": 1773858000000, - "lastRunAtMs": 1773771600014, + "nextRunAtMs": 1773944400000, + "lastRunAtMs": 1773858000004, "lastStatus": "error", - "lastDurationMs": 43121, - "consecutiveErrors": 5, + "lastDurationMs": 232091, + "consecutiveErrors": 6, "lastRunStatus": "error", "lastDeliveryStatus": "not-delivered", "lastDelivered": false, - "lastDeliveryError": "⚠️ ✉️ Message: 蒋先生 failed", - "lastError": "⚠️ ✉️ Message: 蒋先生 failed" + "lastDeliveryError": "⚠️ ✉️ Message failed", + "lastError": "⚠️ ✉️ Message failed" } }, { @@ -111,7 +111,7 @@ "description": "每天凌晨2:30整理昨天的已完成任务到每日回顾", "enabled": true, "createdAtMs": 1741484880000, - "updatedAtMs": 1773772248827, + "updatedAtMs": 1773858626341, "schedule": { "kind": "cron", "expr": "30 2 * * *", @@ -129,11 +129,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773858600000, - "lastRunAtMs": 1773772200010, + "nextRunAtMs": 1773945000000, + "lastRunAtMs": 1773858600004, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 48817, + "lastDurationMs": 26337, "lastDelivered": true, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0 @@ -145,7 +145,7 @@ "name": "zq_workspace_sync", "enabled": true, "createdAtMs": 1772551807455, - "updatedAtMs": 1773772800011, + "updatedAtMs": 1773859200006, "schedule": { "expr": "40 2 * * *", "kind": "cron", @@ -158,8 +158,8 @@ "text": "执行 zq 工作区同步:bash ~/.openclaw/cron/scripts/zq_workspace_sync.sh" }, "state": { - "nextRunAtMs": 1773859200000, - "lastRunAtMs": 1773772800010, + "nextRunAtMs": 1773945600000, + "lastRunAtMs": 1773859200005, "lastStatus": "skipped", "lastDurationMs": 1, "consecutiveErrors": 0, @@ -175,7 +175,7 @@ "description": "每天早上8点整理与各个 AI 代理的对话记录", "enabled": true, "createdAtMs": 1772776369645, - "updatedAtMs": 1773792029087, + "updatedAtMs": 1773878422623, "schedule": { "kind": "cron", "expr": "0 8 * * *", @@ -193,11 +193,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773878400000, - "lastRunAtMs": 1773792000007, + "nextRunAtMs": 1773964800000, + "lastRunAtMs": 1773878400004, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 29080, + "lastDurationMs": 22619, "lastDelivered": true, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0 @@ -210,7 +210,7 @@ "description": "每天7点同步提醒到CalDAV日历", "enabled": true, "createdAtMs": 1772791528528, - "updatedAtMs": 1773788430000, + "updatedAtMs": 1773874820139, "schedule": { "kind": "cron", "expr": "0 7 * * *", @@ -228,11 +228,11 @@ "channel": "last" }, "state": { - "nextRunAtMs": 1773874800000, - "lastRunAtMs": 1773788400005, + "nextRunAtMs": 1773961200000, + "lastRunAtMs": 1773874800006, "lastRunStatus": "ok", "lastStatus": "ok", - "lastDurationMs": 29995, + "lastDurationMs": 20133, "lastDeliveryStatus": "delivered", "consecutiveErrors": 0, "lastDelivered": true diff --git a/delivery-queue/bc3f3807-8735-4bef-bb42-3efd115c3f7e.json b/delivery-queue/bc3f3807-8735-4bef-bb42-3efd115c3f7e.json new file mode 100644 index 0000000..9a0c273 --- /dev/null +++ b/delivery-queue/bc3f3807-8735-4bef-bb42-3efd115c3f7e.json @@ -0,0 +1,21 @@ +{ + "id": "bc3f3807-8735-4bef-bb42-3efd115c3f7e", + "enqueuedAt": 1773858231892, + "channel": "feishu", + "to": "feishu-pm", + "accountId": "feishu-pm", + "payloads": [ + { + "text": "我遇到了 Feishu 目标格式的问题。让我直接在当前会话中向您汇报:" + } + ], + "threadId": null, + "mirror": { + "sessionKey": "agent:haonan:main", + "agentId": "haonan", + "text": "我遇到了 Feishu 目标格式的问题。让我直接在当前会话中向您汇报:" + }, + "retryCount": 1, + "lastAttemptAt": 1773858232086, + "lastError": "Request failed with status code 400" +} \ No newline at end of file diff --git a/feishu/dedup/feishu-art.json b/feishu/dedup/feishu-art.json index 3b55670..c9f86f8 100644 --- a/feishu/dedup/feishu-art.json +++ b/feishu/dedup/feishu-art.json @@ -1,5 +1,31 @@ { - "om_x100b54b68a9658a8c3597587eaea165": 1773677638200, - "om_x100b54b69ae988acc4e308675a1599b": 1773677890863, - "om_x100b54a2c799a0b0c2ea37cee3ecec2": 1773758613782 + "om_x100b5480d5bba4b4b2c9a4e8bf5b189": 1773881784103, + "om_x100b54808ff17c90b374531d1cf7751": 1773882388481, + "om_x100b5481441a38a4b20013813040b59": 1773883566093, + "om_x100b54816cbffcb8b36dfe46fd84a26": 1773883944086, + "om_x100b54817240a0a0b49ad1ecc4230d1": 1773884361569, + "om_x100b548114fc64a4b34dda488c6c4cf": 1773884835671, + "om_x100b548129129490b4b1770d7f3efcc": 1773885054622, + "om_x100b5481243380b0b10b5bef1b92ca3": 1773885104561, + "om_x100b54813d7144bcb2da847227b06a8": 1773885244462, + "om_x100b548139e42d10b3ffcf9447ed48c": 1773885299355, + "om_x100b548133fa3ca4b219d22fcf41e18": 1773885396226, + "om_x100b5481ce0418a0b26dc9b2f68f007": 1773885453209, + "om_x100b548c9b6dcc78b49c9ec428e44f3": 1773899098605, + "om_x100b548ca39e00acc3362a9bf3873de": 1773899477853, + "om_x100b548d47a538b0c2c282968797cea": 1773899927180, + "om_x100b5489ba663ca0c4c8bc1d9b97937": 1773920075261, + "om_x100b548a50d788b8b2c1f705241dbc5": 1773920738205, + "om_x100b548a72e7f4a8b286f0d7e9aea31": 1773921219213, + "om_x100b548a83211ca8c44bcbf00eb049e": 1773923551363, + "om_x100b548a95d45530c29580a9d1bad9a": 1773923762295, + "om_x100b548b1fa8c8a4c42e95eb381a75c": 1773925655047, + "om_x100b54f49f9f20b0b2d58df353d8a5b": 1773931798109, + "om_x100b54f51931bca0c361d933da09d2b": 1773933952255, + "om_x100b54f52b61f0a4c3650d280bf0dad": 1773934171242, + "om_x100b54f53b8678b0c3188a42d675405": 1773934421286, + "om_x100b54f533291ca4c2cf74f54da005d": 1773934558803, + "om_x100b54f5b9ebf8a0c33674c32a8da6c": 1773936499279, + "om_x100b54f64c3844acc14333ab25a59e5": 1773936688149, + "om_x100b54f650ef80a0c2d3c76a3fbe9be": 1773937122637 } diff --git a/feishu/dedup/feishu-worker.json b/feishu/dedup/feishu-worker.json index e1d7b1d..dec6427 100644 --- a/feishu/dedup/feishu-worker.json +++ b/feishu/dedup/feishu-worker.json @@ -1,10 +1,18 @@ { - "om_x100b54a3ae6304a0b28ab7519a1acf1": 1773764107460, - "om_x100b54a3a3782938b379c9a1da0010b": 1773764315828, - "om_x100b54a3b60634a0b2910ed3595f0aa": 1773764493305, - "om_x100b54955fb54964c4e180ded6a68b3": 1773801752225, - "om_x100b54955a75ccb4c11c3b4070aa4fd": 1773801804425, - "om_x100b54951cb128bcc361c4fb7da8c71": 1773802792386, - "om_x100b5498aaee84a0c451ce70c732daf": 1773850178765, - "om_x100b5498a8e104a8c37682c5c9f2e15": 1773850211313 + "om_x100b548be4a02ca4b33d43c7a3208be": 1773927079628, + "om_x100b548be25df0b0b22cdf708250b00": 1773927113615, + "om_x100b548be03aaca4b393638a734446f": 1773927152061, + "om_x100b548bfc0824a4b364c8c0c8977d1": 1773927212950, + "om_x100b548bfd70e48cb22d0cbc00050f8": 1773927228439, + "om_x100b548bf82dbca0b2c751a1b709843": 1773927278588, + "om_x100b548b8e22dcacb2d8d587bb7b14e": 1773927439644, + "om_x100b54f4b7f294a0c45989f17b70fb7": 1773932436549, + "om_x100b54f5454c14a4c42e9b6c6ea2cd3": 1773932728615, + "om_x100b54f64b72d4b4c44dfcb4a5b55ab": 1773936732479, + "om_x100b54f644fc18a4c3e0e0a4d345c50": 1773936803575, + "om_x100b54f654df8534c4ee3346acd1bcd": 1773937057577, + "om_x100b54f6091f78a4c4d71668a805f7d": 1773937789758, + "om_x100b54f6cf31b4a0c453538202af189": 1773938720210, + "om_x100b54f6d65a48a4c4e7a9045781192": 1773939082098, + "om_x100b54f6d48980a0c2cc7e00d22cd8e": 1773939108667 } diff --git a/identity/device-auth.json b/identity/device-auth.json index 713caf9..32eb46b 100644 --- a/identity/device-auth.json +++ b/identity/device-auth.json @@ -10,7 +10,7 @@ "operator.approvals", "operator.pairing" ], - "updatedAtMs": 1773857416774 + "updatedAtMs": 1773943213908 } } } diff --git a/logs/commands.log b/logs/commands.log index 75fe861..f5a563a 100644 --- a/logs/commands.log +++ b/logs/commands.log @@ -60,3 +60,5 @@ {"timestamp":"2026-03-17T16:06:09.290Z","action":"new","sessionKey":"agent:knowledge-base:main","senderId":"ou_9351c287f42069fa30366ac641d24df7","source":"feishu"} {"timestamp":"2026-03-18T02:42:32.742Z","action":"new","sessionKey":"agent:knowledge-base:main","senderId":"ou_9351c287f42069fa30366ac641d24df7","source":"feishu"} {"timestamp":"2026-03-18T16:09:39.151Z","action":"new","sessionKey":"agent:knowledge-base:main","senderId":"ou_9351c287f42069fa30366ac641d24df7","source":"feishu"} +{"timestamp":"2026-03-19T15:00:36.886Z","action":"new","sessionKey":"agent:knowledge-base:main","senderId":"ou_9351c287f42069fa30366ac641d24df7","source":"feishu"} +{"timestamp":"2026-03-19T16:12:12.817Z","action":"new","sessionKey":"agent:knowledge-base:main","senderId":"ou_9351c287f42069fa30366ac641d24df7","source":"feishu"} diff --git a/skills/kb_workflow/.env.example b/skills/kb_workflow/.env.example index dc8e79b..283b878 100644 --- a/skills/kb_workflow/.env.example +++ b/skills/kb_workflow/.env.example @@ -12,3 +12,6 @@ export KB_BASE_PATH="~/work/knowledge-base" # 可选: Git 配置 # export KB_GIT_USER="Your Name" # export KB_GIT_EMAIL="your@email.com" + +# 可选: 工作流行为配置 +# export KB_OVERWRITE_ARCHIVES="false" # 非交互模式下是否覆盖已存在的归档文件(true/false) diff --git a/skills/kb_workflow/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/__pycache__/__init__.cpython-312.pyc index 135bf9e98cdf59db762b332797f90ca13e554461..fcfe64ba5ea9aec00f9876ffa42dd2175657104c 100644 GIT binary patch delta 20 acmaFE^oEK1G%qg~0}xEr+_RB8kr4nsEd_4? delta 20 acmaFE^oEK1G%qg~0}z}%yK5tNA|n7pX9h(8 diff --git a/skills/kb_workflow/__pycache__/__main__.cpython-312.pyc b/skills/kb_workflow/__pycache__/__main__.cpython-312.pyc index 43a557e01037509bc34b75692a7214a3548276a5..a2df4559383ff83a6d45b4bbfb06c6a6db93117d 100644 GIT binary patch delta 20 acmaFK^pc7DG%qg~0}xEr+_RB8ju8MowFPDX delta 20 acmaFK^pc7DG%qg~0}z}%yK5tN93uci?*=>o diff --git a/skills/kb_workflow/__pycache__/cli.cpython-312.pyc b/skills/kb_workflow/__pycache__/cli.cpython-312.pyc index 9333d1c96866c12024f00247a7d4df05b2b9c179..93f868ecb0fcf5b65ee6d59ccd91c9f6625b38f8 100644 GIT binary patch delta 1786 zcmZ8hZ)_Ar6rZ`>+uPmyvsbQHXs@()M`?Stt@5W}u@-C#C>N>nN2wC7*W0CBdv|-x z?pd0&wv9x$NQ^i!NYum_{URoWG||KulS03d7?cm<5+aH5gNYxGAZel|I&ZH9b&~n* zn>X*hdGF0{)=J(=2Cn)2GGgQ1#Mjx*=;gpo&g)}Kj?H^67ro1^3%;S8M-o!8>dK)! zKBLgJd~0uBJ`=#8bmzQz-x)tfmd6q;$&xLv<+J=&z)~!1xh!A66dp5^Os1D#Vso_1B7;>Q~3B45iiw;0CgeLjZc^ll(Tce%qn+inI^C2S@tK@f6u zmy|j>Lu-(NpF*I9L^@&1GPpdYUHi*p`#H4M~aAb`w8k%Wl3!{eZ(Mx7Z%@WecP9ahH zskAjw17a8;03ZTL0Fbk>hC$RBv-B@%3SXd;a&JRD4g464-U!AkSoVAB0@l8})u^V) zt{J4)<-MVvJ`peO#C-yP1$mgfpRA{C-c8NWPTNB?qm(m^Jqeyb56L3{?EoDN5;b(r zdvwOh$_^H@MJ<;tXq`spgqCNuLB_TxZA=z2S)IgJy=-n&Go5g@Un4{(sk~+w=~2xI z%o<`6TiSgpqZQ4pUPuI;0{>NSD+Fo=aB{?0qhCplGym<65o89#!^6;$U8{q%Fr}>& ziJoC6rZRdypDw86ai)bv8$U4k`_EZaj_!2Jx5Z7j#YdN2oFp$J2KPlIbuIG^Qr$9Q z@OvLdYiiF&&P6U9egD{vDq8k^vi_}pUw;H$jY$2y+|?$fznicK7a zo6rD$hjP`Ob-P)%WX}Ugf&>MUXK1o|hZkr}_5md6+tnM|_Ja5v05~Q@5(D{J1~zX_ z7BySYN5-^_Ne1cd>W0l=nc!`AP9Mz{NDr6=G9^N^g2@`Ek7qTqfhi51L3DO@mbB20 z@HTvQK@D%k+kq^$XZ2qDV8xFa>^8B@=XF&}xE}Ipe5_SZ^w7ncTJMw0n)Cv6 z(qC#4Gn+vK_SiDG=2B`YU$nhO`W0<86DY-2oK#aIlc|#=!wf4FlPk%l7^}!Oh}6X( z5g^?l?*Mps9y~0Tb-Dm#Brn2dGdjR<1jNGt+X0{(goeTN#XP6{7ho39Zsh zWo9SYG(z;JwZC~kI(}V=-cUNOiyid4b*=3JT9h9fVE9Cw;gyD`TG(evkOo?~CB;7w z=a%X*$Z_{T%fiOm(*ho+pEX8==OK@Adady+QyhtQ1q+}UbF=`5>4(uN!BLLW##on7 zshqwPJMlF1!j_%?QYuYG4Uz>9cn|-3bnNFvvIUb2frA?^z#vf1N*k#+zAp+-(hwL_ z`cUZspHDOTyvDSb7E19iFiz6;rkK|RU*}?n=#i$*UMCTLMAvhb)1BDhWs9j)A)VJ! zsS4E}u>23Phq0lQ(|XC9bg60kjzNs&o06~0fp90d?!C}Cf7uO-vVg+1Wsf6@NRX`! VmSMHd9lIwvs^he4;~)=#{{b93x?=zU delta 1608 zcmYk6O>7fK6vubGyY_zIwNuA9&X+ePCfPs(lq3{`B8pAiGOFi_0_&Buchl)6qZiUp|t11y~kvNpjTPM(w_P76e zGw;p3nYa7vSba?UE*Mle*1rAW>+8?Ys=`qr8YgvMoja`-{gBy^ifpoXxh5gk77h(fv35NOot14HbF1+j$%TNXPa{p(?RIodw&FRD&o+mZ$u={5L7+Snq_ z``FA4C6?ufWR5I_;EuGLgh7=DVxlWf=c#SFQqh{NRjho`_a3dXHdYpE^Np;8OJ{HOiP}82NIQEO|pzv6zffzt+VKAc5 zP)|bZhRB&H>$-uweW6w;TU1}4;qoQZ@m$@YNvTD1RnxZfCDRMy^0q@=`M~*tIqj6K znh~M_^pp@XVl$!_;q8Z*@VlHii;Gc>!45G@PEKOV`_>vwF`FBjrdEM1l`B})YQ9#a zBg~pWm!BCPKU(Bg^iff{FYUN5ZF}hBW#w-UA#w8XL!nD<|A%AvYab+nUv|Bfkux#w zR!q*M`CED@GtA%GMovyv;$RHt3l&Yv^a*G|C2SaiW#Mrm#*$LfO)q32Ep zL!-M;KaY4C9}9u)D__I1qQ`O2tC$H?#}LB^Oqafj@Nz^iESt5)ilDW`4q`mBFXDa3> ztwSueD?Lt#a!=l{!lU>t-1dHCIk+NXXT{G&<0}DAN}OM*Z>rn%{)-b2WY1KDdg`cv Gf&T+8w}J}* diff --git a/skills/kb_workflow/auth/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/auth/__pycache__/__init__.cpython-312.pyc index 9586cdd17526869ae68a02dd893a373da31c351e..abed6ec0b18b348059046d441119936e0503740f 100644 GIT binary patch delta 20 acmZo-Zer#>&CAQh00dJt_iW@YX9NH)!347a delta 20 acmZo-Zer#>&CAQh00bw`?%K#*&IkZA`vr*r diff --git a/skills/kb_workflow/auth/__pycache__/zhihu_login.cpython-312.pyc b/skills/kb_workflow/auth/__pycache__/zhihu_login.cpython-312.pyc index 9143470925289629dfad0be6f348728188d577a3..55283bd164e68dde94cb47b138006380f92e500a 100644 GIT binary patch delta 20 acmdlNwJ(bMG%qg~0}xEr+_RC}SQh|8i3Py` delta 20 acmdlNwJ(bMG%qg~0}z}%yK5u2u`U2e!v>cC diff --git a/skills/kb_workflow/cli.py b/skills/kb_workflow/cli.py index ca2fe0f..d8440a1 100644 --- a/skills/kb_workflow/cli.py +++ b/skills/kb_workflow/cli.py @@ -49,7 +49,7 @@ def process_command( from .pipeline.pipeline import Pipeline from .pipeline.processors import ( ASRProcessor, AnalyzeProcessor, ArchiveProcessor, - DownloadProcessor, ParseProcessor + DownloadProcessor, ParseProcessor, SRTProcessor ) from .pipeline.schemas import PipelineConfig @@ -63,13 +63,14 @@ def process_command( ParseProcessor(), DownloadProcessor(), ASRProcessor(), - AnalyzeProcessor(), + SRTProcessor(), # B站视频使用 + AnalyzeProcessor(), # 知乎/文章使用 ArchiveProcessor(), ] # Create pipeline config config = PipelineConfig( - steps=["parse", "download", "asr", "analyze", "archive"], + steps=["parse", "download", "asr", "srt", "analyze", "archive"], output_dir=out_dir, skip_completed=skip_completed, ) @@ -165,7 +166,7 @@ def login_command( result = login.login_by_qrcode() if result.success and result.cookie_str: manager.save_cookie(result.cookie_str) - return result.model_dump() + return result.model_dump(mode='json') except Exception as e: return {"success": False, "error": str(e)} diff --git a/skills/kb_workflow/crawler/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/crawler/__pycache__/__init__.cpython-312.pyc index f098ea1e7427cb4ca94f7be1e36ca50e943ad5e7..2e8f7b3c6af556688b1e2d66519a85c5f1352f0c 100644 GIT binary patch delta 20 acmdnOwuO!RG%qg~0}xEr+_RBelNkUt{{+zh delta 20 acmdnOwuO!RG%qg~0}z}%yK5u2CNls#IR&Qx diff --git a/skills/kb_workflow/crawler/__pycache__/config.cpython-312.pyc b/skills/kb_workflow/crawler/__pycache__/config.cpython-312.pyc index c7577fe8967f5fd1d1c5891af509eec000f8080f..0695e004944745321ec9464363e87b99a366e951 100644 GIT binary patch delta 20 acmeyP_eYQWG%qg~0}xEr+_RCpP#ge8^9Bt7 delta 20 acmeyP_eYQWG%qg~0}z}%yK5tNp*R3cEe7KN diff --git a/skills/kb_workflow/crawler/__pycache__/models.cpython-312.pyc b/skills/kb_workflow/crawler/__pycache__/models.cpython-312.pyc index 7ff52373b32b1fb395680bbb0764eafd44459659..daccb743bf0156426c04954fbdbdf6b012fa4241 100644 GIT binary patch delta 20 acmX?LeZZRgG%qg~0}xEr+_RB;t1JLOZ3X!N delta 20 acmX?LeZZRgG%qg~0}z}%yK5u&R#^Z=rv}de diff --git a/skills/kb_workflow/crawler/__pycache__/proxy_pool.cpython-312.pyc b/skills/kb_workflow/crawler/__pycache__/proxy_pool.cpython-312.pyc index 4b37a08e4924b48eaa09d34b003a17e945f25593..28fd0cafe5cc61f6d59001ae641b459a90ff0af9 100644 GIT binary patch delta 20 acmeD2?eXP4&CAQh00dJt_iW^DR09Ay-UW&P delta 20 acmeD2?eXP4&CAQh00bw`?%K%Rs0IK)7zSVf diff --git a/skills/kb_workflow/crawler/__pycache__/zhihu_crawler.cpython-312.pyc b/skills/kb_workflow/crawler/__pycache__/zhihu_crawler.cpython-312.pyc index 8b45c2fca5c56098731a07e09ed1b27396a76b27..ab455b73b87116df4c57717d17e8e6e4e3f6a1b5 100644 GIT binary patch delta 22 ccmZoT!`N_!k^3|+FBbz4Ox4`8k^5^Z08k_cB>(^b delta 22 ccmZoT!`N_!k^3|+FBbz4oIJa0Blp)-099TG`v3p{ diff --git a/skills/kb_workflow/crawler/stealth/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/crawler/stealth/__pycache__/__init__.cpython-312.pyc index 70a8de03fc64d153e430907f3ce0407c24058050..efdc635968a07b6ef972f8585b968efcd735c03f 100644 GIT binary patch delta 20 acmZ3)yoj0mG%qg~0}xEr+_RB;IwJrx!37fl delta 20 acmZ3)yoj0mG%qg~0}z}%yK5u&bVdL;`vvI$ diff --git a/skills/kb_workflow/crawler/stealth/__pycache__/stealth_config.cpython-312.pyc b/skills/kb_workflow/crawler/stealth/__pycache__/stealth_config.cpython-312.pyc index c047be459d24c80f622b4397c7bda458a0cbac6f..88d3160acecfbd6dbc2b163529452d0e5161b0a4 100644 GIT binary patch delta 20 acmeCt?$PEx&CAQh00dJt_iW^D6a@e_1qC$# delta 20 acmeCt?$PEx&CAQh00bw`?%K%RC<*{OKL!f` diff --git a/skills/kb_workflow/deep_analyzer/__init__.py b/skills/kb_workflow/deep_analyzer/__init__.py index 2f947ce..4b06248 100644 --- a/skills/kb_workflow/deep_analyzer/__init__.py +++ b/skills/kb_workflow/deep_analyzer/__init__.py @@ -90,22 +90,35 @@ class SimpleAIClient: def _call_zhipu(self, prompt: str, max_tokens: int, api_key: str, model: str, base_url: str) -> str: import requests + import sys + base_url = base_url or "https://open.bigmodel.cn/api/coding/paas/v4" url = f"{base_url}/chat/completions" headers = {"Authorization": f"Bearer {api_key}"} - payload = {"model": model, "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens} + # GLM-4.5/4.6/4.7/5 默认启用thinking mode,使用 thinking.type 参数禁用 + payload = { + "model": model, + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "thinking": { + "type": "disabled" # 禁用思考模式,获取直接响应 + } + } response = requests.post(url, headers=headers, json=payload, timeout=600) response.raise_for_status() data = response.json() message = data.get('choices', [{}])[0].get('message', {}) content = message.get('content', '') - reasoning = message.get('reasoning_content', '') - if not content and reasoning: - sentences = reasoning.strip().split('。') - useful = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 10] - if useful: - return '。'.join(useful[-3:]) + '。' - return reasoning.strip()[:500] + + if not content: + # 如果仍然为空,检查是否有reasoning_content(用于调试) + reasoning = message.get('reasoning_content', '') + if reasoning: + print(f"[DEBUG] Zhipu API返回空的content,但有reasoning_content。", file=sys.stderr) + print(f"[DEBUG] 这可能表示thinking.type参数未生效或模型不支持。", file=sys.stderr) + print(f"[DEBUG] reasoning_content前200字符: {reasoning[:200]}", file=sys.stderr) + raise ValueError(f"Zhipu API返回空内容。请检查API配置和模型参数。响应数据: {data}") + return content def _call_minimax(self, prompt: str, max_tokens: int, api_key: str, model: str, base_url: str) -> str: @@ -129,6 +142,13 @@ class DeepAnalyzer: def __init__(self, ai_client: SimpleAIClient = None): self.ai_client = ai_client or SimpleAIClient() + # 弃用警告:B站视频应使用 SRTProcessor + import logging + logger = logging.getLogger(__name__) + logger.warning( + "DeepAnalyzer is deprecated for Bilibili video processing. " + "Use SRTProcessor instead." + ) def analyze(self, metadata: Dict[str, Any], content: Dict[str, Any], content_type: str = "video") -> AnalysisResult: """一次性调用 AI 生成完整分析报告 diff --git a/skills/kb_workflow/deep_analyzer/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/deep_analyzer/__pycache__/__init__.cpython-312.pyc index d9d9811a7b770378b434eec66a1a99d899ea6b8d..b4bd08edeb754cefcec82f525ae663ada46c389b 100644 GIT binary patch delta 3139 zcmb7GeN+_J6`!|XEW5BQU&|sYBO*EyCF|5?82KN5u6n? znqUgW_%w;t_)$%3qEYNNje7j5Ij!WRJy~KXxRMsDyWk%^1x?K#{ipBFW|bzVJ$BF8 z-+S-g`+o1<_wK#B?E`4*UyMDFLft6Bql2I#cS~q)6@jP`B3W697wwlb|KwC2D+x zkWwaTF&iDx)uZuba+C`(W2{sDFY8n-L0|;ebK@AerN`Ks73dzIItiXQbJ7uxS!d_8<%Cl-0d>!I7%eW$tI zx{X6Y(*{Wv8yAgX8@{|R)OmXNwTr_?jt?C>A3Zf6{0nt%3-9d;ANa6kXwRYW-nMYt z+u`F!LI;0(=WIuK&*^acR?6Bqbqw1Njt%YXC@d%lee}-I$zRT9?{-t$#vfDoVGyC$ zcZ5!zqlCk!KM5ag4Ig@+hIn^J$I!)7p^Dw@k%J>1QKJy)2%X;p-gTUs&2q8$B}y7J zDoqMrLuYs`z$$<|fD#Hpi=ql5#&{N8a88n$JlTM&$)&VHv*M&867QZ9pGRM-mAPq6 zyJ<}tBrDz1xDgBE%IMFU*0ri9y(Hi+?H`vt=ybhn8!;Nv9iy=^$+otb5hs%}p4>{F zM$Wruw%kf^4Z1TA+4tE4*-xHLy6!G%TYMwIJ>X0YxL*wLLLXn-!`JpYWnHp9P*dAi zgL`UluT#+_bFaj9XLlD}NeIj>54ctYRUD>m`ve#KPkTgD!DF^Br0x}LvP_8Gb zfjD(A!PVx#<7o(0&;|U*(|?W3n1_(oXMBJ(zis?PguW)+gqFfgbK2p_BMcq7iuB=1 zvOaWI=tE%by2vP0-v^_SGUUH8;Y(zL)}ptUE#%|OT$|oj3_gQqfZ}(nR;t!N(QNk_ zH6tzRo_Q%PQ4wOA7|ys1LnmmiSRjh^^F6%W)GT6FQdmK($6^()iUPaAi`g(iYoF#K4-z1lpA9T!5Gl78^Eg)|Jcp0D-U@L$O&;;Ni**PVa?J(UzHslnUgGO2Okb^l{ z2$SFCNB4@1 zPM?Lo_UGpQ%QRy*m2i&8-9I)9SyNDp!v1RoOHDaCfo%-I_Z*Jg9+@>`Ihy9*Iiteh zzWWU=YtD-8;7k&QpjoMxC6(NJGCP~jRV>wG_(9?95!$0*m<#|fhMP!n@g}sIoGA{X zGye8jEk^V!5?7jGxN9R^=@a8mQlp?*_P$DU3~2CifD-__Nki#WbcXCJWl#O`>I>sJy32#BMl2 zKg$a$v!sf33XN0GYdtA^ge{wac9A0m$wVqkLI?d?*#ne!88$*8;!Cj6SWqb73_9b? zk-gVIifV$WE{GIzWI++S;_qE>4kbaP`iW7?E#&aCMU&p5;^uMx8B>)k@}6~kqgvy| zpOJ5#Rg4^3M&yN0BQNP%=w1Z(fOi4x0RRf!2fdTw39xL4LOY!(z4Q^cGdE0`qb94_ zb~yh&Lx<56zjIMHLJj^Ki{~NKPsS~oXueI&2T19X^>+Odo(7sG|M4Z4jBE8ui=%>N z_+yIA)+sOMO{vS9B1F<&15>>a^(@*3%hv$j0B8ef1=vZU*_fA?*KAcKRTgKnS$H$K zy1Xc-57g5^ZHw0VY!>T%S!#u7eMhofbKvK zozVs;gWx*AR{*^fzUzK08+n4igB-x^AAbIv9kr8RtUqPZgX|=$_$lZC(fCQ0Z^7&V zIm&-T1uH7&WIw9xhiQiOtSbgf{W><0uF7+ko3I`r-m04xJ?h(}Tu5^!d8r7xwbx66!6%qVS?0UTLR3hRRZ2D?CPbN~PV delta 2592 zcmb7`YfxLq6@c$bS3Kum^f+7rY z?3Q}$IQS$1PudB6P3=kT;@o6nCz&)eq$#f3ST>VbzD`=t)IZ83KcM!c({y5Y&#IU- zKQgUm^zGTR=bYU=dv>L#|3VIZO!Vh7Gt&h2IrpbG{o~&~tuMCNQ&?BczBl%%1YuH; zh$IAr5iN9DtobRDC~0EqmxQ0F{A5HI6D4hoNGU&7`AAG9>ENKL01o7M!DKa)RPdyk zO&NaS6&43SBN?%b-CPSBa?`aVnaf?fLDJtb(#JBxQ!zb#F{X#d>Mbz%9UW9}u(%}S zE95_M3^7BiaKzZB@(F#ao{cQBF=kvgHipR|OT>^g6@--m%d)>@dc0K-m|6A8pm9QV zXp<<6m{=$iwM#~Lx7eC(lCmTrN4Vo?jIyxZ!ph1Vk|3FLP?g!u)VP+>h`U4npgRll%|+mHTHqN+Zl01F_68%~V4pXvkg%d13VZv90^uwc)8%qOwa)@) z9HnU+S>!R{s?MH(PaS23gtOp`ZLaO&UET4j2jUVW%+(-e*NrADxsx3e9dTFfOxL`n zemwPx!FH@6;V5|7aLh38a9?&*U365!V?{)usEpy~T z%DI&Dsk2+-+aH{7eegNYq;JCaf?$X+F^f^Q z+?6B!5>cDjhaQiz`eM4}7vir#a`FfdKwRXk+ zS&Z?GE9y&!f-M%uWZPr942|R|0%(1IcW{$+~UCB7a%et+G)G=hV@3+%8@k+Rp&H8j1_47rU7Z z?(}5HpQFNzz|*5qm}z*Dw1T@)A*W<<{ir$*qYY^puAg~p;b&9ymrPVhvG& zPU^dAB$~@4wQLXBqyGy<_`@D`NhVNHpJ` zUVB4e&5~B$fA1V2L-Ll6jfBj@vzzlYe`e-$aC-A&>3sJs7$zz^x4ff%bPVMThOM-h zF{&$hxUyuRvP4Q2{0I*7E6D5R8SL&yj3a)8;CJ&NlcMU%%F3uN;*SKpjjlo(gSyVz z(o1Mlj>BooH@wkB`IX|8!&Ck$9O;zEHMrT?rMg9+y=$QDHT2uXz$)%5t48e^#2AC3 zlROdc%7bxdp^B=1fG><)D=V0~?V8V+d0ejSzD%_Eg!B=ZY0HI@?VoS@1MBkQOrHK9 zS=TKl--UoJ}W(!QOjH%eVU+obN3mi?GyNpj|?< zMR4?;WQzCu)))VG>GEo$y!h(Tk|$;uN~E*ekFb9ku1i?eh`Fo~_eWAf&T8aI) z5e`JM;9m?6n`nwOW00SKmQ?#ayH+{m|+k#WuD6O8?gN=yu?oGJ1t3TuQ{Gl5hvFhq%_ ziluU=DB1wol}eh*o1>Ypu>`o48Uy(+mbE>fJ?-iCO;6X(csggr^I0uV_fC4YaPhNg z?N4@Zd%vgS<=lBsb}f0bYtpk73!m=kf4`^mmQa3KYEgMnW=U#%Vo`ENW?5>npQiX^ z6OI{Nnm{9Ou_Wc^=S)7$A;%=bIQcclcd5?|Oss4lxfm2wZwQE9P>%S*%%s3}fkkWv z%jQR%+KkE|hd2XqF%OVvVE7`!z$4aebAd&?NCqh2r^#QWJK2CcRUV|SNE$?d#EWEs m#4iq;-29Z%oK(9a%gI}~HF*pf*(S(*VE|GeGA6&|mInaSAZ6nK delta 157 zcmbOs*&)GqnwOW00SHc>-IaNaVAM#jF)Cm8z~St{i<6*gBgUt?kR(-fUNpJT>k zCr(+WUksBIIlpf%;?ibRHU#Rk1L9&HAko0^MTCJztlQ=Si+GU?P{2=@~ diff --git a/skills/kb_workflow/pipeline/__pycache__/errors.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/errors.cpython-312.pyc index 90ff78edd507cb04232aeb37f72e0aac8075bd74..5cd8e69126973f1ab3d652d6004e0cacc35d761b 100644 GIT binary patch delta 20 acmZ2wxXO_GG%qg~0}xEr+_RB;z61a}kOiIq delta 20 acmZ2wxXO_GG%qg~0}z}%yK5u&d6 delta 20 acmew)_DPKUG%qg~0}z}%yK5tNIyV4EF$ReM diff --git a/skills/kb_workflow/pipeline/__pycache__/link_parser.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/link_parser.cpython-312.pyc index bb7f393392de9f597cd22516449a5ddb24d64a6b..07eceb88645d5f59e7a61657ffaaa987a6a69cda 100644 GIT binary patch delta 20 acmcbldr6o3G%qg~0}xEr+_RDUxEKIIJ_ZZ` delta 20 acmcbldr6o3G%qg~0}z}%yK5u&aWMc!cn0DC diff --git a/skills/kb_workflow/pipeline/__pycache__/pipeline.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/pipeline.cpython-312.pyc index 12dac89d09c9fcd29aca377d581a04a87f0c1eeb..0135d0aba48b0959107ed39177a029290294f6b7 100644 GIT binary patch delta 3627 zcmbtXdu&tJ89&GNwc|LBU-2{XD|Q}+yh9)*wB-??A&^j@qbZQ!K|52|m1?2O?&?k|B?hEA(_{$4*Q?Ep=Z&@Kw06{t4K*i60 z1p6G!a^7+%z)rxIeh=`vIYo}={Kbl20af^!qFE!C?B92wC$>;#l}y z$;S#;gX?7frrdi9d9^Z55Osi3=pTyb{lV`?xunhmz)DyBzSfjR-K59>u zM;$3c)R{6yT`3a;M5&!A^(%m+mmuD2X>iRu=9J78kTTCIV($=;lY!Rb-If;o8*`(< zHOH+&?7`2>euFat@-a?XBnxh-u$n6QssgWq9AO{_G!naKO*W#ZqH5l~N)e3xs}P2p zl7=YosfZ>@Sn+=?WlC#StHHBMt4*>}xz1ZWoxWRqMa(IfG!s{uMf%ki+3}#YMJ3rJ zd(NP;HN1aX2S7fM^VM_ef}b$9t&oFD|EGBdOVPZPL+rvHLxT#aY%Hdt?>S#t%nX(yKjoC1EG{+0gL7EMH`NyTDHq9+j)qg&q0+M>XCy%60{121)qMdX0u4ZOwse~Enu{)Iwa|)a>t--wA z!ZkAPVu%#Gz01U%Kd1s?uB`yzTk&JNvvPM{TM(eJlt;AX-^cr@)0It93F+=;$@4z= zU{!Y$<9hKc4lfx&gN~hDEm=8nLOHLRQ)P9GCm{U^`X=HkQBk`Ch#KeuhXRsOns*AAQVp1HUdPC}>C2|Szm~pc^UeE8q=_-dC(bv^RurtvC2Ex#7nJF9?QlNE| z-9dNgi^t2H7!ipt>hPpE7DsHatVvErqhWL!^%1mO7Dgjuk&^;5gUDzS1)iNPmnOm} zJc`1Jv81dL!=p)AJ3_PwBci}h>)*g9+*|eah+I(smD8mS-*x-o2K>3(;w{*gy*_Cx zA$FSvC>6vO4-b3rDNo0yZIsbX7uup|2VH1Ol6nx4tQ-|YWT3n^C{L3so1DM+3s1k@ zi`W&+a$iT0G{I;C-sN?v*HhAs$Gx@frbu!qU%f-w;?)+45WUa}eB0|kOgmC84JXkM z5|Wb>VlljE9~G77>OC)J31Y_TNL?p}vdNcAk41zDekc}>3NjRuUb5Ba!?iw-$3wL! z(PV78Y;PnXOhjTr6L}ja?4+p1!@g@Uiof-JGm|L+Ci`W>1;h1?X=CG80GgUtjJ9QC zAZ-j>pGq5>G91u*2+HWZ((}MjlU~#H*l4?~yP#Y23_dg-ePncG)Ks|rfx*98*n5A+ zU;01oU+g`+c=*WTAiq3#B0YFw@d#S1NkWN)=IWVSu{*;{-Yv`C?P>4!yTbi_OWwV+uYKVUtpq~LfpzJ?x)p!jPrX0( zt`Jsj`{Q8UE#2*Cx_;YIuzRIu?Q%_Lx~3Cf4tO^_s2RAs;a=BoyY5dee&@}lZ3DA? zE5Wwq;QDlM{q33iilt!hgP?f-%yNG?-5*{YjxU}_F7=D}e&7TYWv}sIi{haL9&A!StazEwpP9FGkjJv7j>wmrAf;Qh1AcD#r7e{M zRotTrNM0UUHAMWVilek&IS^dt(8IEm>skP;zpZwfQvHG@gvdrd3U3te}31*g}>AePl2e#sliZ zSDMD)YZx|Lbw%zT>~F5OZ>D?plI6J_2k_Bm@62i)bTUm>?ubvuc%N`8k`(hpJzMJ= zeIX5MqPpl5kkt&5L}Qf0+JA&nL9&F(vc1dV#d?+XF70AjKO9a9tY3y=!W3R?u7Q2{ z-_7mWI10GAr9s1!Z}rJm#lmpQ-=NkA%I#Nb9_YRJ%U1WRf!gb-r9j7*8dbGNd}iF0(F0>`Mgz)yh<jiLrKq%Gr*iftOQMzYr;3% zcMlv@a;0B)Yi!)QOv?et^};6>TZUs4nG|MQMw6vWfX$WBW@!?|%8V{cmx0pqr+P+b z51_h3E_4m|uKQ%qC_RY&_(6KQusec|a#>4;`tJDXsDRK_Dq%&z%=&=7(mXIzpOtKi y#`(z!;T3cf_jj~~jxK?tPqeOg?8{nLTI;&fxugxARsTnQNDX`8SAad(_WuDOv~rLD delta 2757 zcmZuzX>1$E6`tAUeTfwB6{(BVv92V`)|uFfkJypqq*7q32IM0ItKC?nd11J+Y;{=` zNf8u?>)M&1SDYeFQ51-&*t)I$_m7YSDbNCL=M6di$d1k8ZaEvCkhq`c)=)@P;RX@7(BFejFnKsbY2yxORWk&>F5; z&V&Uti4@1Tk>Xqvwrr7iA9e^>q>qHX)iLpVW~ZMyNB~5*x^8<%z!F_`cH(&TO{Z7D zVOn)3EE2f8=|guE$LSYtJ=V(%3tNy(MaaeeUqlFhcFs|~=J8?CbUs#nv#CoEQ8qyL zIs8^zl!a8B^yB8~o~B$dAF8g5{crns#iuWP|CmjOPUG%VjZ``*g z!U1fr?>a8L&mx+6#3+k!(~a;E`jI~p^{<(CM7_+S+&t%ayJNdXKHZs}fJV0Y z>SSI-iueuIobQW25MMHzQAN_F>=O{PDrFDXXzr9s=S@Mk>b8T-2$4H|HJywriDT$| z2WxhV_GI0!OL|M4M;TxBYRA9K1ITd2^p=B&y)jo4CZt=klR(F3t5e3PaFSW4!D_ZK z5{K!{Xw;Fc!(+PcO;B%(-`vHW*9QXBBOO>ZU2QWVZR;W;q{xcM&J%B&)=V`nr_dO> zY9x?$qRuUe$YLd?h3f0T+i5J`iwEiPc)~Kk z4kG=u5dVOF*m`W0)gH+rNrJWJVwz!7ipzyuF|EkvhLsG0%e7D}Xlg-AX;(@r8QW!g zK9f@ov~!Q)xRg=UVycwZ7728?24YPY(zz=uszG09+uyZ)E}4ReV;~^ba2A)e(vp?} zT>7XjvbqzRBaxYUu#64+DNQX+7uXJ2XwYW*5Cjb~A=?{A7-lV_F}{}clD1eR9Lcbj zm-6{Exk8{HhAT}L7BiPrPJsDL7c-?2{ayQHa}VhOOb>{C5Gv91<3T#mVHp~QrTc=a zrHUlAK-9FRHptW_Zv?jSLEH&if?nuQ@RRgU9V25CV1QSJ90G9|#1SS8v8)k8WUyow zmevv}E~w@5R&I2nb9O9F_~7Qf?1qidFjC`F))MmZzr%eM6y&^aXp(Uskux+9bl_0GnbE>>7Nrf@hm0DNA#av12|ii zx=Xn4#)(ZY3bnl<-wS?aqpR=dXK$Uo6aD4U`(1~w&-^91|C8kCdUEv6>W9Mp#g`NXuN({2+$e;z8WX+z*mw&}DJ1ulmNGoZ!C%**HmNVR3*}H(%1o zGHvVc4)QL=n6uU(2xFcz{fQ*xPv)6eiEQVeN|!IPs3e2VP;0Q5I=o17R3P?8E=h;nR&%CThcN` zHZ#sX%Ld7L*;E@?DSB!=xyr9P`wBqhyC7gfZwG8!;Z0@8oE#_5$w4gNWthHREaqg# zjsTtl{K}|xiHGh!$}r6o3o89$d~mQ8;`m5_X+Sg(&_{BPiMJ3>j^A$L1*?~ye(ag~ zqoUdI*_0(@9^M?B#^!0f;SX)H2m9N!pytq~wWixpNZPd5bO-XaY&vVY3pqTWySZLF z0I=mKq4YnG{d(#-I0$~H5jO}ZfMI9zecIk)X_wC7VS^i}qW;~5QMf}0H{|}uHYCiw~ diff --git a/skills/kb_workflow/pipeline/__pycache__/retry.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/retry.cpython-312.pyc index 1acd86578b4acd62d5c96511eb9aa553e5584a86..21536d2d9f9ccd31b7d48281e1a9c98345400aa6 100644 GIT binary patch delta 20 acmey!_K}VIG%qg~0}xEr+_RB8jTrzxhXsfL delta 20 acmey!_K}VIG%qg~0}z}%yK5tN8Z!Vy!3JIc diff --git a/skills/kb_workflow/pipeline/__pycache__/schemas.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/schemas.cpython-312.pyc index fdaf977185c703c9123158bc5f8a19bbbc9116bd..ceccc3054cb93d11c646ddab8329562caea6fbee 100644 GIT binary patch delta 1236 zcmY*XTTByK7@nyW+d}JVp{3kBU{{@o#VQb#uuWLO3u;@bmn_w+BXhQGmUOyjrYe1* zaap%!jgAg#(FY;ArV$V?Sue=$#+QBBXqqsIp>Mmf7oLns_uX@*P;?&tbN>H)|7HH~ zdwicxKch|m7>x!Bf7h6~;HKUyrfS;xXtP=4&`^6QZtDoeIpDgSLoH0}6Q|~lN50c)p(Xj>_FB1l^@gjR&aLHK#guVUvy^XuWpE8=>csBQTIP$i z&f9l*Unm?ABUYNCup=L8tF(_M-_2eE*z+KM015(+K<4g9PC5_1$xV)Brf+0kT$3Bk z4Q3>M_kC~>b`UIBif?mQ$^Uh>$YWod_23|je&F~sA=%+}48Na?FTPCVAKV3}gB;}F z&s{J8{57K{JWkZ+VZ{Itd9{`hIVA?gK?q2BWXJDmov_pmox?8riR9ER=kO5`|3s-> zG3{4^^AlXfEUN#HADnu#H`k(wQl*R`7YDdH3J zH(ru(6kmR(K4|o-bss9Js+z>cWMgXQVWsj&Y;SyEUVCK0){qdAKc<}hip>{0D7DOM zeGAsQgg1F8)!DCDeX;$LnZ&B=$NkTpN_B7SfV5H3_M!&0k^IVGqpxEU+lHZzE1OzV zqE8Pofi?`nB8VYJLM*j(>{1c2}8N+=pg9>Q;Oa^+)pIcpFq_L<2ke&W*@xJ_Q zM|*jZT$O)$#>HS_jq7uxx3h66cXOn)a?G%$Hq0W{kJxZP=l~^1_QCIY=@0p2mu2{} zl(~EzXJyBK$xZ*6iQmXiBvlO%9SraY!2uC+9Uz;S$w>F|f8EMYTzofs9&~y8KyG$? zadfPxnCOOO;{7lL5sQ;bgA}=E$R-Je(aJp~NML0?)P~>EhLu3n)N@uuEa?wK)-Rx| z7_d9qbSwbH07U8tOA`k95IDmMfDMNSgOF3@l-8HVcQGa+Dn=F1il|PJi5?Q8Cx(0P-En(dxWLS7-8eellWO-VmgBK~aZ%A8FUpMcaxt5b6`yIHZ1KJKRsaA1 diff --git a/skills/kb_workflow/pipeline/__pycache__/state.cpython-312.pyc b/skills/kb_workflow/pipeline/__pycache__/state.cpython-312.pyc index 569b93e2a880c3692b08537f45f02d27d3257a1c..90f9f2af5cb6ef4d884433c02332c41bce252a8c 100644 GIT binary patch delta 20 acmZ3YzeJz=G%qg~0}xEr+_RB;rZ@mPR|SUv delta 20 acmZ3YzeJz=G%qg~0}z}%yK5u&OmP4|kp^7= diff --git a/skills/kb_workflow/pipeline/config.py b/skills/kb_workflow/pipeline/config.py index 855a113..3422b5b 100644 --- a/skills/kb_workflow/pipeline/config.py +++ b/skills/kb_workflow/pipeline/config.py @@ -58,6 +58,11 @@ class KBSettings(BaseSettings): default=SecretStr(""), description="B站 Cookie (for video download)" ) + # 工作流行为配置 + overwrite_archives: bool = Field( + default=False, description="覆盖已存在的归档文件(非交互模式)" + ) + @lru_cache def get_settings() -> KBSettings: diff --git a/skills/kb_workflow/pipeline/pipeline.py b/skills/kb_workflow/pipeline/pipeline.py index 60d57fc..f96f62d 100644 --- a/skills/kb_workflow/pipeline/pipeline.py +++ b/skills/kb_workflow/pipeline/pipeline.py @@ -21,7 +21,7 @@ from typing import Dict, List, Optional from kb_workflow.pipeline.config import get_settings from kb_workflow.pipeline.link_parser import LinkParser, ParsedLink from kb_workflow.pipeline.processors.base import BaseProcessor -from kb_workflow.pipeline.schemas import PipelineConfig, PipelineInput, PipelineOutput +from kb_workflow.pipeline.schemas import PipelineConfig, PipelineInput, PipelineOutput, SRTInput logger = logging.getLogger(__name__) @@ -174,6 +174,20 @@ class Pipeline: logger.info(f"Skipping ASR for text content type: {parsed.content_type}") continue + # B站视频:跳过 analyze 步骤,使用 srt 步骤替代 + is_bilibili_video = ( + parsed.platform == "bilibili" and + parsed.content_type == "video" + ) + if step_name == "analyze" and is_bilibili_video: + logger.info(f"Skipping analyze for Bilibili video, using SRT processor instead") + continue + + # B站文章/知乎等:跳过 srt 步骤 + if step_name == "srt" and not is_bilibili_video: + logger.info(f"Skipping SRT for non-Bilibili-video content: {parsed.platform}/{parsed.content_type}") + continue + processor = self.processors[step_name] # Mark step as started @@ -228,10 +242,26 @@ class Pipeline: content_type=parsed.content_type, output_file=str(content_dir / "analysis.md"), ) + elif step_name == "srt": + # SRT needs srt file from ASR step and metadata + download_result = results.get("download", {}) + asr_result = results.get("asr", {}) + metadata_file = download_result.get("metadata_file", "") + # SRT file from ASR output + srt_file = asr_result.get("output_files", {}).get("srt", "") + + step_input = SRTInput( + content_id=parsed.content_id, + platform=parsed.platform, + srt_file=srt_file, + metadata_file=metadata_file, + output_file=str(content_dir / "analysis.md"), + content_type=parsed.content_type, + ) elif step_name == "archive": - # Archive needs analysis file from analyze step - analyze_result = results.get("analyze", {}) - analysis_file = analyze_result.get("analysis_file", "") + # Archive needs analysis file from analyze step (or srt step for Bilibili video) + analyze_result = results.get("analyze", {}) or results.get("srt", {}) + analysis_file = analyze_result.get("analysis_file", "") or analyze_result.get("output_file", "") step_input = ArchiveInput( content_id=parsed.content_id, @@ -261,7 +291,7 @@ class Pipeline: # Add output fields to results for field_name in ["metadata_file", "content_file", "title", "author", "analysis_file", "summary", "output_files", - "archive_file", "skipped"]: + "archive_file", "skipped", "output_file", "paragraphs", "tags", "corrected"]: if hasattr(step_output, field_name): results[step_name][field_name] = getattr(step_output, field_name) @@ -397,3 +427,14 @@ class Pipeline: "analysis_file": str(analysis_path), } logger.info(f"Found existing analysis: {analysis_path}") + + elif step_name == "srt": + # Scan for SRT output (same file as analyze - analysis.md) + analysis_path = content_dir / "analysis.md" + if analysis_path.exists(): + results["srt"] = { + "success": True, + "error_message": None, + "output_file": str(analysis_path), + } + logger.info(f"Found existing SRT output: {analysis_path}") diff --git a/skills/kb_workflow/pipeline/platforms/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/pipeline/platforms/__pycache__/__init__.cpython-312.pyc index cbe3eae405b592f2b1bc23756fd3684a4d7441ff..fe3e842891e356d2981abc12a20d3bfb4395a888 100644 GIT binary patch delta 20 acmaFI_KuDFG%qg~0}xEr+_RB8nHc~-4+Vb! delta 20 acmaFI_KuDFG%qg~0}z}%yK5tNGBW@~Nd{E_ diff --git a/skills/kb_workflow/pipeline/platforms/__pycache__/base.cpython-312.pyc b/skills/kb_workflow/pipeline/platforms/__pycache__/base.cpython-312.pyc index 39e5d427654f5e710cbb486f35c15a56dbdf9db5..2a395db04cf4f8c22367fbbe166aa4995c587e55 100644 GIT binary patch delta 20 acmcbndQFx4G%qg~0}xEr+_RDUv=9J5x&{OQ delta 20 acmcbndQFx4G%qg~0}z}%yK5u&X(0eb^ak1h diff --git a/skills/kb_workflow/pipeline/platforms/__pycache__/bilibili.cpython-312.pyc b/skills/kb_workflow/pipeline/platforms/__pycache__/bilibili.cpython-312.pyc index 2c4ec8e68735530f0e063fdd34205e8c25a92845..45e8bae54bafd72b1c078561c098796bb91ae71f 100644 GIT binary patch delta 22 ccmZ28n{mx-M()$Ryj%=GFjaHUM(%~a08aS^uK)l5 delta 22 ccmZ28n{mx-M()$Ryj%=GaPsV~job@;0aP;wg#Z8m diff --git a/skills/kb_workflow/pipeline/platforms/__pycache__/zhihu.cpython-312.pyc b/skills/kb_workflow/pipeline/platforms/__pycache__/zhihu.cpython-312.pyc index 06650be005bbaa506a04c8af3658dc2ccde0c804..d1087726ece84a17eb5c1f8602bad2dcebc63095 100644 GIT binary patch delta 22 ccmbQah;im3M()$Ryj%=GFjaHUMsB`9088Bl2mk;8 delta 22 ccmbQah;im3M()$Ryj%=GaPsV~jof^J08tkP-T(jq diff --git a/skills/kb_workflow/pipeline/processors/__init__.py b/skills/kb_workflow/pipeline/processors/__init__.py index 5354519..b7f91d0 100644 --- a/skills/kb_workflow/pipeline/processors/__init__.py +++ b/skills/kb_workflow/pipeline/processors/__init__.py @@ -5,6 +5,7 @@ This module contains processors for various content transformation tasks: - ParseProcessor: URL parsing for various platforms - DownloadProcessor: Metadata and content downloading - ASRProcessor: Audio-to-text conversion (subtitle download + Whisper.cpp) +- SRTProcessor: SRT subtitle processing (parse, segment, correct, markdown) - AnalyzeProcessor: AI-powered content analysis using DeepAnalyzer - ArchiveProcessor: Copy analysis reports to archive directory """ @@ -13,6 +14,7 @@ from kb_workflow.pipeline.processors.base import BaseProcessor from kb_workflow.pipeline.processors.parse import ParseProcessor from kb_workflow.pipeline.processors.download import DownloadProcessor from kb_workflow.pipeline.processors.asr import ASRProcessor +from kb_workflow.pipeline.processors.srt import SRTProcessor from kb_workflow.pipeline.processors.analyze import AnalyzeProcessor from kb_workflow.pipeline.processors.archive import ArchiveProcessor @@ -21,6 +23,7 @@ __all__ = [ "ParseProcessor", "DownloadProcessor", "ASRProcessor", + "SRTProcessor", "AnalyzeProcessor", "ArchiveProcessor", ] diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/__init__.cpython-312.pyc index f701381d31c5ad01bd6f3004b2fa4b8b0534434b..7684ca762c019ab929583d004f38ff54682e319c 100644 GIT binary patch delta 255 zcmdnOd5(+sG%qg~0}xEr+>^O%BCjN4%0%@*=@iBkjvU@xz9>FMhIED$&P9w-{FPjq z+=&`Y8{6X;J%WQm0*dmJQ;Uo9i>wrYJcZ)Yq>{{%oK%GZh$Pm9-#ioE18TK*(cv&lH(}i1Uko0Q(*F6 zrd^E6ljE5cn2U=_Cf{e);K)nNsjNyZ;+Xu9S(lM>GCxb6l^9SlBM=vB0ErLGjEsyQ onYb8PzDh7K+TUexxXa*nm%#>%T<$Wsd}86?W@=Ejzx@7yp^1qT#0uWH%^XYoNUW< zVX`cVy2_NYD+kLpUnINJ`Ok1Ww>&>`B$=I28C{{g5t7w<0M6usJFPTV%%7 zG|Xd%2=mwLWL;K0CJNT_7rN2I24T9c|yF* z!wF5Wu_G?mFp!uBbs@dX$$B*i100}8lg1&)liEXAil686jk1v(k{M5N+(g5aRDD6% zfv#ycDRsPHV0QerivErWdE&F(jwR^njv{MDT&3Cjh-bmkE_Aoqx;7y z8;_$wCSiYCclCqpl+hA%7TMx?lDcaT+N9;0Sg^^KMAKKX^pqy^tkAGHlr3^%#S_9( z1`JsZ;7drA4d7814+hekWPV6{a(;BKHq*JPgt@AS@2z2AmtAn}dG6pQ%G7S6PG%Dln2>d^wKWENaWC4# zZbH5I4BLyHS>12I0d5@p_}0lcr_Ud`aq`3IQ~PfY959)b^QIo2&<1KAU2d+zdgr_d z8|(`zyDgvCxS%AseG%RrbV*X5l4CuZV~gm5qIY}5qSEU{E3Ou;_$)Nh z_~NyqO`|`ge2JTyYT;?conXgX)Vmyly<91_{nH?{zo!7*( zR}co`3g$p?h36=4W}9rI_tHkNc_lnK{Pe3P*lA5d^+V`1#s12#hN z!XcpX-S?pTjv<*LTa*7N!rnug#?=pmR7%@m@ht%V#FO|+Sd-cVb00hIsf)$K|Y`dTvp1tW?5zO@L zLkTlo1~)~v=IRlS%<-3+zHFXcde_g>6n2j8DMcWUa~k?FC+HxG?WfAHGvvDekcpE~;%XhG-S9y_QQ>-77Aey>QLrvkIe3J`x+Kq5_)!@7dIlwa%xtMx>kj8(uS z^d43XqB{DRkBEVY!2tLZgS5Y6)gbBwEmUf2G-ncp@~%=J$MLZGx8*D-gZUYot_9! zM5&wjh!F@UAH5McDlMT4{nH?uVd=NE1SGfL=kjiT!6oh7>cowpt!7Eprm2ePw)y>D zRh?`BT5;tpE0{|?)YGMuE(>8fjmTPx*C}UmO6F|EobB#j#>OqZ?&zxE zD>FHB@iHYP_mXofHBoCzg~Uads2jlkFjPaoc6-HDWS8<+?%K3%3@#rC-A5FvYJUUF CFe>B# delta 2391 zcmbUiTWDNGbml(xaW{MOw0SlpZo0|ZBzSXw-loN(&Z(M&pNwST6_xY3W~uDoDRPb2qbhTM`uK<2+{0 zW9Gc(yWKaU>i5B*f?zHG@MUsm^=E2>pa_gd2;Xg|QK8K*&qyzOU-v|m#WCSm-1Djj zp=S_AZzDglke-*l4-#YjeO;uha-AQimtd#Ru zlI3xndf~#&t|F)4927X`Vt$RB&+-uhx*_+BQ~* za8=h)aV{*M-pWd1ADAol%(V(w3s}C(XjUk4@1_y!Z7gN7Vk!DoW^US0%?_@sl~_u# z>KSZq<7fJIPPy1pEH4k;zg{cIY-_6J1rJi;ayvHai^Grw*HeMY_r9nXS$-whmeJ*bJ0RUAy#MbnN9lyKSlZB&%ZLdg~p56I zs4$x<_-q;xhLKj83%n5+TEkO#3Y`##(G>nzoWhZ+$@B2ATW`L9`^sB4uU@=$<=va_ z&Cq+wsl`))Jy<*#X{1+6KA>jsQe}xP8Hv=Wtt2P1lV(hdn{nE#_EoU?VFwc@l7^X_ z7>kW2Q}mqLB9*{4{aig;`Wwuy+vxAp=#Pp+*ss!SLLroL-c_v7eD1Hs=Dc3c234)*KaN=1 zhY(M6M49x!??wm38oIFQBl=ZkBmJg2Oli%2+)sb5d1TsemyqPhc;`rF!pNi&Fjz#L!28xh-%1y1TY?v1+c*mVDrOUXq3(1Id;OVR-RO&GUx)a0I|g_# zB9Sh7r>>^gNiEBvlV8%tsKWqGs?TN3WM(3sI&vs(j=us5&XpWdCSXg)GMN+|s^8wg z{!p@yfiMHD06s*dpV7Vao%(RkFqYN3#|2g1_GeR#9L*oG2^Xm*M*!JMEH*l6PLf0{ zMxaWd&Qh7NG3W=JY7B{;OK?i JUx;Bx>>pw!hfV+h diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/asr.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/asr.cpython-312.pyc index 073a6ae4e3138f97f5519278ebdac561fd87dee6..13283d5a6ee62f3b9f5fc54cf3137c23bbab3553 100644 GIT binary patch delta 22 ccmcb7iShC!M()$Ryj%=GFjaHUMsANx09^wIE&u=k delta 22 ccmcb7iShC!M()$Ryj%=GaPsV~jocoY0Af7|1ONa4 diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/base.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/base.cpython-312.pyc index 64bd84bfa83963867fec3326cd595f885d4a6977..5e229eeedec453f40abeb92141cf8b86d4fea51a 100644 GIT binary patch delta 20 acmdlYv_**fG%qg~0}xEr+_RBelM?_pwFK_~ delta 20 acmdlYv_**fG%qg~0}z}%yK5u2CMN(q?*+vG diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/download.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/download.cpython-312.pyc index 50002193689656f7b8476b78a28f20dd7cdf6f96..65c90984f04ec608eb319216d56fee64ab1a91b0 100644 GIT binary patch delta 20 acmZqAXwTq2&CAQh00dJt_iW_;V+;U3?*={q delta 20 acmZqAXwTq2&CAQh00bw`?%K%x#~1)aDF+k) diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/parse.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/parse.cpython-312.pyc index 46fe483d054471c67405711b2ee1966a12a7de6a..c9795a49a34a42643547d58a6994ebdfc0b3399c 100644 GIT binary patch delta 20 acmbOtF-3y=G%qg~0}xEr+_RCJlLr7ZkOZCp delta 20 acmbOtF-3y=G%qg~0}z}%yK5siCl3HP$^~=) diff --git a/skills/kb_workflow/pipeline/processors/__pycache__/srt.cpython-312.pyc b/skills/kb_workflow/pipeline/processors/__pycache__/srt.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8d8f6901d3c972d55f3d3463616e584ee0574d9 GIT binary patch literal 13168 zcmbt4X>b!~mfbpAhvh@EO}(-QDIZn@wnun@$bu^|4GLWaD?L1u(th%SO58JUxG$+~1D z=H*U#mz+-(T?$AQPNhrPrR3X`PL)gDrRMW0C*{&~X{`Lfn&aus(K^Ld@K#AWU>^Lf3q)K%71MiMf@uIO7L zGA^a|iqoV}>Z+6x_YzFrL4q;Z3r@>ZwQ~mKmVEy&0I2hU9otz?x83XYuyiB6Zu<0z z5Q*TVYGRzYcNPTl5 zbY=X>>F3AjejDqxvrT+r^|C%|kHcyI=E9J6F`%7$_wxHg$K0r@@5Wf2S2?@5_!?N~npNDNq<-WOaIC=Ek)dNF_w4{lie*V>|&~Vl` zV5C}YYKTK27{TRNl4svf z{_1e@sVAq7k53&ri!*W+7sEnwI-A?=@!5P1kK5Y;Ws0%+>^_If4td3Pn{T&8!717u z-9Ap<<{sb_TOD2>N8Q(t2sS4G>3i)ytJm)HIo!QoDCe!SdF|;{wa7RMmTZ%|-|yoz zkh{CDMN5+tFP-I;S4WS_z9eyFzhln0HL4SF$P8nshY`W zRFF~vWcHUSVARm2VG0=vQY}-&Xdu-wMn(%MP~A^<<#iGk6Q|GI)qbn6%~N|eV@K2Q z!(ILNZD_=vK7VRz=wmu-zG3;VKK`5JNAJ+v9sPEv!)>S2eNK(Nmb`G3KMsiA=JT>MF0Q?Njfb^ zkCOv;-6`q9Zw8d2P$sXjF#E^J5yHn;<7Y@6B#uCPkUX7|QX~=BnHS^z^XH02gn-2S1wYjCD`ggRR;bXu|-N5DyVvq zcv&V<*$HzosCrB_Knm$Tav!l@zLVHTS~ULG0W%qTIeF@rAe4nYE{Jp#=He!3V|z39 z9@ctqZ}%Xm-EG_Dv|CfV1f&sc+0k>!;kS~*=TL@cN#3jHN3WbcN$+6&yckD({B^Ln z>L6nD0KHxm%P5389j<<-y=~Karvt<~)M#uDYd2pAlp*Q#?xY=VhtFYiIs$eku()j# z-L`!by~jR4gIEP9caNjj&)S&=o*{`_nsIn>08F5EgAKF`L;E}#q%xsBaE>hboYL#> zx3e5&wPKG}D_3~a6tD#_98QxyT%0D&CRes+mvvuC&x)`yt+8MjO-wJ2g zIS{b3EN((hZR>YfVO*Tb>FI^D%_$u29uKFvXMeYy*9M$=pN(~c7O+s9!fSW-aCuqF z$>M(F`{D_*W~55mIgJ%2*5>tEtzHVM_=TbA1J^e-@AkOt%|L?%)!MRn+0y1FPruy_ z!26oLdmK)uw>e`>&HX8X*W53f6y9bWXa4|;XD3jcDVLkXSzH87-Z$a%!vW&E5<+7b zQb(!M(K&HyL1NzGkRnc1T`a4Ql{JJEhYg9cIhg!KMd;_u$n<$z$V&z5E)}#x#hit) z%7$T0!dN+Jto_1R`&C`b#HM)NhT-<-J1&~aN46Z>5}{XpXKWkA!zB?_*02D>plPCxuQ4z!zf&}fTA%a97RNwexAIM$bmnm3N zNNJ%a0|B@!DCx5zBKpz_K%@&J&!GyW z-Ub!7ps&o}G2IvNsLJG#QG>!F`qC;w>Ya556|D0XeNfUEdWk_*P|;32yo?|QLHXvQ z*_T7%1k(y@5L8d!T!{lQ7tH7-NCnkw{n_6+&v(NtE=`k3XL8SC#<=z382{0EeS%;L2lbEXC4=u>IT(FK{@(*Lg13?3 zRsWc|=Rmj63LdAemB+a9(R0bC-V~fv3GV^B&(FGVuoQW>i8m>y&p$Wy%1asGHitLU zr$K`vZKSk{?$F8iuKeqpQzO5fI`m@lnGcc|o=8qiXj8Ta9Vprk5dbh{c&^a-Z>0lj ztOGkOpb@6nht*^o@Pp?lZAya0YR1V=Sy{+M4{yM> z(jArxwhmzxPLGY@3SAJ0v3h-AN}>@5!-oif=wO10XnWMo(nudAE=Pf_#y~rst_>Zj1tG)9&`YJXBO=Ya4~%>i#mW)f{D`rQ;7pO55W4bu%iW ztSYoAk#9Wu*rCT>t4Et!cT5+lTpBM}mRMkk6x1dv=YLBm3N6F!i8{-vM_zkmvTjwh zZq-D4tgdaiBVoE@($pL^HIEzOrn?g6x-nDC+?c4n*T>B5p^egj%A3cvlZ!S)7j1|%-y1327}sw~7|oN$ zx~Q=(+!r&pgxVAOf=PW%R9|zkk`9+o&RZUxw>(z4B2hLkQBjvDE}PL1d9~jW$~?nN z0a0N3@ui~|o>@PxJ$+wf;p&O4pO<~Ei&X6xzL#Io#;B=rj5+6i+da`6H*LtZrk9PC zuqQ57&JAlP>19!RS*&uoz(k_F<~yCrWSAjTd4?ap14hceB`8f1uFKY;t&xhw<1OPg zk+NlR{qoz|U-eN_{n(OoYu{db;m(P@=avD0F&=>w~rVu*%KW__c^5KW+P_PjBmXzns zA(<>m3hGk~BB#RdzdM)w=%9q3Q&*>PFmV4=hMFzgmQqqQ>R3Kv(&O>FLCbL@dtK@G z^)#+{8)DMn^m`c4G#wVTpcqv4{Sb%t3hJWqP$?@`>WBSbIk9t-LJA1GWt|14ZWTp9lG z^2EDSM~9O~UziF%GyU#I(sJO9!I9(1v#(4)J1$%rT>kK5jE-&Hx=pw#xbpGy$roP{ z;$s3f28elW0-vdu-kEy&O$fJMKL2L&^;eP?K7gARM5cxQ(!pdzxOwkLnhk<;Jg3Rf z$d%VJLV!8YG#DdA^%H&7md@bK;WKyU>|o{>o>5bW?wx;$}=VSp!xJi`w|!Hm3D27a-e z-4DKPE~%0B`@s9j4Urj9kfe#6QDS&NwDlR)o^~)Es9>s|Q8TJ?!mSX?;F0H~9#m&Y zsN6JV4CRKKlt4yrx5I1h{uqtO@omwNLp-4tW)dRbGv-@|&`K9|i0x3KIAB!*rxr`^Xo;6};^0s+*6$KjTI z4vR(*D7>e`8`&sY1WQ|&L5<6UIKB_Yho(9VQrqKTVURd+>^*?N%NB8_Qo3a|)CcBf z%Or6iX@}$A5P1m*co0l9p)pNT9sybHctcd9A2mw&P#LRH17%@--dQQeR(QqnxG8yk$6)_h5=Wgmpu z%ewYUTXP?PB!|ZnWG^`=hm)PQ>Hs12Lz_!Hxq~1Lb6I$dVisARmdy|~Rc1y_XX#7D zsKvhalinhbaJhM+BskO&Y`%0A8BAXfG8#r(4lz}CjU=J_wBop=^mHnlOM)>vQQD#o z$~kuZjn{p%@SMjO#Ib=c$xSPNmU&D^c(d)}S$Zm93e!D-KFi54W-N>6P6Sjg+yC zOnFWXRdEpPly*YgIq5x;Hw&+o!8|wtbFxlA?peyXY-g%+^)2}{VBOC3pMr%*X~msw z+qTjf!dNnHZ&r)9Qgu1BSXve4v_x<09G`JapLy%@#6PF;0`kVjMo!6x{&=MZHgodW zhwyMBK=GBWboF(z8dN042VHh{Cyb5LL&@pz?uPJ>&DU`i&u@UL+u2y>YOG@dDtv?k zPLRsx_wDwueiUp#cKX#f1C&q&ja0wW=IimWE{@vea60fmpcU(|o$*7=5rT=l>J4bc zA~Y#>J?em7@CzlmpLGV5@PXi>45ERV;!*)U--#E97+Df75QD_S!0sSCu|b1|Azqfh zK#q_kaagZ_R|tG4SjP4Mcn7-!zF-~5nig@SFVkedu33%BaDP9$Hn4OSC7H42O{<-r zZkyA)wkeG^54iM}0)0M=^##3R%oc75-ydEd-g~k%O0W1|-q(Agpok2pg=K%}I{7gB z5OSp9J*6qQM3mfzzCBD&(^&YIJkXx+tqB)ti zD)@}xwxUBCsJ`{AeFJdbPX+73L=>5a)gdM5>%y`T{W1OMPr`d*1&c$<1eJGGKcpYt zA687#Em68wsh|FC%Q5Y#+6{l7Mim_s(;aJ1yz*u#xsPT)U`^QUvSNUG$g!}Vd zv6lNo>aTP~7tQ6P%byFv^gj?W-!ZiDvU%=E@L2G<$0p59QFGH+AZBh2Z4~=@baeM< zSETq3Fc^S(QTa&avC7fiV^plLC8QF8B!=e4=#`PVt!Z{ll_RT){#km4&{z6iH0TpKPKog3F9)A{D3&LQXM(r|UmP#2Par89_N zOT)&n5*`rf7ocHQkT6$_3>+JPEjhj+>y!s6Ms>3iVNJ92qh=^k6(1sp z$WGt~RDi~R00st+JK?P*%}e3wq4%fG9unm5kLdt3^ug76_EmU$1U}K!OJ^kLoHAbM zBsu&nyd6y;h^Nk?^ur_*|9#+>sq}CvJ;JBood5WHT$In=<%K#gTsSgYfFeFGK<7i| zIGGlq@YH1xB7;M+M~Rg9)PndVc=1W5B&VF+3rHN<4fi%MDlgK4mob7A@IoQbkSzr= zL|G1zxe`StzEAi;keP-xqxXmQ4!0j3h#J-i^72VTA!$)wCtG;|ew|#vkLfyD!9lTNoyz=Q&zk(}M2{h*}PZP#n z&eKktGV~oAH@0ru*4o)gPlaBf`J5$f-#7|D4_!rfmbW9=gOKCoixxA2c31Km5bx)B zZa5O+tS*l9dQmFy=SbMD15gai$zJXoRO(&05ap?hPFaRrw{Etv@}s-9_}0^N9T>MA2o-`@I25!=7`Y}(OVKl#$n@d+puj^KKzr= zV-ac&zqA%v#}3%7b;wVZ_>TFZT#u0-=3lIVvE=bM0iz8tSd7$iDwe;I=5Gd}0Yl7! zR^h0no;K`qR;9&s3sKRv(zMRJDhgk$;Ux1HAE8Mc;TnV>)$1%r8A+Vhf zMevY34yWkwxa}5I1}GXG5&+Kybcm-=;z+DL3bmXZE~f_+^o#1BSaS$=<@VZ!3MW^uLQ!c}d;Wdj5>M zO1Xvn+Pri|i5XD8mGt*~-L<>P?WD5k8Y9bB($}gD%Ay&%UU}Eoa7m_w6d;ZBX4IIY zh~lal4d%2&e(8)3b9zE+xRwXGYdStyWpKYt3%MroS@$B-SBSS%WqlX61zM%9Ntm0Xl!yNnE?FT zNqq9a@;CC?4@9vs{meA=>=bP3t^Cta_5=_U!fgB!qZ=ShD0&gssnxmxz{2#PY8- h bool: + def _ask_overwrite(self, existing_file: Path, is_non_interactive: bool = False) -> bool: """询问用户是否覆盖已存在的文件 使用 rich.prompt.Confirm 模式(与 Pipeline._ask_rerun_step 相同) Args: existing_file: 已存在的文件路径 + is_non_interactive: 是否为非交互式环境(stdin不是tty) Returns: True 如果用户选择覆盖 """ + # 非交互式模式:使用配置决定 + if is_non_interactive: + if self.settings.overwrite_archives: + logger.info(f"Non-interactive mode: Overwriting existing archive (KB_OVERWRITE_ARCHIVES=true): {existing_file.name}") + return True + else: + logger.info(f"Non-interactive mode: Skipping existing archive (KB_OVERWRITE_ARCHIVES=false): {existing_file.name}") + return False + + # 交互式模式:询问用户 try: from rich.console import Console from rich.prompt import Confirm diff --git a/skills/kb_workflow/pipeline/processors/srt.py b/skills/kb_workflow/pipeline/processors/srt.py new file mode 100644 index 0000000..2723e38 --- /dev/null +++ b/skills/kb_workflow/pipeline/processors/srt.py @@ -0,0 +1,333 @@ +"""SRTProcessor - B站字幕处理处理器 + +将 B站 SRT 字幕文件转换为简化的 Markdown 文档。 + +处理流程: +1. 解析 SRT 文件(调用 parser.parse_srt_file) +2. 语义分段(调用 segmenter.segment) +3. ASR 纠错(调用 corrector.correct,可选) +4. 生成 Markdown 文件 + +简化版:不做深度观点分析,只保留字幕内容和元数据。 +""" + +from __future__ import annotations + +import json +import logging +from datetime import datetime +from pathlib import Path +from typing import Dict, Any, List, Optional + +from kb_workflow.pipeline.config import get_settings +from kb_workflow.pipeline.processors.base import BaseProcessor +from kb_workflow.pipeline.schemas import SRTInput, SRTOutput +from kb_workflow.pipeline.state import WorkflowStateManager +from kb_workflow.srt.parser import parse_srt_file +from kb_workflow.srt.segmenter import SemanticSegmenter + +logger = logging.getLogger(__name__) + + +class SRTProcessor(BaseProcessor): + """SRT 字幕处理器 + + 将 SRT 字幕文件转换为简化的 Markdown 文档。 + 继承 BaseProcessor,遵循 Pipeline 处理器模式。 + + Attributes: + STEP_NAME: 步骤名称 "srt" + settings: KB 配置 + state_manager: 状态管理器 + segmenter: 语义分段器 + corrector: ASR 纠错器(可选) + """ + + STEP_NAME = "srt" + + def __init__( + self, + state_manager: Optional[WorkflowStateManager] = None, + enable_correction: bool = True + ): + """初始化 SRT 处理器 + + Args: + state_manager: 状态管理器 + enable_correction: 是否启用 ASR 纠错(默认 True) + """ + super().__init__(state_manager) + self.enable_correction = enable_correction + self.segmenter = SemanticSegmenter() + self.corrector = None + + # 延迟初始化纠错器 + if enable_correction: + try: + from kb_workflow.srt.corrector import ASRCorrector + from kb_workflow.deep_analyzer import SimpleAIClient + + # 尝试创建 AI 客户端 + ai_client = SimpleAIClient() + if ai_client.api_key: + self.corrector = ASRCorrector(ai_client=ai_client) + logger.info("ASR corrector initialized") + else: + logger.info("AI API key not configured, ASR correction disabled") + except Exception as e: + logger.warning(f"Failed to initialize ASR corrector: {e}") + + async def process(self, input_data: SRTInput) -> SRTOutput: + """处理 SRT 文件 + + Args: + input_data: SRT 输入数据 + + Returns: + SRT 处理结果 + + Raises: + Exception: 处理失败时抛出异常 + """ + try: + # 记录开始状态 + if self.state_manager: + state = self.state_manager.load() + if state: + state.mark_step_started(self.STEP_NAME) + self.state_manager.save(state) + + # Step 1: 解析 SRT 文件 + logger.info(f"Parsing SRT file: {input_data.srt_file}") + segments = parse_srt_file(Path(input_data.srt_file)) + logger.info(f"Parsed {len(segments)} segments") + + # Step 2: 语义分段 + logger.info("Segmenting subtitles into paragraphs") + paragraphs = self.segmenter.segment(segments) + logger.info(f"Generated {len(paragraphs)} paragraphs") + + # Step 3: ASR 纠错(可选) + corrected = False + if self.corrector and paragraphs: + logger.info("Applying ASR correction") + corrected_paragraphs = [] + for para in paragraphs: + corrected_para = self.corrector.correct(para) + corrected_paragraphs.append(corrected_para) + if corrected_para != para: + corrected = True + paragraphs = corrected_paragraphs + logger.info(f"ASR correction applied: {corrected}") + + # Step 4: 加载元数据 + metadata = self._load_metadata(input_data.metadata_file) + + # Step 5: 生成 Markdown + logger.info(f"Generating Markdown: {input_data.output_file}") + tags = self._extract_tags(paragraphs) + self._generate_markdown( + output_file=Path(input_data.output_file), + metadata=metadata, + paragraphs=paragraphs, + tags=tags + ) + + # 记录完成状态 + if self.state_manager: + state = self.state_manager.load() + if state: + summary = f"paragraphs={len(paragraphs)}, corrected={corrected}" + state.mark_step_completed(self.STEP_NAME, summary) + self.state_manager.save(state) + + return SRTOutput( + success=True, + output_file=str(input_data.output_file), + paragraphs=paragraphs, + tags=tags, + corrected=corrected + ) + + except Exception as e: + logger.error(f"SRT processing failed: {e}") + # 记录失败状态 + if self.state_manager: + state = self.state_manager.load() + if state: + state.mark_step_failed(self.STEP_NAME, str(e)) + self.state_manager.save(state) + raise + + def _load_metadata(self, metadata_file: str) -> Dict[str, Any]: + """加载元数据文件 + + Args: + metadata_file: 元数据文件路径 + + Returns: + 元数据字典 + """ + path = Path(metadata_file) + if not path.exists(): + logger.warning(f"Metadata file not found: {metadata_file}") + return {} + + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + + def _extract_tags(self, paragraphs: List[str]) -> List[str]: + """从段落中提取标签 + + 简单实现:不使用 LLM,只返回默认标签。 + 未来可以增强为 LLM 提取。 + + Args: + paragraphs: 段落列表 + + Returns: + 标签列表 + """ + # 简单实现:返回空列表 + # TODO: 可以使用 LLM 提取标签 + return [] + + def _generate_markdown( + self, + output_file: Path, + metadata: Dict[str, Any], + paragraphs: List[str], + tags: List[str] + ) -> None: + """生成 Markdown 文件 + + Args: + output_file: 输出文件路径 + metadata: 元数据 + paragraphs: 段落列表 + tags: 标签列表 + """ + # 确保输出目录存在 + output_file.parent.mkdir(parents=True, exist_ok=True) + + # 生成 frontmatter + frontmatter = self._generate_frontmatter(metadata, tags) + + # 构建内容 + content_parts = [frontmatter, ""] + + # 添加摘要(可选) + if paragraphs: + content_parts.append("## 摘要") + content_parts.append("") + # 使用第一段作为简单摘要 + first_para = paragraphs[0][:200] if len(paragraphs[0]) > 200 else paragraphs[0] + content_parts.append(first_para + ("..." if len(paragraphs[0]) > 200 else "")) + content_parts.append("") + + # 添加内容 + content_parts.append("## 内容") + content_parts.append("") + for para in paragraphs: + content_parts.append(para) + content_parts.append("") + + # 写入文件 + output_file.write_text('\n'.join(content_parts), encoding='utf-8') + logger.info(f"Markdown file generated: {output_file}") + + def _generate_frontmatter(self, metadata: Dict[str, Any], tags: List[str]) -> str: + """生成 YAML frontmatter + + Args: + metadata: 元数据 + tags: 标签列表 + + Returns: + YAML frontmatter 字符串 + """ + lines = ["---"] + + # 标题 + title = metadata.get('title', '未命名') + lines.append(f'title: "{self._escape_yaml(title)}"') + + # 日期 + pubtime = metadata.get('pubtime', '') or metadata.get('published_at', '') + if pubtime: + date_str = str(pubtime)[:10] + else: + date_str = datetime.now().strftime('%Y-%m-%d') + lines.append(f'date: {date_str}') + + # 作者 + author = metadata.get('author', '未知') + lines.append(f'author: {self._escape_yaml(author)}') + + # 平台 + platform = metadata.get('platform', 'bilibili') + lines.append(f'platform: {platform}') + + # 时长(视频) + duration = metadata.get('duration', 0) + if duration: + duration_str = self._format_duration(duration) + lines.append(f'duration: {duration_str}') + + # BVID + bvid = metadata.get('bvid', '') + if bvid: + lines.append(f'bvid: {bvid}') + + # URL + url = metadata.get('url', '') + if url: + lines.append(f'url: {url}') + + # 标签 + if tags: + tags_str = ', '.join(f'"{t}"' for t in tags) + lines.append(f'tags: [{tags_str}]') + + lines.append("---") + return '\n'.join(lines) + + def _escape_yaml(self, text: str) -> str: + """转义 YAML 特殊字符 + + Args: + text: 原始文本 + + Returns: + 转义后的文本 + """ + # 移除零宽度字符 + invisible_chars = ['\u200b', '\u200c', '\u200d', '\ufeff', '\u00ad'] + for char in invisible_chars: + text = text.replace(char, '') + + # 移除换行符 + text = text.replace('\n', ' ').strip() + + # 如果包含特殊字符,用引号包裹 + if any(c in text for c in [':', '"', "'", '\n', '#']): + text = text.replace('"', '\\"') + return text + + return text + + def _format_duration(self, seconds: int) -> str: + """格式化时长 + + Args: + seconds: 秒数 + + Returns: + 格式化的时长字符串 (HH:MM:SS 或 MM:SS) + """ + h = seconds // 3600 + m = (seconds % 3600) // 60 + s = seconds % 60 + if h > 0: + return f"{h}:{m:02d}:{s:02d}" + return f"{m}:{s:02d}" diff --git a/skills/kb_workflow/pipeline/schemas.py b/skills/kb_workflow/pipeline/schemas.py index 83a3e02..1f5922a 100644 --- a/skills/kb_workflow/pipeline/schemas.py +++ b/skills/kb_workflow/pipeline/schemas.py @@ -444,3 +444,42 @@ class ArchiveOutput(StepOutput): archive_file: str = Field(default="", description="Path to archived file") skipped: bool = Field(default=False, description="Whether archiving was skipped") platform: str = Field(default="", description="Platform name") + + +# ============================================================================= +# SRT Schemas (Phase 12) +# ============================================================================= + + +class SRTInput(StepInput): + """SRT 步骤输入 + + 用于 SRTProcessor 处理 B站字幕。 + + Attributes: + srt_file: SRT 字幕文件路径 + metadata_file: 元数据 JSON 文件路径 + output_file: 输出 Markdown 文件路径 + content_type: 内容类型(默认 video) + """ + srt_file: str = Field(..., min_length=1, description="Path to SRT subtitle file") + metadata_file: str = Field(..., min_length=1, description="Path to metadata JSON file") + output_file: str = Field(..., min_length=1, description="Path to output Markdown file") + content_type: str = Field(default="video", description="Content type") + + +class SRTOutput(StepOutput): + """SRT 步骤输出 + + 继承 StepOutput 的基础字段,添加 SRT 处理特定字段。 + + Attributes: + output_file: 生成的 Markdown 文件路径 + paragraphs: 生成的段落列表 + tags: 提取的标签列表 + corrected: 是否进行了 ASR 纠错 + """ + output_file: str = Field(..., description="Path to generated Markdown file") + paragraphs: List[str] = Field(default_factory=list, description="Generated paragraphs") + tags: List[str] = Field(default_factory=list, description="Extracted tags") + corrected: bool = Field(default=False, description="Whether ASR correction was applied") diff --git a/skills/kb_workflow/srt/__init__.py b/skills/kb_workflow/srt/__init__.py new file mode 100644 index 0000000..581c0aa --- /dev/null +++ b/skills/kb_workflow/srt/__init__.py @@ -0,0 +1,27 @@ +"""SRT Processing Module + +B站字幕处理模块,包含: +- SRT 解析器 (parser) +- 语义分段器 (segmenter) +- ASR 纠错器 (corrector) +""" + +from kb_workflow.srt.parser import parse_srt, parse_srt_file, Segment + +__all__ = [ + "parse_srt", + "parse_srt_file", + "Segment", + "SemanticSegmenter", + "ASRCorrector", +] + +# 延迟导入,避免循环依赖 +def __getattr__(name: str): + if name == "SemanticSegmenter": + from kb_workflow.srt.segmenter import SemanticSegmenter + return SemanticSegmenter + if name == "ASRCorrector": + from kb_workflow.srt.corrector import ASRCorrector + return ASRCorrector + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/skills/kb_workflow/srt/__pycache__/__init__.cpython-312.pyc b/skills/kb_workflow/srt/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c873d859d51d8409cdf031566236ec41aad40fb9 GIT binary patch literal 946 zcmZWnT}TvB6ux(6c4u7EH4#Z97<_V|b}Q(I2+P7CBMG;seYuS@<2vljj@%g$MMJ{G zz+fMe&BCw}GQ@i7!SKQNUN-ANhJ32==Sj;T@TqfWb(14+{E(QZzUj0$OS zAgYLckDx5oc}KoY9nElEGnbKXIeMRE#9~)B=BI*%La;a)%#Lp4$JXaw1k;7>rHNqT zL6D!{UV0ikDYM$m*RR%}=Yy#^xw%)Ru0a(5Hs39LUwjyhkFCFbA40B?v5lNpLR(Xv z^2Udmt;bU#ra6=vn&&{}7Q2qZ`xPM-ajEB5mqXHRT81yCN>oaO@{mjg<|5$yqbb8y zbDpW~XBgB!0!Ljtp!bEGY8x7c+yje3z*u(gf-9gQkT2T|e3H)TyoGE$j4>L*DTWAT zBw~TM!yF)73V9V!8k!#U!cno!Gfo$Wz)uVF`{&)qiqt{bfs~C+!KGL%#cUZA?NEGq zuvYF-T{-8-s^?L&tIsoJCF(b1yGXx7v)z`{p8#nRWl=+_|M9Z0l+pXutp{vvlseRx z2w{Q{qVQCLgue~BOcPm__ga!Yj%_4ePo*%v(`U||O(vXPBd1wvf6~pGmgOc7b(Ms! zl7yH!(<3C&JK$Fmk}*7X3qoidNbBHpTfmK=Rk5-dZ(0$X)}+g;ak(hT@Mrwqig`1qL{d{T(>M08*Y4#wHJlj?4Uo}laC_3F(g<2;J#5+JTzC#>hsPNI;G$UgU4!s-{0X)F W6rz>fh`3WL;+r_H{X%RFnf?G6{1^QI literal 0 HcmV?d00001 diff --git a/skills/kb_workflow/srt/__pycache__/corrector.cpython-312.pyc b/skills/kb_workflow/srt/__pycache__/corrector.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d3e08776bfcecb5a52af7e60830f8b33bee60ef GIT binary patch literal 5393 zcmb_geQXow8Gp|{f94W9A8rzpv60Nz*&-JP3| zIaNvnI3eS|~{$*`%x2HRF#}e!KHwP4~vaVsAI77q9l+?RYIQ)+1M79jM$J>jEWW_(-B_ zRJ_KDT@?a-O<8V$KfjKE$AU0{$qChk!qZ^yoaA zPF+0-7pNDk%AplK!5o+ZdlH9!mk574Jvb~L?@0~~xl=0DRiI7Gk&^m@ghLagpgMhO zAQA4BPgeUOa%csTV|@DreKQ+qYUTxVldILq2M#cPj>GzBn*Mf6#r{AOQ^D~rwu$kt zUH9V+73G0u#_#sIS}M2$UZ0PvII!2*5?~MP^97)b}CkHBIaw%iVM$Cxn(6;$gWydBkWyLvVQnu3<&Sr#8c(0cv6pl2CA$og3;^f+{a z(dqQNni!{3Fgu-1l8Hf`ayow#boo*v2B*^#a66qWHgnd5GYif#ab{4mT&QpuUY+PG zvgjL9qoZUoEDIQrBOURPkL0^dM9z!Dqw$fq#Gy~XIO8|okB=M!nT3WZ z-#u6-7@RnI`rcSKm=svB*nL>+9b97e=H&3FV$Vl1FjTviQGspw(xLA+XV4Bdo4oW| z;?$_v(~qsSvqyZhd*;H4L}X0Hk7U6ZKfmRfR=(YAmRA#dPrxSSbK#|WK1zQ6mU#4f zy3y1k$qz2f9DM^&1lrtiOKM8lBr&%Mx)z(c37L?`fO9B~9xXO_kI6%JlJz<$bx`V| z)I(`FsBxGD3TQ-SkzfoIVqSq=VGN(WY&L_j(%(xe@sX?eWD-lL1S8L@;d$1(H^?(w zm81vja(SI@iEgSe1*mYtcXIRa%=sJQ(0kJ-uF;AK(}^>qiI3rb?NT-Db@A@~PR`rL zR4rl`*$AeuTuPk22LIH|jsE1&MS4SJWu-K=b`h2_pGsJ!r8^7}A>@RboI)CCrT7^k z3FjZeh!}oX{NR|_eP+q^;A7RW4GRwWJqLyJ#XmoW?ex;2#CzxBBiHHLfS-YroW-_v z^PS}RZdt#2V65$j99o@#xzeOwv|yf}?9j3pge)dVb{S*}AxUt>c=!6fywllcTWA;M zN_PdGftj)c%|3$cE5l@WR?J=!CXZVrG`mx73M8cKm9O|8(C9-Ug0+T-M)g`H87Uh~ zRXKf#XeBtwoz|fcYFwx-CDW15WA>F&Bj%1J$)g!etw6wvb%@7=B&86cjDTjz(JLcz zc>wN&&4w{dTuajYTxnmJ-5)N&!4>ke+BKTh!@-j#k5|<1W87KG;e%OF8e}ILb z7yzS_waiQJk!X5}#mNgUWE$-1B{N&>Zo_AkU0t94@H*JyLhmcl7~T<1+gn{YM3{#! z?yRn^rd3}=Z>ATT${N+|Ha8tiA>E-GrZSslxS)@Rsj8atRBg?Q_>GSEXc%Zdaqc=W zka+b{d^ED+VI9pZIIK0Yrp2RaHK=Yz9fL39=4!pGMZa{`CV@XRFR_isk0NS=GNT zN)^xQ5oPVE?y8PvEWb_V^z7`~87=tPB()Pn!aEVumy=XNwDxf@G?Af$f+^qmy|XxL&Oyo zcJ08bjVgHOrP9GTq)7){kERW#UC}DhaJx37ZNy>BlB5ty9a@Qa9Nb8$1hK2yNnVY{ z8zIJDnmlBPdb32UWDkpoqUz7OO-7|3u@KAh*}7TA^19Ikpo=u_-P!y zbd4sioLAlbJZirS<1!k>BZtMI(Rt|~h;h}h9R#}ycH(^i{|waex!dxwH%}QBM9%a+ zuyn>#MYk1xr>)mIOhWe7U3>Oy-}=IiUA4~Ldv-my`vpP2FTggrc-eDEq23BufPD-y z!L+^A%}Aj+OXIeo#l`voL=K(oZSc0_a1-K7jC_G$q_`CGfo%TlN|0$=w#f8V^hjv> zH3)DVWZ-*p^rnK_w$)R%vTCanlS)#Vm3EOTE$Sv&KbaK;M)lq8sziwL!zD62Q1YoJ{s*M(}zxMpKt^UtPUi;aS^E-t3>35EA(a;@$Tlqii5$?+r(!pJofL1fyh#Ee?1*KBRtemMm zBVkLJLK;5fJcpXT4M?NwH~%jFQNPRoR|;Y=PJ%eNf_3kWy>@SGz|3MN2j7x< zS2KLydRU1zaOf$JjRCJ;0=|QqCknyD!D9q`2{W8va0lUm1HMa1_kI^E7$hQq{>)T= z%FkvgP4L7G{C>fxXb`ef+{buiQJAZfeX~g&;V@GwSBy*f(-kZLwLO-ekW_b$Ju+7g zgbSopfNc3;Q!FPhY>4FnwUDOb@U|G0+sk+D3O^mQ-?cuCD1ys{oXncd2L1Q*5LE~}$F$hLZeZit zjgie0%hpa(>tIX8k424zFIgD#g$W3U1yXXnEf^@nVSglZB}1IC7eNn_GBiW!aYo$(pz*{wY{VM11#LvOM(j~X&_N-ArBN&5ZHEwVzd-ADFBtO#oiq|G zy(^7sPQg2%y)37}5KRlg0=|HE9%6zX-ov|~EaVG$H>BH~*`r6DVhXYOyQUl5T z{s8&a`2Bvawss?DG_1!Bq{p4{@tG^b>0`gqXHCEQO8UdAPNHheH94g(WGbhE*{pDeX{q@L@IF6$vW}RG4SOY7Z`Vh>F@}Y%nP-dWBjHfG~;# z6r{if=;z_!;m~P;;Vn;rhG#%xvyzoaHfXbBX~5xgYBpof;vYea@s-I}GavpwbNo`~ z=!g3D>R;Tfs&Wv83 z8~x~uiR7JgZ+v9PK+ zm!{V2rg6=psItaj8|^+;vxh=bI4XofnlluN66XWi9SZ#@9*!6qTPVcGx}do*6v{cJ zQ0P2bguO!do~rMOMTL4i9e7tcO^1jl!vUfC~3B1--Ky`cj!c|WWSnq;-U zkNA6C-+*if6C_&&g0dE}SI`1uW9{=r$l-pub-JiD#gtBaN?zVI?JPL7O~ye;1`GzP zJNa+CI$rX zG-~lrAdI2}>dn;}y)f*ULEj1RF+>f~398rdP}3AHdCLW3d?#p!EJMr?s~SLtP$H}4 znL%jH(JE_2-14r~bc8w36(0ISCLnolXyid zPyq-dA)~6;z?QLg8Hb4G$^UHmF=%XnzcK*XFq-F(x8h9UslqYK*!NOp^;1mg@V4Yj z(`&2F#7@P=x>9Q!r*%pc{(;vXXUNE1?I@55^UITzpuj#^UHe+m-?HMsmrd*rWTvJ1e0JB z)`V@`e!}* zIbJ}#W61Fn$C3B+@&I)J^;=#*2PmH_{|6 zFX`my?7L%x!{9Jz&chvu7!`<)0XsQnb>zwADPMt_e3GP3W1v!4RB|u(?}sCCp+%Ns zfN#1<42I{gWs)a!tjvs_%^XkO2dRv8a!+%W4V}_``BtZtC0qD1s&pE`Pk>9&iJbz5 zvM5k(7+=CI3~>=bsy2HsX>`7aE#f|={zdNQ&nY`P(|7Xi(8_7>Hs-BW41xx zHtD5sMC3_hcTA3k;TWVCr#J46#Jct?Tz4!k@iO))`9a9$u%uDk9FB7zBV%4KyK#mC zN5)amK3H>K!{(ax@8^9kz+9kXeVTLWl(b@CyP-`xc5VJ{=-KAoyIY=X*KFOQ#D^mh zg3SaoHKsQvN-|~~gidJIa38Q1UUP1fc%h%1HztP02Wxh7YQXHU+SLP?%@P5amVPWD zkze*T7}W_C*C;{3UuzyF$LV11$R4BoZBXxozj7C{VYE<(+$ATg&eWc&Jzam(Q#R?X zNqK4~?X|ZHi-+3)jtjXPo~kKN)tD{i@ejBD-Sq(JwI$_gnY6duE-w2L6|<#DcAhSB zl-~5NIr;qAhEqR$t30{=rnhR09Sfhgr@ReNTlMH!VXXPxp46)PWZUhMm7^Qp*l@Dt zY{jXzGwr9^Q)_B&tZAHD)0kTGc&cQ5a?8xh^68TD>D7-;dn=|(y>}h#Do?T%?ty~R zW4m78b+YxZ?nma!Vd{m)kb4DLQ)|l8Hfe9W?J61G^{=~ysCf11?URL7Un00zX51_O zJ&$jgdkV&{KiZoEn@i|VOA0sFu%A}3kmubh81Klf(htHnaWNA+6;JMsl0a{vw{!0U z)SG2pV9ZE7J)UJ?aPMSqn?&|T0_McT&ja4gT>rWLF3Y7*1cY=bn7w#Cee8tZMaPZw z)yw}E&UK3s0qBF;V;~)Q4bFaiG5yIQ1G1JAsgArS;Qi2TE7)$2satWV8X%`HdFIN| z^am%?r^m9muoPk@5dM$&*Dc4Q5V2}cMBK}X(Y~0h!jo6+$y&~Zqg-$c;V-YoU8^gV z_+C|1BZ5)~l&{jaglhM}JN@}OxB@?au_-_(RY#=&H^|1-?%ECTu!QGPSBw{>eLhCB z3jOfPRJ7u+?2G1*g)qF)04YhZ`e@C8ZIXkL8^M1RjBjRW=kPS#Oa2b%lDJbdWWdwb zA$REw*Xk+P>c6+cMZ+uhfc_b*$No?B z@+Odc#WD^-{1)tv12Knpp^TAQqh&#JWnbiiBI793Y{r2SE1Fdu=z|@Ut)SBJ6p%C8 z2VbsPx9r;8ykm!4i&dDelI hZTs1_MF(3-Pom<*A}{4#+^~UKarY(4ORXi#_%9XRSP%dJ literal 0 HcmV?d00001 diff --git a/skills/kb_workflow/srt/__pycache__/segmenter.cpython-312.pyc b/skills/kb_workflow/srt/__pycache__/segmenter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00d6540a3f26eba76bb41a03212344d4fe952902 GIT binary patch literal 3091 zcmbtW-ES0C6uEIzs5L3XSRc$9G8uPhPX&2mQpQ&YNZ+q63_~@q|nNj9P^d7nTD)rd9BVgs?1#%)SU84GIp2 zcJ-KcxnH~4rzywMSAI$-PGo+#JbUwm)~jeY$7XMg!(^uS*4^%pwe#b%DEs6 zX}v#ar#{yvPGv@VG-cr4#Fvgn50ma6%l7|>e_wu)8T%wNc|3bo(YhyK-avQk03jVO z+c&9=U(62lYRZ_yF?-|W?5)YUFTP`1TJ|xS$*(d)31f$*45!arzBiGWojjLL95qir zozNyPWxLOVR*o#T)=j8Sap7=8=Hx&mEWwa&2}m-uHq&IEL5fjRMnf#tL8l)H$V&jv zBu{v$0#pDz;u=~jPxe^6G;ian9@=Z^q}_IPU5?B=m(c)$kja`(qBiiI!@)pVL2l;O zX!_*Wz^Ng;f?0z-?R3K6W{Fvhw4H%jQ{w1gc4+e6gwo{aI#{_)6r{FDkZ0ybPT!k2 zF?(we4lu}@`}n4=#`edM4*Nsg5w?R9Ilsttw6VT6PLvq93v;6v0mm@lby*ezA4X+C z+N~pDRhFH)dzsS>cLTJc3%D)&%h}A8VdgncLw^5bj@`|`oigZ14|Z$ABZeC3zNF8ALEE@<1WR~qJUG)GwxwUe9_j*8(GPuL7BXO5=_M~w{- zp|qc`X`lU)9T+x(YFfFT4|*8rjU!yBBPc9IJv`ae)WqZ;1@j7IQzNbBGeM0z|72Il zOCbJ%DOb~2$28P2JL{M&bEBiPH@dY`-)?c&<&tyF4t=c=MD+FXguZ@Y6?8s!WAMCQ z9ltj-{j3b-t)5!UxGx9#YrjrU2hBx2dimC&_FM zPNe`xErg604f3p^Q&>2xQV<;_9QH8pdJ&!YSetolSIH`8<%p^UIf;TR+Km<%RJbWs zv_W}$X2V9skt(a0vX`VvN|iOKO=XH}rs$dbXj6e(*)y}bddgWgZ$*XWQ)_Ed#U=gW z&%+nu$>QgGsZ>$%+4a*UwaJp&-->Ee+iIuQS7XVFVd}!3k>;W1Z(PZ>+pp5o4b91h z=HJ#f|4~$JNG*UAldRsEDlDBYtV|YGUZ@(W8LFA8efjp5SMC%xrK-116;`GyxAd+7 zKv%`IvpVUlzR)`I{?Pk(oSu}kbkJvP`6`rj)++l`Wt-2p478lsH{S?qFYH3j@|3f9 z+F71-mY?;VZyRWv+VG)7?}c zEW`Xpkm_>bZ>`+zKA>7f{S|f<=+lxc9&lT6{s?m+fn`+(%ZBuiPv|>Y_M<2lG*@ga z%SU`HE8<`&Ziec6q-&)6QncY&T&;t@gx%JI7RUmNeZCYiRjI12^ERjDg_(_I^A>1< zp^C~yJoBJzm!;Ie!Mpdn?c!Ewu_6bf5HCO}5$8$ODtC4S!hR8df~&=kq(cv+Ll0bA zNf1SxI1E*`;T|STSMQ str: + """生成文本""" + ... + + +# 纠错 Prompt 模板 +CORRECTION_PROMPT = """你是一个文本校对专家。以下是语音识别生成的字幕,可能包含识别错误。 +请只纠正明显的错误(如同音字、漏字、错字),不要改变原意和风格。 + +原文: +{text} + +纠正后的文本(只输出纠正后的文本,不要解释):""" + + +class ASRCorrector: + """ASR 纠错器 + + 使用 LLM 纠正语音识别中的明显错误。 + + Attributes: + ai_client: AI 客户端(必须实现 generate 方法) + batch_size: 分批处理的字符数,默认 5000 + """ + + def __init__( + self, + ai_client: Optional[AIClientProtocol] = None, + batch_size: int = 5000 + ): + """初始化纠错器 + + Args: + ai_client: AI 客户端实例,如果为 None 则不进行纠错 + batch_size: 分批处理的字符数 + """ + self.ai_client = ai_client + self.batch_size = batch_size + + def correct(self, text: str) -> str: + """纠正文本中的语音识别错误 + + Args: + text: 原始文本 + + Returns: + 纠正后的文本。如果 AI 不可用或文本为空,返回原文。 + + Example: + >>> corrector = ASRCorrector(ai_client=my_client) + >>> result = corrector.correct("什么是机器学习") + >>> print(result) + 什么是机器学习 + """ + # 空文本直接返回 + if not text or not text.strip(): + return text or "" + + # AI 不可用,返回原文 + if self.ai_client is None: + logger.info("AI client not available, returning original text") + return text + + # 短文本直接处理 + if len(text) <= self.batch_size: + return self._correct_batch(text) + + # 长文本分批处理 + return self._correct_long_text(text) + + def _correct_batch(self, text: str) -> str: + """处理单个批次的文本 + + Args: + text: 文本内容 + + Returns: + 纠正后的文本 + """ + try: + prompt = CORRECTION_PROMPT.format(text=text) + result = self.ai_client.generate(prompt, max_tokens=len(text) + 500) + return result.strip() + except Exception as e: + logger.warning(f"ASR correction failed: {e}, returning original text") + return text + + def _correct_long_text(self, text: str) -> str: + """分批处理长文本 + + 按句子分割,每批不超过 batch_size 字符。 + + Args: + text: 长文本内容 + + Returns: + 纠正后的文本 + """ + # 按句子分割(简单分割,基于句号、问号、感叹号) + sentences = [] + current = "" + for char in text: + current += char + if char in '。!?\n': + sentences.append(current) + current = "" + + # 添加最后一个不完整的句子 + if current: + sentences.append(current) + + # 分批处理 + batches = [] + current_batch = [] + current_length = 0 + + for sentence in sentences: + if current_length + len(sentence) > self.batch_size: + if current_batch: + batches.append(''.join(current_batch)) + current_batch = [sentence] + current_length = len(sentence) + else: + current_batch.append(sentence) + current_length += len(sentence) + + if current_batch: + batches.append(''.join(current_batch)) + + # 处理每个批次 + corrected_batches = [] + for batch in batches: + corrected = self._correct_batch(batch) + corrected_batches.append(corrected) + + return ''.join(corrected_batches) diff --git a/skills/kb_workflow/srt/parser.py b/skills/kb_workflow/srt/parser.py new file mode 100644 index 0000000..6565792 --- /dev/null +++ b/skills/kb_workflow/srt/parser.py @@ -0,0 +1,161 @@ +"""SRT Parser Module + +解析 SRT 字幕文件格式,将字幕转换为结构化的 Segment 对象。 + +SRT 格式示例: + 1 + 00:00:00,000 --> 00:00:05,000 + 第一句字幕内容 + + 2 + 00:00:05,000 --> 00:00:10,000 + 第二句字幕内容 +""" + +from __future__ import annotations + +import re +from dataclasses import dataclass +from pathlib import Path +from typing import List + + +@dataclass +class Segment: + """字幕分段数据结构 + + Attributes: + id: 字幕序号 + start_time: 开始时间(秒) + end_time: 结束时间(秒) + text: 字幕文本 + """ + id: int + start_time: float + end_time: float + text: str + + +# SRT 块正则表达式 +# 格式: 序号\n时间码\n文本(可能多行)\n\n +SRT_BLOCK_PATTERN = re.compile( + r'(\d+)\s*\n' + r'(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})\s*\n' + r'(.*?)(?=\n\n|\Z)', + re.DOTALL +) + + +def parse_srt_time(time_str: str) -> float: + """解析 SRT 时间格式 (HH:MM:SS,mmm) 为秒数 + + Args: + time_str: SRT 时间字符串,如 "00:05:30,500" + + Returns: + 秒数,如 330.5 + + Example: + >>> parse_srt_time("00:05:30,500") + 330.5 + >>> parse_srt_time("01:02:03,004") + 3723.004 + """ + # 分割时间和毫秒 + main_part, millis = time_str.rsplit(',', 1) + hours, minutes, seconds = main_part.split(':') + + total_seconds = ( + int(hours) * 3600 + + int(minutes) * 60 + + int(seconds) + + int(millis) / 1000 + ) + return total_seconds + + +def parse_srt(content: str) -> List[Segment]: + """解析 SRT 格式字符串 + + 将 SRT 格式的字幕内容解析为 Segment 对象列表。 + + Args: + content: SRT 格式的字符串内容 + + Returns: + Segment 对象列表 + + Raises: + ValueError: 内容为空或格式无效 + + Example: + >>> content = "1\\n00:00:00,000 --> 00:00:05,000\\n第一句\\n\\n" + >>> segments = parse_srt(content) + >>> len(segments) + 1 + >>> segments[0].text + '第一句' + """ + # 检查空内容 + if not content or not content.strip(): + raise ValueError("Empty SRT content") + + segments = [] + + # 查找所有 SRT 块 + matches = SRT_BLOCK_PATTERN.findall(content) + + if not matches: + raise ValueError("Invalid SRT format: no valid blocks found") + + for match in matches: + try: + segment_id = int(match[0]) + start_time = parse_srt_time(match[1]) + end_time = parse_srt_time(match[2]) + # 清理文本:移除前后空白,合并多行为单行 + text = ' '.join(match[3].strip().split()) + + segment = Segment( + id=segment_id, + start_time=start_time, + end_time=end_time, + text=text + ) + segments.append(segment) + except (ValueError, IndexError) as e: + # 跳过解析失败的块 + continue + + if not segments: + raise ValueError("Invalid SRT format: no valid segments parsed") + + return segments + + +def parse_srt_file(file_path: Path) -> List[Segment]: + """从文件解析 SRT 格式 + + 读取文件内容并解析为 Segment 对象列表。 + + Args: + file_path: SRT 文件路径 + + Returns: + Segment 对象列表 + + Raises: + FileNotFoundError: 文件不存在 + ValueError: 内容格式无效 + + Example: + >>> from pathlib import Path + >>> segments = parse_srt_file(Path("subtitles.srt")) + >>> len(segments) + 10 + """ + if not file_path.exists(): + raise FileNotFoundError(f"SRT file not found: {file_path}") + + content = file_path.read_text(encoding='utf-8') + return parse_srt(content) diff --git a/skills/kb_workflow/srt/segmenter.py b/skills/kb_workflow/srt/segmenter.py new file mode 100644 index 0000000..7af8d12 --- /dev/null +++ b/skills/kb_workflow/srt/segmenter.py @@ -0,0 +1,97 @@ +"""Semantic Segmenter Module + +将字幕分段按语义合并为段落。基于简单启发式规则: +1. 时间间隔超过阈值时分段 +2. 段落长度限制 + +不使用 LLM 进行分段判断,保持简单和快速。 +""" + +from __future__ import annotations + +from typing import List + +from kb_workflow.srt.parser import Segment + + +class SemanticSegmenter: + """语义分段器 + + 将字幕分段转换为语义段落。基于时间和长度规则: + - 长时间停顿(>gap_threshold 秒)作为分段点 + - 段落长度限制(max_paragraph_chars 字符) + + Attributes: + gap_threshold: 时间间隔阈值(秒),默认 3.0 + max_paragraph_chars: 单段落最大字符数,默认 500 + """ + + def __init__( + self, + gap_threshold: float = 3.0, + max_paragraph_chars: int = 500 + ): + """初始化分段器 + + Args: + gap_threshold: 时间间隔阈值(秒),超过此值时分段 + max_paragraph_chars: 单段落最大字符数 + """ + self.gap_threshold = gap_threshold + self.max_paragraph_chars = max_paragraph_chars + + def segment(self, segments: List[Segment]) -> List[str]: + """将字幕分段转换为语义段落 + + Args: + segments: 字幕分段列表 + + Returns: + 段落文本列表(已去除时间戳) + + Example: + >>> segmenter = SemanticSegmenter() + >>> segments = [Segment(1, 0, 5, "第一句"), Segment(2, 5, 10, "第二句")] + >>> paragraphs = segmenter.segment(segments) + >>> len(paragraphs) + 1 + """ + if not segments: + return [] + + paragraphs = [] + current_paragraph = [] + current_length = 0 + last_end_time = None + + for seg in segments: + # 检查是否需要分段 + should_start_new = False + + # 规则1: 时间间隔超过阈值 + if last_end_time is not None: + gap = seg.start_time - last_end_time + if gap > self.gap_threshold: + should_start_new = True + + # 规则2: 段落长度超过限制 + if current_length + len(seg.text) > self.max_paragraph_chars: + if current_paragraph: # 只有当前段落有内容时才分段 + should_start_new = True + + # 执行分段 + if should_start_new and current_paragraph: + paragraphs.append(' '.join(current_paragraph)) + current_paragraph = [] + current_length = 0 + + # 添加当前分段 + current_paragraph.append(seg.text) + current_length += len(seg.text) + last_end_time = seg.end_time + + # 添加最后一个段落 + if current_paragraph: + paragraphs.append(' '.join(current_paragraph)) + + return paragraphs