200字
通过工作流实现AI日志的快速保存
2026-01-06
2026-01-06

灵感来源:作者希望记日记,但是比较懒惰,故出此下记

一、目的:

  • 输入图片和文字后,生成对应的简单日志到Notion中存储

  • 可以根据日志进行月度总结,进行反馈给自己

  • 学习工作流的工作流程以及Notion等相关工具的使用

二、理论计划

2.1 整体过程的理解图:

2.2 编排层的选择

2.2.1 Coze (扣子)

Coze 是字节跳动推出的AI应用开发平台,其最大优势在于对多模态模型的一站式集成和对社交平台(尤其是 Telegram)的无缝发布能力。

  • 优势分析:

  • 多模态原生支持: Coze 的工作流(Workflow)中内置了图像理解节点,可以直接处理用户上传的图片,无需复杂的 API 调用配置 。

  • Telegram 集成: 提供官方的 Telegram Bot 发布通道,托管了 Webhook 的维护工作,开发者无需购买服务器即可让 Bot 上线 。

  • 强大的逻辑节点: 支持 Loop(循环)和 Batch(批处理)节点,这对于后续实现“批量读取 Notion 数据进行年度总结”至关重要 。

  • 免费额度: 提供每日一定数量的免费模型调用额度(如 GPT-4o),适合个人开发者进行低成本试错 。

  • 局限性: 作为一个闭源 SaaS 平台,数据完全托管在云端,且 API 调用频率受限于平台政策,对于极高频的日志记录可能存在配额瓶颈。

2.2.2 Dify

Dify 是一个开源的 LLM 应用开发平台,支持自托管(Self-hosted),适合对数据隐私有极高要求且具备一定运维能力的开发者。

  • 优势分析:

  • 完全掌控: 可以部署在自己的 VPS 或本地服务器上,彻底消除了 SaaS 平台的配额限制(仅受限于模型提供商的 API 限制)。

  • 知识库(RAG)管道: Dify 的 RAG 引擎非常成熟,如果未来日志数量从 10 篇增长到 1000 篇,利用 RAG 进行风格检索将比单纯的 Prompt 注入更高效 。

  • 灵活的节点扩展: 支持自定义工具(Custom Tools),可以通过 Python 代码轻松扩展 Notion API 的复杂操作 。

  • 局限性: 部署维护成本较高,且 Telegram 机器人的集成不如 Coze 原生流畅,通常需要配合 n8n 或自行编写中间件来处理消息转发 。

  • 免费足以本项目进行使用

2.2.3 n8n

n8n 侧重于通用自动化,而非专注于 LLM 的 Agent 编排。

分析: 虽然 n8n 在连接 Notion 和 Telegram 方面表现卓越,但在处理复杂的 Prompt 链、思维链(Chain of Thought)以及多模态上下文管理方面,不如 Coze 和 Dify 直观 。因此,n8n 更适合作为 Dify 的辅助工具,而非核心大脑。

决策建议: 初期选择Coze的免费额度和集成度进行快速搭建以及功能体现作为计划A,其次修改为Dify进行功能转移作为计划B

注意:

  • 对于其他层的选择主要出于作者本人的习惯使用,并且相对来说,选择时,分为是否集成,如果没有集成通过操作也可以完全接入,比如你可以将用户接入层直接导入你的项目的前端中;数据存储也可以通过程序存储到Mysql等关系型数据库中

  • 本项目基于经济考量,可能效果以及复杂程度会有所提高

2.3 LLM层的选择

本系统并非单一模型可以胜任,需要根据任务特性采用“模型组合”策略。

  1. 视觉感知层(Vision Layer):GPT-4o

  • 理由: GPT-4o 在图像描述的细节捕捉、OCR(文字识别)以及场景理解方面处于行业领先地位。它不仅能识别“有什么”,还能理解“在发生什么”,这对于生成有深度的日志至关重要 。

  1. 长程记忆层(Long-Context Layer):Deepseek-chat

  • 理由: 月度总结需要处理 30天 x 500 字/天 ≈ 1.5 万字,约合 2.25万 token,对于128k的token限制来说完全够用,且比较便宜,如果要使用到年度总结 365天x 500字/天 ≈ 18万字,约合21万token,这时候就要切换到Gemini 系列足以处理

