# 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 地址提供给钉钉。 ## 架构边界 ```text 钉钉 / 内部调用方 ↓ DobotAI ├─ 钉钉 Stream、richText、图片、AI 卡片、反馈与通知 ├─ 独立 PostgreSQL:渠道身份、消息、会话映射、Turn、覆盖与支持流程 └─ WeKnora 官方 REST / SSE API ↓ WeKnora:检索、Agent、知识与引用 ``` 责任边界: - **DobotAI**:钉钉协议、媒体收发、会话映射、命令路由、卡片、覆盖判定、人工支持、渠道审计。 - **WeKnora**:通用 Agent Chat、检索、知识存储、引用与模型调用。 - **DobotAI PostgreSQL**:只保存渠道与业务编排数据,不保存 WeKnora 知识正文。 - **Redis**:仅用于队列、重试、幂等租约和短期状态。 ## 目录结构 ```text . ├── 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` | ### 源码部署 ```bash go run ./cmd/dobotai curl --noproxy '*' -fsS http://127.0.0.1:8090/healthz ``` 源码方式不自动安装Docker、不创建目录、不检查RDS表,也不适合作为长期渠道消费者。 ### Docker + 阿里云RDS部署 克隆仓库后在根目录执行: ```bash sudo bash ./install-docker.sh sudo dobotai-dingtalk-configure ``` 第一条命令自动安装Docker/Compose(如缺失)、构建应用镜像、准备目录和配置模板;第二条命令安全录入RDS、WeKnora与钉钉配置,然后检查RDS表:空库初始化`000001`至`000021`,完整表不写入,异常/半成品表拒绝启动。Docker只运行`dobotai-dingtalk`,不创建`dobotai-postgres`、本地数据卷或本地数据库。 完整安装、RDS检查、升级和回滚见 [Docker部署手册](deploy/dobotai-dingtalk/README.md)。 ## 钉钉 Stream 使用官方 `dingtalk-stream-sdk-go`,不暴露公网回调入口。 - 群聊仅在机器人被 `@` 时处理; - 私聊按用户保持长期会话;群聊默认按群共享会话; - 回调 `sessionWebhook` 严格限制为 `https`、`*.dingtalk.com`、443端口且不允许重定向; - 启用 Stream 必须同时设置以下三项;三者均为空时渠道禁用,部分配置会失败关闭: ```text DINGTALK_CLIENT_ID DINGTALK_CLIENT_SECRET DOBOTAI_TENANT_KEY ``` ## 推理模式命令 ```text /think <问题> 仅当前问题使用智能推理 /think on 开启持续智能推理 /think off 或 /rag 关闭持续智能推理 /setting rounds <1-50> 设置持续推理自动关闭轮数 /setting hint 设置剩余轮次提示 /help、/帮助、/h 查看当前模式和帮助 ``` 持续推理默认最多10轮,成功回答才消耗轮次;10分钟无交互后逻辑到期,下一次问题自动回到快速问答。状态按 `tenant + channel + external_actor_id` 隔离。 ## 首位管理员终端绑定 首位管理员不通过网页生成验证码。先确保钉钉 Stream 机器人已经正常运行,登录部署服务器后执行: ```bash dobotai bootstrap-admin ``` 若二进制未加入 `PATH`,使用其实际绝对路径执行。该命令只读取 `DATABASE_URL` 和 `DOBOTAI_TENANT_KEY`,不会启动 WeKnora、钉钉消费者或问答服务。 命令会在终端使用加密随机数生成一个有效期10分钟的六码绑定码,并保持等待。候选管理员必须私聊机器人发送: ```text /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 或真实用户/群组标识。 关键配置: ```text 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/` 中的迁移;当前发布范围包含: ```text 000001_core ... 000018_coverage_visual_recognition_reason 000019_reasoning_mode_state 000020_feedback_support_trigger 000021_coverage_non_knowledge_response_reason ``` 迁移前应执行逻辑备份并验证恢复;迁移、二进制部署和钉钉渠道切换是独立步骤,不能把健康检查代替真实渠道验收。 ## 本地运行 ```bash go run ./cmd/dobotai ``` 健康检查: ```bash curl http://127.0.0.1:8090/healthz ``` 运行版本: ```bash 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`。