# datalight-services-ai **Repository Path**: boundivore/datalight-services-ai ## Basic Information - **Project Name**: datalight-services-ai - **Description**: No description available - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-09-01 - **Last Updated**: 2026-09-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # datalight-services-ai DataLight 平台的 **AI 服务集合**(Python)。 平台主体是 Java(`boundivore-datalight`),AI 相关能力全部收在本仓库, 通过 DataLight Master 的 REST API 与平台对接,返回结构沿用平台统一的 `Result` 协议。 [English](README.en.md) | [架构说明](services-ai/docs/architecture.md) | [services-ai 详细文档](services-ai/README.md) --- ## 它是什么 一个能听懂人话的运维助手。你问「NameNode 起不来,帮我看看」, 它会去查集群健康摘要、翻组件日志、对照最近的作业历史,然后告诉你根因和处置步骤。 它也能把「在这 5 台机器上装一套 HDFS」这句话,变成一份逐节点展开的部署计划, 让你确认之后再执行。 它做不到的事同样明确:不能执行任意命令,不能直连数据库,不能绕开平台改集群。 这些限制写在代码里,不靠提示词约束。 --- ## 架构 ![DataLight 智能体服务架构](./services-ai/docs/assets/architecture.zh.png) 三类触发方式汇聚到同一条链路,最终**只通过 Master REST API** 落到集群上。 ### 为什么不做成被 DataLight 部署的服务 本服务在平台里的角色名是 **AIAgent**,与 Master、Worker 并列,由配置开关控制启停,不进 DLC 包。 给它一个角色名,是因为它既不是 Master 也不是 Worker,在配置、启停、日志、文档里都要被单独称呼。`datalight-services-ai` 是仓库名,`AIAgent` 是它在平台里的角色名,启停命令用的也是这个名字:`sh bin/datalight.sh start aiagent`。 理由是鸡生蛋:它的核心能力是问答式部署集群,而那正是集群还不存在的时刻。 做成被集群部署的服务,第一步就走不通。 另外它是 Python + uv,而 DataLight 的服务部署模型围绕 tgz + shell + JDK, 硬套过去要为虚拟环境和离线依赖单独设计一套分发机制,改造量大收益低。 启停由 `bin/datalight.sh` 承担,支持 `aiagent` 角色(`ai` 是简写,两个都收), 开关是 `directory.yaml` 里的 `datalight.ai.enabled`,默认关闭。不装 Python 也不影响 DataLight 正常运行。 --- ## 流式对话链路 ![流式对话链路](./services-ai/docs/assets/stream-path.zh.png) **前端不直接连 services-ai,中间过 Master。** Master 是平台唯一的鉴权入口。让浏览器直连 Python 服务,等于要在 Python 侧把 satoken 的会话校验、角色判定、审计落库重写一遍,两边判定一旦有偏差就是越权。绕这一层之后, Python 侧只认一个内部密钥,也不必对浏览器暴露端口。 Master 侧用 OkHttp 把响应体按字节原样写回,不解析 SSE 帧,不缓冲,每次 `read` 后立刻 `flush`。以后新增事件类型不用改 Java 代码。 回答是流式的,模型吐一个字前端显示一个字。工具调用过程实时展示,不是等全部跑完才给结论。 | 事件 | 时机 | 关键字段 | | --- | --- | --- | | `meta` | 每次对话开始,仅一次 | `SessionId` `AutonomyLevel` `Model` `ToolCount` `Skills` | | `turn_start` | 每轮工具循环开始 | `Turn` | | `delta` | 模型吐字 | `Content` | | `tool_call` | 决定调用某个工具 | `Tool` `Arguments` | | `tool_result` | 工具返回 | `Tool` `Ok` `Summary` | | `done` | 正常结束 | `Reply` `Status` `Turns` `ToolCalls` | | `error` | 链路异常 | `Message` `Code` | | `ping` | 15 秒保活 | 无,客户端直接丢弃 | 细节见[架构说明](services-ai/docs/architecture.md#二流式对话链路)。 --- ## 会话式部署 ![会话式部署状态机](./services-ai/docs/assets/state-machine.zh.png) 状态落库,不放在对话上下文里。会话可能中断、可能换设备继续, 用户问「现在到哪一步了」时基于持久化状态作答,不翻聊天记录去猜。 澄清有轮次上限,计划有超时,非法流转直接拒绝,执行前会再查一次活跃作业。 --- ## 安全边界 ![工具调用的安全闸门](./services-ai/docs/assets/tool-guard.zh.png) 模型每发起一次工具调用,都要顺序通过三道闸门。这三道由 `BaseTool.execute` 统一把关, 子类只写业务,绕不过去。 自治分级: | 级别 | 模型能做什么 | 人的位置 | | --- | --- | --- | | L0 | 查状态、查日志、查配置、解释报错 | 全程 | | L1 | 给出处置方案和具体步骤,不执行 | 手动执行 | | L2 | 每个变更动作执行前逐个确认 | 逐步确认 | | L3 | 生成完整计划,人确认一次,之后自动执行到底 | 确认一次 + 监督 | | L4 | 在预定义边界内自主决策并执行 | 事后审查 | 不可逆操作(移除集群、删除节点、格式化存储)在任何级别都要人确认, 且要求手动输入一个具体值,不能只点「确定」。 --- ## 数据从哪来 **services-ai 不具备直连数据库的能力。** 集群元数据、作业历史、节点日志, 全部经由 Master 的 REST 接口获取。 Master 侧为此提供了一组聚合接口(`IMasterAgentAPI`),设计目标是 「一次调用拿全一个语义完整的快照」,减少往返与上下文消耗: | 接口 | 用途 | | --- | --- | | `GET /api/v1/master/agent/cluster/snapshot` | 集群全景:集群 + 节点 + 服务 + 组件状态 + 活跃作业 | | `GET /api/v1/master/agent/health/summary` | 健康摘要:只返回异常节点、异常组件、待重启组件 | | `GET /api/v1/master/agent/job/history` | 作业历史,按开始时间倒序 | | `GET /api/v1/master/agent/log/tail` | 组件日志尾部,路径由服务端拼,调用方不传路径 | 这些接口对人同样开放,不是给 Agent 单开的后门。前端需要相同的聚合视图时可以直接复用。 --- ## 工具与方法论 **工具(tool)解决「能做什么」,方法论(skill)解决「该怎么做」。** ### 工具 当前 26 个,23 个只读、3 个变更,按业务域分文件放在 `app/agent/tools/`。 | 分类 | 工具 | | --- | --- | | 聚合只读 | `agent.cluster_snapshot`、`agent.health_summary`、`agent.job_history`、`agent.tail_log` | | 集群与节点 | `cluster.list`、`node.list`、`node.list_with_component`、`node.init_progress` | | 服务与组件 | `service.list`、`component.list`、`service.dlc_list`、`component.placement_recommendation`、`service.web_ui` | | 作业 | `job.active`、`job.progress`、`job.log` | | 配置 | `config.list_summary`、`config.get`、`config.versions` | | 监控与日志 | `monitor.query`、`alert.list`、`log.collection`、`log.content` | | 变更 | `node.operate`、`config.save`、`job.submit` | L0 与 L1 只暴露只读工具,模型连变更工具的存在都不知道,省得它反复尝试再被拒。 ### 方法论 6 篇,放在 `app/agent/skills/builtin/`,按用户这一轮的话做关键词匹配后注入, 不全量塞进上下文,那样几万字符既贵又稀释注意力。 | SKILL | 什么时候注入 | | --- | --- | | `safe-operations` | 每轮都注入,变更纪律的底线 | | `cluster-deployment` | 提到部署、安装、扩容、加节点 | | `fault-diagnosis` | 提到起不来、报错、异常、排查 | | `log-analysis` | 提到日志、堆栈、异常栈 | | `config-tuning` | 提到配置、参数、调优 | | `capacity-planning` | 提到分布、容量、几台、够不够 | 新增一篇只要建目录加 `SKILL.md`,触发词写在 frontmatter 里,不用改代码。 --- ## 界面 有两个入口,用途不同。 **DataLight 控制台**是给人用的正式入口。右上角的智能体抽屉,样式跟平台其他页面一致, 走 `前端 → Master → services-ai` 这条链路,由 satoken 鉴权。回答流式渲染, 工具调用带状态图标逐条展开,可以随时中断、清空,抽屉宽度可拖。 **`http://:8010/ui`** 是本服务自带的调试入口,随服务发布,不依赖前端工程。 顶部实时显示服务状态、自治级别、可用工具数、存储后端、模型是否已配置, 用来在没起前端的时候验证智能体本身是否正常。 调试入口直连本服务,不经过平台鉴权。如果配了内部密钥, 需要以 `http://:8010/ui?token=密钥` 打开。生产环境建议只监听回环地址。 --- ## 快速开始 ```bash uv sync ``` ```bash cp services-ai/env.example services-ai/.env.dev ``` 填好 `DATALIGHT_MASTER_*` 与 `DATALIGHT_LLM_*` 之后启动: ```bash uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8010 --app-dir services-ai ``` | 地址 | 说明 | | --- | --- | | `http://localhost:8010/ui` | 问答界面 | | `http://localhost:8010/api/v1/ai/py/health` | 健康检查 | | `http://localhost:8010/api/v1/ai/py/docs` | 接口文档 | 模型没配也能启动,只是涉及模型的接口会明确报错,不会编造答案。 ### 选哪个模型 只要是 OpenAI 兼容的接口都能接,配 `DATALIGHT_LLM_BASE_URL` 与 `DATALIGHT_LLM_MODEL` 即可。 国产化交付要求模型不出网时,把 BASE_URL 指向内网推理服务,代码不用动。 默认给的是 DeepSeek `deepseek-flash`: | 配置项 | 默认值 | 说明 | | --- | --- | --- | | `DATALIGHT_LLM_MODEL` | `deepseek-flash` | 全部场景 | | `DATALIGHT_LLM_PLAN_MODEL` | 空(同 `MODEL`) | 留着按需换成更强的模型 | **为什么不用 `deepseek-v4-pro` 跑部署计划。** 试过分两个模型,快的查状态、 强的做规划。后来统一回 flash,因为只有它支持图像理解。实测同一张柱状图, flash 答出「3 根柱子,从左到右越来越高」,pro 回「无法确定」且 prompt token 只有 101(图根本没进去)。运维场景经常要看截图,这个能力比那点推理差距重要。 模型名在 2026 年 9 月改过:`deepseek-v4-flash` 变成了 `deepseek-flash`。 旧名目前还能调通,但已经不在 `GET /models` 的返回里,不要依赖它。 ### 图像理解 运维场景里截图往往比描述准:Grafana 面板的曲线、报错弹窗、终端里那段栈、 配置页面的勾选状态。抽屉里可以点按钮选图,也可以直接把截图粘进输入框。 请求里图片放在 `Images` 字段: ~~~json { "SessionId": "...", "Message": "这个面板里哪个指标不对劲", "Images": [ { "Data": "data:image/png;base64,...", "Caption": "NameNode 监控面板" } ] } ~~~ 几条约束: | 项目 | 限制 | 为什么 | | --- | --- | --- | | 单张大小 | 10 MB(`DATALIGHT_AI_MAX_IMAGE_MB` 可调) | DeepSeek 允许 32 MiB,但请求要过 Master 的 Java 堆,base64 后约 43 MB 整份驻留 | | 单次张数 | 8 张 | 官方给到 600,那是批量场景的。对话里贴几十张已经不正常 | | 单次总量 | 20 MB | 单张不超标但八张加起来一样能打满内存 | | 格式 | JPEG、PNG、GIF、WebP | 按文件头判断,不信客户端声明的 mime | **图片不进会话历史。** 单张就有几 MB,而历史是整存整取的,几轮下来能把 Redis 撑爆。历史里只留一行占位,写明发过什么图。代价是翻旧会话看不到原图, 换来的是历史体积可控。真要留档该走对象存储,不是塞进会话里。 **一张不合规就整批拒绝**,不做「跳过坏的留下好的」。用户贴了三张, 我们静默丢掉一张,他会以为模型看过全部三张,结论错得没头没脑。 将来做自动化巡检时,自动截图走的是同一条路:抓到组件 Web UI 的图之后, 按同样的结构送进来。 --- ## 仓库结构 整个仓库由 **uv workspace** 统一管理,根目录执行一次 `uv sync` 装好全部服务 (共享一个根 `.venv` 与根 `uv.lock`)。 ```text datalight-services-ai/ ├── pyproject.toml # workspace 根:成员、开发工具、镜像源 ├── uv.lock # 全工程锁文件,提交入库 └── services-ai/ # workspace 成员:智能体服务 ├── app/ │ ├── main.py │ ├── config/ # 配置与日志 │ ├── store/ # 键值存储,内存与 Redis 可切换 │ ├── utils/ # Result 协议 │ ├── clients/ # Master 客户端与接口路径常量 │ ├── llm/ # 模型接入 │ ├── prompts/ # 提示词加载 │ ├── web/static/ # 内置问答界面 │ ├── agent/ │ │ ├── autonomy.py # 自治分级与危险操作分级 │ │ ├── models.py # 意图、计划、回执、会话状态 │ │ ├── state_machine.py # 会话式部署状态机 │ │ ├── confirm.py # 确认令牌 │ │ ├── redaction.py # 脱敏 │ │ ├── audit.py # 审计 │ │ ├── engine.py # 模型与工具的执行循环 │ │ ├── skills/ # 方法论,按需注入 │ │ └── tools/ # 工具层 │ └── api/v1/ ├── prompts/v1/ # 提示词,按版本存放 ├── docs/ # 文档与架构图 └── tests/ ``` 后续新增 Python AI 服务,按 `services-xxx/` 平级新增, 并在根 `pyproject.toml` 的 `[tool.uv.workspace].members` 里追加。 --- ## Redis 会话状态与确认令牌落在存储层,后端可以是进程内存也可以是 Redis。 **单实例部署可以不配。** 留空即用内存,功能完全一致,代价是重启失忆。 **多副本部署必须配。** 否则在 A 副本上确认、请求打到 B 副本会认不出令牌。 ```bash DATALIGHT_REDIS_URL=redis://:password@127.0.0.1:6379/0 DATALIGHT_REDIS_NAMESPACE=datalight:ai ``` 配了但连不上时,服务打一条警告后回退到内存,不会起不来。 健康检查的 `StoreBackend` 字段显示当前实际用的是哪个。 --- ## 关于密钥 本仓库开源。`.env`、`.env.*`、各类证书私钥都在 `.gitignore` 里, `env.example` 只放占位值。任何真实凭据都不要提交。 `DATALIGHT_AI_PROFILE=prod` 时,服务启动会校验密钥是否仍是占位值,未替换直接拒绝启动。 --- ## 开发规范 见 `D:\6.DataLight 文档\01.开发文档\开发规范\`: - `006.AI Agent 协作开发规范.md` - `007.自动化运维与会话式部署规范.md` 仓库根目录的 [`AGENTS.md`](AGENTS.md) 是给 AI 编码助手的入口约定。 --- ## 开源协议 Apache License 2.0,与 DataLight 主仓库保持一致。