17 Commits

Author SHA1 Message Date
starmen1265 5264c93a03 Merge pull request 'feat(semantic): 添加广告植入内容识别功能' (#44) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/44
2026-03-24 00:38:26 +08:00
starmen1265 a1076dfcb4 feat(semantic): 添加广告植入内容识别功能
PR Test / lint (pull_request) Successful in 1s
在LLM语义分段Prompt中添加广告识别要求:

【分段要求】新增:
- 第7条:要求识别UP主广告植入内容
- 明确常见的电商平台推广关键词(转转、京东、得物、淘宝、拼多多等)
- 要求将广告内容单独分段,title设为"广告"或"推广"

【注意事项】新增:
- 添加广告识别提示,列出常见广告植入特征:
  * 突然话题转换到商品推荐
  * 电商相关词汇(回收、卖、优惠、下单等)
  * 具体品牌名称提及
  * 推销语气的风格变化
  * 与视频主题无关的内容

测试结果:
- 转转广告被正确识别为独立段落,标题为"广告"
- 广告内容不再混入正常内容段落中

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 00:36:20 +08:00
starmen1265 390d497558 Merge pull request 'fix/crawler-proxy-config' (#42) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/42
2026-03-24 00:21:00 +08:00
starmen1265 bbc8816d16 refactor(semantic): 移除语义分段降级功能,LLM失败直接报错
PR Test / lint (pull_request) Successful in 1s
主要变更:
- 移除 _segment_single_chunk() 中的降级逻辑,失败直接抛出 RuntimeError
- 移除 _segment_multiple_chunks() 中的降级逻辑,分块失败直接抛出异常
- 移除 segment() 中 AI 客户端不可用时的降级逻辑,直接报错
- _fallback_chunk() 方法已弃用,调用时直接抛出异常
- VideoSegment 标题长度限制从 15 字增加到 30 字
- 将 ValidationError 从不可恢复错误改为可恢复错误,允许重试
- 更新相关 docstring,将 fallback_segmenter 标记为已弃用

现在 LLM 语义分段失败时会抛出明确的 RuntimeError 异常,
包含失败原因(HTTP 错误、JSON 解析错误等)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 00:20:28 +08:00
starmen1265 2cfdbd5aef Merge pull request 'fix/crawler-proxy-config' (#41) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/41
2026-03-21 23:34:06 +08:00
starmen1265 3c0a67b67a Merge pull request 'debug: add API response logging to investigate content vs reasoning_content' (#40) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/40
2026-03-21 22:49:06 +08:00
starmen1265 8d5beadcc7 Merge pull request 'fix(srt): add disable_reasoning parameter for ASR correction and include title in archive filename' (#39) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/39
2026-03-21 22:23:16 +08:00
starmen1265 36e48f90f7 Merge pull request 'fix/crawler-proxy-config' (#38) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/38
2026-03-21 21:44:56 +08:00
starmen1265 4fb9635d15 Merge pull request 'fix: correct Zhipu API thinking parameter and add non-interactive archive mode' (#37) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/37
2026-03-20 00:51:27 +08:00
starmen1265 e1db39e36f Merge pull request 'fix(deep-analyzer): add disable_reasoning parameter to GLM API calls' (#36) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/36
2026-03-20 00:29:34 +08:00
starmen1265 996c25af3f Merge pull request 'fix(12-02): add SRTProcessor to processors/__init__.py exports' (#35) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/35
2026-03-20 00:17:26 +08:00
starmen1265 d17b55f6c5 Merge pull request 'fix/crawler-proxy-config' (#34) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/34
2026-03-20 00:12:03 +08:00
starmen1265 e61a299d84 Merge pull request 'fix(cli): use json mode for model_dump serialization' (#33) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 2s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/33
2026-03-19 23:00:23 +08:00
starmen1265 0ac50d17ec Merge pull request 'fix/crawler-proxy-config' (#32) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 2s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/32
2026-03-19 00:07:32 +08:00
starmen1265 22fd634498 Merge pull request 'fix/crawler-proxy-config' (#31) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/31
2026-03-18 20:48:32 +08:00
starmen1265 cb540eb113 Merge pull request 'fix(kb_workflow): resolve module/package naming conflict' (#30) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/30
2026-03-18 10:42:16 +08:00
starmen1265 94a51abed8 Merge pull request 'fix/crawler-proxy-config' (#29) from fix/crawler-proxy-config into main
Deploy kb-skills / deploy (push) Successful in 1s
Reviewed-on: http://192.168.1.22:8418/starmen1265/kb-skills/pulls/29
2026-03-18 00:05:47 +08:00
+46 -71
View File
@@ -80,7 +80,6 @@ class UnifiedSegmentationStrategy:
Attributes:
ai_client: AI 客户端(实现 AIClientProtocol)
fallback_segmenter: 规则分段器(用于降级)
"""
def __init__(
@@ -93,7 +92,7 @@ class UnifiedSegmentationStrategy:
Args:
ai_client: AI 客户端实例,如果为 None 则尝试创建 SimpleAIClient
fallback_segmenter: 降级时使用的规则分段器
fallback_segmenter: 已弃用,保留仅为向后兼容
cache: 缓存管理器,如果为 None 则不使用缓存
"""
self.ai_client = ai_client
@@ -112,18 +111,16 @@ class UnifiedSegmentationStrategy:
logger.warning(f"Failed to initialize AI client: {e}")
self.ai_client = None
# 初始化降级分段器
if self.fallback_segmenter is None:
from kb_workflow.srt.segmenter import SemanticSegmenter
self.fallback_segmenter = SemanticSegmenter()
@staticmethod
def _is_recoverable_error(error: Exception) -> bool:
"""判断错误是否可恢复
可恢复错误:网络错误、超时、限流、服务器错误(500/502/503/504)、
JSON 解析错误(JSONDecodeError, 非 ValidationError 的 ValueError)
不可恢复错误:其他 HTTP 错误(4xx)、Pydantic 验证错误(ValidationError)
不可恢复错误:其他 HTTP 错误(4xx)
注意:Pydantic ValidationError(如标题超长)现在被视为可恢复错误,
因为这是LLM生成内容的问题,应该允许重试。
Args:
error: 异常对象
@@ -131,10 +128,8 @@ class UnifiedSegmentationStrategy:
Returns:
True 如果错误可恢复,False 否则
"""
# Pydantic 验证错误(不可恢复)
# 注意:ValidationError 继承自 ValueError,所以需要先检查
if isinstance(error, ValidationError):
return False
# 注意:ValidationError 现在被视为可恢复错误
# 因为这可能是LLM偶然生成的内容问题,重试可能解决
# 网络层错误(可恢复)
if isinstance(error, requests.exceptions.ConnectionError):
@@ -174,15 +169,11 @@ class UnifiedSegmentationStrategy:
Returns:
VideoSegment 对象列表(包含标题和内容)
"""
# AI 不可用,直接使用规则分段
# AI 不可用,直接报错
if self.ai_client is None:
logger.info("AI client unavailable, using fallback segmenter")
# 将规则分段结果转换为 VideoSegment 格式(无标题)
paragraphs = self.fallback_segmenter.segment(segments)
return [
VideoSegment(title=None, content=para, start_index=0, end_index=0)
for para in paragraphs
]
raise RuntimeError(
"AI客户端未配置,无法进行LLM语义分段。请检查AI_API_KEY环境变量。"
)
# 合并字幕文本
content = " ".join(seg.text for seg in segments)
@@ -249,7 +240,11 @@ class UnifiedSegmentationStrategy:
- 基于语义停顿判断,不仅依赖标点符号
- 保持内容的连贯性和可读性
5. 保持论点完整性,不要在前提-结论结构中间截断
6. 为主题/章节类型的分段生成简洁标题(5-15字),论点/观点类型分段不加标题
6. 为主题/章节类型的分段生成简洁标题(最多30字),论点/观点类型分段不加标题
7. **广告识别**:识别UP主的广告植入内容(包括但不限于"转转""京东""得物""淘宝""拼多多"等电商平台推广,以及其他明显的商业推广内容)
- 将广告内容单独分为一个段落
- 广告段落的 title 必须设为"广告"或"推广"
- 不要将广告内容混入正常内容段落中
【输出格式】
必须返回以下 JSON 格式(不要添加任何额外文本):
@@ -271,7 +266,13 @@ class UnifiedSegmentationStrategy:
【注意事项】
- 优先保证语义完整性,不要为了凑数而强行截断
- 标题应简洁准确,如「机器学习概述」「梯度下降原理」
- 如果某个分段无法生成合适的标题(如过渡性内容),将 title 设为 null"""
- 如果某个分段无法生成合适的标题(如过渡性内容),将 title 设为 null
- **广告识别提示**:常见的广告植入特征包括:
* 突然话题转换到商品推荐
* 提及"回收""卖""优惠""下单"等电商相关词汇
* 提及具体品牌名称(如转转、京东、得物等)
* 说话风格明显变化(如口语化的推销语气)
* 内容与视频主题无关的商品推荐"""
return prompt
def _infer_video_type(self, metadata: dict) -> str:
@@ -329,7 +330,7 @@ class UnifiedSegmentationStrategy:
Args:
llm_output: LLM 原始输出
fallback_segments: 降级时使用的原始分段
fallback_segments: 已弃用参数,保留仅为向后兼容
Returns:
VideoSegment 对象列表
@@ -383,7 +384,6 @@ class UnifiedSegmentationStrategy:
max_tokens=8000,
disable_reasoning=True # 分段不需要思考模式
)
last_llm_output = llm_output
# 解析和验证
video_segments = self._parse_result(
@@ -395,7 +395,6 @@ class UnifiedSegmentationStrategy:
return video_segments
except Exception as e:
last_error = e
is_recoverable = self._is_recoverable_error(e)
is_last_attempt = (attempt == max_attempts - 1)
@@ -407,7 +406,7 @@ class UnifiedSegmentationStrategy:
)
continue
# 不可恢复错误或最后一次尝试:降级
# 最后一次尝试或不可恢复错误:直接抛出异常
error_type = type(e).__name__
# 为 HTTPError 提取更详细的信息
@@ -417,31 +416,14 @@ class UnifiedSegmentationStrategy:
error_msg = f"HTTP {status_code}"
if response_text and isinstance(response_text, str):
error_msg += f" ({response_text[:100]})"
logger.warning(
f"{error_type}: {error_msg}, 降级到规则分段"
)
# 对于 JSON 解析错误(排除 ValidationError),记录 LLM 输出片段(前 500 字符)
elif isinstance(e, (json.JSONDecodeError, ValueError)) and not isinstance(e, ValidationError) and last_llm_output:
error_msg = str(e) if str(e) else error_type
logger.warning(
f"{error_type}: {error_msg}\n"
f"LLM 输出片段: {last_llm_output[:500]}...\n"
f"降级到规则分段"
)
raise RuntimeError(
f"{error_type}: {error_msg}, LLM语义分段失败"
) from e
else:
error_msg = str(e) if str(e) else error_type
logger.warning(
f"{error_type}: {error_msg}, 降级到规则分段"
)
break
# 降级到规则分段
logger.info("使用规则分段(降级)")
paragraphs = self.fallback_segmenter.segment(fallback_segments)
return [
VideoSegment(title=None, content=para, start_index=0, end_index=0)
for para in paragraphs
]
raise RuntimeError(
f"{error_type}: {error_msg}, LLM语义分段失败"
) from e
def _segment_multiple_chunks(
self,
@@ -482,10 +464,10 @@ class UnifiedSegmentationStrategy:
chunk_results.append((segments, offset))
logger.info(f"Chunk {i} segmented successfully: {len(segments)} segments")
except Exception as e:
logger.warning(f"Chunk {i} failed: {e}, using fallback for this chunk")
# 该分块降级到规则分段
chunk_segments = self._fallback_chunk(chunk_text, offset)
chunk_results.append((chunk_segments, offset))
# 分块处理失败,直接抛出异常
raise RuntimeError(
f"分块 {i}/{len(chunks)} LLM语义分段失败: {type(e).__name__}: {e}"
) from e
# 合并结果
return self._merge_segment_results(chunk_results)
@@ -567,7 +549,9 @@ class UnifiedSegmentationStrategy:
return all_segments
def _fallback_chunk(self, chunk_text: str, offset: int) -> List[VideoSegment]:
"""对单个分块使用规则分段(降级)
"""对单个分块使用规则分段(已弃用)
此方法已弃用,不再被调用。如被调用将抛出异常。
Args:
chunk_text: 分块文本
@@ -575,20 +559,11 @@ class UnifiedSegmentationStrategy:
Returns:
VideoSegment 对象列表
Raises:
RuntimeError: 此方法已弃用
"""
# 创建虚拟的分段对象用于规则分段器
# 规则分段器需要 Segment 对象列表,我们创建一个包含整个文本的虚拟分段
from kb_workflow.srt.parser import Segment
virtual_segment = Segment(
id=1,
start_time=0.0,
end_time=1.0,
text=chunk_text
)
# 使用规则分段器
paragraphs = self.fallback_segmenter.segment([virtual_segment])
raise RuntimeError("_fallback_chunk 方法已弃用,LLM语义分段失败将直接抛出异常")
# 转换为 VideoSegment,并设置正确的偏移量
segments = []
@@ -664,7 +639,7 @@ class VideoSegment(BaseModel):
"""
title: Optional[str] = Field(
None,
description="分段标题,简洁描述型(5-15字)"
description="分段标题,简洁描述型(最多30字)"
)
content: str = Field(
...,
@@ -684,9 +659,9 @@ class VideoSegment(BaseModel):
@field_validator('title')
@classmethod
def validate_title_length(cls, v: Optional[str]) -> Optional[str]:
"""验证标题长度(5-15字)"""
if v is not None and len(v) > 15:
raise ValueError(f"标题长度不能超过15字,当前为{len(v)}字")
"""验证标题长度(最多30字)"""
if v is not None and len(v) > 30:
raise ValueError(f"标题长度不能超过30字,当前为{len(v)}字")
return v
model_config = {