# Dingtalk-bot-fix
**Repository Path**: yushui688/Dingtalk-bot-fix
## Basic Information
- **Project Name**: Dingtalk-bot-fix
- **Description**: No description available
- **Primary Language**: Python
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-10-11
- **Last Updated**: 2026-10-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# DingTalk Repair Multi-Agent
一个基于 **LangGraph 多 Agent 编排** 的钉钉群报修系统。
系统采用 **Supervisor + Ticket Agents** 模式:群级 Dispatcher Graph 是 Supervisor,负责理解消息上下文并找到目标工单;每张工单都是一个独立的 Ticket Agent,持续负责自己的自助排障、工程师跟进、SLA 和完工确认。
同一个群可以同时运行多张工单。消息先在同一群内并发完成语义识别,再由群级闸门按 `(sent_at, message_id)` 顺序提交路由;不同工单随后可并行运行 Ticket Agent,同一工单严格有序。所有 Ticket Agent 复用同一份 LangGraph 定义,但分别使用自己的 `thread_id` 和 checkpoint,因此上下文、阶段和处理进度不会串单。
## LangGraph 多 Agent 编排图
```mermaid
flowchart TB
M[钉钉群消息] --> W[InboxWorker
不同群协程并行]
W --> GA[群 A:消息识别并行]
W --> GB[群 B:消息识别并行]
GA --> QA[群 A:OrderedCommitGate
按 sent_at + message_id 有序提交]
GB --> QB[群 B:OrderedCommitGate
按 sent_at + message_id 有序提交]
subgraph LG[LangGraph 多 Agent 编排层]
QA --> D[Supervisor
Dispatcher Graph]
QB --> D
D --> R{Conditional Edge
消息属于哪张工单?}
R -->|工单 12| A1[Ticket Agent #12
排障 · 跟进 · 确认]
R -->|工单 19| A2[Ticket Agent #19
排障 · 跟进 · 确认]
R -->|其他工单| AN[Ticket Agent #N
排障 · 跟进 · 确认]
A1 <--> C1[(Checkpoint
ticket:12)]
A2 <--> C2[(Checkpoint
ticket:19)]
AN <--> CN[(Checkpoint
ticket:N)]
end
A1 --> DB[(tickets.db
业务真相源)]
A2 --> DB
AN --> DB
S[SchedulerWorker
SLA · 订单 · 提醒] --> DB
classDef supervisor fill:#6c5ce7,color:#fff,stroke:#4b3fc1
classDef agent fill:#0984e3,color:#fff,stroke:#0769b2
classDef checkpoint fill:#e8f4ff,color:#16425b,stroke:#74b9ff
class D supervisor
class A1,A2,AN agent
class C1,C2,CN checkpoint
```
图中的 `Ticket Agent #12`、`#19` 和 `#N` 是同时存在的逻辑 Agent。消息识别可并行,但路由提交不乱序;提交完成后,不同工单可并行执行,同一工单由 `GraphRuntime` 的工单级锁串行恢复状态。它们没有各自启动进程,而是由 LangGraph 按 `thread_id` 恢复对应状态。
## Agent Loop:工单 Agent 的循环
上面那张图回答的是“消息去哪张工单”,这一张回答的是“到了工单之后 Agent 怎么运转”。Ticket Agent 不是跑一次就结束的函数,而是一个被消息反复唤醒的循环:每轮只处理一条消息,处理完把自己的位置存进 checkpoint 就休眠,等下一条消息到来再转一圈。
```mermaid
flowchart TB
WAKE(["① 被消息唤醒
按 thread_id = ticket:N 恢复 checkpoint"])
LOAD["② load_ticket_state
重读 tickets.db,校正 agent_stage"]
DETECT["③ detect_ticket_event
这条消息是什么事件"]
ROUTE{"④ route_event"}
TROUBLE["⑤ build_retrieval_query
→ retrieve_knowledge
→ generate_guidance"]
NOTIFY["⑤ persist_and_notify_guidance
落库 + 群内发出排障建议
stage = WAITING_MANAGER"]
HAND["⑤ handoff_engineer
通知已转工程师
stage = ENGINEER_TRACKING"]
SYNC["⑤ sync_lifecycle
按工单业务状态同步 stage"]
SLEEP(["⑥ 本轮 END
checkpoint 落盘,Agent 休眠"])
WAKE --> LOAD --> DETECT --> ROUTE
ROUTE -->|"NEW_ISSUE / UNSOLVED"| TROUBLE
TROUBLE --> NOTIFY
ROUTE -->|"HANDOFF"| HAND
ROUTE -->|"RESOLVED / ENGINEER_* / PASSIVE / CLOSED"| SYNC
NOTIFY --> SLEEP
HAND --> SLEEP
SYNC --> SLEEP
SLEEP -.->|"下一条属于本工单的消息"| WAKE
classDef step fill:#0984e3,color:#fff,stroke:#0769b2
classDef idle fill:#e8f4ff,color:#16425b,stroke:#74b9ff
class LOAD,DETECT,ROUTE,TROUBLE,NOTIFY,HAND,SYNC step
class WAKE,SLEEP idle
```
循环的两端各自承担一半连续性:
- **唤醒端(②)**:`load_ticket_state` 每次被唤醒都重读 `tickets.db`,用真实工单状态校正 `agent_stage`。checkpoint 只回答“上次停在哪一步”,业务事实永远以数据库为准。
- **休眠端(⑥)**:三个分支最终都收敛到本轮 END,checkpoint 落盘;下一条属于该工单的消息由 Dispatcher 送进来,循环从①重新开始。
- **失败口**:工单不存在时 `load_ticket_state` 直接结束本轮,不会进入后续节点。
跨多轮看,阶段也不是单向推进的:店长说“还是不行”(`UNSOLVED`)会把 Agent 从 `WAITING_MANAGER` 拉回 `SELF_SERVICE` 再生成一轮建议;店长说“没修好”(`MANAGER_REJECTED`)会把 Agent 从 `WAITING_CONFIRM` 拉回 `ENGINEER_TRACKING`。进入 `CLOSED` 后循环不再产生动作,只由 `sync_lifecycle` 收尾。
## 主要能力
- **自然语言报修**:从群消息提取主题、位置、故障描述、时效等字段并创建工单。
- **单群多工单**:通过完整编号、短编号、消息引用、用户上下文和语义匹配确定消息归属。
- **每单独立 Agent**:所有工单复用同一份 Ticket Agent Graph,通过 `ticket:{ticket_id}` 隔离上下文。
- **自助排障循环**:新建工单后由 LLM 生成检查建议,并结合关键词和 LLM 判断店长反馈。
- **工程师跟进**:店长提出“联系工程师”后,Agent 转入工程师跟进阶段,继续记录诊断、维修方案、订单、超时原因和完工信息。
- **完工确认**:工程师报完工后进入待店长确认状态;店长可以确认修好,也可以反馈未修好并退回处理。
- **SLA 调度**:支持响应提醒、升级通知、临近到期、工单超时和确认超时。
- **订单协作**:记录采购订单,读取共享表中的物流状态,到货后持续提醒维修。
- **图片附件**:归档群内图片,并可使用独立视觉模型提取故障信息。
- **AI 表格看板**:把 SQLite 中的工单同步到钉钉 AI 表格,用于看板和统计。
- **三种运行模式**:支持 `SHADOW`、`ASSISTED`、`PRODUCTION`。
## LangGraph 编排
LangGraph 实际承担消息节点流转、目标 Agent 选择和工单级 checkpoint 恢复;具体业务规则仍由原有业务能力层执行。
| LangGraph 组件 | 在项目中的作用 |
|---|---|
| `StateGraph` | 定义 Dispatcher 和 Ticket Agent 的节点、状态与流转关系 |
| Dispatcher Graph | 充当 Supervisor,每条群消息运行一次并选择目标 Ticket Agent |
| Conditional Edge | 根据业务处理结果和工单归属决定继续、结束或调用哪张工单 |
| Ticket Agent Graph | 处理单张工单的排障、转工程师、等待配件和完工确认 |
| `thread_id` | 将同一份 Ticket Agent Graph 实例化为多张互不串线的逻辑 Agent |
| Async SQLite Saver | 持久化每个 Ticket Agent 的阶段、摘要和最近运行结果 |
多 Agent 的关键不是复制多份代码,而是用同一张 Ticket Agent Graph 创建多个独立状态空间:
```text
同一群消息
└─ Dispatcher Graph(Supervisor)
├─ ticket:12 → Ticket Agent #12
├─ ticket:19 → Ticket Agent #19
└─ ticket:27 → Ticket Agent #27
```
Dispatcher 不保存跨消息的长期对话。Ticket Agent 才拥有工单级记忆,每张工单固定使用:
```text
thread_id = ticket:{ticket_id}
```
LangGraph 保存的是“Agent 下一步应该做什么”,例如继续排障、等待店长、跟进工程师或等待确认。工单状态、消息、订单和 SLA 仍由 `tickets.db` 管理,现有语义协议、Router、Validator 和 Executor 继续决定业务结果。
Agent 阶段包括:
| 阶段 | 含义 |
|---|---|
| `TRIAGE` | 进入工单并判断当前事件 |
| `SELF_SERVICE` | 正在生成或执行自助排障建议 |
| `WAITING_MANAGER` | 等待店长反馈检查结果 |
| `ENGINEER_TRACKING` | 已转工程师,持续跟进处理进度 |
| `WAITING_PARTS` | 等待配件、订单或其他外部条件 |
| `WAITING_CONFIRM` | 工程师已报完工,等待店长确认 |
| `CLOSED` | 工单已完成、取消或停止 |
Agent 每次被消息唤醒时都会重新读取业务数据库,以真实工单状态校正 checkpoint。完整消息、工单状态、SLA 和订单仍保存在业务数据库中。
## 一条报修如何处理
1. `event_listener` 通过 `dws` CLI 监听钉钉群消息。
2. `event_normalizer` 把原始事件转换成统一的 `NormalizedMessage`。
3. 消息写入 Inbox,`InboxWorker` 让跨群及群内识别并行,再由群级闸门按消息顺序提交。
4. Dispatcher Graph 恢复附件和上下文,然后调用现有业务管道。
5. 云端模型完成语义识别(全 AI 判断,关键词快路径已永久停用)。
6. Router 根据编号、引用、用户上下文和候选评分定位工单。
7. Validator 检查角色权限、必填字段和工单状态。
8. `TicketCommandExecutor` 在 SQLite 事务中执行建单或状态变更。
9. 消息被交给对应的 Ticket Agent Graph,更新该工单的 Agent 阶段和排障上下文。
10. 通知通过 Outbox 去重后发送到钉钉群。
11. `SchedulerWorker` 独立扫描业务数据库,处理 SLA、订单和看板同步。
模型只负责理解消息和生成建议,不能直接修改数据库,也不能绕过本地协议校验。
## 店长与 Agent 的交互
新故障创建工单后,Agent 会结合工单信息生成一轮排障建议。店长后续反馈由工单 Agent 内部的关键词优先识别,表达不明确时再交给 LLM 分类(这是 Agent 内部的反馈分类,与上节已永久停用的消息级关键词快路径无关):
| 店长表达示例 | Agent 行为 |
|---|---|
| “清理以后还是不行” | 记录新现象,重新生成排障建议 |
| “已经恢复正常了” | 同步工单完成结果或当前生命周期 |
| “联系工程师处理吧” | 转入 `ENGINEER_TRACKING` |
| 普通闲聊或无法判断 | 不修改工单业务状态 |
在单群多工单场景下,用户可以通过工单编号、回复某条已归档消息或先选择工单来明确上下文,避免把反馈写到其他工单。
## 语义识别:全 AI 判断
关键词快路径(`#报修` 等显式命令的本地解析)已于 2026-08-20 停用,并于 2026-09-16
明确为**永久停用**:包括 `#报修` 在内的所有消息统一交由云端模型判断,系统不再保留
本地规则兜底。
这意味着模型接口是硬依赖:模型不可用时,消息会按现有重试策略进入
`RETRY_PENDING`,累计失败后进入 `DEAD_LETTER`,不会有任何消息被本地规则接管。
`semantics/keyword_matcher.py` 仍然保留,但只服务于离线评测(`semantics/evaluator.py`、
`semantics/run_eval.py`)和单元测试,不在运行时决策链路中。
## RAG 状态
项目已经定义统一的 `KnowledgeRetriever` 接口,并在 Ticket Agent Graph 中保留以下节点:
```text
build_retrieval_query
-> retrieve_knowledge
-> generate_guidance
```
当前使用 `NullKnowledgeRetriever`,固定返回空文档。未配置知识库时,LLM 只依据工单信息和店长最新反馈生成通用建议,不会声称引用了知识库内容。
后续接入向量库、企业知识库或维修手册时,只需要实现新的 Retriever,无需修改 Graph 的主体流程。
## 数据与 checkpoint
系统使用两个独立的 SQLite 文件:
| 文件 | 用途 |
|---|---|
| `data/tickets.db` | 工单、消息、状态、SLA、订单、PendingAction 和 Outbox,是业务真相源 |
| `data/langgraph-checkpoints.sqlite` | Ticket Agent 的阶段和精简上下文 |
LangGraph checkpoint 不参与报表统计,也不替代工单数据库。即使 checkpoint 中的阶段与业务状态不一致,下一次运行也会以 `tickets.db` 为准重新校正。
## 工单生命周期
```text
ACTIVE
-> ACTIVE_OVERDUE
-> PENDING_CONFIRM
-> COMPLETED
ACTIVE / ACTIVE_OVERDUE
-> PENDING_NEGOTIATION
-> ACTIVE
ACTIVE / PENDING_CONFIRM / PENDING_NEGOTIATION
-> CANCELLED 或 STOPPED
```
- 工程师报完工后进入 `PENDING_CONFIRM`。
- 店长确认修好后进入 `COMPLETED`。
- 店长反馈未修好后返回处理中。
- `PENDING_NEGOTIATION` 用于时效暂时无法确定的工单。
- 已取消或停止的工单可以按现有协议重开。
## 技术栈
- Python 3.13+
- asyncio
- LangGraph 1.2
- SQLite / WAL
- httpx
- OpenAI-compatible Chat Completions API
- `dws` CLI
- openpyxl
## 快速开始
### 1. 创建环境
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### 2. 准备群配置
```bash
cp data/groups.example.json data/groups.json
```
在 `data/groups.json` 中配置群 ID、门店名称、店长、工程师、工程负责人、区域经理,以及 `openDingtalkId -> userId` 映射。
### 3. 准备环境变量
```bash
cp .env.example .env
```
至少需要配置模型接口:
```dotenv
LLM_ENABLED=true
LLM_API_KEY=your-api-key
LLM_BASE_URL=https://your-provider.example/v1
LLM_MODEL=your-model
LANGGRAPH_ENABLED=true
LANGGRAPH_CHECKPOINT_PATH=data/langgraph-checkpoints.sqlite
RAG_ENABLED=false
```
文本模型使用 OpenAI-compatible Chat Completions 协议。视觉模型可以通过 `VISION_*` 单独配置。
### 4. 启动
直接启动主系统:
```bash
python main.py --mode PRODUCTION
```
也可以使用管理脚本同时管理主系统和管理后台:
```bash
bash manage.sh start
bash manage.sh status
bash manage.sh logs
bash manage.sh stop
```
运行模式:
| 模式 | 行为 |
|---|---|
| `PRODUCTION` | 按协议执行工单动作并发送通知 |
| `ASSISTED` | 模型来源的动作先等待人工确认 |
| `SHADOW` | 只记录语义决策,不修改工单或发送消息 |
可以只监听指定门店群:
```bash
python main.py --mode PRODUCTION --group "门店名称"
```
也可以指定其他群配置文件:
```bash
python main.py --mode PRODUCTION --groups-config /path/to/groups.json
```
## 常用配置
| 配置项 | 默认值 | 说明 |
|---|---:|---|
| `LANGGRAPH_ENABLED` | `true` | 是否启用 LangGraph 编排 |
| `LANGGRAPH_CHECKPOINT_PATH` | `data/langgraph-checkpoints.sqlite` | Agent checkpoint 文件 |
| `RAG_ENABLED` | `false` | RAG 预留开关,当前仍使用空检索器 |
| `LLM_ENABLED` | `true` | 是否启用文本模型 |
| `LLM_TIMEOUT_SECONDS` | `60` | 单次文本模型超时秒数 |
| `VISION_ENABLED` | `false` | 是否启用视觉模型 |
| `IMAGE_ARCHIVE_ENABLED` | `true` | 是否归档图片附件 |
| `RESPONSE_SLA_ENABLED` | `true` | 是否启用响应 SLA 提醒 |
| `AITABLE_SYNC_ENABLED` | `true` | 是否同步钉钉 AI 表格 |
完整配置见 [`config.py`](./config.py) 和 [`.env.example`](./.env.example)。
## 项目结构
```text
.
├── main.py 应用入口与组件组装
├── pipeline.py 业务管道兼容入口
├── db.py SQLite schema、迁移和数据访问
├── notifier.py Outbox 与钉钉通知
├── graphs/
│ ├── runtime.py Graph 和 checkpoint 生命周期
│ ├── dispatcher_graph.py 每条消息的 Dispatcher Graph
│ ├── ticket_agent_graph.py 每张工单的 Ticket Agent Graph
│ ├── troubleshooting.py 关键词 + LLM 反馈识别与建议生成
│ └── retrieval/ RAG 接口和空检索器
├── semantics/ 语义协议、关键词、LLM 分类和校验
├── routing/ 多工单路由、选单上下文和 PendingAction
├── tickets/ 工单命令、仓储和事务执行器
├── workers/ Inbox、Scheduler 和 AI 表格同步
├── images/ 图片归档与视觉分析
├── reconciling/ 订单共享表协作
├── admin/ 管理后台
├── protocols/ 工单语义协议 JSON 与 Schema
├── scripts/ 导出、同步、回放和运维脚本
├── docs/ 业务规则、架构与流程文档
└── data/ 本地数据库和群配置,不提交仓库
```
## 语法检查
项目当前只执行 Python 语法检查:
```bash
python -m compileall -q .
```
## 相关文档
- [LangGraph 运行流程设计](./docs/superpowers/specs/2026-09-15-langgraph-runtime-design.md)
- [LangGraph 运行流程说明](./docs/langgraph-runtime-flow.md)
- [业务流程与工单生命周期](./docs/业务流程与工单生命周期.md)
- [SLA 架构与绩效扩展](./docs/SLA架构设计与绩效考核扩展.md)
- [Docker 部署说明](./README-Docker.md)
- [业务使用须知](./使用须知.txt)
## 当前边界
- RAG 只有接口和空实现,尚未接入真实知识库。
- 钉钉消息监听、通知和 AI 表格同步依赖本机已登录的 `dws` CLI。
- 项目采用单进程 asyncio 和 SQLite,适合当前门店群规模,不面向多实例同时写入。
- 原有业务状态、协议和执行器保持不变,LangGraph 主要负责流程规范化和按工单保存 Agent 上下文。