Skip to main content

Dify对接钉钉开发者后台添加的机器人

你说的这个场景和前面假设的**“钉钉群自定义机器人”**不是一类。前面给你的版本偏“概念代码”,这里改成工程化 services 分层结构。

你现在使用的是:

钉钉开发者后台创建的机器人(企业内部机器人 / 钉钉应用机器人)

这种机器人没有固定 Webhook 地址,消息链路也不同。

正确架构应该是:

钉钉用户
   |
   | @机器人 / 私聊 / 群聊消息
   ↓
钉钉开放平台机器人服务
   |
   | Stream模式(长连接)
   ↓
你的 FastAPI 服务
   |
   | Dify Streaming API
   ↓
Dify
   |
   | SSE流
   ↓
FastAPI
   |
   | Stream回复
   ↓
钉钉机器人

也就是说:目标:

  • ❌钉钉 不使用开发者后台机器人

Stream 模式

Python oapi.dingtalk.com/robot/senddingtalk-stream

❌FastAPI 不使用 Webhook 回调发送项目结构

✅Dify 使用钉钉Chat Stream模式API Streaming

✅Service 你的服务主动连接钉钉长连接网关层隔离

✅后续可扩展 收到事件后调用 Dify

✅ 调用钉钉 SDK 回复Redis、知识库、权限


dingtalk-stream

一、需要准备的信息

SDK

在钉钉开发者后台创建机器人应用:本身就是针对钉钉 Stream 模式的机器人收消息、事件回调设计的。当前 Python 包版本可直接通过 pip 安装。(PyPI)

进入:

钉钉开放平台

获取:

Client ID
Client Secret
Robot Code

通常类似:

client_id:
dingxxxxxxxx

client_secret:
xxxxxxxxxxxx

robotCode:
dingxxxxxxxx

二、安装钉钉 Stream SDK

官方推荐 Python:

pip install dingtalk-stream
pip install fastapi
pip install requests
pip install python-dotenv

三、

一、项目结构

dify-dingtalk-stream/bot/

├── app/
│
│   ├── main.py
├──│   dify.py│
│   ├── dingtalk_bot.config/
│   │   └── settings.py
│   │
│   ├── config.services/
│   │   │
│   │   ├── dify_service.py
│   │   │
│   │   ├── dingtalk_service.py
│   │   │
│   │   └── chat_service.py
│   │
│   ├── handlers/
│   │   └── robot_handler.py
│   │
│   └── utils/
│       └── logger.py
│
├── requirements.txt
└── .env

1. 配置二、依赖

.envrequirements.txt

fastapi
uvicorn
python-dotenv
requests
dingtalk-stream

安装:

pip install -r requirements.txt

三、配置

.env

# 钉钉Stream
DING_CLIENT_ID=dingxxxxxxxxdingxxxx
DING_CLIENT_SECRET=xxxxxxxxxxxx


# Dify
DIFY_API_KEY=app-xxxxxxxxxxxx
DIFY_URL=DIFY_API_URL=https://api.dify.ai/v1/chat-messages


# 服务
APP_NAME=dify-dingtalk-bot

2.四、配置服务

app/config/settings.py

import os
from dotenv import load_dotenv


load_dotenv()


class Settings:


    DING_CLIENT_ID = os.getenv(
        "DING_CLIENT_ID"
    )


    DING_CLIENT_SECRET = os.getenv(
        "DING_CLIENT_SECRET"
    )


    DIFY_API_KEY=os.getenv(
        "DIFY_API_KEY"
    )


    DIFY_API_URL=os.getenv(
        "DIFY_API_URL"
    )


settings=Settings()

五、Dify Streaming调用Service