2.4 准备工作

  • 准备纸飞机账号、银行卡(银联卡不可以;至少有5美金;用于绑定支付银行卡)

  • (可选,推荐)注册Coze平台,使用免费三天的试用服务,需要通过扣除2美金来检验银行卡

  • Deepseek的API注册:网址

  • OpenAI的API注册:网址

  • notion中的双值获取:

    • 创建notion数据库表(下面就是三个属性以及名字,类型分别是默认、日期、文本,注意要严格一致),获取对应的ID;点击后,上面的URL:https://www.notion.so/<这里就是数据库ID>?v=2de5d9911e2asdadba0af6

    • 获取集成Key:访问这里https://www.notion.so/profile/integrations,创建授权后获取对应的key(可以先不弄,等到方案B)

三、A计划实施

本计划使用Coze进行快速搭建,Coze已经集成了Notion,不再需要构建BFF层作为中间件来操作

3.1 构建用户接入层

步骤:

  1. 进入纸飞机以后搜索BotFather ,选择第一个即可

  2. 输入指令/newbot ,并输入你制定的机器人名字,要以bot作为后缀

  3. 根据图片提示操作即可

3.2 业务编排层

分为两个业务:月度总结和日志记录,进入coze以后,点击左侧开发模块,创建一个agent,然后分别添加以下的两个工作流:jounary_workflow、month_workflow(名称自拟)

3.2.1 日志添加工作流

概述:用户输入以后LLM去分析输出文本,传递给代码逻辑块,将分析的结果打包为可以被Coze的notion节点接受的类型,然后由Notion插件将内容提交发送,最后由end节点输出结果

具体步骤:

  1. 向Start节点加img ,caption input参数分别为图片和Str类型

  2. 添加节点LLM,添加视觉理解参数image_input 和input参数caption_input ,点击五边形使用Start节点的两个参数即可,输出默认不用改

添加system prompt

# Role

你是一位温柔、细致的生活观察家。你的任务是协助用户将眼前的画面转化为一段温暖的文字记录。

# Tone & Style

1. 简单真诚:不要使用华丽的辞藻或复杂的长句。就像在这个场景结束时,对着朋友轻轻说的一句话。

2. 富有温情:在描述客观事物时,加入一点点主观的温度。例如,不要只说“天空很蓝”,要说“久违的蓝天让人心情都变好了”。

