姚章浩 d6a781ba5b fix(dingtalk): send answer language via Accept-Language header
WeKnora's system prompt hard-enforces 'ALWAYS respond in {{language}}',
fed by its Language middleware (WEKNORA_LANGUAGE env -> Accept-Language
header -> zh-CN fallback). The previous localizedQuery() prompt prefix
lost against that instruction, so a user-set 'en' preference still got
Chinese answers on English questions.

- AgentChatRequest.Locale is transmitted as the Accept-Language header
  (never in the JSON body); WeKnora maps it to the prompt language
- 'auto' detects the query script client-side (latin->en, Han->zh-CN,
  kana->ja, Hangul->ko); es is never auto-guessed
- unset/unknown locale sends no header, preserving prior server behavior
- drop localizedQuery prompt prefixes entirely
2026-09-14 16:58:52 +08:00

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。

S
Description
No description provided
Readme 1.5 MiB
Languages
Go 96.3%
Shell 2%
Python 1.6%