把微信机器人升级成 FAE AI Workbench:RAG、Qdrant、FastAPI、Next.js 和 GitLab SDK 版本联动实战
AI 应用,第一步是接一个大模型 API,然后做一个聊天框。
但我一直觉得,这种方式离真实工作场景还差很远。
在 FAE、技术支持、售前支持、嵌入式 SDK 支持这类岗位里,真正困难的不是“让 AI 回答一句话”,而是把客户零散、不完整、带情绪、带附件、带日志的问题,整理成一个可以被定位、可以被回复、可以转给研发继续处理的问题流。
所以我做了一个项目:wechat_bot。
它最初看起来像一个微信机器人,但我后来把它扩展成了一个面向 FAE 技术支持流程的本地 AI Workbench。
它不只是接入微信,也不只是调用大模型,而是把这些能力串成了一条完整链路:
客户消息 / 微信群 / 剪贴板 / 企业微信
↓
客户问题结构化
↓
本地资料库检索 + Qdrant RAG + 工程源码扫描
↓
SDK 版本关联 GitLab tag / branch / commit / Release Notes
↓
日志初筛 + 错误码提取 + 模块归属判断
↓
客户回复草稿 + 内部研发工单
这套系统的价值不在于“自动回复”,而在于把一个技术支持人员脑子里的经验、资料、判断逻辑和沟通方式,工程化成一个可复用的 AI 工作台。
一、为什么普通 AI 聊天不适合 FAE 场景
真实客户问题通常不是这样的:
请问 ESPxxxx 在 SDK v2.3.1 下 OTA 失败的原因是什么?
而更可能是这样的:
客户:某客户
型号:ESPxxxx
SDK v2.3.1,固件 1.8.0
升级后无法联网,日志里看到 WIFI_ERR_INIT_FAILED,已附 boot.log 和截图。
对人来说,这句话背后其实包含很多隐含任务:
- 提取客户、型号、SDK、固件版本、现象、错误码
- 判断属于 WiFi、Boot、OTA、BLE、低功耗还是驱动问题
- 查公司内部资料、历史问题、PDF 手册、Excel 表格
- 看 SDK 版本是否对应某个 GitLab tag 或 Release Notes
- 从日志里找第一个关键错误点
- 判断还缺哪些信息
- 给客户写一版简洁回复
- 给研发整理一版内部工单
如果只是把这段话扔给大模型,很容易得到一段看似完整但没有依据的回答。
所以我在这个项目里坚持一个原则:
AI 不直接替我“编答案”,而是先进入工程流程,基于真实资料、真实日志、真实版本和真实工程上下文生成结果。
二、整体架构设计
这个项目分成几层。
入口层:
- local 手动模式
- clipboard 剪贴板模式
- wechat_pc 微信 PC
- wecom 企业微信
核心处理层:
- MessageBot
- Router
- SmartRouter
- ConversationContext
- KeywordLearner
- StyleLearner
- CorrectionLearner
Web 工作台:
- FastAPI backend
- Next.js frontend
AI 与知识层:
- LLMClient
- KnowledgeBase
- DocumentLoader
- RagStore
- EmbeddingClient
- ProjectContext
- GitLabSdkResolver
- LogAnalyzer
- ResponseGenerator
- TicketGenerator
后端接口集中在 FastAPI Router 中,例如:
@router.post("/api/workbench/run", response_model=WorkbenchRunResponse)
def run_workbench(req: WorkbenchRunRequest) -> WorkbenchRunResponse:
issue = inbox.parse(...)
knowledge = kb.query(...)
log_analysis = log_analyzer.analyze(...)
response = response_generator.draft(...)
ticket = ticket_generator.generate(...)
return WorkbenchRunResponse(
issue=issue,
knowledge=knowledge,
log_analysis=log_analysis,
response=response,
ticket=ticket,
)
这就是整个系统最核心的设计:不是单个 AI 问答,而是一个 pipeline。
客户输入进来后,会依次经过:
- 问题结构化
- 知识库检索
- 日志分析
- 客户回复生成
- 研发工单生成
这比单纯做聊天机器人更接近真实业务系统。
三、资料库解析:不只读 txt,还要读 PDF、Word、Excel、源码
FAE 场景里的资料非常杂。
有 PDF 手册,有 Word 报告,有 Excel register map,有 Markdown FAQ,还有 .c、.h、.py、.yaml、.log 这些工程文件。
所以我写了一个 DocumentLoader,统一把各种资料解析成 chunk。
支持的文件类型包括:
TEXT_EXTENSIONS = {
".md", ".txt", ".log", ".csv", ".json",
".yaml", ".yml", ".c", ".h", ".py"
}
DOC_EXTENSIONS = TEXT_EXTENSIONS | {
".pdf", ".docx", ".xlsx"
}
PDF 用 pypdf 提取文本,Word 用 python-docx,Excel 用 openpyxl。
核心思路是:
不管原始文件是什么格式,最后都变成统一结构:
@dataclass(frozen=True)
class DocumentChunk:
chunk_id: str
path: Path
title: str
text: str
line_start: int | None = None
line_end: int | None = None
这样后面的 RAG、关键词检索、资料引用、客户回复生成都不需要关心原始格式。
我认为这一步很关键。
很多 AI demo 只处理 Markdown 或纯文本,但真实企业资料绝对不是这么干净的。一个能落地的 AI 工具,必须能面对脏数据、旧文档、多格式资料和工程文件。
四、RAG 检索:Qdrant 向量检索 + 文件扫描兜底
这个项目里我用了 Qdrant 做向量检索。
索引过程大致是:
client.create_collection(
collection_name=self.settings.qdrant_collection,
vectors_config=models.VectorParams(
size=vector_size,
distance=models.Distance.COSINE,
),
)
每个 chunk 会被写入 Qdrant:
models.PointStruct(
id=str(uuid.uuid5(uuid.NAMESPACE_URL, chunk.chunk_id)),
vector=vector,
payload={
"chunk_id": chunk.chunk_id,
"path": str(chunk.path),
"title": chunk.title,
"line": chunk.line_start,
"snippet": chunk.text[:500],
"text": chunk.text,
},
)
搜索时,先把问题转成 embedding,再查 Qdrant:
query_vector = self.embeddings.embed_texts([query])[0]
results = client.search(
collection_name=self.settings.qdrant_collection,
query_vector=query_vector,
limit=top_k,
with_payload=True,
)
但我没有把系统设计成“Qdrant 挂了就不能用”。
在 KnowledgeBase.query() 里,我做了降级逻辑:
sources = self.rag.search(query, req.top_k)
retrieval_mode = "qdrant_vector" if sources else "file_scan"
if not sources:
sources = self.keyword_search(query, req.top_k)
也就是说:
- Qdrant 可用:走向量检索
- Qdrant 不可用:自动退回文件扫描
- 本地资料少:也可以直接跑
- 后续交付:再接正式 embedding API
这个设计看起来简单,但非常实用。
真实工程里,最怕的是 demo 时很炫,一部署就因为某个服务没起来直接崩。我的思路是:AI 系统也要有工程兜底能力。
五、本地 embedding:离线也能跑的确定性向量器
很多 RAG 系统依赖外部 embedding API。
但在企业内部资料、客户资料、FAE 支持场景里,不是所有数据都适合直接传出去。
所以我给系统设计了两种 embedding 模式:
@property
def mode(self) -> str:
if self.settings.embedding_mode == "api" and self.settings.embedding_api_key:
return "api"
return "local"
如果配置了 API,就走外部 embedding。
如果没有配置,就走本地确定性 token vectorizer。
本地模式会提取这些 token:
r"0x[0-9a-fA-F]+|[A-Za-z_][A-Za-z0-9_]{2,}|[\u4e00-\u9fff]{2,8}|\d+\.\d+(?:\.\d+)?"
并对错误码、宏、函数名、十六进制值加权:
if "_" in token or token.startswith("0x") or token.isupper():
weight = 1.8
这个设计非常适合嵌入式资料:
WIFI_ERR_INIT_FAILEDNVDSBLE_GAP0x53022044gpio_intr_callbackv2.3.1
这些关键词在普通自然语言 embedding 里未必特别突出,但在 FAE 场景里可能就是定位问题的关键。
六、工程上下文扫描:客户工程也参与回答
很多客户问题最后发现不是 SDK bug,而是工程配置、调用顺序或宏开关问题。
所以我做了 ProjectContext。
用户可以输入本地客户工程路径,系统会扫描:
PROJECT_EXTENSIONS = {
".c", ".h", ".cpp", ".hpp", ".py",
".js", ".ts", ".tsx", ".md", ".txt",
".log", ".ini", ".cfg", ".json",
".yaml", ".xml", ".cmake", ".uvprojx"
}
然后按 query 关键词匹配工程源码和配置文件。
命中的结果会以 project_scan 的形式进入知识库回答:
retrieval_mode = f"{retrieval_mode}+project_scan"
这让系统从“查资料”升级成了“结合资料和客户工程一起分析”。
这点是我认为这个项目比较有价值的地方:
它不是泛泛的知识库问答,而是在接近真实 FAE 排查流程。
七、SDK 版本联动 GitLab:把版本信息接进 AI 流程
技术支持里经常会遇到一个问题:客户说的 SDK 版本到底对应哪个 tag?有没有 Release Notes?有没有已知问题?
所以我写了 GitLabSdkResolver。
它会根据 SDK 版本尝试匹配:
GITLAB_REF_PATTERNS={version},v{plain},sdk-{version},release/{version}
例如客户输入:
SDK v2.3.1
系统会尝试:
v2.3.1
2.3.1
sdk-v2.3.1
sdk-2.3.1
release/v2.3.1
release/2.3.1
如果匹配到 tag 或 branch,就返回:
- ref 类型
- commit sha
- commit title
- web url
- release notes 片段
然后作为 gitlab_sdk source 加进知识库:
retrieval_mode = f"{retrieval_mode}+gitlab_sdk"
这一步的意义是:
AI 不只是读文档,还能理解“客户当前版本”对应的真实代码状态。
八、日志分析:正则规则 + 模块归属 + 知识库二次检索
我没有直接让大模型看完整日志。
因为日志分析第一步往往不需要大模型,而是需要快速提取结构化线索。
比如错误行匹配:
ERROR_LINE_RE = re.compile(
r"error|failed|fail|assert|panic|fault|exception|timeout|crc|invalid|"
r"异常|失败|超时|复位|死机|错误|断连|无法",
re.I,
)
模块归属规则:
MODULE_RULES = [
("WiFi SDK", ("wifi", "wlan", "dhcp", "ssid", "WIFI_")),
("BLE SDK", ("ble", "gap", "gatt", "att", "smp", "bond", "pair", "蓝牙")),
("Boot/OTA", ("boot", "ota", "crc", "image", "upgrade", "flash", "升级")),
("Power/LowPower", ("sleep", "wake", "rtc", "pwr", "deepsleep", "lightsleep", "唤醒", "低功耗")),
]
日志分析结果包括:
- 关键错误行
- 错误码
- 模块归属
- 时间线
- 错误上下文
- 初步假设
- 缺失信息
- 检查清单
- 相关资料
最有意思的是,日志分析后还会再次调用知识库搜索:
related_docs = self.kb.search(search_query, req.top_k_sources)
这意味着日志不是孤立分析,而是会和资料库联动。
例如日志里出现:
ERROR WIFI_ERR_INIT_FAILED wifi init failed
系统会判断偏向 WiFi SDK,再用型号、SDK 版本、错误码和模块信息去资料库里找相关文档。
九、客户回复和研发工单分离
很多 AI 系统最大的问题是输出对象不清楚。
客户要的是简洁、明确、可执行。
研发要的是环境、日志、复现路径、资料依据和待确认事项。
所以我把输出分成两个生成器。
客户回复:
class ResponseGenerator:
def draft(self, req: ResponseDraftRequest) -> ResponseDraft:
...
要求是:
结构固定为:已查到、建议先处理、还需补充。
总长度控制在 220 字以内。
必须基于给定资料/工程/日志依据。
内部工单:
class TicketGenerator:
def generate(self, req: TicketRequest) -> InternalTicket:
...
生成 Markdown 报告:
## 问题摘要
## 客户环境
## 关键日志
## 相关资料
## 初步判断
## 已尝试步骤
## 需要研发确认
## 建议分配模块
## 严重程度
这就是我理解的 AI 工程化:
不是让 AI 输出一段万能文本,而是根据不同工作对象生成不同格式的结果。
十、一次完整调用示例
请求:
curl -X POST http://127.0.0.1:8000/api/workbench/run ^
-H "Content-Type: application/json" ^
-d "{\"customer_text\":\"客户:某客户\n型号:XC6517\nSDK v2.3.1,固件 1.8.0\n升级后无法联网,日志里看到 WIFI_ERR_INIT_FAILED,已附 boot.log 和截图。\",\"log_text\":\"[00:00:01.112] boot start\n[00:00:03.410] sdk version v2.3.1\n[00:00:05.002] wifi init start\n[00:00:05.351] ERROR WIFI_ERR_INIT_FAILED wifi init failed\n[00:00:05.777] retry wifi init timeout\",\"question\":\"v2.3.1 升级后无法联网怎么排查?\",\"sdk_version\":\"v2.3.1\",\"attachments\":[\"boot.log\",\"screenshot.png\"]}"
系统会返回:
{
"issue": {
"product_model": "XC6517",
"sdk_version": "v2.3.1",
"firmware_version": "1.8.0",
"symptom": "升级后无法联网",
"error_keywords": ["WIFI_ERR_INIT_FAILED"],
"attachments": ["boot.log", "screenshot.png"],
"priority": "P1/P2",
"suggested_owner": "WiFi SDK"
},
"knowledge": {
"retrieval_mode": "qdrant_vector+gitlab_sdk",
"sources": []
},
"log_analysis": {
"module_owner": "WiFi SDK",
"hypotheses": [
"WiFi 初始化、配置迁移或连接参数不一致,优先核对初始化顺序、SSID/加密方式和 DHCP 阶段日志。"
]
},
"response": {
"draft": "已查到该问题和 WiFi 初始化失败相关。建议先核对升级前后 WiFi 初始化顺序、旧配置迁移、SSID/加密方式和 DHCP 阶段日志。还需补充完整 boot log、升级前版本和复现步骤。"
}
}
这类结果比“AI 给我解释一下 WiFi 初始化失败”更接近实际工作流。
十一、为什么我认为这个项目有价值
这个项目表面看是一个 wechat_bot。
但我自己更愿意把它定义成:
一个面向技术支持场景的个人 AI 基础设施。
它包含了现在 AI 应用里非常热的几个方向:
- RAG 知识库
- 向量数据库 Qdrant
- 多格式文档解析
- 本地 embedding fallback
- LLM 工作流编排
- FastAPI 后端服务
- Next.js Web 工作台
- GitLab DevOps 数据联动
- 日志智能分析
- 客户回复生成
- 内部工单自动化
- Docker Compose 多服务部署
更重要的是,它不是为了炫技术而堆技术。
每个模块都对应 FAE 工作里的真实问题。
资料难找,用 RAG。
版本难对,用 GitLab resolver。
日志难看,用 LogAnalyzer。
客户回复慢,用 ResponseGenerator。
研发协作乱,用 TicketGenerator。
微信消息碎片化,用 MessageBot 和多入口接入。
这就是我做这个项目最想表达的东西:
真正有价值的 AI 应用,不是把大模型放进聊天框,而是把 AI 放进真实业务流程。
十二、结语:AI 时代,个人能力也可以工程化
做完这个项目后,我最大的感受是:
AI 最适合放大那些已经有行业经验的人。
如果一个人只会调用 API,那很容易被替代。
但如果一个人懂业务、懂工程、懂客户、懂研发协作,还能把这些东西抽象成系统,那他的价值会被 AI 放大。
这个项目对我来说,不只是一个工具。
它更像是我把个人经验、资料体系、问题判断、客户沟通和研发协作方式,沉淀成了一个可以持续进化的工作台。
未来我会继续完善:
- 更强的 OCR 和扫描版 PDF 解析
- 更高质量的 embedding 模型
- 客户数据隔离和权限系统
- 工单流转和审计
- 历史问题自动学习
- 团队级知识沉淀
- 更完整的企业微信接入
我相信,未来每个技术团队都需要这样的 AI Workbench。
而真正稀缺的人,不是单纯会用 AI 的人,而是能把 AI 和真实业务流程结合起来,并持续做出结果的人。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)