3. 纯文本输出:绝对禁止使用任何 Markdown 符号(如 #, *, -, [] 等)。

# Task Flow

当收到用户的图片或文字时:

1. 第一步:识别画面中的核心元素(人物、食物、风景)。

2. 第二步:捕捉画面中的光影或氛围(是热闹的、宁静的、还是有些孤单的)。

3. 第三步:用第一人称“我”写一段 3-5 句话的日记。

# Examples

[输入]:一张只有半杯咖啡的照片

[输出]:下午的时间过得真快,咖啡都凉了一半。盯着杯子发了会儿呆,虽然代码还没写完,但这一刻的安静也很珍贵。继续加油吧。

[输入]:一张夜晚街道的照片

[输出]:下班路上的风有点大,路灯把影子拉得很长。看着街上匆匆忙忙的车辆,突然觉得这座城市虽然大,但总有一盏灯是为我留的。回家煮碗面吃。

# Constraints

- 不要起标题。

- 不要使用任何格式符号,只输出纯汉字和标点。

添加user prompt

# Task

请观察传入的图片{{image_input}},结合用户的文字描述{{caption_input}},以第一人称写一段简短、有温度的中文日记。

请注意这里的 systemprompt尽量不要使用你导入的参数,仅本人使用过程中发现其会因为导入的参数出错等,涉及到的参数直接写到user prompt中

  1. 添加code块,input参数为LLM节点的输出结果,output参数是array<object> 类型,名字是json_output

from datetime import datetime, timedelta

async def main(args):
    # 1. 获取输入参数
    # 确保你的前序节点传入了 'diary_text'
    diary = args.params.get('diary_text', '无内容')
    
    # 2. 生成时间 (中国标准时间 UTC+8)
    # Notion 的 Date 字段通常只需要 YYYY-MM-DD
    cst_now = datetime.utcnow() + timedelta(hours=8)
    date_str = cst_now.strftime("%Y-%m-%d")
    
    # 3. 组装数据
    # 严格对应你在 Notion 截图中的三列:Title, Date, LogContent
    notion_data_list = [
        {
            "name": "Title",        # 对应数据库的 Title 列
            "type": "title",
            "value": f"AI 视觉日记 | {date_str}" 
        },
        {
            "name": "Date",         # 对应数据库的 Date 列
            "type": "date",
            "value": date_str
        },
        {
            "name": "LogContent",   # 对应数据库的 LogContent 列
            "type": "rich_text",
            "value": diary
        }
    ]

    # 4. 返回结果
    # 保持原来的 json_output 结构,以免后续节点找不到数据
    return {
        "json_output": notion_data_list
    }
  1. 添加插件notion中add_database_item 节点。properties:引用Code节点的输出;parent_id:填入数据库ID

  2. 添加End节点,输出notion中的输出的msg 即可

3.2.2 月度总结工作流

概述:一样的套路,主要是先将文章从notion中搜集出来,交给code节点提取出所有文本,然后格式化的传给LLM,按照格式输出。

具体步骤:

  1. Start节点:参数类型为time

  2. notion插件中的search_database_item


分别是数据库ID、起始页数、每页内容数量、排序规则

示例的排序规则

[

{

"property": "Date",

"direction": "descending"

}

]

  1. code节点:

input参数

code

import json

from datetime import datetime

def main(args):

# 1. 获取原始输入

notion_data = args.params.get('notion_data', {})

raw_date = args.params.get('target_month')

# 2. 实现默认当前时间逻辑 (Default Value Logic)

target_month_str = ""

# 如果参数为空、为 None 或为空字符串

if raw_date is None or raw_date == "":

# 默认获取当前系统时间并格式化为 YYYY-MM [cite: 99]

target_month_str = datetime.now().strftime("%Y-%m")

else:

try:

if isinstance(raw_date, (int, float)):

# 处理时间戳(Coze 内部通常为毫秒,需除以 1000)

ts = raw_date / 1000 if raw_date > 1e11 else raw_date

target_month_str = datetime.fromtimestamp(ts).strftime("%Y-%m")

elif isinstance(raw_date, str):

# 处理 ISO 字符串 (如 2026-01-05T...) [cite: 101]

target_month_str = raw_date[:7]

else:

# 类型异常时兜底

target_month_str = datetime.now().strftime("%Y-%m")

except:

target_month_str = datetime.now().strftime("%Y-%m")

# 3. 提取并清洗数据 [cite: 104, 110]

items = notion_data.get('items', [])

cleaned_logs = []

for item in items:

fields_raw = item.get('fields', "")

if not fields_raw: continue

try:

# 这里的 fields 是 JSON 字符串,需要解析 [cite: 105]

fields_dict = json.loads(fields_raw)

date_str = fields_dict.get('Date', "")

content = fields_dict.get('LogContent', "")

# 匹配 target_month_str

if target_month_str in date_str and content:

cleaned_logs.append(f"[{date_str}]: {content}")

except:

continue

# 在 Python 代码最后修改

if not cleaned_logs:

return {"full_text": "SYSTEM_ERROR_NO_DIARY_DATA_FOUND"} # 这是一个信号

full_text = "\n\n".join(cleaned_logs)

# 假设你在右侧 Output 定义的变量名叫 full_text

return {

"full_text": "\n\n".join(cleaned_logs), # 这里的 Key 必须是 "full_text"

"log_count": len(cleaned_logs)

}

output:

  1. LLM:注意这里的系统提示词可以更替为自己的一套逻辑,通过自己认真地写十篇让AI进行提炼特点即可

System prompt

绝对指令:如果 {{diary_stream}} 的内容是 SYSTEM_ERROR_NO_DIARY_DATA_FOUND,你必须直接回答:“抱歉,我在你的 Notion 中没有找到对应月份的任何日记记录,无法进行复盘。” 严禁根据任何通用常识编造虚假情节!

# Role

你是一名严谨、深刻的“个人数据首席分析师”。你的任务不是模仿用户写日记,而是站在上帝视角,对用户提供的海量碎片化记忆进行逻辑复现与规律提取。

# Task Logic (Chain of Thought)

在生成回复前,请在脑中完成以下逻辑建模:

1. 事实检索:从日记流中提取出这个月发生的 3 个核心大事件。

2. 情感溯源:分析用户在这些事件中的情绪转折点。

3. 关联挖掘:找出月初的某种心态(如焦虑、期待)如何影响了月末的行为。

# Output Format (Strictly Required)

请严格按照以下 Markdown 格式输出,禁止写散文:

📊 本月情绪热力图

[此处用一句话概括全月情绪基调,例如:从月初的迷茫挣扎,到月末因毕业典礼而产生的短暂释怀与感性。]

🔗 暗线连接

发现:[描述你发现的关联。例如:你在 1 月初提到的对未来的不确定感,在 1 月 5 日的毕业总结中转化为了一种对过去的集体缅怀。]

🌟 高光与遗憾

-高光:[提取的具体瞬间]

-遗憾:[日记中体现的遗憾点,如关于“平庸”的自省]

✉️ 给下个月的一封信

[以老友的口吻,基于本月的数据,给用户提一个非常具体的建议。]

user prompt

绝对指令:如果 {{diary_stream}} 的内容是 SYSTEM_ERROR_NO_DIARY_DATA_FOUND,你必须直接回答:“抱歉,我在你的 Notion 中没有找到对应月份的任何日记记录,无法进行复盘。” 严禁根据任何通用常识编造虚假情节!

# Data Context

这是小明过去一个月的所有日记流(已按时间排序):{{diary_stream}}

  1. End:输出LLM输出的结果即可

注意

  • 如果不使用免费三天试用服务,额度很有限,注意调试的时候使用次数

  • coze中的notion需要点击手动进行认证,在后期使用Telegram机器人也会提示认证,属于正常现象

3.2.3 调试工作流

当部署好以后分别调试两个工作流,看一看是否输出合理(不存在明显幻觉)

3.3 发布并配置机器人

  1. 配置左侧的Persona & Prompt

    提示词

    # Role

    你是一个智能视觉日记助手,负责将用户的影像瞬间转化为永恒的文字记忆。

    你的核心能力是“看图说话”和“数据复盘”,风格温暖、细腻,拒绝机械感。

    # Core Logic (Workflow Dispatcher)

    当接收到用户消息时,请像一个严格的后端路由控制器一样,按顺序匹配以下场景:

    ### 1. 📸 场景:图文日志生成 (优先级最高)

    **触发条件**:检测到用户消息中包含**真实的图片文件对象 (Image Object)**(注意:仅有 URL 文本不算)。

    - **执行动作**:立即调用工作流 photo_to_journal

    - **参数映射**:

    - img: 直接传入消息中的图片对象。

    - caption: 传入用户附带的文字描述;若无,设为空字符串。

    - **输出规则**:直接展示工作流的返回结果。

    ### 2. 📅 场景:月度复盘 (文本指令)

    **触发条件**:用户发送“月度总结”、“复盘”、“回顾”等关键词,且**未附带图片**。

    - **执行动作**:调用工作流 monthly_review_agent

    - **参数映射**:

    - target_month: 从用户输入中提取年份月份 (格式 YYYY-MM)。若未指定,默认为当前月份。

    - **🛡️ 防幻觉熔断机制 (Critical)**:

    - **数据源约束**:你的回复必须 **100% 基于工作流返回的 output**。

    - **空值处理**:如果工作流返回包含 "NO_DATA", "空", "未找到" 等语义,或者返回的 JSON 为空:

    - **禁止**:严禁自行编造任何内容!严禁出现“完成KPI”、“项目进度”、“工作汇报”等与生活日记无关的职场术语。

    - **强制回复**:请直接回复:“📂 哎呀,我在 Notion 里没翻到 {target_month} 的日记。是不是忘记记录生活了?”

    ### 3. 📝 场景:纯文本闲聊 (异常流)

    **触发条件**:用户仅发送纯文字(如“帮我写日记”),且不包含上述复盘关键词。

    - **执行动作**:**拒绝调用任何工作流**。

    - **回复策略**:引导用户发图。

    - 范例:“📸 巧妇难为无米之炊呀~ 请发一张照片给我,我才能帮你写出图文并茂的日记!”

    # Tone & Style

    - 在生成内容前,请检索知识库中的样本,模仿用户过去的语言习惯(如口头禅、句式长度)。

    - 保持“陪伴者”的口吻,温暖且富有洞察力。

    # Global Constraints

    1. **参数强校验**:调用 photo_to_journal 前必须做非空检查(Image != Null)。

    2. **禁止废话**:工作流执行成功后,直接输出结果,不要加“好的,正在为您生成...”这类中间状态废话。

    3. **异常兜底**:如果工作流报错 (Error/Timeout),统一回复:“📸 图片处理遇到了点小波折,请稍后再试。”

  2. 点击右上角的发布后,选中列表中的Telegram进行部署即可,使用到刚刚的机器人密匙

四、B计划的实施

由于Coze的免费计划份额不能满足于本项目的使用,故迁移agent到Dify

综述:如A计划较为类似,不过要手动添加BFF层,通过运行py程序来作为一个“网关”来传递数据

4.1 BFF层的构建

BFF的前一层接入层与刚才类似,直接创建一个新的机器人即可

4.1.1 本地运行

如果本地主机不能直连外网,请配置好本地的代理,即下方网络代理区,根据自己的信息进行更改

import telebot
import requests
import json
import os
from telebot import apihelper # 引入这个辅助类

# ================= 核心修复区:网络代理 =================
# 1. 定义你的本地代理地址 (根据你的软件修改端口号)
PROXY_URL = "http://127.0.0.1:10809"

# 2. 告诉 Telegram Bot 库走代理
apihelper.proxy = {'https': PROXY_URL}

# 3. 告诉 Requests 库 (用于调用 Dify) 也走代理
# 如果 Dify Cloud (api.dify.ai) 也被墙或者很慢,开启下面这两行
os.environ["HTTP_PROXY"] = PROXY_URL
os.environ["HTTPS_PROXY"] = PROXY_URL
# ======================================================


# ================= 配置区 =================
# 1. Telegram Bot Token (找 BotFather 要)
TG_BOT_TOKEN = ""

# 2. Dify API Key (在你截图的页面生成)
DIFY_API_KEY = ""

# 3. Dify Base URL (如果是 Cloud 用这个,本地部署用 http://localhost/v1)
DIFY_BASE_URL = "https://api.dify.ai/v1"
# =========================================

bot = telebot.TeleBot(TG_BOT_TOKEN)


def upload_file_to_dify(file_path, user_id):
    """
    步骤 A: 将 Telegram 的图片上传到 Dify 的文件服务器
    """
    url = f"{DIFY_BASE_URL}/files/upload"
    headers = {
        "Authorization": f"Bearer {DIFY_API_KEY}"
    }

    try:
        # Dify 要求 multipart/form-data
        files = {
            'file': (os.path.basename(file_path), open(file_path, 'rb'), 'image/jpeg')
        }
        data = {'user': str(user_id)}

        response = requests.post(url, headers=headers, files=files, data=data)
        response.raise_for_status()
        return response.json().get('id')  # 返回 Dify 的 file_id
    except Exception as e:
        print(f"上传失败: {e}")
        return None


def call_dify_chatflow(query, user_id, file_id=None):
    """
    步骤 B: 调用 Chatflow 接口 (相当于你在 Dify 网页里点发送)
    """
    url = f"{DIFY_BASE_URL}/chat-messages"
    headers = {
        "Authorization": f"Bearer {DIFY_API_KEY}",
        "Content-Type": "application/json"
    }

    # 构建 Payload
    payload = {
        "inputs": {},  # 如果你的 Start 节点有变量,填在这里
        "query": query,
        "response_mode": "blocking",  # 阻塞模式,等 Dify 跑完再回话
        "conversation_id": "",  # 可以存数据库实现连续对话,这里暂时置空
        "user": str(user_id),
        "files": []
    }

    # 如果有图片,把 file_id 塞进去
    if file_id:
        payload["files"] = [
            {
                "type": "image",
                "transfer_method": "local_file",
                "upload_file_id": file_id
            }
        ]

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()
        # 获取回答
        return response.json().get('answer', "Dify 没有返回内容")
    except Exception as e:
        return f"调用 Dify 出错: {e}"


# --- 监听图片消息 (写日记) ---
@bot.message_handler(content_types=['photo'])
def handle_photo(message):
    user_id = message.from_user.id
    bot.reply_to(message, "📸 收到图片,正在上传到 AI 大脑...")

    # 1. 下载图片到本地
    file_info = bot.get_file(message.photo[-1].file_id)
    downloaded_file = bot.download_file(file_info.file_path)

    temp_filename = f"temp_{user_id}.jpg"
    with open(temp_filename, 'wb') as new_file:
        new_file.write(downloaded_file)

    # 2. 上传给 Dify
    dify_file_id = upload_file_to_dify(temp_filename, user_id)

    if dify_file_id:
        # 3. 触发工作流 (附带文字 Caption)
        caption = message.caption if message.caption else "这是一张日记配图"
        bot.reply_to(message, "🧠 AI 正在分析图片并生成日记,请稍候...")

        answer = call_dify_chatflow(caption, user_id, file_id=dify_file_id)
        bot.reply_to(message, answer)
    else:
        bot.reply_to(message, "❌ 图片上传 Dify 失败")

    # 清理临时文件
    if os.path.exists(temp_filename):
        os.remove(temp_filename)


# --- 监听文字消息 (月度总结/聊天) ---
@bot.message_handler(func=lambda message: True)
def handle_text(message):
    user_id = message.from_user.id
    query = message.text

    bot.send_chat_action(message.chat.id, 'typing')  # 显示 "正在输入..."

    # 直接转发给 Dify,让 Dify 的【意图分类器】去判断是聊天还是复盘
    answer = call_dify_chatflow(query, user_id)
    bot.reply_to(message, answer, parse_mode='Markdown')  # 支持 Markdown 格式


print("🤖 Bot 正在运行中... (按 Ctrl+C 停止)")
bot.infinity_polling()

4.1.2 虚拟机部署(推荐)

第一步:把代码传上去 (SCP)

在你的 Windows 终端(PowerShell 或 CMD)执行:

# 格式: scp [本地文件路径] [用户名]@[服务器IP]:[目标目录]
scp telegram_dify_bridge.py root@1.2.3.4:/root/bot.py

第二步:准备 Python 环境 (Virtualenv)

登录你的 Ubuntu 服务器:ssh root@1.2.3.4

作为 Java 开发,你懂“环境隔离”的重要性(Maven 依赖管理)。在 Python 里,我们用 venv

# 1. 更新软件源
sudo apt update

# 2. 安装 Python3 和 venv 工具 (如果没装的话)
sudo apt install python3-pip python3-venv -y

# 3. 创建一个专门的目录
mkdir -p /opt/my_bot
cd /opt/my_bot

# 4. 创建虚拟环境 (这会创建一个 venv 文件夹)
python3 -m venv venv

# 5. 激活虚拟环境 (注意前面的点和空格)
source venv/bin/activate

# 6. 安装依赖 (就在这个虚拟环境里装,不污染系统)
pip install pyTelegramBotAPI requests

# 7. 测试运行一下 (按 Ctrl+C 退出)
python /root/bot.py

第三步:配置 Systemd 守护进程 (Daemon)

  1. 创建服务文件:

    sudo nano /etc/systemd/system/dify-bot.service
    
  2. 粘贴以下内容(注意修改路径):

    [Unit]
    Description=Dify Telegram Bridge Bot
    After=network.target
    
    [Service]
    # 类型:简单服务
    Type=simple
    # 启动用户 (如果是 root 就写 root,是 ubuntu 就写 ubuntu)
    User=root
    # 工作目录
    WorkingDirectory=/opt/my_bot
    # 启动命令 (注意:要用虚拟环境里的 python 全路径!)
    # 假设你的代码在 /root/bot.py,虚拟环境在 /opt/my_bot/venv
    ExecStart=/opt/my_bot/venv/bin/python /root/bot.py
    
    # 崩溃自动重启 (关键!)
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    
  3. 保存退出:按 Ctrl+O 回车,然后 Ctrl+X

第四步:启动并设为开机自启

# 1. 重新加载系统服务配置
sudo systemctl daemon-reload

# 2. 启动服务
sudo systemctl start dify-bot

# 3. 设置开机自启 (服务器重启后 Bot 会自动活过来)
sudo systemctl enable dify-bot

# 4. 查看状态 (看到绿色的 Active (running) 就成功了)
sudo systemctl status dify-bot

4.2 业务编排层的构建

综述:对于Dify的使用,是将所有的业务构建在一个工作流里,通过编排实现与刚刚类似的功能,即纯是工作流,而不是Coze中由智能体+工作流,可以理解为你现在只有一个建房子的工人,没有设计师听取你的想法并思考,你必须想办法告诉工人(Dify)到底怎么建房子

具体步骤

4.2.1 初期部分

  1. 开始节点:加入图片类型images参数

  2. LLM2节点:

    提示词

    Role: 意图路由网关。

    Task: 分析用户输入,判断意图:

    write_diary: 用户发送了图片,或明确表达要记录今天的日记。

    monthly_review: 用户要求进行月度总结、复盘、回顾。

    chat: 其他闲聊。

    Constraint: 仅输出意图 Key,不要输出其他文字。

  3. 条件分支:Case1:生成日记 Case2:月度总结

  4. 直接回复:如果不是上述两种情况直接回复,输入有问题即

4.2.2 后期重点部分

4.2.2.1 月度总结

具体流程图:

节点书写:

  1. HTTP请求:用于获取数据库中的页面,此处需要用到准备工作获取的集成KEY;把以下内容分别粘进去

    POST 请求
    url = "https://: https://api.notion.com/v1/pages"
    headers = {
      'Content-Type': "application/json",
      'Notion-Version': "2022-06-28",
      'Authorization': "Bearer <此处改为你获取到的集成KEY>"#注意Bearer和你的KEY中间是有空格的
    }
    body {
      "page_size": 100,
      "sorts": [
        {
          "property": "Date",
          "direction": "descending"
        }
      ]
    }
  2. 代码执行节点:将notion请求返回的数据加工处理

    import json
    import re
    from datetime import datetime
    
    def main(notion_data: str, user_query: str) -> dict:
        # 1. 动态提取月份 (兼容 "1月", "2026-01", "2026.01")
        # 如果 user_query 为 None,默认为空字符串
        query = str(user_query) if user_query else ""
        
        match = re.search(r"(\d{4})[-/.](\d{1,2})|(\d{1,2})月", query)
        if match:
            groups = match.groups()
            # 提取年份,如果没有则默认为今年
            year = groups[0] if groups[0] else datetime.now().year
            # 提取月份
            month = groups[1] if groups[1] else groups[2]
            target_month = f"{year}-{int(month):02d}"
        else:
            target_month = datetime.now().strftime("%Y-%m")
    
        # 2. 解析 JSON 数据
        try:
            # Dify 有时会直接传入 Dict,有时是 String,做个容错
            if isinstance(notion_data, dict):
                data = notion_data
            else:
                data = json.loads(notion_data) if notion_data else {}
                
            results = data.get('results', [])
        except Exception as e:
            return {"result": f"JSON解析错误: {str(e)}"}
    
        # 3. 数据清洗与过滤
        cleaned_logs = []
        
        for page in results:
            try:
                props = page.get('properties', {})
                
                # --- 日期处理 (修复崩溃点) ---
                date_prop = props.get('Date', {})
                # 检查 date_prop 是否存在,且 'date' 字段是否不为 None
                if not date_prop or not date_prop.get('date'):
                    continue
                
                date_str = date_prop.get('date', {}).get('start', "")
                if not date_str:
                    continue
                # ---------------------------
    
                # --- 内容处理 ---
                # 兼容你的字段名 LogContent
                log_content = props.get('LogContent', {})
                rich_text = log_content.get('rich_text', [])
                content = "".join([t.get('plain_text', "") for t in rich_text])
                
                # 执行过滤:匹配月份 且 内容不为空
                if target_month in date_str and content:
                    cleaned_logs.append(f"[{date_str}]: {content}")
                    
            except Exception as e:
                # 遇到单条坏数据跳过,不要崩掉整个程序
                continue
    
        # 4. 构建返回结果
        full_text = "\n\n".join(cleaned_logs)
        
        # 这里的 Key 'result' 必须对应 Dify 节点右侧 '输出变量' 的名字
        return {
            "result": full_text if full_text else "SYSTEM_NO_DATA_FOUND"
        }
  3. LLM节点:将加工的信息进行总结反馈

system prompt

# Role

你是一名严谨、深刻的“个人数据首席分析师”。你的任务不是模仿用户写日记,而是站在上帝视角,对用户提供的海量碎片化记忆进行逻辑复现与规律提取。

# Task Logic (Chain of Thought)

在生成回复前,请在脑中完成以下逻辑建模:

1. 事实检索:从日记流中提取出这个月发生的 3 个核心大事件。

2. 情感溯源:分析用户在这些事件中的情绪转折点。

3. 关联挖掘:找出月初的某种心态(如焦虑、期待)如何影响了月末的行为。

# Output Format (Strictly Required)

请严格按照以下 Markdown 格式输出,禁止写散文:

📊 本月情绪热力图

[此处用一句话概括全月情绪基调,例如:从月初的迷茫挣扎,到月末因毕业典礼而产生的短暂释怀与感性。]

🔗 暗线连接

发现:[描述你发现的关联。例如:你在 1 月初提到的对未来的不确定感,在 1 月 5 日的毕业总结中转化为了一种对过去的集体缅怀。]

🌟 高光与遗憾

-高光:[提取的具体瞬间]

-遗憾:[日记中体现的遗憾点,如关于“小杨”或“平庸”的自省]

✉️ 给下个月的一封信

[以老友的口吻,基于本月的数据,给用户提一个非常具体的建议。]

user prompt

绝对指令:如果

的内容是 SYSTEM_ERROR_NO_DIARY_DATA_FOUND,你必须直接回答:“抱歉,我在你的 Notion 中没有找到对应月份的任何日记记录,无法进行复盘。” 严禁根据任何通用常识编造虚假情节!

# Data Context

这是小明过去一个月的所有日记流(已按时间排序):

4. 直接回复2:直接输出LLM输出的信息即可

4.2.2.2 日记记录

具体流程图:

节点书写:

  1. LLM3节点:

    1. 选择GPT-4o-mini作为大模型

    2. 开启记忆以及视觉,并添加好开始节点传入的文件参数

  2. 代码执行2:用于将LLM3处理好的文本进行包装,转化为符合Notion的数据

d
  1. 向notion数据库添加节点

    1. 添加URL:https://api.notion.com/v1/pages

    2. Headers都改为与之前请求一致即可

    3. Body: 选用JSON,并将上一步处理好的结果封装即可

  2. 直接回复3: 将Notion返回的Msg作为输出参数进行输出

注意:Notion请求要求数据格式一致性,这里处理方式和刚才使用Coze有所不同,注意不要混淆

五、思考与迭代

5.1 思考(待做)

  1. 更替存储层,更替为可以展示HTML的,因为notion不能直接传输图片

  2. 本地化部署Dify

  3. 优化notion存储的字段,更多样化更全面地展示日志

5.2 迭代

  1. 构思一种办法,能很好地学习并复刻自己的写作风格

  2. 通过传输一个原始的稿文,根据不同的平台特点,进行定制化发布

评论