feat: 更新 created_by_message_id 逻辑,仅在内容或状态变化时进行更新

This commit is contained in:
oudecheng 2026-06-29 15:01:32 +08:00
parent bf1549b88b
commit 26cbe7aa2d
4 changed files with 71 additions and 120 deletions

View File

@ -1,108 +0,0 @@
import fitz
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
pdf_path = r'C:\Users\qwer\.picobot\media\ws\aa56c052-ea10-4bc1-aed4-7d06770b6fd9_夜读 _ 明白了这4点就不难养出有主体性的孩子.pdf'
output_path = r'C:\Users\qwer\.picobot\media\夜读_明白了这4点_就不难养出有主体性的孩子.docx'
pdf_doc = fitz.open(pdf_path)
doc = Document()
# 页面边距
for section in doc.sections:
section.top_margin = Cm(2.54)
section.bottom_margin = Cm(2.54)
section.left_margin = Cm(3.18)
section.right_margin = Cm(3.18)
# 正文样式
style = doc.styles['Normal']
style.font.name = '宋体'
style.font.size = Pt(12)
style.paragraph_format.line_spacing = 1.5
style.paragraph_format.first_line_indent = Pt(24)
def add_run(paragraph, text, bold=False, size=None, color=None, italic=False, font_name=None):
run = paragraph.add_run(text)
run.bold = bold
if size: run.font.size = Pt(size)
if color: run.font.color.rgb = RGBColor(*color)
run.italic = italic
if font_name: run.font.name = font_name
return run
# 收集所有文本
full_text = []
for i, page in enumerate(pdf_doc):
text = page.get_text().strip()
if text:
full_text.append(text)
all_text = '\n'.join(full_text)
lines = [l.strip() for l in all_text.split('\n') if l.strip()]
# 定义段落标记
sections_headers = ['塑教育,提倡积极养育', '懂互动,给予丰盈幸福',
'有边界,养出人生底气', '稳情绪,才能赢得孩子']
skip_lines = ['南方都市报电商官方账号。', '南都甄选', '公众号']
first = True
for line in lines:
# 跳过广告行
if line in skip_lines:
continue
# 主标题
if first:
p = doc.add_paragraph()
p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_after = Pt(12)
add_run(p, line, bold=True, size=22, font_name='黑体')
first = False
# 引用句(引号开头结尾、较短)
elif (line.startswith('"') and line.endswith('"')) or \
(line.startswith('"') and line.endswith('"') and len(line) < 60):
p = doc.add_paragraph()
p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_before = Pt(6)
p.paragraph_format.space_after = Pt(6)
add_run(p, line, italic=True, size=12, color=(102, 102, 102))
# 日期来源
elif line in ['2026年6月14日 22:28 广东', '南方都市报']:
p = doc.add_paragraph()
p.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT
p.paragraph_format.first_line_indent = Pt(0)
add_run(p, line, size=10.5, color=(128, 128, 128))
# 4个小标题
elif line in sections_headers:
p = doc.add_paragraph()
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_before = Pt(18)
p.paragraph_format.space_after = Pt(6)
add_run(p, line, bold=True, size=15, font_name='黑体')
# 作者信息
elif any(line.startswith(x) for x in ['作者:', '统筹:', '图片:', '投稿邮箱:']):
p = doc.add_paragraph()
p.paragraph_format.first_line_indent = Pt(0)
add_run(p, line, size=10.5, color=(128, 128, 128))
# 末尾信息
elif '转载自' in line or '把世界当成' in line:
p = doc.add_paragraph()
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_before = Pt(6)
add_run(p, line, size=10.5, color=(102, 102, 102))
else:
doc.add_paragraph(line)
pdf_doc.close()
doc.save(output_path)
print('转换完成!')

View File

@ -186,21 +186,44 @@ impl BusToolCallEmitter {
.unwrap_or_default() .unwrap_or_default()
.as_secs() as i64; .as_secs() as i64;
// 读取现有 DB 记录,独立对比决定 created_by_message_id 是否更新
let existing = self.store.list_todos(&scope_key).unwrap_or_default();
let existing_map: std::collections::HashMap<&str, &crate::storage::TodoRecord> =
existing.iter().map(|r| (r.id.as_str(), r)).collect();
let records: Vec<crate::storage::TodoRecord> = todos_array let records: Vec<crate::storage::TodoRecord> = todos_array
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(idx, item)| { .filter_map(|(idx, item)| {
let id = item.get("id")?.as_str()?;
let content = item.get("content")?.as_str()?;
let status = item.get("status")?.as_str()?;
// 仅 content 或 status 实际变化时更新 created_by_message_id
let changed = match existing_map.get(id) {
Some(old) => old.content != content || old.status != status,
None => true, // 新项
};
let msg_id = if changed {
message.tool_call_id.clone()
} else {
existing_map
.get(id)
.and_then(|r| r.created_by_message_id.clone())
};
Some(crate::storage::TodoRecord { Some(crate::storage::TodoRecord {
id: item.get("id")?.as_str()?.to_string(), id: id.to_string(),
scope_key: scope_key.clone(), scope_key: scope_key.clone(),
session_id: session_id.clone(), session_id: session_id.clone(),
topic_id: topic_id.clone(), topic_id: topic_id.clone(),
content: item.get("content")?.as_str()?.to_string(), content: content.to_string(),
status: item.get("status")?.as_str()?.to_string(), status: status.to_string(),
priority: "medium".to_string(), priority: "medium".to_string(),
created_at: now + idx as i64, created_at: now + idx as i64,
updated_at: now, updated_at: now,
created_by_message_id: message.tool_call_id.clone(), created_by_message_id: msg_id,
}) })
}) })
.collect(); .collect();

