- BodyStartOffset:内容首个非空行即结构行时正文从头开始,避免收尾裁剪 把标题+首段一起删掉(118 条生产样本中 31 条、最多 526 字) - 语料回归守卫:隐藏前缀不得含结构行、上限 600 字;单测补 suffix 断言 - 卡片诊断新增 body_ok/body_line/body_prefix_runes/banner_pos/first_heading_line (定位流式残留推理末句,只看结构不看文本)
DobotAI 平台
DobotAI 是 WeKnora 的企业外部渠道集成层,负责钉钉协议、身份与会话映射、消息编排、流式卡片、媒体收发、覆盖判定和人工支持通知。它只通过 WeKnora 官方 REST/SSE API 调用知识问答与 Agent Chat:不导入 WeKnora 私有包、不修改 WeKnora 源码,也不访问 WeKnora 数据库。
当前能力
- 通过官方
POST /api/v1/sessions与POST /api/v1/agent-chat/:session_id调用 WeKnora; - 严格解析 SSE 的
answer、references、tool_call、tool_result、error与终止事件;error + done=false被视为可恢复的工具错误,不会提前结束钉钉卡片; - 解析普通引用及智能推理工具结果中的检索证据,供覆盖判定与后端审计使用;
- 按渠道、会话和用户维护长期 WeKnora Session,并保证回调幂等;
- 钉钉 Stream 机器人支持文本、richText 以及多图片输入;图片下载经过钉钉官方接口、大小与文件签名校验后,才以 WeKnora 通用多模态请求发送;
- 钉钉 AI 流式卡片与最终消息回退;通道侧保留原始回答,不修改 WeKnora 持久化消息;
- 默认快速问答,支持
/think <问题>单次推理、/think on持续推理、/think off或/rag关闭推理;快速问答与智能推理共用同一 WeKnora Session; - 用户级推理轮次、空闲到期、提示开关和首次使用指引;
- 成功回答的覆盖判定、持久化 Support Case、FAQ Outbox 与钉钉人工支持群通知;
- 对已覆盖答案点踩时创建
feedback_down人工处理提醒;对已先因未覆盖创建 Case 的同一问题保持“一题一个 Case”幂等; - 可选的钉钉问题表异步写入;写入失败不会改变问答成功状态;
- 可选的回答图片外发:仅将 WeKnora
resource://图片通过鉴权下载、内容哈希转存到私有 OSS 后,以受控 HTTPS 地址提供给钉钉。
架构边界
钉钉 / 内部调用方
↓
DobotAI
├─ 钉钉 Stream、richText、图片、AI 卡片、反馈与通知
├─ 独立 PostgreSQL:渠道身份、消息、会话映射、Turn、覆盖与支持流程
└─ WeKnora 官方 REST / SSE API
↓
WeKnora:检索、Agent、知识与引用
责任边界:
- DobotAI:钉钉协议、媒体收发、会话映射、命令路由、卡片、覆盖判定、人工支持、渠道审计。
- WeKnora:通用 Agent Chat、检索、知识存储、引用与模型调用。
- DobotAI PostgreSQL:只保存渠道与业务编排数据,不保存 WeKnora 知识正文。
- Redis:仅用于队列、重试、幂等租约和短期状态。
目录结构
.
├── cmd/
│ ├── dobotai/ 主程序入口、运行时装配与后台 Worker
│ └── directory-import/ 钉钉通讯录导入命令
├── internal/
│ ├── channels/dingtalk/ 钉钉 Stream、卡片、媒体、反馈与群通知
│ ├── qa/ 问答主流程、WeKnora 调用和检索信号记录
│ ├── weknora/ SSE、工具结果和引用解析
│ ├── coverage/ 覆盖判定、Support Case 和人工提醒
│ ├── reasoningmode/ /think 推理模式
│ ├── settings/ /setting 与首位管理员绑定
│ ├── outboundimage/ resource:// 图片私有 OSS 外发
│ ├── questiontable/ 问题表工作流
│ ├── faqtable/ FAQ 表工作流
│ ├── directory/ 钉钉通讯录查询与导入
│ ├── audit/ 审计查询
│ ├── httpapi/ /healthz、/version、/v1/qa 与审计 API
│ ├── core/ 核心领域类型
│ ├── session/ 会话键和会话标题
│ ├── agentbinding/ 渠道到 WeKnora Agent 的绑定
│ ├── feedback/ 点赞/点踩领域逻辑
│ ├── secretstore/ 本地密钥引用存储
│ ├── dingtalkconfig/ 钉钉凭据校验
│ ├── runtime/ 运行时管理
│ └── store/postgres/ PostgreSQL 持久化实现
├── db/migrations/ SQL 迁移及其契约测试
├── deploy/dobotai-dingtalk/ 容器 Compose、安装/配置/启动/绑定脚本和部署手册
├── docs/ 架构说明与 API 边界文档
├── scripts/ 本地部署与部署资产契约测试
├── Dockerfile DobotAI 容器镜像构建
├── .env.example 无真实凭据的配置示例
└── README.md 项目功能、边界和开发说明
目录职责规则:cmd/只装配进程,internal/承载业务模块,internal/store/postgres/只实现数据库持久化,db/migrations/只放Schema变更,deploy/只放容器运行资产。历史计划、备份、镜像归档和真实环境文件均不进入Git。
部署方式
| 方式 | 适用场景 | 数据库 | 入口 |
|---|---|---|---|
| 源码/原生二进制 | 本地开发、快速调试 | 手工配置阿里云RDS连接 | go run ./cmd/dobotai |
| Docker | 测试、预发布、生产候选 | 既有阿里云RDS;Docker不运行数据库 | sudo bash ./install-docker.sh |
源码部署
go run ./cmd/dobotai
curl --noproxy '*' -fsS http://127.0.0.1:8090/healthz
源码方式不自动安装Docker、不创建目录、不检查RDS表,也不适合作为长期渠道消费者。
Docker + 阿里云RDS部署
克隆仓库后在根目录执行:
sudo bash ./install-docker.sh
sudo dobotai-dingtalk-configure
第一条命令自动安装Docker/Compose(如缺失)、构建应用镜像、准备目录和配置模板;第二条命令安全录入RDS、WeKnora与钉钉配置,然后检查RDS表:空库初始化000001至000021,完整表不写入,异常/半成品表拒绝启动。Docker只运行dobotai-dingtalk,不创建dobotai-postgres、本地数据卷或本地数据库。
完整安装、RDS检查、升级和回滚见 Docker部署手册。
钉钉 Stream
使用官方 dingtalk-stream-sdk-go,不暴露公网回调入口。
- 群聊仅在机器人被
@时处理; - 私聊按用户保持长期会话;群聊默认按群共享会话;
- 回调
sessionWebhook严格限制为https、*.dingtalk.com、443端口且不允许重定向; - 启用 Stream 必须同时设置以下三项;三者均为空时渠道禁用,部分配置会失败关闭:
DINGTALK_CLIENT_ID
DINGTALK_CLIENT_SECRET
DOBOTAI_TENANT_KEY
推理模式命令
/think <问题> 仅当前问题使用智能推理
/think on 开启持续智能推理
/think off 或 /rag 关闭持续智能推理
/setting rounds <1-50> 设置持续推理自动关闭轮数
/setting hint <on|off> 设置剩余轮次提示
/help、/帮助、/h 查看当前模式和帮助
持续推理默认最多10轮,成功回答才消耗轮次;10分钟无交互后逻辑到期,下一次问题自动回到快速问答。状态按 tenant + channel + external_actor_id 隔离。
首位管理员终端绑定
首位管理员不通过网页生成验证码。先确保钉钉 Stream 机器人已经正常运行,登录部署服务器后执行:
dobotai bootstrap-admin
若二进制未加入 PATH,使用其实际绝对路径执行。该命令只读取 DATABASE_URL 和 DOBOTAI_TENANT_KEY,不会启动 WeKnora、钉钉消费者或问答服务。
命令会在终端使用加密随机数生成一个有效期10分钟的六码绑定码,并保持等待。候选管理员必须私聊机器人发送:
/setting <六码绑定码>
验证码正确后,终端自动将该钉钉用户绑定为 super_admin 并显示成功。已有有效管理员时命令会拒绝执行,防止通过该入口追加或替换管理员。DobotAI 不提供网页管理面;钉钉凭据和运行配置由部署环境文件管理。
覆盖与人工支持
- 有结构化检索引用的回答标记为已覆盖;
- 没有引用但已执行检索的回答标记为未覆盖;
- 没有引用且未执行检索的文本标记为
covered_non_knowledge_response,不创建 Support Case; - 纯图片视觉识别单独记录为
covered_visual_recognition,不计入“未覆盖”; - 无权威知识时,产品统一回复:
当前暂无已发布的权威知识,暂不建议操作。; - 人工通知只带当前问题和同一用户此前最多5个问题,不包含回答、引用或置信度;
- 未覆盖与点踩共用幂等 Support Case,避免对同一 Turn 重复通知。
配置
复制 .env.example 为仅本机可读的运行时环境文件,并在 Git 外注入真实值。不得提交 API Key、数据库密码、钉钉凭据、OSS AccessKey、预签名 URL 或真实用户/群组标识。
关键配置:
DOBOTAI_LISTEN_ADDR
DOBOTAI_INGRESS_TOKEN
DOBOTAI_AGENT_BINDINGS
DOBOTAI_QUICK_AGENT_ID
DOBOTAI_REASONING_AGENT_ID
WEKNORA_BASE_URL
WEKNORA_API_KEY
DATABASE_URL
可选能力必须整组配置或整组留空:
- 钉钉 Stream;
- 钉钉 AI 卡片;
- 未覆盖问题 FAQ 表;
- 人工支持群通知;
- 问题表异步写入;
- 回答图片私有 OSS 外发。
任何部分配置均应在启动时失败关闭,而不是以不完整权限运行。
数据库迁移
DobotAI 使用独立 PostgreSQL,禁止指向 WeKnora 数据库。生产部署前必须按版本顺序执行并演练 db/migrations/ 中的迁移;当前发布范围包含:
000001_core ... 000018_coverage_visual_recognition_reason
000019_reasoning_mode_state
000020_feedback_support_trigger
000021_coverage_non_knowledge_response_reason
迁移前应执行逻辑备份并验证恢复;迁移、二进制部署和钉钉渠道切换是独立步骤,不能把健康检查代替真实渠道验收。
本地运行
go run ./cmd/dobotai
健康检查:
curl http://127.0.0.1:8090/healthz
运行版本:
curl http://127.0.0.1:8090/version
部署脚本应将 version、commit_sha 和 build_time 写入二进制,并仅在 /version 返回预期提交时接受部署。候选源码必须干净,避免运行版本掩盖未提交改动。
安全要求
- 不修改 WeKnora 源码、Schema 或私有包;
- 不直接连接 WeKnora 数据库;
- 不将凭据、Token、预签名 URL 或知识正文写入日志;
- 不接受任意外部图片 URL 作为可信媒体;
- 对钉钉图片、WeKnora
resource://图片和 OSS 返回体均执行来源、大小、类型和签名校验; - 外发 OSS Bucket 保持私有;
- 生产、测试的钉钉应用、数据库、OSS Bucket 和凭据必须相互隔离;
- 生产上线必须使用已验收的同一构建产物,禁止在生产重新编译。
更多设计与边界见 docs/architecture.md。