dify.负责:

    调用 Dify

    消费 SSE

    返回完整答案

    app/services/dify_service.py

    import requests
    import json
    
    from configapp.config.settings import settings
    
    
    
    class DifyService:
    
    
        def __init__(self):
    
            self.url = (
                DIFY_API_KEY,settings.DIFY_API_URL
            DIFY_URL)
    
            self.key = (
                settings.DIFY_API_KEY
            )
    
    
    
        def ask_dify(question,user)chat(
            self,
            question:str,
            user:str
        ):
    
    
            headers={
    
                "Authorization":
                f"Bearer {DIFY_API_KEY}self.key}",
    
                "Content-Type":
                "application/json"
            }
    
    
            body=payload={
    
            "query":
            question,
    
                "inputs":{},
    
                "query":
                question,
    
    
                "response_mode":
                "streaming",
    
    
                "user":
                user
    
            }
    
    
    
            r=response=requests.post(
    
                DIFY_URL,self.url,
    
                headers=headers,
    
                json=body,payload,
    
                stream=TrueTrue,
    
                timeout=120
    
            )
    
    
            answer=""
    
    
            for line in r.response.iter_lines():
    
    
                if not line:
                    continue
    
    
    
                line=line.decode(
                    "utf-8"
                )
    
    
    
                if not line.startswith(
                    "data:"
                ):
                    continue
    
    
    
                data=json.loads(
                    line[5:]
                )
    
    
                event=data.get(
                    "event"
                )
    
    
                if data.get("event")=event=="message":
    
                    answer += data.get(
                        "answer",
                        ""
                    )
    
    
                if event=="message_end":
    
                    break
    
    
    
            return answer
    

    3.六、聊天业务 钉钉 Stream机器人Service

    dingtalk_bot.py以后加:

      核心代码:用户权限

      Redis

      会话ID

      日志

      都放这里。

      app/services/chat_service.py

      from app.services.dify_service import DifyService
      
      
      
      class ChatService:
      
      
          def __init__(self):
      
              self.dify=DifyService()
      
      
      
          def ask(
              self,
              message,
              user
          ):
      
      
              return self.dify.chat(
      
                  question=message,
      
                  user=user
      
              )
      

      七、钉钉发送 Service

      app/services/dingtalk_service.py

      import dingtalk_stream
      
      
      
      fromclass difyDingTalkService:
      
      
      
          def reply(
              self,
              handler,
              text,
              message
          ):
      
      
              handler.reply_text(
      
                  text,
      
                  message
      
              )
      

      八、机器人 Handler

      这是核心。

      app/handlers/robot_handler.py

      import logging
      
      import ask_difydingtalk_stream
      
      from dingtalk_stream import AckMessage
      
      
      from app.services.chat_service import ChatService
      from app.services.dingtalk_service import DingTalkService
      
      
      
      class RobotHandler(
          dingtalk_stream.ChatbotHandler
      ):
      
      
          def __init__(self):
      
              super().__init__()
      
              self.chat_service=(
                  ChatService()
              )
      
      
              self.ding_service=(
                  DingTalkService()
              )
      
      
      
          async def process(
              self,
              messagecallback:
              dingtalk_stream.CallbackMessage
          ):
      
      
              # 用户消息
      
              textincoming = (
                  message.contentdingtalk_stream
                  .get("text","")ChatbotMessage
                  ).from_dict(
                      sender = (
                  message.sender_staff_id
              )
      
      
              # 调用Dify
      
              answer = ask_dify(
                  text,
                  sender
              )
      
      
              # 回复钉钉
      
              self.reply_text(
                  answer,
                  message
              )
      
      
      
      def start_bot():
      
          credential = (
              dingtalk_stream.Credential(
                  client_id=
                  "你的client_id",
      
                  client_secret=
                  "你的secret"callback.data
                  )
              )
      
      
              clientquestion=(
      
                  =incoming
                  .text
                  .content
                  .strip()
      
              )
      
      
              user=(
      
                  incoming
                  .sender_staff_id
      
              )
      
      
              logging.info(
                  f"user={user}, msg={question}"
              )
      
      
      
              try:
      
      
                  answer=(
      
                      self.chat_service.ask(
      
                          question,
      
                          user
      
                      )
      
                  )
      
      
                  self.ding_service.reply(
      
                      self,
      
                      answer,
      
                      incoming
      
                  )
      
      
              except Exception as e:
      
      
                  self.reply_text(
      
                      "服务异常:"+str(e),
      
                      incoming
      
                  )
      
      
      
              return (
                  dingtalk_stream.AckMessage.STATUS_OK,
                  "OK"
              )
      

      九、启动 Stream Client

      app/main.py

      import logging
      
      import dingtalk_stream
      
      
      from app.config.settings import settings
      
      from app.handlers.robot_handler import RobotHandler
      
      
      
      def start():
      
      
          logging.basicConfig(
      
              level=logging.INFO
      
          )
      
      
      
          credential=(
      
              dingtalk_stream
              .Credential(
      
                  settings.DING_CLIENT_ID,
      
                  settings.DING_CLIENT_SECRET
      
              )
      
          )
      
      
      
          client=(
      
              dingtalk_stream
              .DingTalkStreamClient(
      
                  credential
      
              )
      
          )
      
      
      
          handler=RobotHandler()
      
      
          client.register_callback_handler(
      
              handlerdingtalk_stream
              .chatbot
              .ChatbotMessage
              .TOPIC,
      
      
              RobotHandler()
      
          )
      
      
      
          client.start_forever()
      
      
      
      if __name__=="__main__":
      
          start()
      

      4. 启动入口十、启动

      main.py

      frompython dingtalk_bot-m import start_bot
      
      
      if __name__=="__main__":
      
          start_bot()app.main
      

      运行:正常日志:

      pythonINFO main.endpoint is wss://xxxx
      INFO websocket connected
      

      钉钉开发者后台机器人上线后:

      群里:

      @机器人 公司的年假政策是什么?
      

      流程:

      ChatbotMessage
             |
             ↓
      RobotHandler
             |
             ↓
      ChatService
             |
             ↓
      DifyService
             |
             ↓
      Dify SSE
             |
             ↓
      reply_text()
             |
             ↓
      钉钉群
      

      十一、下一步建议改造(生产版)

      你这个架构建议继续升级:

      1. 增加 Redis 会话

      现在:

      用户
       |
      Dify
       |
      新会话
      

      升级:

      staff_id
         |
      Redis
         |
      conversation_id
         |
      Dify上下文
      

      2. 增加真正 Stream 卡片回复

      当前:

      Dify:
      chunk1
      chunk2
      chunk3
      
      
      钉钉:
      一次回复
      

      升级:

      Dify chunk
          |
          |
      钉钉AI卡片stream
          |
          |
      逐字刷新
      

      可以做到类似 ChatGPT 的效果。


      3. 推荐生产目录

      大型项目:

      services
      
      ├── dingtalk
      │    ├── stream_client.py
      │    ├── message.py
      │    └── card.py
      │
      ├── dify
      │    ├── client.py
      │    ├── stream.py
      │    └── workflow.py
      │
      ├── memory
      │    └── redis.py
      │
      └── security
           └── permission.py
      

      启动后:

      这个结构可以直接扩展成企业级
      connected to DingTalk Stream server
      

      表示机器人在线。


      五、Stream模式和Webhook模式区别

      项目 群自定义机器人 开发者机器人 创建位置 群设置 开放平台 Webhook 有 无 长连接 无 有 Stream模式 ❌ ✅ SDK 不需要 需要 企业应用 弱 强 权限控制 少 完整 私聊 ❌ ✅ 群@机器人 ✅ ✅

      六、如果要真正实现“流式回复”

      你的场景反而更适合。

      可以做:

      Dify SSE:
      
      chunk1:
      正在
      
      chunk2:
      查询
      
      chunk3:
      知识库
      
      
      ↓
      
      钉钉 Stream:AI 机器人:
      正在查询知识库...
      
      机器人:
      正在生成答案...
      
      机器人:
      最终结果...
      

      因为开发者机器人支持主动发送消息,不受 Webhook 限制。


      七、生产建议架构

      建议改成:

                       钉钉Stream
                           |
                           |
                    FastAPI助手 + SDK
                           |
              ┌────────────┴────────────┐
              |
           Redis
         会话管理
              |
           Dify Workflow
              |
         企业知识库/RAG 

      你前面的需求(Dify + 钉钉开发者机器人 + Stream模式)应该采用这一套,而不是 Webhook 机器人方案。

      如果你需要,我可以继续给你补一版真正可运行的钉钉 Stream SDK 最新版代码(包含 Card Stream 流式卡片回复,实现类似 ChatGPT 打字效果)知识库平台。