1. 项目缘起:当数据查询遇上即时通讯
最近在做一个内部数据中台项目时,遇到了一个挺典型的痛点:业务部门的同事,尤其是非技术背景的运营、产品同学,经常需要查询一些业务数据。他们要么得在复杂的BI系统里自己拖拽报表,要么就得在群里@我们,描述半天需求,我们再手动写SQL、跑脚本、截图发回去。一来一回,沟通成本高,效率低,还容易出错。我们就在想,能不能做一个“数据助手”,让它能像同事一样,在群里直接回答数据问题?比如运营同学在钉钉或飞书群里问一句:“昨天我们App的日活是多少?”,这个助手就能自动理解问题,去数据库里查,然后把结果用清晰的表格或图表发回群里。
这就是“DataClaw”这个项目想法的雏形。它本质上是一个自治数据代理,核心目标是将数据查询与分析能力无缝集成到日常的即时通讯工具中,让数据获取变得像聊天一样自然。这不仅仅是做一个简单的“SQL机器人”,而是希望它能理解自然语言意图,自主规划查询步骤,并利用历史对话记忆来优化回答,形成一个真正智能的交互闭环。市面上虽然有一些类似工具,但要么集成度不够,要么智能化程度有限,我们希望能打造一个更贴合实际业务场景、更“懂行”的解决方案。
2. 核心架构拆解:从接收到响应的智能链路
DataClaw的设计不是一蹴而就的,我们参考了当前AI Agent领域的一些成熟范式,并结合数据查询这个垂直场景做了大量定制。整个系统的核心工作流可以概括为“感知-思考-行动-学习”的循环,其架构主要围绕以下几个关键模块构建。
2.1 消息接入与意图理解层
这是整个系统的“耳朵”和“大脑皮层”。我们首先需要让DataClaw能“听到”群里的消息。
2.1.1 即时通讯平台适配器我们选择了飞书和钉钉作为首批集成平台,因为它们在国内企业中的普及率最高。这里的关键不是简单地调用官方Webhook,而是要处理完整的会话上下文。我们为每个平台开发了一个轻量级的适配器(Adapter),主要职责是:
- 监听消息:通过平台提供的机器人API,监听指定群组中@机器人的消息或私聊消息。
- 标准化消息格式:将不同平台格式各异的消息体(包括文本、图片、文件等),统一转换成系统内部的标准化结构。例如,提取发送者ID、消息内容、会话ID、消息类型等。
- 管理会话状态:维护一个会话映射表,将外部平台的会话(如飞书的
chat_id)与DataClaw内部的会话上下文关联起来。这对于后续的记忆功能至关重要。
2.1.2 自然语言理解与意图分类用户的问题可能是模糊的、口语化的。比如“上个月卖得最好的产品是哪个?”和“给我看看七月份的销售冠军”,表达不同但意图相同。这一步的目标是将自然语言转化为结构化的查询意图。 我们最初尝试了简单的关键词匹配,但发现泛化能力太差。后来采用了基于预训练语言模型(如BERT或类似轻量级模型)的意图分类模型。我们将常见的查询意图分成了几大类:
- 指标查询:询问具体的数值指标,如DAU、GMV、订单量等。通常对应简单的聚合查询(
SELECT SUM(...) FROM ... WHERE ...)。 - 维度分析:询问按某个维度(如地区、渠道、产品类别)的分布或排名。对应
GROUP BY查询。 - 趋势查询:询问数据随时间的变化,如“最近一周的日活趋势”。对应时间序列查询。
- 对比查询:如“对比一下A产品和B产品本季度的收入”。
- 明细查询:请求查看原始数据或详细列表,通常需要分页。
- 澄清与反问:当用户问题模糊时,Agent需要主动提问澄清,例如“您指的是哪个地区的销售额?”
模型会对输入的问题进行意图分类和实体抽取(如时间实体“昨天”、“Q3”,指标实体“销售额”,维度实体“华东区”),输出一个结构化的意图表示(Intent),作为后续规划的基础。
2.2 基于ReAct范式的推理与规划引擎
这是DataClaw的“思考中枢”,也是其“自治”能力的核心体现。我们采用了经典的ReAct(Reasoning + Acting)框架来驱动整个决策过程。ReAct的核心思想是让Agent交替进行“推理”和“行动”,通过内部“思考”来指导外部“动作”。
2.2.1 ReAct循环的具体实现当接收到一个结构化意图后,规划引擎会启动一个ReAct循环。我们使用一个强大的大语言模型作为“推理”的核心(例如通过API调用GPT-4或部署开源模型如DeepSeek),并为其设计了一套清晰的提示词(Prompt)模板。
这个模板会告诉LLM:
- 目标:用户想查询什么。
- 可用工具:系统目前有哪些“手脚”(即下一节会讲到的“工具集”),比如
query_database、get_table_schema、calculate_growth_rate等,并详细描述每个工具的用途和输入格式。 - 历史:当前会话的记忆(由记忆系统提供,见2.3节)。
- 输出格式:要求LLM严格按照“Thought: ... Action: ... Action Input: ...”的格式输出。
一个典型循环如下:
- Thought: “用户想查询昨天的DAU。我需要先确认‘昨天’的具体日期,然后去‘user_activity’表中查询该日期的去重用户数。”
- Action:
query_database - Action Input:
{“sql”: “SELECT COUNT(DISTINCT user_id) AS dau FROM user_activity WHERE date = ‘2023-10-26’”, “db”: “bi_core”}
系统执行query_database工具,得到结果(比如{"dau": 1250000})后,会将这个观察结果(Observation)连同之前的记录一起,再次喂给LLM。
- Thought: “查询得到昨天DAU是125万。用户可能还想知道环比变化。我可以计算一下与前天DAU的环比增长率。”
- Action:
calculate_growth_rate - Action Input:
{“current_value”: 1250000, “previous_value”: 1220000, “metric_name”: “DAU”}
如此循环,直到LLM认为已经充分回答了用户问题,最终输出一个“Final Answer”动作,将收集到的所有信息整合成一段面向用户的自然语言回答。
2.2.2 为什么选择ReAct?相比直接让LLM生成SQL,ReAct范式有几个显著优势:
- 可解释性强:每一步的“Thought”都记录了Agent的思考过程,这极大方便了调试和追溯。当查询结果出错时,我们可以清晰地看到是哪个推理环节出了问题。
- 容错与恢复能力强:如果某个工具执行失败(比如SQL语法错误或数据库超时),观察结果会是错误信息。LLM可以根据这个错误进行反思,调整策略,例如重写SQL或选择另一个工具,而不是直接崩溃。
- 支持复杂多步查询:对于“计算上个月销售额最高的三个品类,并分别列出它们的同比增速”这类复杂问题,ReAct可以自然地将其分解为多个顺序或并行的子任务。
2.3 动态工具集:Agent的“瑞士军刀”
工具(Tools)是ReAct框架中“Act”的具体执行者。DataClaw的工具集被设计成可插拔、可扩展的。
2.3.1 核心数据工具
get_table_schema: 根据表名获取字段名、类型和注释。这是安全且高效查询的基础,避免Agent对不存在的字段进行操作。query_database: 执行SQL查询。这是最核心的工具。这里有一个关键的安全设计:我们并没有给Agent直接的数据库写权限,甚至读权限也通过一个中间层进行了管控。所有通过此工具执行的SQL,都会先经过一个SQL审核与重写层。该层会做几件事:- 禁止
DELETE、UPDATE、DROP等危险操作。 - 自动为查询加上行级限制(例如
LIMIT 1000),防止全表扫描拖垮数据库。 - 根据用户身份,动态在WHERE条件中注入数据权限过滤条件(例如,销售只能看到自己区域的数据)。
- 禁止
explain_sql: 解释SQL的执行计划,当查询较慢时,Agent可以调用此工具分析性能瓶颈,甚至尝试优化。query_metric_platform: 直接查询预定义的指标平台API。对于一些已经固化、计算复杂的核心指标(如“毛利率”),直接调用指标平台比实时计算更准确、高效。
2.3.2 分析与可视化工具
calculate_growth_rate/calculate_percentage: 进行简单的数学计算。generate_chart: 根据查询结果的数据结构,自动选择合适的图表类型(折线图用于趋势,柱状图用于对比,饼图用于占比)并调用绘图库(如Matplotlib或ECharts)生成图片。format_to_markdown_table: 将查询结果集格式化为美观的Markdown表格,这在IM中展示数据非常清晰。
工具的注册和管理通过一个中央注册表完成。开发新功能时,我们只需要按照接口规范实现一个新的工具类并注册,Agent在下一个推理周期就能自动识别并使用它,实现了能力的无缝扩展。
2.4 记忆系统:让对话拥有“上下文”
没有记忆的Agent就像金鱼,每次对话都是全新的开始。DataClaw的记忆系统旨在解决这个问题,它由几个部分组成:
2.4.1 短期会话记忆存储在内存或Redis中,键为会话ID。它完整记录了当前会话中所有的用户消息、Agent的Thought-Action-Observation链、以及最终的回答。这直接服务于ReAct循环,让LLM能知道“刚才我们说到哪了”。例如,用户问“DAU是多少?”,接着问“那MAU呢?”,Agent需要能理解“那”指的是同一个时间范围。
2.4.2 长期实体记忆这是一个向量数据库(我们选用ChromaDB)。它的作用是记住跨会话的、关于“实体”的知识。例如:
- 业务术语映射:当新用户第一次问“GMV”时,Agent通过查询和澄清得知“GMV”在本公司特指“
orders表中status='paid'的amount字段之和”。这个映射关系会被向量化后存入长期记忆。 - 用户偏好:某位产品经理总是喜欢看“按渠道细分”的数据,这个偏好可以被记录。
- 历史复杂查询:一个成功的、多步的复杂查询可以被抽象成“查询模式”存储下来。
当新的查询进来时,除了短期记忆,系统还会从向量数据库中检索相关的长期记忆片段,作为上下文提供给LLM。这能让Agent的回答越来越“个性化”和“专业化”。
2.4.3 记忆的更新与衰减记忆不是只增不减的。我们设计了简单的衰减机制和手动清理接口。对于长期未使用的记忆片段,其重要性权重会降低。业务指标口径发生变更时,管理员可以主动更新或删除相关的记忆,确保信息的准确性。
3. 关键技术选型与实战踩坑
构建这样一个系统,技术选型直接关系到开发效率和最终效果。下面分享我们的一些决策和遇到的典型问题。
3.1 LLM选型:效果、成本与可控性的平衡
LLM是大脑,选型至关重要。我们评估了几个方向:
- 闭源大模型API(如GPT-4):效果最好,开发最简单,但成本高、数据出境有合规风险、响应速度受网络影响。适合初期快速验证原型。
- 国内大模型API(如文心、通义、DeepSeek):合规性好,成本相对较低。但早期版本在复杂推理和指令跟随上有时不如GPT-4稳定,需要更精细的Prompt工程。
- 本地部署开源模型(如Llama 3、Qwen系列、DeepSeek Coder):数据最安全,长期成本可能更低,可控性最强。但对硬件有要求,且需要一定的模型微调(Fine-tuning)和优化能力。
我们的实践路径:为了快速启动,我们初期使用了GPT-4 API作为推理核心。在Prompt工程上下足了功夫,设计了包含丰富示例(Few-shot)和严格格式要求的模板,效果非常出色。同时,我们并行在内部服务器上部署了Qwen-14B-Chat模型,通过OpenAI兼容的API服务(如vLLM或Ollama)进行封装,让我们的系统可以无缝切换后端LLM。经过大量测试和Prompt调整后,Qwen在大多数常见查询任务上已经能达到接近GPT-4的水平,我们便逐步将流量切到了自部署模型上,在保证效果的同时控制了成本和安全。
踩坑记录:Prompt工程的魔鬼细节最初我们的Prompt写得比较简略,导致LLM经常“放飞自我”,不按格式输出,或者使用不存在的工具。后来我们总结出几个关键点:
- 系统指令(System Prompt)要强硬:开头必须明确角色、职责和绝对规则,例如“你是一个严谨的数据分析师,必须使用提供的工具,严禁编造信息。”
- 格式示例要极其具体:在Few-shot部分,不仅要给正确的例子,还要给几个典型的错误输出例子,并说明为什么错。这比只给正面例子有效得多。
- 工具描述要结构化:每个工具的名称、描述、输入参数(JSON Schema格式)、输出示例,都必须清晰无误地提供给LLM。
- 温度(Temperature)参数要调低:对于这种需要严格遵循流程的任务,我们将温度设为0.1或0.2,以降低输出的随机性,提高稳定性。
3.2 数据库连接与查询安全
这是数据项目的生命线。我们绝不允许Agent直接连接生产数据库。
我们的架构:
- 专用查询从库:为DataClaw单独搭建了一个只读的数据库从库,同步延迟在分钟级,这既隔离了负载,也提供了基础的数据保护。
- 查询网关与审计层:所有
query_database工具的请求,都先发送到一个自研的“查询网关”。这个网关负责:- SQL解析与重写:使用
sqlparse等库解析SQL,强制添加LIMIT,拦截危险操作。 - 权限注入:根据当前请求的用户身份(从IM上下文获取),在SQL的WHERE条件中自动拼接对应的数据范围过滤条件。这部分逻辑与我们公司的统一权限中心对接。
- 查询性能监控与熔断:设置查询超时时间(如30秒),对复杂查询进行资源限制。记录所有查询的日志,用于审计和优化。
- 结果集处理与脱敏:对查询结果中的敏感字段(如手机号、邮箱)进行脱敏处理后再返回给Agent。
- SQL解析与重写:使用
踩过的坑:有一次,一个同事问了一个看似简单的问题:“列出所有用户”。Agent生成的SQL是SELECT * FROM users。尽管有LIMIT 1000,但这个查询没有WHERE条件,直接全表扫描,瞬间导致查询从库的CPU飙升,影响了其他业务查询。我们立刻在网关层增加了规则:对于没有WHERE条件的SELECT *查询,除非目标表是明确的小型配置表,否则一律拦截并返回警告,要求用户添加过滤条件。同时,我们优化了Agent的Prompt,鼓励其在查询前先思考数据量级,并主动向用户询问过滤维度。
3.3 即时通讯集成的稳定性
IM机器人对接看似简单,但隐藏着稳定性陷阱。
消息去重与幂等:IM平台可能因为网络问题对同一个事件推送多次。我们的适配器必须根据平台提供的消息ID进行去重处理,确保同一条用户指令不会被执行两次。异步与超时处理:一个复杂查询可能需要十几秒,而IM平台的机器人消息发送接口通常有超时限制(如5秒)。我们不能同步阻塞。我们的做法是:收到消息后,立即回复一个“正在思考...”的占位消息。然后,将查询任务放入异步队列(如Celery或Redis Queue)中处理。处理完成后,再通过机器人API更新之前的那条“占位消息”为最终结果。这提供了良好的用户体验。限流与配额:IM平台对机器人调用API有频率限制。我们需要在系统层面实现全局限流,避免因为突发的大量查询导致机器人被平台暂时禁用。
4. 效果评估与迭代方向
DataClaw上线后,我们首先在一个约50人的产品运营团队内部进行了试点。
效果评估:
- 查询效率:简单指标查询的响应时间从平均15分钟(人工处理)缩短到10秒以内。
- 使用频率:试点群内,日均主动查询量超过200次,说明需求真实存在。
- 准确率:我们对前1000条交互进行了人工复核,对于明确、常见的业务问题,准确率(返回结果正确且格式清晰)达到92%以上。主要错误集中在模糊问题理解偏差和极复杂的多表关联查询上。
- 用户反馈:非技术同学反馈“找数据方便多了”,数据分析师则反馈“从重复的取数工作中解放出来,可以更专注于深度分析”。
持续迭代方向:
- 复杂查询能力增强:当前对需要多表JOIN、复杂子查询的场景处理还不够好。我们计划引入更强大的“数据图谱”模块,将数据库中的表关系、字段业务含义以图谱形式存储,辅助LLM进行更准确的查询规划。
- 主动洞察与预警:不止于被动问答。我们正在尝试让DataClaw具备“主动说话”的能力。例如,定时分析核心指标,发现异常波动(如DAU突然下跌20%)时,自动在相关群组发出预警,并附上初步的下钻分析线索。
- 多模态输入:支持用户上传Excel或截图,让Agent能读取文件中的数据,或结合截图中的图表进行解读和分析。
- 记忆系统的深度利用:基于长期记忆,实现“个性化数据简报”。例如,每周一早上,自动向每位总监推送其负责业务线的核心指标周报。
构建DataClaw的过程,是一个将前沿的AI Agent理念与传统的企业数据栈深度融合的过程。它不是一个炫技的玩具,而是一个切实提升效率、降低数据获取门槛的工具。最大的体会是,技术方案的选择必须紧密围绕业务场景,在效果、成本、安全和易用性之间找到最佳平衡点。例如,用ReAct框架而不是简单的端到端模型,虽然增加了复杂度,但带来了可解释性和可靠性,这在企业级应用中是不可妥协的。未来,随着多模态和代码生成能力的进步,这类自治数据代理的潜力会更大,或许真的能成为每个团队里的那个“最懂数据的同事”。