| name | hlzd-daily-report |
| description | B2B 业务员日报自动化 —— LangBot 事件采集 + LLM 摘要 + 飞书卡片推送 + 次日 8:00 早会总结。ActivityWatch + Inbox Zero + LangBot 三方借鉴。 |
| license | MIT |
| metadata | {"author":"HLZD (海联智达 / 海良数科)","version":"0.1.0","industry":"cross-border-b2b","category":"operations","triggered_by":["日报助手","业务员日报","每日总结","周报助手","飞书日报卡片","早会总结","今日工作","明日计划","活动追踪","时间块统计","daily report","sales daily standup","morning briefing","activity tracking","time blocking"]} |
HLZD 日报助手 v0.1
关键借鉴(基于 GitHub 调研 3 个高星标项目):
- LangBot(16,712 stars)—— HLZD 日报助手作为 LangBot 插件运行(不自己写飞书机器人)
- ActivityWatch(18,136 stars)—— Event/Bucket 数据模型 + heartbeat 合并
- Inbox Zero(11,550 stars)—— Pydantic 结构化输出
核心原则:日报不是"写",是"自动汇总 + 一键确认"——从 4 大数据源(邮件/IM/订单/客户)抽取当日活动 → 结构化日报 → 飞书卡片推送。
定位
| 对比项 | 手工写日报 | HLZD-日报助手 v0.1 |
|---|
| 写日报时间 | 30-60 分钟 | < 1 分钟(一键确认) |
| 数据来源 | 人工回忆 | 4 大数据源自动聚合(邮件/IM/订单/客户) |
| 早会总结 | 临时拼凑 | 昨日日报自动生成 to do list |
| 待办事项 | 凭感觉 | 基于客户互动 + SLA 自动推荐 |
| 主管 dashboard | 手工汇总 | 每日自动聚合 |
数据源借鉴
| 借鉴项目 | Stars | 借鉴内容 |
|---|
| 🏆 langbot-app/LangBot | 16,712 | IM 机器人框架(HLZD 日报助手作为插件) |
| 🏆 ActivityWatch/activitywatch | 18,136 | Event/Bucket 数据模型(活动聚合) |
| 🏆 Inbox Zero | 11,550 | Pydantic 结构化输出 |
| AstrBotDevs/AstrBot | 35,910 | AI Agent + IM(参考) |
核心能力矩阵
| 能力 | 实现 | 数据源 |
|---|
| 每日 18:00 自动触发 | Panmira scheduler | LangBot 调度器 |
| 邮件活动聚合 | imap_tools 拉取(HLZD-询盘响应 skill) | 网易/Gmail/Outlook IMAP |
| IM 对话聚合 | 飞书 API(LangBot 飞书适配器) | 飞书 IM 历史 |
| 订单系统聚合 | HLZD-智能报价 + HLZD-物流装箱 skill | SQLite |
| 客户互动聚合 | HLZD-客户画像 skill(v0.2) | SQLite |
| 早会总结生成 | 从昨日日报 → to do list | 飞书多维表格 |
| 风险提醒 | SLA 临近 + 客户投诉 + 危险品 | 多 skill 联动 |
| 飞书卡片推送 | card-renderer | LangBot 飞书 |
| 一键提交主管 | 飞书卡片按钮 | 飞书 API |
工作流 v0.1
每日 18:00(Panmira scheduler)
↓
[1] LangBot 飞书适配器接收 trigger
↓
[2] HLZD-日报助手插件启动
↓
[3] 拉取 4 大数据源(并行)
├─ 邮件: HLZD-询盘响应 skill(imap_tools 拉取今日询盘)
├─ IM: 飞书 API 拉取今日对话
├─ 订单: SQLite 查 HLZD-智能报价/物流装箱 今日记录
└─ 客户: SQLite 查 HLZD-客户画像 今日互动
↓
[4] 数据归一化 → Event Stream(借鉴 ActivityWatch)
↓
[5] LLM 生成日报结构(Claude + Pydantic schema)
↓
[6] 飞书卡片渲染(card-renderer)
↓
[7] 业务员一键确认/编辑/提交
↓
[8] 提交后写入飞书多维表格(主管 dashboard)
↓
[9] 早会总结自动生成(次日 8:00)
- 昨日日报
- to do list 完成度
- 今日待办
HLZD 日报结构(Pydantic schema)
from pydantic import BaseModel, Field
from typing import Literal
from datetime import date
class InquiryStats(BaseModel):
"""今日询盘统计"""
total: int = Field(description="今日询盘总数")
by_grade: dict[str, int] = Field(description="按 A/B/C 分级数量")
by_category: dict[str, int] = Field(description="按 4 类分类数量")
replied: int = Field(description="已回复数量")
pending: int = Field(description="待回复数量")
sla_breached: int = Field(description="SLA 临近/超期数量")
class QuoteStats(BaseModel):
"""今日报价统计"""
total: int = Field(description="今日报价总数")
sent: int = Field(description="已发送数量")
draft: int = Field(description="草稿数量")
framework_quotes: int = Field(description="快速框架报价数量")
by_incoterm: dict[str, int] = Field(description="按 INCOTERMS 分布")
by_currency: dict[str, int] = Field(description=)
():
total_orders: = Field(description=)
total_volume_m3: = Field(description=)
avg_utilization: = Field(description=)
containers_used: [, ] = Field(description=)
hazmat_count: = Field(description=)
():
a_customers_contacted:
b_customers_contacted:
new_customers:
complaints:
pending_followups:
():
priority: [, , , ]
description:
customer_id: [] =
deadline: [datetime] =
reason:
():
: [, , , ]
severity: [, , ]
description:
action_required:
():
yesterday_completed: []
yesterday_pending: []
today_priorities: [TodoItem]
today_blockers: []
():
sales_id:
date: date
inquiry_stats: InquiryStats
quote_stats: QuoteStats
shipment_stats: ShipmentStats
customer_interaction: CustomerInteraction
todos: [TodoItem]
risks: [RiskAlert]
achievements: []
next_day_plan: []
confidence: = Field(ge=, le=)
data_completeness: = Field(description=)
scripts/ 设计 v0.1
scripts/
├── langbot_plugin.py # ⭐ v0.1: HLZD 日报助手作为 LangBot 插件
├── daily_report_schema.py # ⭐ v0.1: Pydantic 日报结构
├── event_collector.py # ⭐ v0.1: 4 大数据源活动聚合(借鉴 ActivityWatch)
├── llm_summarizer.py # ⭐ v0.1: LLM 生成日报结构
├── morning_standup.py # ⭐ v0.1: 早会总结生成
├── feishu_card_renderer.py # ⭐ v0.1: 飞书卡片渲染
├── feishu_bit_table_writer.py # ⭐ v0.1: 飞书多维表格写入
├── data_normalizer.py # ⭐ v0.1: 4 大数据源归一化为 Event Stream
├── orchestrator.py # ⭐ v0.1: 主调度
└── scheduler.py # ⭐ v0.1: 每日 18:00 自动触发
scripts/event_collector.py 设计
"""
HLZD 日报助手 - 活动数据收集器
借鉴 ActivityWatch Event/Bucket 数据模型
"""
from dataclasses import dataclass, field
from datetime import datetime, date
from typing import Literal
import asyncio
@dataclass
class ActivityEvent:
"""借鉴 ActivityWatch Event 数据模型"""
timestamp: datetime
duration: int
bucket: Literal["email", "im", "order", "customer"]
data: dict
sales_id: str
@classmethod
def merge(cls, e1: "ActivityEvent", e2: "ActivityEvent") -> "ActivityEvent":
"""心跳合并:相同数据 + 在 pulsetime 内 → 合并"""
if (e1.bucket == e2.bucket and
e1.data == e2.data and
(e2.timestamp - e1.timestamp).total_seconds() < 300):
return ActivityEvent(
timestamp=e1.timestamp,
duration=e1.duration + e2.duration,
bucket=e1.bucket,
data=e1.data,
sales_id=e1.sales_id,
)
return e2
class EventCollector:
"""4 大数据源活动聚合"""
async def collect_today() -> [ActivityEvent]:
tasks = [
._collect_email_events(sales_id, date),
._collect_im_events(sales_id, date),
._collect_order_events(sales_id, date),
._collect_customer_events(sales_id, date),
]
results = asyncio.gather(*tasks)
all_events = []
events results:
all_events.extend(events)
._merge_events(all_events)
() -> [ActivityEvent]:
hlzd_inquiry_response InquiryResponse
inquiries = InquiryResponse.fetch_today_inquiries(sales_id, date)
[
ActivityEvent(
timestamp=inq.received_at,
duration=,
bucket=,
data={
: ,
: inq.,
: inq.sender,
: inq.category,
: inq.customer_grade,
: inq.status,
},
sales_id=sales_id,
)
inq inquiries
]
() -> [ActivityEvent]:
langbot.platforms.feishu FeishuAdapter
adapter = FeishuAdapter(sales_id)
chats = adapter.get_today_chats(date)
[
ActivityEvent(
timestamp=chat.start_time,
duration=chat.duration_seconds,
bucket=,
data={
: ,
: chat.,
: chat.participants,
: chat.message_count,
},
sales_id=sales_id,
)
chat chats
]
() -> [ActivityEvent]:
hlzd_smart_quote SmartQuote
quotes = SmartQuote.fetch_today_quotes(sales_id, date)
hlzd_shipment Shipment
shipments = Shipment.fetch_today_shipments(sales_id, date)
events = []
q quotes:
events.append(ActivityEvent(
timestamp=q.created_at,
duration=,
bucket=,
data={
: ,
: q.,
: q.amount,
: q.currency,
: q.incoterm,
: q.status,
},
sales_id=sales_id,
))
s shipments:
events.append(ActivityEvent(
timestamp=s.created_at,
duration=,
bucket=,
data={
: ,
: s.,
: s.container_type,
: s.volume_utilization,
: s.is_hazmat,
},
sales_id=sales_id,
))
events
() -> [ActivityEvent]:
hlzd_customer_persona CustomerPersona
interactions = CustomerPersona.fetch_today_interactions(sales_id, date)
[
ActivityEvent(
timestamp=inter.timestamp,
duration=,
bucket=,
data={
: ,
: inter.customer_id,
: inter.customer_grade,
: inter.,
: inter.summary,
},
sales_id=sales_id,
)
inter interactions
]
() -> [ActivityEvent]:
events:
[]
events.sort(key= e: e.timestamp)
merged = [events[]]
e events[:]:
last = merged[-]
last.bucket == e.bucket last.data == e.data:
merged[-] = ActivityEvent.merge(last, e)
:
merged.append(e)
merged
scripts/llm_summarizer.py 设计
"""
HLZD 日报助手 - LLM 摘要生成器
借鉴 Inbox Zero Zod schema + Pydantic 验证
"""
from daily_report_schema import DailyReport
from event_collector import ActivityEvent, EventCollector
class LLMSummarizer:
"""LLM 生成日报结构"""
async def generate_report(self, sales_id: str, date: str) -> DailyReport:
"""LLM 摘要生成(带 Pydantic 验证)"""
events = await EventCollector().collect_today(sales_id, date)
events_text = self._format_events(events)
prompt = f"""请基于以下活动数据生成 HLZD 业务员日报:
# 活动数据
{events_text}
# 输出格式(必须严格遵守)
请输出 JSON,符合以下结构:
{{
"sales_id": "{sales_id}",
"date": "{date}",
"inquiry_stats": {{ ... }},
"quote_stats": {{ ... }},
...
}}
# 要求
1. 数字必须与活动数据一致
2. 风险提醒基于 SLA 临近 + 客户投诉 + 危险品
3. 待办事项按优先级排序(P0 最高)
4. 早会总结简洁(≤ 200 字)
"""
from langbot.provider.runners import ClaudeRunner
response = await ClaudeRunner.generate(
prompt=prompt,
response_schema=DailyReport,
model="claude-sonnet-4.6",
)
try:
report = DailyReport.parse_raw(response)
except ValidationError as e:
raise LLMOutputError(f"Pydantic 验证失败: {e}")
return report
() -> :
lines = []
e events:
lines.append()
.join(lines)
飞书卡片设计
更多细节
完整设计文档(v0.1.x 实现细节 + 错误地图 + 性能目标)见 references/deep-dive.md。