TL;DR:用企微|钉钉: WebSocket + Claude Agent SDK 搭了个内部智能助手,靠"滚动摘要窗口"解决上下文断裂、进程池解决冷启动、热插拔工具系统实现低成本扩展。Non-goals 比功能列表更值钱。上线后研发被打断次数下降 75%,工单响应从 15 分钟缩到 3 分钟。
这不是一篇"如何用 AI 改变世界"的宏大叙事,是一个部门级内部工具的真实落地记录------从被杂活淹没到机器人接手 60% 对外咨询,踩过的坑、做的取舍、最后跑通的那套架构。

为什么我们要造这个东西
先说痛点。我们部门对外的日常事务大致是这些:
- 技服每天回复工单------客户报 502,要查日志、查工单、查版本、给排查结论
- 其他部门来问知识库------"支付模块回调怎么设计的""怎么申请服务器权限"
- 项目经理想知道进度------这周 sprint 推到哪了,最近提交了什么
- 构建失败要通知------Jenkins 挂了,谁在盯
这些活单个不难,但架不住量大、重复、碎片化。一个研发一天被打断 5 次回答"怎么查日志",正经活就别干了。
所以目标很明确:造一个内部智能助手,把这些重复性对外咨询接过来 60%-70%,让研发测试产品能专心做自己的事。
产品思路:少做比多做重要
内部工具最大的陷阱不是功能不够,是什么都想做。我们花了大量时间定义"不做什么"。
Non-goals 清单------这条最值钱
| 不做 | 理由 |
|---|---|
| 不对外部客户 | 内部工具,信任容忍度高,外部客户要求完全不一样 |
| 不做代码自动修复 | 只查日志给结论,不改代码------边界要清晰 |
| 不做知识库自动生成 | FAQ 人工维护,不让 AI 自己"创造"知识 |
| 不做多语言 | 中文够了 |
| 不做自动操作生产环境 | Jenkins 触发、Git 操作必须人工确认 |
成功指标------怎么知道真的省了 60% 的活
"替代 60%-70% 杂活"是愿景,不可衡量就是耍流氓。我们定了两个指标,简单粗暴:
| 指标 | 定义 | 目标 |
|---|---|---|
| 机器人接手率 | 机器人独立解决的问题数 / 总咨询数 | ≥50% |
| 回答可用率 | 用户未在同一会话内追问同一问题的比例 | ≥70% |
测的方式也很轻------服务日志里给每条会话打个标:resolved_by_bot / escalated_to_human / user_reasked。不需要数据平台,grep 一下就出来了。
安全边界------机器人不能碰的红线
这条是产品审核时被拍桌子补上的。工具都给齐了(Jenkins、JIRA、Git),不划清边界等于没安全。
text
禁止机器人自动执行:
- 生产环境变更(重启服务、修改配置、切换流量)
- 数据库写操作(UPDATE / DELETE / INSERT)
- 客户数据修改(退款、订单变更、账户操作)
- 敏感信息返回(密码、密钥、Token、手机号明文)
还有一条兜底规则:连续 2 轮回答不确定时,自动附加"⚠️ 以上分析供参考,建议人工核实"。内部工具最大的信任危机不是功能少,是瞎说还不认。
整体架构
先看全景,再拆开讲。
#mermaid-svg-3pDHCKxgAeewEf36{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3pDHCKxgAeewEf36 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3pDHCKxgAeewEf36 .error-icon{fill:#552222;}#mermaid-svg-3pDHCKxgAeewEf36 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3pDHCKxgAeewEf36 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3pDHCKxgAeewEf36 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3pDHCKxgAeewEf36 .marker.cross{stroke:#333333;}#mermaid-svg-3pDHCKxgAeewEf36 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3pDHCKxgAeewEf36 p{margin:0;}#mermaid-svg-3pDHCKxgAeewEf36 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3pDHCKxgAeewEf36 .cluster-label text{fill:#333;}#mermaid-svg-3pDHCKxgAeewEf36 .cluster-label span{color:#333;}#mermaid-svg-3pDHCKxgAeewEf36 .cluster-label span p{background-color:transparent;}#mermaid-svg-3pDHCKxgAeewEf36 .label text,#mermaid-svg-3pDHCKxgAeewEf36 span{fill:#333;color:#333;}#mermaid-svg-3pDHCKxgAeewEf36 .node rect,#mermaid-svg-3pDHCKxgAeewEf36 .node circle,#mermaid-svg-3pDHCKxgAeewEf36 .node ellipse,#mermaid-svg-3pDHCKxgAeewEf36 .node polygon,#mermaid-svg-3pDHCKxgAeewEf36 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3pDHCKxgAeewEf36 .rough-node .label text,#mermaid-svg-3pDHCKxgAeewEf36 .node .label text,#mermaid-svg-3pDHCKxgAeewEf36 .image-shape .label,#mermaid-svg-3pDHCKxgAeewEf36 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3pDHCKxgAeewEf36 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3pDHCKxgAeewEf36 .rough-node .label,#mermaid-svg-3pDHCKxgAeewEf36 .node .label,#mermaid-svg-3pDHCKxgAeewEf36 .image-shape .label,#mermaid-svg-3pDHCKxgAeewEf36 .icon-shape .label{text-align:center;}#mermaid-svg-3pDHCKxgAeewEf36 .node.clickable{cursor:pointer;}#mermaid-svg-3pDHCKxgAeewEf36 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3pDHCKxgAeewEf36 .arrowheadPath{fill:#333333;}#mermaid-svg-3pDHCKxgAeewEf36 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3pDHCKxgAeewEf36 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3pDHCKxgAeewEf36 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3pDHCKxgAeewEf36 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3pDHCKxgAeewEf36 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3pDHCKxgAeewEf36 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3pDHCKxgAeewEf36 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3pDHCKxgAeewEf36 .cluster text{fill:#333;}#mermaid-svg-3pDHCKxgAeewEf36 .cluster span{color:#333;}#mermaid-svg-3pDHCKxgAeewEf36 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3pDHCKxgAeewEf36 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3pDHCKxgAeewEf36 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3pDHCKxgAeewEf36 .icon-shape,#mermaid-svg-3pDHCKxgAeewEf36 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3pDHCKxgAeewEf36 .icon-shape p,#mermaid-svg-3pDHCKxgAeewEf36 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3pDHCKxgAeewEf36 .icon-shape .label rect,#mermaid-svg-3pDHCKxgAeewEf36 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3pDHCKxgAeewEf36 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3pDHCKxgAeewEf36 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3pDHCKxgAeewEf36 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} WebSocket 长连接
需要压缩
不需要
响应
WebSocket 推送
企微群/私聊 @机器人
本地服务 Python
会话管理器
权限管理器
推送管理器
滚动摘要判断
Haiku 压缩历史
直接处理
Agent SDK + Sonnet 4.6
工具层 热插拔
wiki
jira
oss_logs
git_repos
jenkins
...更多工具
记忆系统
MEMORY.md 索引
facts/ 明细
本地向量库
FAQ 知识库
整个链路是:企微消息 → WebSocket → 本地服务 → Agent SDK(Sonnet 4.6)→ 调工具 → 回企微。所有东西跑在本机内网,不依赖外部 API(embedding 用的也是本地模型)。
核心技术决策
决策一:Agent SDK 而不是 CLI 外挂
最早的版本是把 Claude Code CLI 当黑盒用------stdin 喂 prompt,stdout 读结果。能用,但有个致命问题:上下文是 CLI 的私有状态,你改不了。
后来迁到 Agent SDK,整个世界清爽了。上下文变成了你可以程序化读写的对象:
python
# 伪代码示意
class RollingContextManager:
def before_turn(self, agent, session):
if session.should_compress():
old_msgs = session.get_messages()[:-5]
summary = cheap_llm.summarize(old_msgs)
session.replace_messages([
SystemMessage(summary),
*session.get_messages()[-5:]
])
不用杀进程,不用 stdin/stdout 拼接,直接替换消息历史。这一步迁移是整个架构最重要的一次升级。
决策二:滚动摘要窗口------解决上下文断裂
这是整个项目最痛的一个问题。
工单场景经常这样:技服 @ 机器人查一个 502,从查 JIRA → 查日志 → 分析 → 追问 → 再查别的日志,一来一回 30 多轮,上下文就爆了(180k tokens 硬上限)。一旦爆了,机器人"失忆",前面排查到哪一步全忘------技服同事当场就弃用了。
试过三个方案:
| 方案 | 思路 | 问题 |
|---|---|---|
| 会话快照 | 爆了才压缩,存个摘要 | 用户能感知到"断了" |
| 工单锚点 | 结构化存工单状态 | 工具不触发就漏 |
| 滚动摘要 | 主动压缩,永不让它爆 | 选这个 |
滚动摘要的核心思路:不等它爆,到了水位就压。
#mermaid-svg-2gClxhlHxvLo1Ub8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2gClxhlHxvLo1Ub8 .error-icon{fill:#552222;}#mermaid-svg-2gClxhlHxvLo1Ub8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2gClxhlHxvLo1Ub8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .marker.cross{stroke:#333333;}#mermaid-svg-2gClxhlHxvLo1Ub8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2gClxhlHxvLo1Ub8 p{margin:0;}#mermaid-svg-2gClxhlHxvLo1Ub8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster-label text{fill:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster-label span{color:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster-label span p{background-color:transparent;}#mermaid-svg-2gClxhlHxvLo1Ub8 .label text,#mermaid-svg-2gClxhlHxvLo1Ub8 span{fill:#333;color:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .node rect,#mermaid-svg-2gClxhlHxvLo1Ub8 .node circle,#mermaid-svg-2gClxhlHxvLo1Ub8 .node ellipse,#mermaid-svg-2gClxhlHxvLo1Ub8 .node polygon,#mermaid-svg-2gClxhlHxvLo1Ub8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .rough-node .label text,#mermaid-svg-2gClxhlHxvLo1Ub8 .node .label text,#mermaid-svg-2gClxhlHxvLo1Ub8 .image-shape .label,#mermaid-svg-2gClxhlHxvLo1Ub8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-2gClxhlHxvLo1Ub8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .rough-node .label,#mermaid-svg-2gClxhlHxvLo1Ub8 .node .label,#mermaid-svg-2gClxhlHxvLo1Ub8 .image-shape .label,#mermaid-svg-2gClxhlHxvLo1Ub8 .icon-shape .label{text-align:center;}#mermaid-svg-2gClxhlHxvLo1Ub8 .node.clickable{cursor:pointer;}#mermaid-svg-2gClxhlHxvLo1Ub8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .arrowheadPath{fill:#333333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2gClxhlHxvLo1Ub8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2gClxhlHxvLo1Ub8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2gClxhlHxvLo1Ub8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster text{fill:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 .cluster span{color:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2gClxhlHxvLo1Ub8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2gClxhlHxvLo1Ub8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-2gClxhlHxvLo1Ub8 .icon-shape,#mermaid-svg-2gClxhlHxvLo1Ub8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2gClxhlHxvLo1Ub8 .icon-shape p,#mermaid-svg-2gClxhlHxvLo1Ub8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2gClxhlHxvLo1Ub8 .icon-shape .label rect,#mermaid-svg-2gClxhlHxvLo1Ub8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2gClxhlHxvLo1Ub8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2gClxhlHxvLo1Ub8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2gClxhlHxvLo1Ub8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 120k tokens
25 轮
距上次压缩 >= 5 轮
都不满足
新消息到达
触发判断
压缩
直接处理
保留最近 5 轮原文
其余历史 → Haiku 压缩
拼装: 摘要 + 5 轮原文
替换 Agent 上下文
继续对话
三个触发条件,满足任意一个就压:
- 水位触发:上下文逼近 120k tokens(留 60k 缓冲)
- 轮次触发:对话超过 25 轮
- 频率保护:距上次压缩至少隔 5 轮,避免震荡
压缩逻辑极简:保留最近 5 轮原文,其余全部丢给 Haiku 压成 ≤500 字摘要。prompt 就一句话:"把以下对话压缩成不超过 500 字的摘要,保留关键决策、排查步骤、未解决问题。"
用户侧完全无感------他不会知道中间压了一次,对话照样连贯。这是整个项目最值钱的一个设计。
决策三:进程池预初始化
冷启动是延迟大头------加载模型、初始化工具、读权限配置,一次就要好几秒。对内部工具来说,每次 @ 机器人等 5 秒才出字,体验就崩了。
解法很直白:预启动一批 Agent 实例放池子里。
text
进程池:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Agent-1 │ │ Agent-2 │ │ Agent-3 │ │ Agent-4 │
│ (闲置) │ │ (使用中) │ │ (闲置) │ │ (闲置) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
会话到达 → 取闲置实例 → 注入上下文 → 执行 → 归还
心跳 30s,挂了的自动补
默认 4 个实例,配置可改。实测 10 个并发会话完全扛得住,首字响应中位数 < 3 秒。
决策四:工具热插拔------skill.md + tool.py
每个工具就是一个目录,里面两个文件:
text
tools/
├── wiki/
│ ├── skill.md # 工具描述,喂给 Agent
│ └── tool.py # 实际执行逻辑
├── jira/
│ ├── skill.md
│ └── tool.py
└── ...
skill.md 告诉 Agent 这个工具能干什么、怎么调。tool.py 暴露统一接口:
python
async def run(params: dict, context: ToolContext) -> ToolResult:
"""每个工具暴露唯一的 run 函数"""
...
服务启动时扫一遍 tools/ 目录自动注册。新增工具就是加个目录然后重启,核心代码一行不用改。这个设计让我们加工具的成本极低,一周加了 4 个新工具,都是顺手的事。
决策五:权限按群和用户双重控制
内部工具必须管权限------技服能查客户信息,研发不能;研发能触发 Jenkins,技服不能。
用 YAML 配,简单明了:
yaml
# 角色 → 工具
basic: [wiki, knowledge_base, version_query] # 全员
support: [customer_query, oss_logs, jira] # 技服
devops: [git_repos, jenkins] # 研发
# 用户 → 角色
zhangsan: [basic, support]
lisi: [basic, devops]
# 群 → 角色
技服群: [support]
研发群: [devops]
全员群: [basic]
校验在工具调用前完成,无权限直接返回提示。用户级和群级双重校验------在研发群里@机器人,即便你是技服,也只能用研发工具。这个设计防止了"在错的地方调错的工具"。
记忆系统:简单够用就行
没上向量数据库存记忆,就是文件系统:
text
memory/
├── MEMORY.md # 索引
├── facts/ # 明细
│ ├── 2026-07-08.md
│ └── ...
MEMORY.md 长这样:
markdown
# 机器人记忆索引
## 工单
- TKT-0712: XX公司 支付502 → 后端OOM,已重启
## FAQ(被问 ≥3 次自动收录)
- 支付回调失败怎么排查?→ 查nginx → 回调服务 → 支付网关
## 版本
- v3.2.1: 2026-07-05 发布,修复 OOM
规则也简单:
- 同一问题被问 ≥3 次 → 自动写入 MEMORY.md
- 每天凌晨梳理一次,合并同类项
- 15 天没被引用的记忆标记 pending_delete,再等 3 天还不引用就物理删
不上数据库的理由:内部工具,记忆量不大,文件系统 grep 一下比查数据库还快。等真撑不住了再迁不迟。
踩过的坑
坑一:上下文断裂是信任杀手
前面说过,工单多轮追问爆 180k 后机器人"失忆"。这个问题坑了我们好几天------技服同事第一反应是"这玩意不靠谱",直接不用了。
教训:内部工具可以容忍功能少,不能容忍"突然变笨"。滚动摘要窗口上线后,断裂问题消失,信任才回来。
坑二:CLI 外挂做不了上下文管理
最早用 CLI stdin/stdout,想做滚动摘要得杀进程重启,极其难看。迁到 Agent SDK 后,上下文变成可操作的对象,问题自然没了。
教训:选 AI 框架时,先想清楚你要多大控制权。如果你只是问答,CLI 够了;如果你要做上下文管理、工具编排、会话隔离,老老实实用 SDK。
坑三:不加兜底提示,出错没人兜
有一次机器人把日志分析错了,技服拿着错误结论回了客户,差点出事。后来加了"连续 2 轮不确定就提示人工核实"的规则。
教训:AI 给的结论,内部场景也要标"仅供参考"。尤其是会影响对外决策的(工单回复、进度汇报),必须有兜底提示。
跑起来之后
目前运行良好,上线前后的对比数据:
| 指标 | 上线前 | 上线后 | 变化 |
|---|---|---|---|
| 研发日均被打断次数 | 5-8 次 | 1-2 次 | ↓ 75% |
| 工单平均响应时间 | 15 分钟 | 3 分钟 | ↓ 80% |
| FAQ 咨询人工介入率 | 100% | 30% | ↓ 70% |
| 研发连续专注时长 | < 1 小时 | 2+ 小时 | ↑ 100% |
| 首字响应延迟 | --- | < 3 秒(中位数) | --- |
| 并发会话数 | --- | 10 个无压力 | --- |
| 已接入工具数 | 0 | 8 个,持续增加 | --- |
以上数据为部门内部统计,不同团队规模和场景会有差异。
使用场景覆盖:技服工单排查、知识库咨询、项目进度查询、构建失败推送、健康提醒。
最直观的感受:研发被打断的次数明显少了。以前一天被 @ 5 次回答"怎么查日志",现在机器人接了,研发能连续写两小时代码不被打断------这才是我们想要的。
一些反思
回头看,有几个决策做对了:
- Non-goals 先写------防止范围蔓延,这是内部工具存活的关键
- 滚动摘要优先级最高------上下文断裂是信任杀手,必须先解决
- Agent SDK 而不是 CLI------控制权决定了你能做多少事
- 工具热插拔------加工具成本极低,迭代速度快
- 安全边界明确------不碰生产、不碰数据库写、不碰客户数据
也有做得不够的:
- FAQ 知识库 一开始想上向量检索,后来发现关键词匹配就够用,白纠结了一周。教训:先跑起来再优化,别一上来就过度设计
- 记忆系统还没上自动梳理,目前人工维护,量大了会成负担
下一步计划:上向量检索升级 FAQ 精度,把记忆系统的自动梳理补上,再扩展几个推送场景(代码走查严重问题、发布通知)。
如果你也在考虑给团队搭一个内部 AI 助手,我的建议是:先想清楚不做什么,再想做什么。 功能可以慢慢加,边界一旦模糊了,收不回来。