View File

@ -221,21 +221,44 @@ impl SubAgentEmitter {
.unwrap_or_default() .unwrap_or_default()
.as_secs() as i64; .as_secs() as i64;
// 读取现有 DB 记录,独立对比决定 created_by_message_id 是否更新
let existing = self.store.list_todos(scope_key).unwrap_or_default();
let existing_map: std::collections::HashMap<&str, &crate::storage::TodoRecord> =
existing.iter().map(|r| (r.id.as_str(), r)).collect();
let records: Vec<crate::storage::TodoRecord> = todos_array let records: Vec<crate::storage::TodoRecord> = todos_array
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(idx, item)| { .filter_map(|(idx, item)| {
let id = item.get("id")?.as_str()?;
let content = item.get("content")?.as_str()?;
let status = item.get("status")?.as_str()?;
// 仅 content 或 status 实际变化时更新 created_by_message_id
let changed = match existing_map.get(id) {
Some(old) => old.content != content || old.status != status,
None => true, // 新项
};
let msg_id = if changed {
message.tool_call_id.clone()
} else {
existing_map
.get(id)
.and_then(|r| r.created_by_message_id.clone())
};
Some(crate::storage::TodoRecord { Some(crate::storage::TodoRecord {
id: item.get("id")?.as_str()?.to_string(), id: id.to_string(),
scope_key: scope_key.clone(), scope_key: scope_key.clone(),
session_id: scope_key.clone(), session_id: scope_key.clone(),
topic_id: None, topic_id: None,
content: item.get("content")?.as_str()?.to_string(), content: content.to_string(),
status: item.get("status")?.as_str()?.to_string(), status: status.to_string(),
priority: "medium".to_string(), priority: "medium".to_string(),
created_at: now + idx as i64, created_at: now + idx as i64,
updated_at: now, updated_at: now,
created_by_message_id: message.tool_call_id.clone(), created_by_message_id: msg_id,
}) })
}) })
.collect(); .collect();

View File

@ -144,8 +144,8 @@ impl Tool for TodoWriteTool {
None => return Ok(error_result("todo_write requires session_id or topic_id in tool context")), None => return Ok(error_result("todo_write requires session_id or topic_id in tool context")),
}; };
// 2. 提取当前消息 ID用于记录待办的创建来源 // 2. 提取当前 tool call ID用于定位修改该项的 tool 消息,前端用 tool_call_id 作为 data-message-id
let message_id = context.message_id.clone(); let message_id = context.tool_call_id.clone();
// 3. 解析入参 // 3. 解析入参
let todos_array = match args.get("todos").and_then(|v| v.as_array()) { let todos_array = match args.get("todos").and_then(|v| v.as_array()) {
@ -219,11 +219,18 @@ impl Tool for TodoWriteTool {
continue; continue;
} }
// 仅在 content 或 status 实际变化时更新 created_by_message_id
let changed = old_item.content != content
|| old_item.status.as_str() != new_status.as_str();
processed_items.push(TodoItem { processed_items.push(TodoItem {
id, id,
content, content,
status: new_status.as_str().to_string(), status: new_status.as_str().to_string(),
created_by_message_id: message_id.clone(), created_by_message_id: if changed {
message_id.clone()
} else {
old_item.created_by_message_id.clone()
},
}); });
} else if merge_mode { } else if merge_mode {
// merge 模式id 不匹配,尝试 content fallback // merge 模式id 不匹配,尝试 content fallback
@ -239,11 +246,17 @@ impl Tool for TodoWriteTool {
validation_errors.push(format!("Item '{}': {}", content, err)); validation_errors.push(format!("Item '{}': {}", content, err));
continue; continue;
} }
// content 匹配上说明 content 不变,仅需比较 status
let changed = old_item.status.as_str() != new_status.as_str();
processed_items.push(TodoItem { processed_items.push(TodoItem {
id: old_item.id.clone(), id: old_item.id.clone(),
content, content,
status: new_status.as_str().to_string(), status: new_status.as_str().to_string(),
created_by_message_id: message_id.clone(), created_by_message_id: if changed {
message_id.clone()
} else {
old_item.created_by_message_id.clone()
},
}); });
} else { } else {
// 全新项 // 全新项