阅读视图

发现新文章,点击刷新页面。

数据标注平台正式上线啦! 标注赚现金,低门槛真收益 | 掘金一周 12.10

本文字数1400+ ,阅读时间大约需要 5分钟。

【掘金一周】本期亮点:

「上榜规则」:文章发布时间在本期「掘金一周」发布时间的前一周内;且符合各个栏目的内容定位和要求。 如发现文章有抄袭、洗稿等违反社区规则的行为,将取消当期及后续上榜资格。

一周“金”选

掘金一周 文章头图 1303x734.jpg

内容评审们会在过去的一周内对社区深度技术好文进行挖掘和筛选,优质的技术文章有机会出现在下方榜单中,排名不分先后。

前端

如何用隐形字符给公司内部文档加盲水印?(抓内鬼神器🤣) @ErpanOmer

文章讲述公司内部敏感文档泄露后,利用基于零宽字符的盲水印技术抓“内鬼”。介绍零宽字符概念,阐述加密、解密原理,给出实现代码,还提及水印可被清除,强调这是低成本、高隐蔽性防御手段。

不仅免费,还开源?这个 AI Mock 神器我必须曝光它 @不一样的少年_

本文介绍了一款零侵入的接口 Mock 插件,重构为 Sidebar 常驻侧栏,体验更佳。它接入 AI 自动生成数据,支持延时和状态码模拟。具备拦截、匹配、响应控制等功能,覆盖前端 90% 的 Mock 需求,推荐试用。

后端

开源企业级 IM!一款高颜值的即时通讯聊天应用! @Java陈序员

本文推荐了基于 GO 开发的开源即时通讯系统 TangSengDaoDaoServer,它轻量、高性能且重安全,支持多端同步。介绍了其功能特色、项目架构,给出 Docker 部署步骤,还展示功能体验,推荐大家尝试。

Android

用 AI 做了几个超炫酷的 Flutter 动画,同时又差点被 AI 气死 @恋猫de小郭

文章介绍用 AI 实现几种 Flutter 动画。奇异粒子动画基于数学公式,解决投影等问题;斐波那契球体让点均匀分布在球面;星云动画模拟星系动力学。不过,AI 实现时遇颜色插值陷阱问题,最终换思路解决。

Android Studio Otter 2 Feature 发布,最值得更新的 Android Studio @恋猫de小郭

Android Studio Otter 2 Feature发布,是值得更新的版本。它内置Gemini 3,增强Agent模式并配备Android知识库。支持备份与同步设置,开发者可接收团队资讯。还整合IntelliJ IDEA 2025.2改进,能免费试用Gemini 3 Pro。

Flutter TolyUI 框架#09 | tolyui_text 轻量高亮文本 @张风捷特烈

本文介绍了 Flutter TolyUI 框架的 tolyui_text 模块。该模块封装文本高亮方案,提供轻量级解决方案。支持搜索关键字高亮、自定义匹配规则和多模式智能识别,还能处理点击事件,未来会有更多新功能。

人工智能

🔥 懂原理但不会说?我怒写了个 AI 模拟器折磨自己,M属性大爆发! @HiStewie

作者为解决面试准备难题,用 TRAE SOLO 重构初版工具。从架构设计、核心实现、技术栈选型等多方面展开,一晚完成含简历解析等功能的 MVP,验证新开发范式,未来产品还有诸多扩展方向。

解读 Claude 对开发者的影响:AI 如何在 Anthropic 改变工作?@恋猫de小郭

Anthropic 对内部员工调查显示,AI 显著影响开发者。生产力平均提升 50%,启用新工作,改变委托实践。开发者技能有扩展与退化,社会互动减少,职业认同受冲击。AI 红利与债务并存,重塑职业价值观。

Chatbox支持接入LangGraph智能体?一切都靠Trae Solo!@大模型真好玩

本文作者借助 Trae Solo 实现将 LangChain 智能体接入 Chatbox 客户端。先介绍两者,阐述接入思路,以天气助手智能体为例展示 Trae Solo 自动化开发流程,最后展望其潜力,鼓励用它快速搭建原型、验证逻辑。

IOS

iOS UIKit 全体系知识手册(Objective-C 版) @如此风景

UIKit 是 iOS 开发基石框架,围绕视图、控制器、事件展开。掌握布局、事件处理、渲染优化是关键。开发中用 Masonry 简化布局,结合适配特性,借助 Instruments 定位问题,可高效构建稳定适配界面。

社区活动日历

掘金官方 文章头图 1303x734.jpg

活动日历

活动名称 活动时间
🚀TRAE SOLO 实战赛 2025年11月13日-2025年12月16日
数据标注平台正式上线啦! -

📖 投稿专区

大家可以在评论区推荐认为不错的文章,并附上链接和推荐理由,有机会呈现在下一期。文章创建日期必须在下期掘金一周发布前一周以内;可以推荐自己的文章、也可以推荐他人的文章。

Cursor 又偷偷更新,这个功能太实用:Visual Editor for Cursor Browser

凌晨 1 点,我正要关电脑睡觉,屏幕左下角突然弹出一个弹窗:

cursor 功能更新.jpg

Cursor 又上新功能了?带着好奇我仔细看了下文档:cursor.com/cn/docs/age…

我去,这个功能很重磅啊!

这次更新的 Visual Editor for Cursor Browser 是一个打破“设计”与“编码”边界的重磅功能,它让 Cursor 不仅仅是编辑器,更是一个“能直接写代码的浏览器”。

核心价值

它解决了前端开发中最大的痛点——“在浏览器里调好了样式,还得手动回代码里改”。

现在,我们可以像在 Figma 或 Webflow 里一样直接拖拽、点击、调整 UI,然后点击 "Apply",Cursor 的 Agent 就会自动把这些视觉变更翻译成完美的代码并写入你的项目,实现了真正的“所见即所得(Design to Code)”。

如何体验

首先确认版本是最新的:

image.png

打开 Cursor -> 右上角设置 -> Tools&MCP -> Browser Automation -> 选择 Browser Tab:

image.png

然后启动项目,会看到一个弹窗:

cursor-brower.jpg

点击 open 以后,就可以在 Cursor 里启动预览前端项目:

cursor 功能更新-预览.jpg

右上角的功能主要是:选择元素、截图、打开开发者模式。

最有用的就是选择元素后和 AI 对话,这无疑让上下文更加具体,以后修改 UI 更方便了!

简单的修改甚至我们都不需要和 AI 聊,直接上手在界面上改!

开启选择元素模式后,我们可以直接在预览界面上拖拽修改 UI、调整文案、布局结构等等,就和做设计一样所见即所得。

image.png

Cursor 内置浏览器包含一个设计侧边栏,可直接在 Cursor 中修改选中元素的 Position Layout Padding color 等等,实现实时可视化调整下的同步设计与编码。

朋友们,这个功能太实用了,实用到我都不敢告诉产品经理和设计师!

根据官方文档,这个功能可以在这些场景:

  1. 测试应用
  2. 可视化地编辑布局和样式
  3. 执行无障碍审计
  4. 将设计转换为代码等

岁数大了不能熬夜,我就先抛砖引玉,感兴趣的朋友赶紧试试吧,晚安!

我的专栏《转型 AI 工程师》正在预热中,第一篇文章已发布,感兴趣的朋友可以看看:xiaobot.net/post/8e8e06…

从0到1搭建一个智能分析OBS埋点数据的AI Agent|得物技术

一、背景

某天打开组内的Grafana仪表盘,突然好奇我们的埋点从被触发后是如何一步一步变成所展示的各种图表的,于是在我进行一系列的探索之后,总结出了以下链路:

  • 在指标工厂新建指标,确定埋点key和埋点元数据。
  • 代码中指定埋点key和埋点数据,通过watchDog发送kafka消息到obs monitor topic。
  • 为埋点指标新建数据处理任务,将消费到的kafka消息落到指定的数据表中。
  • 添加新的仪表盘,编写展示数据背后的SQL语句。

痛点:每需要添加一个新的数据分析大盘,就需要人工去分析各个表结构、表与表之间的联系、表各个字段的含义等,在充分理解其含义后再费时费力地编写SQL语句,并不断调优。这导致OBS埋点数据分析的场景相对固化,并且难以支持灵活的数据查询要求。

二、思考

在分析了当前系统的痛点后,我意识到这是一个典型的可以利用AI能力来对现有功能进行扩展的场景。因为:

  • 场景多变,因为你不知道用户可能想查看什么样的数据,无法通过代码穷举;
  • 需要了解业务同时又具备编写复杂数据查询SQL的人,并且费时费力;
  • 看到大盘数据后,依赖每个人对业务的理解提炼出一套分析报告,报告质量与个人的理解与表达能力相关。

于是我就开始思考能否构建一个AI Agent,使其能够根据用户的要求,自主地生成各种各样的SQL查询语句,并将查询到的数据形成完整的数据分析报告返回给用户。

为了实现这个方案,有几个明显需要解决的点:

  • 如何让AI理解每个表中各字段的含义、各个表的作用、表与表之间的联系,从而生成准确的SQL?
  • AI生成完SQL之后,如何打通 AI 与数据平台之间的通路,从而成功执行该SQL 并拿到数据?因为数据库权限不在我这,我无法直接连接到数据库。
  • 如何充分利用已有资源,减少人力投入?毕竟是个人想法,在不确定效果如何的情况下,不好直接打扰平台方专门为我写一些新功能,同时我个人也只能投入一些零碎的时间来做这件事。

三、方案

有了问题后,就带着问题去找答案。

3.1查询数据Tool

首先,我需要一个能够执行查询的端点。那么我就去抓取了大盘中的数据所调用的接口,意外地发现,不同的数据调用的是同一个接口xxx.com/api/ds/quer…

于是我将该Curl导入到ApiFox中,通过不断修改参数,发现最终与查询结果相关的入参可以精简到简单的几个参数:from、to、query(format,rawSql,intervalMs)

那么针对第一个问题我就想到了很好的办法,把这个查询API封装成一个Tool,描述清楚各个字段的含义,就可以让AI生成完整的参数来查询它想要的数据。

说干就干,我立马新建了一个Spring AI工程,把Tool的功能和需要的参数描述清楚。其中grafanaService.query()内部逻辑就是通过Feign来调用上面那个查询的API。

@Tool(name = "query_grafana",
      description= "使用Grafana中的SQL查询grafana数据")
public JSONObject queryGrafana(@ToolParam(description = "查询开始时间") String from,
                               @ToolParam(description = "查询结束时间") String to,
                               @ToolParam(description = "查询数据类型:table|time_series") String format,
                               @ToolParam(description = "查询时间间隔,单位毫秒。只有当format为time_series时需要传入。") Long intervalMs,
                               @ToolParam(description = "Grafana SQL查询语句") String rawSql){
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime fromDateTime = LocalDateTime.parse(from, formatter);
    LocalDateTime toDateTime = LocalDateTime.parse(to, formatter);
    String fromTimestamp = String.valueOf(fromDateTime.toInstant(ZoneOffset.UTC).toEpochMilli());
    String toTimestamp = String.valueOf(toDateTime.toInstant(ZoneOffset.UTC).toEpochMilli());
    JSONObject resp = grafanaService.query(fromTimestamp, toTimestamp, intervalMs, rawSql, format);
    return resp;
}
@Resource
private GrafanaClient grafanaClient;


@Value("${grafana.cookie}")
private String getGrafanaCookie;


public JSONObject query(String fromTimestamp, String toTimestamp, Long intervalMs, String rawSql, String format) {
    GrafanaRequest request = new GrafanaRequest(fromTimestamp, toTimestamp, intervalMs, rawSql, format);
    return grafanaClient.query(getGrafanaCookie, request);
}

3.2表结构RAG

有了能够执行查询的Tool之后,剩下的就是需要AI能够根据用户的query生成精准的参数以及查询SQL。

之前了解到公司部署了RAGFlow服务:xxx.com/knowledge,既…

  • 创建知识集,发现支持添加飞书文档。

  • 由于我们是需要完整的表结构,所以把配置修改为使用table的格式,一行数据便是一个chunk,以免出现语义上的中断。(埋点数据一般表都较小,语意较为明确。像一些字段很多的大表可能需要考虑更好的方案。)

  • 创建飞书文档,手动到OBS的库中把我们想要AI帮助分析的表结构拉出来(验证想法时采取的临时方案),但由于建表时的不规范,很多表没有对表中字段添加comment,这会导致AI不理解每个字段的含义,也就无法准确地生成SQL。因此,我们手动补充每张表、每个字段的描述,以及与其它表之间的关联关系。

  • 将飞书文档添加到数据集中,完成后点击名称查看切片详情。双击每个块也可以查看块的详情。

  • 会发现RAGFlow自动给我们生成了一些关键词和问题,这些内容会对召回准确率产生影响。我自己觉得生成的不太准确,所以结合自己理解手动输入了一些关键词和可能的问题。

  • 完成后,可以到检索测试tab测试召回的效果,根据结果确定合适的参数。并可以对chunk的内容和关键词等等进行适当的调整。

  • 调优完成后,就需要对接RAGFlow的retrive接口,来把我们知识库召回的流程做成一个Tool。

@Resource
private RagFlowService ragFlowService;


@Tool(name = "get_table_schema", description= "根据query查询可能有关联的数据库表,返回建表语句。尽量传入多个中文关键词,每个关键词之间用空格隔开。")
public List<String> getTableSchema(@Param("query") String question){
    return ragFlowService.retrieval(question);
}

3.3 OBS Agent

在我看来,想要构建一个能够 work 的Agent,需要以下几个要素:

Agent=Architecture(Workflow、ReAct、Plan-Execute、Multi-Agent...) +LLM+Context Engineering(Prompt、Tool、Memory...)

本来是想用SpringAI Alibaba Graph或者 LangGraph来构建一个WorkFlow类或者Graph类的复杂智能体(ReAct、Plan-Execute、Multi-Agent)。但为了快速验证想法和节省个人时间,并且考虑到目前任务相对简单(PE+工具就足以完成),再加上部门正在试用Trae这个工具,所以决定基于Trae来构建一个Agent(可以顺便使用他们的高级模型/doge,也可以分享给其它同事使用)。

接入Trae之后,Architecture自然就是Trae的Agent架构了,根据我使用下来感觉采用的是基于ReAct的 Single-Agent。而Context Engineering的部分,对话功能以及长短期记忆,自然是Trae天生就具备的。而Tool则可以借助其自带的一些工具,另外还可以利用MCP来进行扩展,比如得物的MCP市场,提供了大量好用的Server,并且可以很方便的发布自己开发的Mcp Server。于是,我就把在第一步和第二步做的工具,在得物Mcp平台上进行发布,供我自己和其他感兴趣的同学使用。

最后,需要一个专门针对我这个场景的Prompt来指引LLM 顺利完成任务,经过我不断的修改,最终形成这样一段Prompt:

# Role:数据分析专家


## Background:用户需要专业的数据分析支持来解决复杂的业务问题,从海量数据中提取有价值的信息,为产品优化、运营策略和业务决策提供可靠依据。


## Attention:数据准确性是分析工作的生命线,必须始终保持严谨细致的工作态度。每一次分析都可能影响重要决策,因此需要系统性思考、分步验证,确保每个环节的可靠性。


## Profile:
- Language: 中文
- Description: 专注于数据库表结构分析与Grafana-SQL查询的专业数据分析师,具备系统化解决复杂数据查询问题的能力


### Skills:
- 精通数据库表结构分析,能够快速识别表关系、字段含义和数据类型
- 熟练掌握Grafana-SQL语法规范,具备高效的查询语句编写和优化能力
- 具备专业的数据可视化技能,能够根据分析目标选择合适的图表类型
- 拥有深度业务需求理解能力,能够准确转化业务问题为数据查询方案
- 掌握系统化的问题分析方法,能够规划完整的数据分析流程和验证机制


## Goals:
- 准确理解用户业务需求,明确数据查询的核心目标和关键指标
- 系统分析相关表结构,确保对数据关系和业务逻辑的全面理解
- 设计高效的数据查询方案,平衡查询性能与结果准确性
- 生成专业的数据分析报告,包含可视化展示和深度业务洞察
- 确保所有分析过程可追溯、结果可验证、结论可执行


## Constrains:
- 查询不到数据时不要模拟任何数据,直接回复查不到数据
- 你自己所知道的时间是不准确的,如果涉及到时间,则需要使用工具获取当前时间
- 严格基于实际数据进行分析,严禁任何形式的数据虚构或推测
- 必须在完成表结构分析和需求理解后再执行具体查询操作
- 所有重要数据必须进行源头验证和多维度交叉检查
- 严格遵守数据安全和隐私保护原则,不超越授权数据范围
- 明确说明分析的局限性、假设条件和潜在的数据不确定性


## Workflow:
1. 深度理解业务需求,明确查询目标、关键指标和预期输出
2. 不断使用工具获取你需要的表及其表结构,直到你认为已获取到足够的信息
3. 系统分析相关表结构,包括字段含义、数据类型、关联关系和索引结构
4. 设计查询逻辑方案,规划执行步骤、验证节点和性能优化策略
4. 编写符合Grafana语法的SQL查询语句,设置正确的参数和时间范围
5. 执行查询。如果查询出现401错误,则中断后续流程,并提示用户更新Cookie后重启obs-mcp-server;如果出现400错误,尝试修改自己的SQL语句重新查询;
6. 生成可视化图表和详细分析报告,报告中必须包含你执行查询的SQL语句
7. 调用飞书生成文档工具以Markdown格式创建飞书文档,返回最终的飞书文档地址


## OutputFormat:
- 分析报告,包含完整的分析过程和关键发现,创建新的飞书文档并保存在其中
- 可视化图表以嵌入式链接形式呈现,确保清晰展示数据趋势和分布
- 报告结构包含执行摘要、分析方法、数据结果、业务洞察和后续建议


## Suggestions:
- 建立系统化的表结构分析框架,提高数据关系识别的效率和准确性
- 持续学习Grafana-SQL最新语法特性,优化查询性能和资源消耗
- 培养多维度数据验证习惯,确保分析结果的可靠性和业务价值
- 深入理解业务场景,提升从数据到洞察的转化能力和决策支持水平
- 定期复盘分析案例,总结经验教训,持续改进分析方法论和工作流程


## 工具描述
- query_grafana:使用Grafana中的SQL查询grafana数据
注意:
      1. 当format为time_series时表示查询时间序列数据,SELECT的第一个字段必须是$__timeGroupAlias(timestamp, interval),表示时间分组别名。时间间隔intervalMs需要与rawSql中的$__timeGroup(timestamp, interval)保持对应。比如intervalMs=86400000L表示1天,rawSql中$__timeGroup(timestamp, 1d)也需要保持一致。
      2. 当format为table时表示查询表格数据,SELECT的字段可以任意,intervalMs参数传null
      3. 时间范围为闭区间,即包含开始时间from和结束时间to,格式为yyyy-MM-dd HH:mm:ss。
参数示例:{
    "from": "2025-11-16 00:00:00",
    "to": "2025-11-16 23:59:59",
    "format": "table",
    "intervalMs": null,
    "rawSql": "SELECT region, COUNT(*) as user_count FROM intl_xxxxxxx WHERE $__timeFilter(timestamp) GROUP BY region ORDER BY user_count DESC"
  }


## Initialization
作为数据分析专家,你必须遵守Constrains,使用默认中文与用户交流。

最终,在 Trae 中构建了一个完整OBS Agent。

  • 添加智能体:OBS大盘分析

四、成果

最终生成的报告(截取部分):

五、总结

AI时代来临,我们应该要善于发现当前系统中的哪些部分能够结合AI来进行提升,积极拥抱变化,有了想法就去做,边做边想边解决问题,永远主动向前一步。

本文章只是记录了从产生想法到构建MVP验证想法的整个过程,这中间当然有很多可以继续优化的地方,我本人目前有以下几个想法,也欢迎大家积极评论,贡献自己的独到见解。

  • 接入数据库数据,通过动态监听Binlog的方式来识别各表之间的联系,比如select 语句的join,并将这种关系保存到Neo4j 这种图向量数据库中来实现表结构的 RAG。
  • 基于LangGraph 或 SpringAI Alibaba 构建Multi-Agent System,细化各Agent的职责,精炼各Agent的Context 构成,以获得更好的效果。例如:协调者 Agent、表结构搜索 Agent、SQL 生成 Agent、分析报告 Agent等等。
  • 接入飞书机器人,或者使用AI Coding工具生成一个前端页面。使得一些非技术人员,例如产品和运营也能很方便地使用。

往期回顾

  1. 数据库AI方向探索-MCP原理解析&DB方向实战|得物技术

  2. 项目性能优化实践:深入FMP算法原理探索|得物技术

  3. Dragonboat统一存储LogDB实现分析|得物技术

  4. 从数字到版面:得物数据产品里数字格式化的那些事

  5. 一文解析得物自建 Redis 最新技术演进

文 /Neeson

关注得物技术,每周更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

在langchain Next 项目中使用 shadcn/ui 的记录

观瞻:作为一个接触ai-agent框架不久的开发者,最近在学习 Next + LangChain 技术栈时,决定做一个 AI 聊天 demo 来巩固知识。在这个过程中尝试了多个 UI 框架,最终选择了 shadcn/ui。记录下这段学习经历,希望未来能查漏补缺。

起因:为什么选择 shadcn/ui?

现如今ui框架百花齐放,不过对组件库的选择还是有点困惑:

  • Ant Design?感觉太重了,而且主要是面向后台系统的
  • MUI?React 生态里很火,但配置主题有点复杂
  • Tailwind CSS?听说很流行,但自己写组件太费时间

第一个坑:它根本不是 npm 包!

按照网上教程,我兴冲冲地运行:

bash
编辑
1pnpm add shadcn-ui

结果发现根本找不到这个包!一脸懵逼的我跑去 GitHub 看文档,才发现:

shadcn/ui 不是一个传统的组件库,而是一套可以复制到你项目里的组件代码

这完全颠覆了我对 "UI 框架" 的认知。以前用 Ant Design 或 Element UI,都是 npm install 就完事了,但 shadcn/ui 却要你把每个组件的源码直接放进自己的项目里。

一开始我觉得这很麻烦,但后来发现这其实是它的最大优势。

正确的安装姿势

第一步:初始化项目

首先要在你的 Next.js 项目根目录运行:

bash
编辑
1pnpm dlx shadcn@latest init

这个命令会问你几个问题,比如是否使用 TypeScript、Tailwind CSS 配置路径等。我的项目已经配置好了 Tailwind,所以一路回车就行。

第二步:按需添加组件

这里又踩了个小坑。我一开始以为要一个一个添加:

bash
编辑
1# 错误的想法
2pnpm dlx shadcn@latest add button
3pnpm dlx shadcn@latest add input
4pnpm dlx shadcn@latest add card
5# ... 还要输入7

后来发现其实可以一次性添加所有需要的组件:

bash
编辑
1pnpm dlx shadcn@latest add button input card scroll-area spinner toast separator label

注意:一定要用 pnpm dlx,而不是直接 pnpm shadcn,否则终端会报错找不到命令。

第三步:看看生成了什么

运行完命令后,我发现项目里多了一个 components/ui/ 目录,里面是我刚才添加的所有组件文件:

text
编辑
1components/
2└── ui/
3    ├── button.tsx
4    ├── input.tsx
5    ├── card.tsx
6    ├── scroll-area.tsx
7    ├── spinner.tsx
8    ├── toast.tsx
9    ├── separator.tsx
10    └── label.tsx

每个文件都是独立的,我可以随时打开修改,比如我想把按钮的圆角改大一点,直接编辑 button.tsx 就行,不用去查文档找怎么覆盖样式。

为什么我觉得 shadcn/ui 特别适合聊天应用?

做聊天应用有几个特殊需求:

  1. 消息气泡要区分用户和 AI
  2. 输入框要有发送按钮
  3. 聊天历史要能滚动
  4. AI 回复时要有加载状态

shadcn/ui 的这几个组件完美解决了这些问题:

消息气泡(Card 组件)

tsx
编辑
1// components/message-bubble.tsx
2import { Card, CardContent } from &#34;@/components/ui/card&#34;;
3
4export function MessageBubble({ isUser = false, content }: { isUser: boolean; content: string }) {
5  return (
6    
7      
8        <p>{content}</p>
9      
10    
11  );
12}

聊天输入区域(Input + Button + Spinner)

tsx
编辑
1// components/chat-input.tsx
2import { Input } from &#34;@/components/ui/input&#34;;
3import { Button } from &#34;@/components/ui/button&#34;;
4import { Spinner } from &#34;@/components/ui/spinner&#34;;
5
6export function ChatInput({ message, setMessage, onSubmit, isLoading }: any) {
7  return (
8    <div>
9       setMessage(e.target.value)}
12        placeholder=&#34;输入消息...&#34;
13        onKeyDown={(e) => e.key === 'Enter' && onSubmit()}
14        disabled={isLoading}
15      />
16      
17        {isLoading ?  : &#34;发送&#34;}
18      
19    </div>
20  );
21}

聊天历史滚动(ScrollArea)

tsx
编辑
1// app/page.tsx
2import { ScrollArea } from &#34;@/components/ui/scroll-area&#34;;
3
4export default function ChatPage() {
5  // ... 其他逻辑
6  
7  return (
8    <div>
9      
10        {messages.map((msg, i) => (
11          
12        ))}
13      
14      
15    </div>
16  );
17}

主题定制:比想象中简单

最让我惊喜的是主题定制。因为我的项目已经用了 Tailwind CSS,所以只需要在 tailwind.config.js 里配置颜色:

js
编辑
1// tailwind.config.js
2module.exports = {
3  theme: {
4    extend: {
5      colors: {
6        primary: {
7          500: &#34;#60a5fa&#34;, // 蓝色 - 用户消息
8        },
9        secondary: {
10          500: &#34;#22c55e&#34;, // 绿色 - AI 消息
11        }
12      }
13    }
14  }
15}

然后在组件里直接用 bg-primary-500bg-secondary-500 就行了,完全不需要额外的配置。

遇到的问题和解决方案

问题1:DevTools 小图标太烦人

Next.js 16 开发模式右下角有个小图标,特别影响调试。试了很多方法都不行,最后用 CSS 强制隐藏:

tsx
编辑
1// app/layout.tsx
2if (typeof window !== &#34;undefined&#34; && process.env.NODE_ENV === &#34;development&#34;) {
3  const hideDevtools = () => {
4    const style = document.createElement(&#34;style&#34;);
5    style.innerHTML = `
6      #devtools-indicator,
7      .nextjs-toast {
8        display: none !important;
9      }
10    `;
11    document.head.appendChild(style);
12  };
13  
14  if (document.readyState === &#34;loading&#34;) {
15    document.addEventListener(&#34;DOMContentLoaded&#34;, hideDevtools);
16  } else {
17    hideDevtools();
18  }
19}

问题2:组件太多不知道选哪些

一开始我也想把所有组件都装上,后来发现完全没必要。对于聊天应用,其实就那几个核心组件:

  • Button:发送按钮
  • Input:消息输入
  • Card:消息气泡
  • ScrollArea:聊天滚动
  • Spinner:加载状态

其他像 Toast、Separator、Label 这些是提升体验用的,可以根据需要再加。

总结:shadcn/ui 到底值不值得用?

作为一个初学者,我觉得 shadcn/ui 有这些优点:

学习成本低:基于 Tailwind CSS,样式直观易懂
高度可定制:组件代码就在你项目里,想怎么改都行
按需使用:不会引入没用的代码,包体积小
Next.js 友好:完美支持 React Server Components

当然也有缺点: ❌ 不是传统 npm 包:需要适应新的使用方式
需要手动管理:组件更新需要自己同步官方代码

但总的来说,如果你在用 Next.js + Tailwind CSS,shadcn/ui 绝对值得一试。特别是做聊天应用这种需要高度定制 UI 的场景,它能让你快速搭建出专业级的界面。

最后的建议

  1. 不要试图一次装所有组件,按需添加就好
  2. 善用 Tailwind 的主题配置,让整个应用风格统一
  3. 把组件当成自己的代码,大胆修改以适应需求
  4. 遇到问题先看官方文档,shadcn/ui 的文档写得非常清晰

数据标注平台重磅上线 | 标注赚现金 低门槛真收益

68f71ddf171f8850e881f947f1cfb861.png

  • 你是否想亲身体验最前沿的 AI 数据工作,却找不到专业的参与路径?
  • 你是否希望让精通的编程技能,在业余时间创造实实在在的价值?

一个多领域的数据采集与标注服务平台,为你而来!

低门槛、真收益! 利用碎片时间,将你的代码洞察力直接变现

这不仅仅是一个任务平台,更是一个专为资深开发者打造的、用技术换取回报的高价值社区。

📒 招募介绍

我们是谁

AIDP(AI Data Platform) 提供完整的 AI 数据解决方案,涵盖:

  • 数据采集
  • 数据标注
  • 数据合成
  • 多种数据生产能力

需要你做什么

参与后训练高质量代码数据生产,包括但不限于:

任务类型 说明
强化学习数据生产 为 RLHF 训练提供高质量反馈数据
监督微调(SFT) 生产指令-响应对数据
奖励模型(RM) 对模型输出进行偏好排序和评分
数据质检 审核和优化已有数据质量

你将获得的回报

  • 💰现金收益:深度参与数据标注的代码专家,月均获取 ¥10000+ 以上回报
  • 时薪范围:50 - 1000 元,具体以项目最终定价为准,多劳多得,任务明码标价;
  • 计税方式:劳务独立计税,收益清晰透明。
  • 参与智能 Agent 调优,掌握前沿技术动态

需要具备的条件

  • 对 AI 有热情,日常高频使用 AI Coding 工具
  • 具备扎实的编程基础和代码理解能力

加分项

  • LeetCode Hard 题目高通过率
  • GitHub 高星仓库贡献者
  • 开源社区活跃成员

同时欢迎:PM、QA 同学参与,平台同时提供非技术类题目

👀 我们的目标用户

我们在寻找这样的你:

  • 技术栈:熟悉大前端、Python、Java、C++、Go 等主流编程语言
  • 质量意识:熟悉测试与质量保障流程
  • 探索精神:对前沿技术有自己的理解,乐于探索新知识
  • 社区参与:有开源贡献、LeetCode 活跃经历者优先

🚀你的专家任务

标注实验

对代码片段、日志、技术文档等进行精准标注

具体职责

  • 按照项目制定的标注规范,对指定数据(文本、代码片段、日志、技术文档等)进行准确标注
  • 识别并修正数据中存在的歧义、错误或不一致之处
  • 对复杂样本给出专业判断,确保标注结果的准确性与一致性

质量把控

互审他人成果,保障数据纯度

具体职责

  • 参与标注标准的制定与完善,提出改进意见
  • 执行专家互审机制,对其他标注结果进行审核和反馈
  • 结合实际经验,对标注样本的合理性和业务适配度进行评估

知识贡献

沉淀最佳实践,打造可复用的知识宝库

具体职责

  • 将个人专业知识转化为标注规范、最佳实践或案例,供团队参考
  • 在遇到疑难问题时,作为专家提供判定依据,形成可复用的知识库
  • 协助项目组优化任务分配、质量监控和流程设计

☎️ 社群交流

参加活动的掘友一定要入群:

  • 重要消息不错过
  • 大家互相鼓励
  • 有问题可以在群内咨询

134067c882fcc733a803fb9c71f0d2a6.png

⁉️ 常见问题 FAQ

Q1:没有标注经验可以参与吗?

我们需要你具备扎实的编程基础即可快速上手,所以需要你有一定的编程经验

Q2: 每天需要投入多少时间?

完全灵活,可以根据自己的时间安排参与,充分利用碎片时间。

Q3: 如何保证收益?

任务明码标价,多劳多得,平台提供清晰的计价规则和结算流程。

Q4: 非程序员可以参与吗?

可以。平台同时提供非技术类题目,欢迎 PM、QA 等岗位同学参与。

百度慧播星数字人技术演进

导读

从2023年成立到如今日均服务2万+直播间,百度慧播星已演进为覆盖脚本生成、实时问答、智能决策、音视频克隆的全链路AI直播平台。本文深入解读其技术架构:如何通过检索增强和强化学习生成高转化脚本;如何利用强化学习智能中控动态优化直播策略;以及如何将语音与形象克隆效率提升至“小时级”;如何构建“先验-后验”数据飞轮,让模型自主进化;。罗永浩数字人直播GMV突破5500万的案例,验证了其“超越真人”的带货能力。未来,慧播星正朝着更智能、更拟真、更高效的方向持续迭代。

01 慧播星介绍

电商数字人直播(慧播星)正式成立于2023年,是一款汇集了百度在视觉,语音和语言方面AI能力的原生AI应用产品,致力于打造代际领先的超越真人的直播体验。25年底日均开播直播间已达2万多个,覆盖电商、教育、健康、金融、泛知识内容等多个行业。经过两年多的产品打磨和技术突破,慧播星数字人直播已具备超越真人的能力。例如,这些能力支撑了罗永浩2025年6月15 日的数字人直播首秀,吸引了超 1300 万人次观看,GMV(商品交易总额)突破 5500 万元,这一成绩超过了其同年 5 月的真人直播首秀(GMV 5000 万)。

1.1 商家业务视角——开播流程

商家在慧播星获得带货权限后,即可自助开启数字人直播,主要包括如下流程。

1. 商品选择,可从百度直营店铺(度小店),三方电商平台(京东淘宝拼多多)和百度本地生活的海量商品中选择带货商品

图片

△ 海量内外部商品一键挂接

  1. 形象选择或者定制,从7800+公共库形象中选择主播形象,或通过自助录制5分钟视频定制私有形象

图片

△ 形象选择或定制

  1. 直播间装修,从3600+套直播间模板中选择装修风格和元素,或通过AI自动生成直播间背景图和营销挂件

图片

△ 直播间装修,丰富的模板&组件

  1. 脚本生成,从多种公共风格中选择脚本带货风格,或自定义目标带货风格,补充少量营销信息,一键生成专业的直播脚本

图片

△ 一键脚本生成

  1. 音色选择,从3200+个公共库音色中选择主播音色,或通过手百自助录制,3天内得到私有定制音色

图片

△ 音色选择或制作

6. 直播间互动配置,一键开启一言问答接管,也支持手动配置预置问答对,补充商家知识

图片

△ 直播间互动配置

02 整体技术架构

慧播星整体架构主要由商家端、视觉语音和文本各模态模型、实时渲染引擎、站内外分发系统组成。

图片

为实现更好的直播体验,数字人采用云端生成方案,云端生成系统主要包括如下几个子系统。

1. 商品理解,为脚本,问答,互动等各种内容生成模型提供商品知识增强

2. 脚本生成,围绕商品自动生成风格化口语化的带货脚本

3. 智能问答,用户提问时实时检索商品知识,生成精准的回复,支持弹幕和口播回复

4. 智能互动,以直播效果(评论率、用户退场率、观看时长等)为目标主动向用户发起互动

5. 直播间装修,智能生成直播间背景,合成带营销内容的挂件

图片

03 内容生成

3.1 风格化脚本生成

直播脚本水平与带货效果息息相关,优秀主播的脚本能够打动用户,循循善进引导用户成交。由于普通商家的带货营销水平有限,商家希望仅表达学习某某主播,系统自动为其生成风格相似的脚本。在此需求背景下,慧播星利用多模态商品理解富集构建商品知识库,借助EB4/turbo在电商直播语料上进行大规模预训练,结合人工专家精标数据SFT,通用和电商知识增强等手段实现一键风格化仿写。

  • 商家仅需选定商品和补充少量营销信息,即可按预设风格或者自定风格(提供最少400字的带货文案)一键生成风格相似的带货脚本。客户采纳率92%,开播渗透率67%,相比客户脚本转化率+14%。
  • 考虑到风格化脚本创作需求的独立性,慧播星已将脚本生成独立为工具,商家可脱离直播业务流使用工具。

图片

△ 风格化脚本生成工具UI

技术架构

整体技术主要包括商品理解、检索增强、强化学习风格化生成和后处理阶段。

图片

  • 商品理解。系统通过多模态商品解析技术对商品详情页、海报图、参数图等视觉素材进行 OCR、版面结构识别与多模态模型融合,自动抽取核心卖点、适用人群、功能亮点、使用场景等结构化商品知识。可在单张图里同时捕获“文本内容 + 图示含义 + 排版语义”等特征,并利用 LLM 对解析结果进行归一化和字段对齐,形成高覆盖、高一致性的商品知识库。
  • 检索增强(RAG)链路。用户输入的风格范文(不少于 400 字)会先经过标签分析模块,由大模型识别出其关键风格维度,如:表达节奏(快/慢)、情绪浓度(热情/克制)、营造气氛策略(故事、对比、疑问句)、用户痛点定位、直播常用带货技巧(强调稀缺、促单压力、利益点递进)等。基于这些风格标签,系统自动生成 Query,用于从通用知识库与电商知识库中检索对应表达方式、句式模板与知识上下文(卖点顺序推荐、商品类别常用话术、场景化句法等)。
  • 风格化生成模型。模型基于电商专精的电商直播语料预训练能力,并结合海量运营专家的精细化标注数据(SFT),能够在保持范文风格一致性的同时,将内容自动替换为目标商品的卖点和营销逻辑。为确保生成内容既符合直播场景使用习惯,又具备高情绪感染力,系统引入轻量级 RLHF/强化学习优化,通过人类偏好数据持续调优,使模型能够稳定输出“自然、顺畅、带货效果强”的脚本。为持续提升模型能力,通过数据飞轮对该生成模型进行对齐。
  • 标签化与后处理。脚本被进一步结构化,包括:分镜逻辑、开场引导、利益点铺垫、情绪高点、促单推进、收尾金句等,方便商家在实际直播中灵活调用或进行定制化编辑。

脚本数据飞轮

数字人直播的内容绝大部分来自大模型生成,前期领域专家知识为生成标准,脚本、问答、互动场景的生成质量已达到普通真人主播的水平。然而人工先验知识存在主观偏差,且缺乏全面性和快速适应新变化的能力,完全依赖人工只能达到次优水平。为持续攀升超越域内外头部真人主播,需建立业务和大模型的数据飞轮,通过飞轮效应持续提升模型在数字人直播场景的后验效果。

图片

先验对齐

在真实直播场景中,数字人模型最终追求的是“后验效果最优”——即用户停留、评论增长、转化提升等真实业务指标。然而后验目标往往天然伴随风险:例如激进促单、夸大效果、模糊描述等内容可能在短期内获得更高的用户反馈,却越过事实边界与平台规范,形成安全问题。因此,在模型全面对齐后验之前,必须构建一套稳健、可解释、与平台规范一致的先验对齐体系作为基础。先验奖励模型作为“守门人”,以推理专家模型为判断核心,通过结构化的偏好评分与规则奖励引导模型学习合规、高质、可控的内容风格,实现“先验对齐 → 强化学习 → 专精模型 → 回流验证”的闭环。

自动偏好合成。传统先验奖励完全依赖人工标注,成本高且存在主观性。为解决这一问题,我们集成了多个先进推理类基模型(如 EB4-4T、Deepseek-R1/V3、GPT-o 系列等),通过多模型投票、结果对比分级等方式自动合成偏好。这一自动化偏好生成机制能够模拟“专家标注”,但具备:

  • 一致性更高,减少人工主观波动
  • 覆盖范围更广,数百万级先验数据
  • 适应变化更快,模型可随平台规范或内容趋势变化即时更新

最终形成先验 RM(Reward Model)的核心训练数据。先验 RM 的核心职责是确保模型在任何情况下都不会突破内容安全边界,为后续后验对齐提供稳固底座。

图片

后验数据飞轮

为了让模型吸收用户的真实后验反馈,慧播星构建了一套以“内容探索 + 奖励建模”为两条主线的数据飞轮,实现模型的自主进化与持续增强。

图片

基于后验统计的内容探索:可控、高解释的偏好数据生成链路。后验统计路径主要面向高精度、强可控、可解释性强的偏好数据生产需求,结合在线实验框架,通过真实用户反馈驱动的方式生成偏好样本。通过高频在线实验,系统不断沉淀千级规模的偏好数据,支撑后续的模型偏好对齐训练(如 DPO/IPO 等策略优化方法)。

可泛化的奖励 uplift 建模:大规模偏好数据的高效补充路径。相比基于后验统计的实验方式,uplift 建模路径旨在解决用户行为稀疏、实验成本高的问题,通过泛化模型直接对用户偏好进行预测,生成百万级的偏好数据,实现更高效的数据扩容。采用 S-Learner / T-Learner 等 uplift 方法,构建用户行为因果效应模型,直接预测“某段内容是否会提升用户的互动/评论/停留等关键指标?”

3.2 智能问答

慧播星建设了一套完备的直播场景RAG系统,包括电商领域知识检索模型,通过千亿模型蒸馏的低时延生成模型(12s->2s),数据飞轮。目前已实现多模素材调度,高拟真明星问答,客户个性化表达,垂类适配,商家/商品知识库等产品能力。客户可一键开启智能问答,问答端到端可用率95%,优质率90%,客户开启率94%,运营和客户反馈较好。

图片

△ 智能问答架构

技术架构

慧播星的直播实时问答系统在工程上形成了知识整合 → 领域检索 → 低延迟生成 → 后处理 → 数据飞轮的完整闭环,为超拟真数字人提供了媲美真人的实时互动能力。

  • 知识整合层,系统将商家侧的商品图文、卖点、FAQ、视频脚本、类目属性以及运营沉淀的数据统一入库,并通过向量化处理构建高可用的电商知识底座。
  • 领域知识检索模块结合了千帧蒸馏后的 EB-lite/行业模型与高维向量语义搜索,通过「意图识别 → 精准匹配 → 语义聚类 → 知识召回」的流水线,确保系统能够从复杂直播语境中准确捕捉用户提问意图。直播场景中存在大量口语化、短句化、甚至噪声语料(如: “这个能用多久啊”。“有别的颜色吗?”),系统通过深度语义 embedding(如 ernie embedding)实现高鲁棒性的实时检索,使检索召回的准确率在实时环境下依然保持稳定。
  • 低延时生成模块。基于千亿模型蒸馏结果构建,针对直播高并发、低时延、强一致性的要求,模型经过结构裁剪、张量并行优化与 Prompt 规约,使单轮响应时延从 12s 压缩到 2s,在保证语义丰富度和口播自然度的同时提升端到端体验。
  • 数据飞轮实现持续自我优化:运营反馈、用户互动日志、误匹配案例以及高质问答样本会自动回流到数据处理模块,驱动知识库更新与模型重训练。

3.3 智能中控


真人主播会根据直播间实时状态决策当前应发起何种动作(action),比如直播间互动氛围差的时候是应该邀评,换卖点讲解还是促单?确定动作后主播知道如何最好的的执行动作,例如怎么把邀评讲出来?说什么话,用什么语气,邀请特定观众还是所有观众。行为决策和行为内容生成两者相结合实现直播间下单,关注,留联等最大化目标。超拟真数字人需要具备上述两种核心能力,即给定一个长期目标(如每场次的订单总数,评论总数,观看时长等),要求数字人1)判断在不同直播间状态下应该做出什么行为,是切换卖点讲解,促单逼单,邀评还是多轮互动?2)确定某种行为后生成适合的的行为内容,如塑品讲解,优惠讲解,促单逼单等的具体口播内容。

技术架构

智能中控架构核心由基于强化学习的决策Agent,和基于一言大模型的多任务融合两个部分组成。

图片

基于强化学习的行为决策Agent

行为决策的目标是在不同直播状态下选择最优动作,最大化长期目标(订单、评论、观看时长等)

图片

上图展示了直播环境与RL决策Agent的交互流程:

  • 状态 St:观看人数、评论频率、当前商品、用户行为序列、是否有提问等
  • 动作 At:邀评 / 多轮互动 / 促单 / 动态讲解 / 切换卖点 / 回答问题……
  • 奖励 Rt:订单数变化、评论数增加、停留时长、转化率提升等
  • Agent 通过不断试错 & 策略迭代,获得最优策略。

这使数字人能够像真人主播一样:氛围低时发起互动,用户观望时进行促单,新观众进入时进行商品介绍。RL 的优势在于目标导向:不是优化单句话,而是优化整场直播的 KPI

基于大模型的行为内容生成与融合

当 RL Agent 选择了一个动作后,例如“促单”,还需要生成对应的动作参数:如促单的口播内容,使用什么语气?内容是偏温和还是强节奏?是否引用当前观众的评论?实践中我们通过强化学习训练了一系列action内容生成专精模型,能够生成特定参数指定的直播内容。

未来我们将以语言模型为基座对决策和内容生成任务进行端到端训练,减少分阶段建模带来的累计误差。

04 语音克隆与合成

普通商家原声演绎状态不佳,缺乏带货感。慧播星利用风格迁移TTS技术自动合成感染力强,拟真度高的直播音频。经过两年多的迭代TTS开播使用率从30.3%提升至92.8% 制作时效性从1月降低到1分钟。

电商TTS发展主要经历两个阶段:

第一阶段(2023.3~2024.Q2) **:语音定制工牌麦收音,依赖大量人工传导,整个周期长达一个月

第二阶段(2024.Q3至今) **:小程序自助收音提高收音效率,自动训练架构升级,抑扬顿挫带货效果持续优化

8a3ff4868bef454dc11f0b3d563dfd55.png

第一阶段:工牌麦收音效率低下

9c59c70847e98a944f17b4d9b65d9842.jpg

第二阶段:小程序自助录制

现状:当前慧播星支持原生和激情带货两种音色克隆,客户仅需在手百小程序上录制15分钟语音,系统在1天内自动为客户生成克隆音(对比如下)。目前慧播星已制作12w多个音色,2.7w多个客户定制音色。

b8fb96d6e796ac2b84b99e0421469492.png

两种音效可选

1. 原声效果:还原本人说话特点,如语速和语调

http://blob:https://unitools.fun/fb87134d-97ec-42a5-a0a0-b74980b1cfc3

2. 激情带货效果:让整体情绪更激昂,抑扬顿挫

http://blob:https://unitools.fun/85e53903-5672-4988-85ae-19a4c867a607

未来计划利用海量直播场景的语料数据,进一步降低克隆门槛(对齐竞品的30s)、提升克隆效率(分钟级可完成克隆进行合成)、优化朗读效果(对标直播/视频/讲述/咨询等不同语境的真人) ,同时从单声音的克隆和合成成本达到业内头部领先水平。

克隆+合成技术架构

整体架构主要包括离线声纹注册和模型训练,在线合成三个部分。

图片

△ 形象克隆及合成架构

05 形象克隆与合成

主播形象是直播的核心要素,高拟真形象能够提升用户观看时长,进而提升成单效果。慧播星与视觉技术部深度合作,基于2D数字人技术针对直播场景定制形象克隆和合成能力,建设了接近7800+个公共库形象,有效地支撑商家在慧播星的前期探索,为自建形象做好准备。

图片

△ 慧播星形象制作

形象克隆技术发展主要经历了四个阶段:

第一阶段(2023.3~2023Q4) :V1版本唇形驱动方案适配电商直播场景,跑通录制约束较多的**闭嘴且无遮挡录制+**形象克隆流程,建立起第一批公共库形象

第二阶段(2024.Q4~2024.Q2) :V3V4版本唇形驱动通过数据建设和模型算法优化实现张嘴录制和更自然的唇动效果

第三阶段(2024.Q3~2025.Q2) :进一步降低录制门槛,支持录制中遮挡、大幅度侧脸和人脸出镜

当前阶段客户仅需上传5分钟左右的自然演绎视频,系统在3小时内即可自动为客户生成克隆形象。时至25年底慧播星已累计制作32万多多个形象,8万多个客户定制形象,线上可用率95%

第三阶段(2025.Q3~至今):突破唇形驱动,建设多人出镜,动作驱动,表情驱动,持物驱动等下一代形象生成能力(多模协同的超级主播)。

视觉技术

实时场景下早期的唇动方案采用单阶段建模(如wav2lip),输入音频直接输出像素空间的唇形图片。实践中单阶段方案无法达到逼真的唇动效果,后来的商用方案几乎都采用两阶段方案:第一阶段将音频转化为2D关键点或3D人脸模型作为中间表达,第二阶段将中间表达利用GAN网络解码到像素空间。

视觉生成模型

核心由三个模型组成,3D人脸重建模型音频到3D人脸生成模型3D空间到像素空间人脸生模型。

  • 3D人脸重建利用3DMM将人脸图片(像素)转换为3D mesh(三维空间点)
  • 基于Faceformer改进的音频到3D mesh预估模型,mesh作为中间表达携带了丰富的面部动态,使得生成模型能够生成逼真的唇形图片。
  • 基于StyleGan2改进的人脸生成模型,训练目标包括像素空间的重建损失,特征空间的感知损失,以及对抗生成损失。实现个性化增量微调方案,复用预训练底座只学习每个主播的个性化唇动风格,新形象仅需微调,3小时内完成制作。

a257f774f7b3f261f4d91b5ac0fac33c.png

模型pipeline

在线合成架构

形象合成以tts音频、底板视频帧和直播间背景为输入,通过生成模型实时合成主播嘴部区域,最后组装成视频流推送给用户。其中任务队列建立缓冲区,保障了视频流的连续性。目前已实现单卡多路流式渲染,支撑2万多直播间同时开播

f2a61d2b808afc9c277bca6b01b11654.jpg

在线流式合成架构

06 总结

历经两年多的持续打磨与技术突破,慧播星已经从一款数字人直播工具,成长为覆盖脚本生成、实时问答、智能中控、语音克隆、形象合成等多模态全链路的原生 AI 直播平台。它不仅复刻了真人主播的内容表达与带货节奏,更通过商品理解增强、强化学习决策、先验—后验数据飞轮、大规模音视频生成模型等关键技术,实现了“超越真人”的直播能力。随着业务规模的快速扩张与技术体系的持续演进,慧播星已在日均2万+直播间、万级定制形象与音色、覆盖电商与泛行业场景的真实生产环境中验证了 AI 直播的成熟度和商业价值。未来慧播星将继续沿着“更智能、更具说服力、更高效”的方向迭代:让脚本更精准、互动更自然、视觉更逼真、声音更生动、决策更智慧,并通过持续运转的数据飞轮不断突破直播体验的天花板。

2025 复盘 | 穿越AI焦虑周期,进化为 "AI全栈"

1. 今年一定

好几年没写「年终总结」,翻了下「掘金」,上一篇还停留在「2021年」,倒不是这几年没活过,也不是不想写,只是每次都止步于「拖延」。每到年底,脑子里就会蹦出的各种想法,被我零散地记录在 "大纲笔记" 中:

写这玩意挺废「时间」和「心力」,所以总想着 "找个周末,花一大块时间,好好梳理一下",结果都是一拖再拖:拖到元旦,拖到春节,拖到元宵。

然后,转念一想:"这都明年了,还写个🐣毛啊?算了,算了,明年一定!"。2022、2023、2024 就在这样的 "明年一定" 中溜走了...


人到中年,主观感觉「时间」过得越来越快,「记性」 也大不如前,很多当时觉得 "刻骨铭心" 的瞬间 (如:结婚、当爹),如今回忆起,就剩下一个 "模糊的轮廓"。不写的话,又是「 🐟」一年,趁着 2025 年还没过完,很多感觉还是热乎的,赶紧动笔,今年一定!!!

2. 如此生活30年,直到大厦崩塌

2025 年,对我冲击最大的当属 "AI",大到让我不得不重新审视「自己的工作和价值」。

我接触 AI 并不算晚,2024 年那会跟风玩了下 ChatGPT,后面在 GitHub Copilot (便宜盗版,30¥/月) 的协助下,快速交付了一个爬虫单子。尝到 "提效甜头" 的我,咬咬牙上了 年付正版 (100刀/年) 的车。

当时 AI 在我的眼里,只是个 "比较聪明的代码补全工具",可以帮我少些几行代码,仅此而已,因为 逻辑还得我来把控

但到了 2025 年,事情却变得有点不一样了:

AI能读懂/分析整个项目、重构屎山代码、把一个模糊的业务需求实现得有模有样。

当然,最让我 "破防" 的还是它的 "解BUG" 能力:

按照以往的习惯,我得去 Google、翻 Stack Overflow、看源码,少说得折腾半天。现在,把 报错日志相关代码 丢给它,通常几秒就能:指出问题所在给出解决方案,甚至可以 帮我改好并测试通过

积累多年,一度 "引以为傲" 的「编程经验」(对API的熟练程度、快速定位BUG的直觉、配置环境的熟练度等) 在 AI 的面前,变得 "不堪一击"。渐渐地,我的「工作流程」也发生了改变,"亲手" 敲下的代码越来越少,取而代之的是一套 "机械化" 的 "肌肉记忆":

  • 写注释等待AI补全按Tab:哪怕脑子里知道下一行该写什么,手指也会下意识停顿,等待那行灰色的建议浮现,然后无脑按下 Tab。
  • 提需求生成代码Accept:把业务逻辑描述一遍,丢给 AI,都不太细看生成的具体实现,直接点击 Accept All,主打一个 "能跑就行"。
  • 运行报错复制日志丢给 AI:遇到 Bug,第一反应不再是去分析堆栈信息 (Stack Trace),而是CV日志到对话框,问它:"解决报错:xxx"。
  • 效果不对截图丢给 AI:连描述问题的精力都省了,直接截图往对话框里一扔,附上一句 "改成这样"。

代码跑通了,效率提高了,却带来了精神上的「空虚」,我似乎再也感受不到当初那种「编程的快乐」:

  • 为了解决某个问题,苦思冥想,抽丝剥茧,最后成功 "破案" 时 "多巴胺疯狂分泌" 的 "快感"。
  • 查各种资料、反复推敲、验证,最终设计出一个自己觉得 "牛逼哄哄" 代码架构时的 "成就感"。

同时,也陷入了一种深深的「自我怀疑与迷茫」:

  • 越来越搞不清楚自己的「定位」(存在价值),上面那套 "连招" 找个实习生培训两天也能干。我曾赖以为生的那些 "技能",正变得廉价、可替代、甚至有点多余 ...
  • 找不到方向,以前「程序员成长路径」很清晰:学语言 → 学框架 → 学架构 → 学系统设计 → 刷算法 → 搞源码 ... 只要你一步步往上爬,爬到 "山顶" 就能成为 "大牛"。而如今却好像 "失效" 了...
  • 可控感被剥夺,程序员是典型的「内控型人格」—— 相信通过逻辑和细节掌控能预测一切。而但 AI 的「黑箱特性」带来了「工具不可控」,无法完全准确预测AI输出,调试从 "追踪逻辑" 变为 "试探模型"。

3. 调整对待AI的心态

3.1. 从 "焦虑" 到 "接纳"

我深知「焦虑」无用,于是开始探寻「破局之道」,反复阅读大量资料后发现,几乎所有人都在让你「拥抱 AI」,但具体怎么拥抱法?没人说,或者说得含糊不清,有些甚至还想割我 "韭菜" 🤡 ?屏蔽这些噪音,冷静下来复盘,拨开情绪迷雾,透过现象看本质。

首先,坦诚地「接纳」肯定是没错的,历史的车轮从不因个人的意志而停止转动,当 第一次工业革命的蒸汽机 轰鸣作响时,那些坚守手工工场的匠人们,也经历着相同的困境。精细手艺 在不知疲倦、效率千倍的 机械化工厂 面前显得苍白而无力。大机器生产取代手工劳动,不是一种选择,而是一种必然的 "降维打击"。

现在,我们同样站在了 "生产力变革" 的周期节点上, "效率至上" 的底层逻辑从未改变。是选择成为被时代甩下车的 "旧时代纺织工"?还是进化为驾驭机器的 "新时代工程师"?回归「第一性原理」,剥开 "智能" 的外衣,想想 "AI 的本质是什么?" —— 「干活的工具

所以,面对 AI,我们要做的事情就是琢磨 "如何用好这个工具? ",即:详细阅读使用说明后,在合适的场景,用合适的方式,解决合适的问题。

3.2. AI 有什么用?—— 能力放大 + 自学利器

3.2.1. 能力放大器

🐶 经常在 自媒体平台 刷到 "普通人学AI后致富/逆袭" 的 叙事,看到这些 "逆天标题" 没把我笑死:

多的不说,记住这段话就对了:

变现的核心能力从来不是使用工具,而是商业认知、市场洞察、营销推广、客户服务。AI 只是一个环节,不要高估了工具的作用,而低估了 商业常识的重要性,也不要低估了背后的 隐性成本和巨大的工作量

这些 "AI变现教程" 的 最大问题

让你把AI当成一个 独立的、全新的、需要从零开始的 "行业" 去卷。

对于 99% 的普通人而言,把AI看作 "能力放大器" 会更靠谱一点,即:

思考如何利用AI,帮我把我已有的技能/兴趣做得更好?

比如:

  • 用 AI 减少重复劳动,提高工作效率和质量,把时间花在更有创造力的事情。
  • AI 负责广度,你负责深度,在你热爱的小众领域里用AI武装自己,做到 "人无我有,人有我精"。

今年「Vibe Coding (氛围编程) 」很火:

用自然语言描述想要的效果,AI帮你写代码,你只负责验收结果和提修改意见,不用管具体代码怎么实现的。

编程门槛大大降低,普通人 只要能把 创意和感觉 翻译成需求,就能借助 AI 将其快速具象化为 可运行的产品

但你会发现,绝大多数生成的作品都是 "一次性原型或玩具":灵光一现即可实现,却缺乏持续迭代、架构设计与用户验证,因此难以具备商业价值、也难形成可持续的产品形态。

真正能够利用 Vibe Coding 实现变现的,往往是具备一定 编程经验产品思维 的 "专业人士"。他们不仅能用 AI 快速实现灵感,还能对作品进行持续优化、迭代和工程化打磨,从而将 "灵感原型" 进化为 "可用产品"。

再说一个自己观察到的例子,前阵子 OpenAI 发布了用于生成短视频的「Sora2」,B站 很快涌现了一堆 AI 生成的 "赛博科比" 恶搞视频。

看到一个播放量破百万的作品有点意思,点进 UP 主的主页想看下其它作品,结果发现他并不是突然爆火的 "新人",人家已经默默做了好几年视频,只是过去的播放量惨淡 (几十几百)。但他却一直坚持创作,尝试不同的方向,能清晰地看到他的剪辑、叙事和整体制作水平在一点点提高。

AI 不会让没有积累的人"平地起飞",但有可能让有准备的人"一飞冲天"。—《抠腚男孩》

3.2.2. 自学利器

看到这里,可能有人会问:

"那普通人怎么办?我没啥专业技能,也没有长期积累啊? "

简单,那就 "" 啊!!!以前学习的最大限制是什么?

没人教、教不好、学不动、坚持难

而现在,你有了一个「全知全能、知无不言、24小时为你服务的免费老师

  • 不会写代码?手把手教你,从逻辑到示例一步步拆开。
  • 想转行?给你路径、资源、练习清单、复盘建议。
  • 想跨领域?帮你建立知识框架,把陌生领域最难啃的部分变简单。
  • 遇到瓶颈?像一对一导师一样不断提问、引导、纠偏。

当然,想要这台 "自学利器" 高效运转起来,实现 快速学习/试错/跨域 还需要掌握一些 方法论

详细解读可以参加我之前写的《如何借助AI,高效学习,实现快速"跨域"》

3.2.3. 不要神化 AI

🐶 2333,经常刷到 "xx公司发布新的 xx 模型/AI产品颠覆行业,xx师要失业了" 的标题,但事实真的如此吗?最近 Google 家的 Nano Banana Pro 🍌很火,号称当前 "最强AI生图" 模型,亲身体验下确实强 (本文大部分配图就是它出的),天天在群里吹爆。

某天晚上,有 "多年专业设计经验" 的老婆收到一个改图需求 (抠素材,按要求调整海报):

😄 看着简单,感觉 🍌 就能做,于是我提出和老婆 PK 下,她用 PS 改,我用 🍌 嘴遁修图,看谁出的图又快又好。结果:她10分钟不到就改完,而我跟 🍌 Battle了半个小时没搞好,最终的效果图 (左边她的,右边我的):

🤡 "甲方" 的评价 (破大防了😭):

观察仔细的读者可能会问:"你是不是漏了一个车🚗啊?",憋说了,这破车把我调麻了...

那一刻,我深刻体会到了什么叫 "不要拿你的兴趣爱好,去挑战别人的饭碗",真的是 "降维打击" 啊!

AI 确实拉低了创作的门槛,但目前还处于生成 80分 内容的阶段 (效率),最后的 10-20 分 (细节、审美、情感) 才是价值的核心。——《抠腚男孩》

后面复盘,老婆看了下我的 Prompt,说我的流程有点问题,应该让 AI 先把素材全抠出来先,再慢慢组合。后面试了下,效果确实有好一些。不过,不得不说,AI自动抠图 这块确实可以:

🤣 老婆在日常设计时也会用 AI 来偷懒,比如:生成配图、提高清晰度、扩图等。

3.3. AI 是什么? —— 概率预测机器

现阶段谈论 AI,其实都是在谈论 大模型 (LLM) —— 一个极其复杂的 "概率预测机器"。

通过学习海量数据的 "统计规律",逐步逼近这些数据背后的 "概率分布",从而能够在给定 "上下文" 时预测最合理的下一步输出。

不同类型产物的生成原理图解 (看不懂没关系,简单了解下即可):

文本

② 图片 (扩散模型 & 自回归模型)

③ 音频 (自回归模型 & Codec + Token 预测 )

④ 视频 (扩散式 & 自回归/时空Token)

3.4. AI 的能力边界 —— 优/劣势

LLM 擅长发现 "相关性",但难以进行真正的 "因果推理",它只是在 "模仿智能",而非 "真正地理解意图,拥有意识"。 —— 《抠腚男孩

弄清楚 AI 的本质是 "概率预测机器" 后,接着从 "代码生成" 的角度梳理下它的 "优势 & 劣势":

了解完 AI优/劣势 后,接着就可以推演「人 & AI」 的 高效协作方式

一句话概括

AI 负责 "生产力" (重复、繁琐、高上下文、高整合的工作),人负责 "方向与边界" (判断、创造、决策、理解组织与业务)。

4. 必备技能 —— Prompt

一般译作 "提示词" 或 "描述词",个人认为后者更加贴切,即:描述问题/需求的 "词句组合" 。「Prompt Engineering-提示词工程」是所有人都必须掌握的 "使用AI的核心技能"。

4.1. 把话说清楚

🐶 别被几个英文单词吓倒,现在的 AI 比几年前聪明多了,普通人 只要能:

把诉求讲得清晰、完整、有逻辑,就能解决绝大多数问题

示例:

  • ❌ 混乱说法:帮我计划个周末玩的地方。
  • ✅ 有条理说法:周末想带5岁孩子一日游,2大1小,预算500以内,北京,不想跑太远,能放电、有吃饭的地方、避开暴晒,地铁可达最好。

AI输出结果 (前者输出不同城市的游玩方案,后者输出了具体的行程方案):

4.2. 套框架

再往上走,就是了解一些经典的 "Prompt框架",然后再提问时套用,以提高 AI 输出的稳定性、准确性和质量。所谓的 "框架",其实就是 "结构化模板",规定问题中包含哪些 "要素",比如最经典的「CTRF」框架:

套框架示例 (填空题~):

常见的框架还有 RTFCOSTARSPARCOTAPE 等等,适用于不同的场景。杰哥整合了自己知道的所有框架精华和高级技巧,弄了通用的「Prompt 最佳实践清单

无脑套就是了,助记口诀

也可以用故事流程来串联助记,读者可自行发挥,顺序无需固定:

让一位说书人 (角色) ,用生动的语气 (风格语气) ,给孩子们 (受众) 讲个故事 (指令) 。故事的开头 (上下文) 是...,结局 (目标) 要感人。故事的结构 (格式) 要像这样 (示例) ,但不要 (约束) 出现暴力情节。请先构思情节 (逐步思考) ,写完后再想想怎么能更精彩 (反思) 。

😄 懒得记的话,可以用我之前搭的小工具 →「CP AI Prompt助手」

配下 DeepSeekKey,复制粘贴你写的 简单Prompt,它会基于上面的十个维度对提示词进行优化:

4.3. 写出牛逼的Prompt

明白了怎么 "套框架" 写 "结构化的Prompt",但你可能还是会感到疑惑:

用的同样的AI,为什么别人的生成效果就是比我好?

尤其在 AI 生图 领域,看大佬分享的 Prompt,里面一堆看不懂的专业参数:

环境、构图、光影、景深、镜头、光圈、色调、氛围、胶片颗粒、对比度、主体增强、氛围灯...

能写出这么 专业的Prompt,是因为他们有 "相关领域的行业经验" 吗?

答:有加成,但不全是。高手的核心技能不是 "记这些专业知识",而是:知道如何指使 AI 给自己提供专业知识、框架、术语,然后再反向用这些框架让 AI 编写和优化 Prompt。

😄 其实思路很简单,拆解下这套方法论:

维度词术语/词库通用模板填空得第一版PromptAI专家视角优化迭代优化沉淀

详细玩法可以看下图:

4.4. Prompt 逆向

Prompt 逆向工程RPE,Reverse Prompt Engineering),就是:从 "输出" 反推 "是什么Prompt" 生成了它。一般用于:学习优秀案例调试和诊断问题构建Prompt库和模板企业质量控制安全审计 (防御Prompt注入攻击)。

4.4.1. 简单版

普通人 用这个套路就够了,选个聪明点的模型 (如:GPT5Gemini 3 Pro),粘贴图片,写 Prompt 让它反推:

差得有点远,描述「不满意的点」,让AI继续优化Prompt:

接着用优化后的 Prompt 来生成,可以看到效果差不多了,接着让 AI 提取一个「通用的Prompt

拿 AI 生成的 Prompt 生图,看效果,描述问题,循环反复,直到稳定生成自己想要的效果~

4.4.2. 专业版

🐶 其实也差不多,只是流程比较 "标准化",经常搞还能自己搭个 "工作流",适合专业选手,思路:

快速拆解推断 Prompt提取要素重建 Prompt优化迭代模板化沉淀

详细图解:

上面是通用的,还有几个 额外功能 的玩法也罗列下:

5. 锦上添花——懂点AI常识

🐶 懂点AI常识,能让你更 有的放矢用好AI (装逼),比如:连 Token 都不知道的话,就有点贻笑大方了。这里只是简单罗列下相关名词,不用死记,有个大概印象即可,不影响你使用AI,跳过也没关系。😄 详细讲解,建议直接复制名词问题AI,也可移步至《AI-概念名词 & LLM-模型微调》自行查阅~

5.1. AI (人工智能) 基础概念

5.2. NLP (自然语言处理)

5.3. Transformer 架构 (大型模型基础)

5.4. 语言模型基础 (Language Models)

5.5. LLM 核心概念 (Large Language Models)

5.6. 数据与训练流程 (Training & Fine-tuning)

5.7. 推理阶段 (Inference)

5.8. RAG (检索增强生成)

5.9. 多模态 AI

5.10. AIGC (生成式内容)

5.11. 模型压缩、部署与加速 (LLMOps)

5.12. Agent (自主智能体)

5.13. AGI (通往通用智能)

6. AI 编程领域专精

😄 最后,聊聊 AI 编程 领域的一些心得~

6.1. 前置知识

6.1.1. 编程模型

AI 代码写得好不好,主要看 "模型" 的 "编程能力",评估 "模型优劣" 的几个 "常见维度":

LLM 的能力很难用一句话概括,所以厂商们每次发新模型都会用一堆 Benchmark 来证明 (🐶不服,跑个分?)

推理与数学能力 (Reasoning)

"智能的核心指标",高分意味着能够做更复杂任务 (如:工程规划、Agent等),常见基准:GSM8K-小学奥数式数学题、MATH-高难度数学、AIME/AMC-奥林匹克数学、GPQA-博士级科学问答、BigBench Hard (BBH)-推理难题集合 等。

语言理解与知识能力 (Language / Knowledge)

"通用模型 IQ 测试",常见基准:MMLU-大学生多学科理解测试、MMLU-Pro - 更难版本、ARC / HellaSwag - 常识推理、OpenBookQA/TriviaQA - 事实/知识问答 等。

③ ✨编程能力 (Coding)

"商业价值极高的应用点",常见基准:HumanEval - 函数级别代码生成、MBPP / MBPP+ 简单编程题、SWE-Bench / SWE-Bench Verified✨:真实 GitHub issue + 多文件工程 (最接近真实开发场景,近两年厂商都在比这个)、Codeforces-算法比赛、CRUXEval / RepoBench-项目级分析 等。

多模态能力 (Multimodal)

"下一代 AI 产品的必争之地" (做 AI 助手、看图、自动化办公等),常见基准:MathVista:带图的数学推理、ChartQA / DocVQA:文档理解、TextCaps / ImageNet:视觉场景理解、VideoMME:视频理解、V-Bench / VQAv2:视觉问答 等。

安全性 (Safety / Robustness)

企业用户很看重 "安全合规",常见基准:Harmlessness / TruthfulnessAdvBench:对抗攻击、Red Team 红队测试Over-Refusal 测试(不会乱拒绝)、Speculative Safety(推测生成的风险)等。

速度/延迟/吞吐 (Performance Metrics)

"决定实际用户体验",常见指标:Tokens per second (推理速度)、First Token Latency (首字延迟)、Throughput QPS (每秒处理请求数)、Context processing speed (长文档处理速度)。


有时还会发布一些 "技术参数":

  • 模型规模:模型的参数量大小,影响推理与表达上限,规模越大,能力越强,但成本、延迟和部署难度也越高。如:70B = 70 billion = 700亿参数。
  • 训练数据规模:模型预训练时学习的 token 总量,代表其知识 "阅历"。数据越多通常知识覆盖越广,但质量、去重和清洗策略比单纯堆量更关键,高质量数据才能让模型表现更稳。如:15T = 15 trillion = 1.5万亿个token。
  • 上下文窗口:模型单次可接收并 "记住" 的 "输入长度上限",决定你能塞多少代码、文档和对话历史;窗口越大越适合做整仓分析、长文档问答、复杂任务,但会牺牲成本和延迟,且需要额外机制确保在超长上下文中仍能抓住重点。
  • 推理深度:模型答题时的 "思考力度",深推理模式更准确、适合复杂问题,但会更慢、更贵,适合关键任务而非高频交互。
  • 价格:按 token 收费,区分输入价、输出价与最小计费单位;部分模型提供 "缓存命中 (cache hit) ",对重复提示只按更低费率计费,大幅降低长上下文与多轮调用成本。价格决定模型可否大规模、频繁和低成本使用。
  • 延迟标准:包含首 token 延迟 (FTL) 与 生成速度 (token/s),分别决定 "多久开始回应" 和 "内容生成有多快";低延迟让补全、对话、Agent 流程更流畅,而高延迟会严重影响开发体验与实时性,是工程中比 "更聪明一点" 更重要的性能指标。
  • 模型行为控制能力:通过 Temperature、Top-p、System Prompt、工具权限等机制控制模型的随机性、稳定性与执行边界;行为越可控,越能确保输出一致、不跑偏,并安全地接入工具链或生产系统,是把模型从 demo 提升到可上线能力的关键参数。

🤡 个人 "主观" 认为的 "编程模型" 能力排名:

😊 一句话概括我的 "选模型策略":

选好的模型事半功倍!工程大活Claude精细小活 (如改BUG) 用 GPT,写前端页面用 Gemini

🐶 问:这些都是国外的模型啊,怎么才能用上?A社还锁区,经常封号?而且价格好贵啊?

答:😏 这个问题充钱可以解决.jpg,多逛下海 (xian) 鲜 (yu) 市场,国人的 "薅羊毛" 能力不是盖的,各种 "镜像站、第三方中转" 。氪金的时候注意找有 "售后" 的,随用随充,买 "短期 (如月付) ",不要买 "长期 (如年付)",这种看 LLM官方 政策的,一封就直接G了,说不定就 "卷款跑路"~

6.1.2. AI 编程工具的四种形态

😄 一句话归纳:

普通开发者 & Vibe Coding用户AI IDE/插件 居多,DevOps/后端工程师CLI团队/企业系统云端AgentAI 应用开发者AI SDK 构建构建 AI 产品与 Agent 系统。

接着说下 "AI编程" 的三种演进层次~

6.2. 第一层:AI 辅助开发

最早期的AI开发方式,以「人主导 + AI辅助」为核心逻辑,由两种交互模式组成:

  • 补全式:基于输入光标前的上下文,预测下一个单词、下一行代码、甚至整个函数。
  • 对话式:在 IDE 侧边栏或网页中,通过自然语言问答来生成代码、解释代码或查找 Bug。如:"帮我写一个 Python 的正则来验证邮箱" 或 "这段代码为什么报错?"

这一层的局限:

  • 上下文有限:AI 通常只能看到当前文件或少量相关片段,缺乏对整个项目架构的理解。
  • 被动性:AI 不会主动修改你的代码文件,它生成代码,你负责复制粘贴和校验。
  • 人是瓶颈:所有的决策、文件切换、环境配置都必须由人来操作。

6.3. 第二层:AI 驱动开发流程 (规范+Agent)

目前最前沿、最热门的阶段,AI 不再只是吐出代码片段,而是进化为 Agent (智能体),拥有了 "大脑" (规划能力) 和 "手脚" (工具使用能力),可以 "自主完成一个多步骤的开发任务"。

变成了「人定目标 + Agent 自主执行」,如:"实现一个简单的待办事项 Web 服务,要求:REST API,内存存储即可,有单元测试",Agent 可能会进行这样的任务拆解并执行:

设计目录结构 → 创建代码文件 → 写业务逻辑 → 写测试 → 运行测试并自我修复。

为了系统地应用 Agent,业界逐渐采用「 "规范"驱动的开发流程」(Spec-Driven Development):

需求规范文档任务分解Agent执行验证反馈

这个流程确保了 清晰的目标定义可追踪的执行过程,而不是让Agent盲目操作。开发者需要维护的 "三类规范" (规范必须比写代码更清晰):

  • 功能规范:目标、用户故事、输入输出、性能要求、鉴权、边界情况等。
  • 技术规范:模块结构、API、模型字段、状态机、异常流程等,Agent会根据这些自动创建项目。
  • 验收规范:测试通过、接口返回正确、性能满足要求、行为与设计一致,即每个功能的评价方式。

人不再写代码 (或者少写),负责「定义 + 审核 + 授权」,人-Agent 协作 的三阶段循环:

  • 人主导-目标设定:范围、约束、边界、不允许做的事情。
  • Agent主导-执行:分解、规划、写代码、自动Debug、修复、生成报告。
  • 人主导-验收:代码质量、安全性、单元测试覆盖率、偏差是否满足业务需求等。

💡 层2 关注的是「开发流程自动化」,任务的起点通常是 "已经确定好的需求/feature"。

6.4. 第三层:AI 全栈

所谓的 "AI 全栈",本质上就是 "让 AI 同时扮演多个软件开发角色",而 "一人分饰多角" 的自然实现方式就是 "多 Agents"。——《抠腚男孩

6.4.1. 为什么聊到 "AI全栈" 就会扯到"多 Agents"?

🤔 想象一下让 "AI全栈开发一个应用" 需要经历哪些步骤?

产品需求理解技术选型架构设计API 设计前后端代码生成数据库 schema错误处理文档生成单测编写测试执行部署脚本CI/CD 配置

让一个 Agent 承包上面所有的工作,会有什么问题?

记忆量爆炸、目标切换频繁、推理链拉得太长,错误积累变大、一旦一步出错,后续全崩、风格、结构、代码质量难统一、难以并行。

软件工程是 "多角色协作" 的结果:产品经理、架构师、后端、前端、测试、文档、DevOps... 如果想 "AI 模拟完整的软件开发流程",自然也需要 "让 AI 也模拟这些角色",于是就变成了这些 Agent

  • Planner / Architect (产品/架构):理解需求、拆任务、出计划 (Plan)。
  • Coder / Implementer (实现):按计划改代码、增删文件。
  • Searcher / Context Agent (检索):在代码库里找相关文件、API、调用链。
  • Tester / QA (写测 / 跑测):写测试、跑测试、分析报错。
  • Fixer / Debug Agent (修BUG):根据测试/运行结果修复代码。
  • Reviewer / Critic Agent (代码审阅):检查风格、一致性、潜在 bug / 安全问题
  • Ops / Deploy Agent (部署):写 Docker、CI/CD、部署脚本(有些系统只做到生成,不自动执行)

即「AI 全栈 = AI 软件开发流水线 = 模拟整个软件部门 = 多 Agent 系统」,这是开发任务决定的。

"AI全栈" 需要的三大核心能力:

  • 长任务规划 (Planning):开发一个系统不是线性的,是树状决策结构,要拆分任务,就需要 Planner Agent。
  • 并行执行 (Parallel Execution):前端、后端、文档、测试不可能一个个线性做。多 Agent 可以:前端 Agent 改 UI、后端 Agent 写 API、Docs Agent 补文档、Test Agent 补测试。
  • 验证 & 修复 (Validation Loop):真正让 "AI 全栈" 可行的关键是 "循环",写代码、跑测试、找错误、修复、再跑,需要 "多 Agent + 状态机" 才能撑起这个能力。

"AI 全栈系统" 的实现,本质就这四步:

  • 「定义一堆上面这样的角色」
  • 排布拓扑」决定这些角色之间的连接结构和调用关系。谁先谁后 (拓扑/顺序)、有没有循环 (写→测→修→再测)、有并行吗 (前后端Agent同时干活?)、是由一个 "主管Agent" 指挥大家?还是大家按照状态机自己转?
  • 给每个角色接上能用的"工具",让它真的能动手干活」常见工具:文件 (读/写代码、生成 diff)、终端 (执行命令)、搜索 (在 repo 里搜符号 / 用法)、HTTP/Browser (查文档、查API)、Git (开分支、commit、生成PR)、结构化分析 (AST分析、调用图、依赖图)。比如:Coder Agent 配置 "文件读写、diff 生成、代码搜索" 的工具,用来 "在受控范围内改代码"。
  • 套一层安全边界权限 (读写、只能改指定目录、终端命令必须在沙箱里执行)、人在回环 (关键操作必须人工确认,如:Plan-任务规划、大范围diff、部署相关改动/高危脚本)、防注入/误操作 (不轻信代码库里的"指令"-如:恶意README 写 "rm -rf /"、对外部输入做过滤-日志/错误信息/用户Prompt、限制重试次数,避免死循环修改)。

一句话概括就是:

AI 全栈 = 一群小模型/小角色 + 一个调度关系图 + 一堆工具函数 + 一圈安全护栏

😄 弄清楚本质,以后看任何 "AI 全栈多 Agents" 方案,都可以基于这三个问题进行快速拆解:

  • 它有哪些角色?(Planner / Coder / Tester / Fixer / Ops…)
  • 这些角色是按什么拓扑 / 流程连起来的?
  • 每个 Agent 有哪些工具?安全边界是什么?

6.4.2. 业界主流多 Agent 架构模式

前面AI常识部分有提到过,这里直接让🍌画个图~

6.4.3. 个人级 "AI全栈" 演进历程

🤡 上面的理论看起来简单,但对于个人来说,想要亲手实现这样 一整套多 Agents AI 全栈系统,工作量爆炸:

得自己写调度、管状态、接工具、控安全、做可视化,还要维护一堆 prompt 和配置,算完整平台工程了...

🤔 笔者认为 "个人级AI全栈" 更倾向于:

在个人可以承受的复杂度和时间成本内,让AI参与尽可能多的开发环节,而不是一次性造一个企业级AI工厂。

😄 其实,你可能已经在无形中体验 "AI 全栈" 的雏形了,现代 AI 编程工具 本身就内置了 多 Agent 编排能力~

Claude Code Sub Agents

CC 中允许创建多个带 独立角色与上下文Sub Agent (小型专属AI工作者),用法简单:

  1. 创建 Sub Agent
  • Claude Code CLI 输入 /agents,选择「Create new agent
  • 选择作用域:项目级 (推荐,只给当前项目使用)、用户级 (所有项目可用)
  • 填写:name (调用的时候用到)、description (决定CC何时自动调它)
  • 选择可用工具 (file_edit / bash / file_search / git …)
  • 完善系统Prompt:可以先让 Claude 生成,再自己改

保存后,会在 .claude/agents/ 生成一个类似这样的文件:

---
name: backend-dev
description: "专门负责后端接口、服务逻辑和数据库相关代码的实现与修改"
model: sonnet
tools: [file_search, file_edit]
color: blue
---

你是一个资深后端工程师,精通 Node.js + TypeScript 和这个项目的后端架构。
你的职责:
- 只改后端相关的代码(controllers, services, repositories)
- 遵循项目现有的代码风格和结构
- 所有改动都要尽量小步、安全、可读
在给出修改时:
- 标明文件路径
- 用 patch 的风格展示修改
- 如果需要新增文件,要说明用途和引用关系

不想自动生成,可以在 .claude/agents/ 手动按照上面的格式自己写md,保存后 CC 会自动识别。还可以在命令行启动CC时添加 --agents 参数 (适用于临时挂载场景):

claude --agents '{
  "log-analyzer": {
    "description": "分析测试日志和错误堆栈的专用Agent",
    "system_prompt": "你只负责阅读测试输出、日志,帮助定位问题和怀疑文件,不写代码",
    "tools": ["file_search"]
  }
}'
  1. 调用 Sub Agent (串起来) 的三种方式
  • 自然语言编排,用普通指令描述任务,由 Claude 自动判断并调用合适的 Sub Agent,最灵活、最贴近自然对话的方式。如:请用 backend-dev subagent 修改 search controller 的分页逻辑。
  • 结构化点名调用,明确指定要调用哪个 Sub Agent,适合需要精确控制执行顺序或避免模型误判的情况。如:Use the test-runner subagent to run the unit tests.
  • ③ 在 Agentrooms 中使用 @agent-name 直接点名,通过@用户的方式派任务,可同时管理多个 Agent,方便多人视图和多 Agent 协作。如:@backend-dev 帮我调整这个接口的返回格式
  1. 多个 Sub Agents 协同工作简单示例 (开发 → 测试 → 分析 → 再开发):
  • 让 developer 生成补丁
  • 让 test-runner 运行测试
  • 让 log-analyst 分析失败原因
  • 再让 developer 根据分析修复
  • Claude 会自动接力,也可以由你手动编排~

Cursor 2.0 多 Agent 编排

2.0 后,Cursor 界面从 "以文件为中心" 变成 "以Agent为中心",多了个 Agent Layout,切换后,侧边栏会显示当前 Agent、计划(plan)和改动,你把需求丢进去,Agent 负责读文件、计划、改代码、跑测试。

支持 同一指令 下,最多可 并行 (Parallel) 跑 8 个 Agent,每个 Agent 会在自己独立的 Git worktree / 沙盒工作区 内工作:各自改代码、build、跑测试,不会互相冲突 (🤡 就是费 Token...)。还多了一个 Plan Mode (先规划再执行),在 Agent 输入框 中按 Shift + Tab 可以切换到这个模式 (也可以手动选):

Cursor 不会直接假设你的需求,而是询问一系列澄清问题:

通过这些澄清,使 AI获得了完整的上下文,可以生成更精确的计划,避免后续的返工。接着会生成一个 plan.md 的计划文档:

你可以对文件进行编辑:增删任务、调整任务顺序、更新技术细节、调整实现方法等。确定无误后,点击 Build,Agent 会读取最新版本的 plan.md,并完成对应的任务。

🤔 与 CC Sub Agents 可编排不同,CursorAgent 更像是一个组合能力的 "大Agent",由它自动编排多个内嵌的、对用户不可见 的 Agent 来完成 用户提出的任务,收敛复杂性,只展示改动/测试结果。它的 Parallel Agents 探索不同方案,最后再汇总/合并的玩法,不算严格意义上的 "主流多 Agent 架构模式" 中的 "并行Agents模式"-支持显式地定义 / 分配 不同角色的 Agent,并让它们并行协作。

类似的支持 "多Agents" 玩法的 AI 编程工具还有:

  • GitHub Copilot Workspace多步骤 Pipeline Agents,从任务描述 → 生成完整 plan → 自动执行 → 修正,多步骤 cascaded agents,自动提 PR。
  • Google Gemini Code Assistmulti-expert prompt routing,任务自动分配给最擅长的模型/agent,复杂 monorepo 搜索 → 专家 agent 提供答案,针对 cloud infra 的执行-验证循环。
  • Replit 的 AI Dev 环境多工具执行 Agent,轻量一站式多Agent开发流水线。
  • ...等,限于篇幅,就不展开讲了~

觉得 AI编程工具 满足不了,接着就是围绕自己的开发流程,开发基于 LLMAPI 封装一些 小脚本/小工具

// 推进开发闭环的简单伪代码 (需求 → 修改 → 测试 → 修复)
plan = llm("你是架构师,帮我拆解这个改动需求…")
files = find_related_files(plan)

patches = llm("你是后端开发,只能改这些文件…", files + plan)
apply_patches_to_workdir(patches)

test_result = run_tests()

if test_result.failed:
    fix_patches = llm("你是调试工程师,根据报错修复…",
                      test_result + current_code)
    apply_patches_to_workdir(fix_patches)

大多数个人开发者达到这一层,基本够用了,再往上就是加:日志、可配置、一点UI、简单任务管理等,弄成一个仅为自己服务的 "AI 全栈开发小平台" (😄 此时更像是一个 Agent 工程师,搭建 "企业级AI全栈" 的基石)。

6.4.4. 落地方法论

根本原则

在一个完整开发周期里 (从想法到上线),有意识地让 AI 参与尽可能多的环节,并用 "多角色思维" 来组织这些调用,但工程复杂度要控制在个人能持续维护的范围内。


① 项目级自检


② 项目阶段拆解


③ 搭建可复用工作流


7. 结语

行文至此,再回看这篇拖了许久的 "年终总结",心情早已从最初面对 AI 秒解 Bug 时的 "破防" 与 "迷茫",变得平静且笃定,我们:

  • 剥开 AI "智能" 的外衣,看到了它作为 "概率预测机器" 的本质。
  • 学会用 "结构化的Prompt" 去驾驭它,而不是被幻觉带偏。
  • 也见证了开发模式从简单的 Chat 进化成 Copilot,再到如今初具雏形的 Agentic Workflow

但归根结底,AI 带来的最大变量,不在于它替我们写了多少行代码,而在于它重塑了 "专业" 的定义。

  • 懂得"底层原理"依然重要——否则你不知道为什么 AI 会把人修成 "汽车",也无法在它 "一本正经胡说八道" 时进行纠偏。
  • 懂得提问比解答更重要—— Prompt 是新时代的编程语言,清晰的逻辑表达 + 对业务的深度理解,才是最高效的 "编译器"。
  • 懂得架构比实现更重要——当 "AI 全栈" 成为可能,当一个个 Agent 可以各司其职,我们不再是死磕语法的 "搬砖工",而更像指挥数字化施工队的 "包工头 & 总设计师"。

"技术焦虑" 的解药,从来不是拒绝变化,而是成为变化的一部分。以前,我们的壁垒是 "熟练度+记忆力",以后则是 "想象力+判断力+系统工程能力",拥抱AI,在这个属于创造者的时代,进化为无所不能的 "超级个体🦸‍♀️"!

数据库AI方向探索-MCP原理解析&DB方向实战|得物技术

一、MCP设计理念

在浅析 MCP 原理之前,有必要搞清楚两个问题:MCP 是什么?为什么会出现? 以此明晰它存在的价值和意义。

首先,MCP(Model Context Protocol,模型上下文协议)是由人工智能公司 Anthropic 主导推出的一种开放标准协议,旨在统一大型语言模型(LLM)与外部数据源、工具及服务之间的交互方式。该协议通过JSON-RPC 2.0 标准消息格式定义通信规则,使模型能像使用"万能接口"(类比 Type-C 接口)一样即插即用地连接异构资源。

图片来源:zhuanlan.zhihu.com/p/598749792

其架构采用客户端-服务器模式,包含三个关键组件: 

  • MCP Host:运行大模型应用(如 Cursor、Cline、Cherry Studio、Claude Desktop 等),负责发起任务请求。
  • MCP Client:集成在 Host 中的协议客户端,解析任务需求并与服务器协调资源调用。
  • MCP Server轻量级服务程序,动态注册与暴露本地资源(例如文件、数据库)或远程服务(如云API),处理客户端请求并返回结构化数据,同时提供安全控制,包括访问权限管理和资源隔离。

简单概括为 MCP 是一种开放标准,本质是应用层协议(Protocal),跟我们熟知的 TCP/IP,HTTP 协议类似。借助它开发者可以安全地在数据源和 AI 工具之间建立双向连接。其架构概括起来就是:

  • 开发者可以通过 MCP 服务器公开他们的数据;
  • AI 应用(MCP 客户端) 可以连接到 MCP 服务器,获取所需数据,LLMs 再分析投喂的数据。

那为什么会出现呢?这就要说到RAG和Function Calling 技术了。

RAG(检索增强生成) 通过检索外部知识库获取与问题相关的实时信息并将其注入模型提示词,生成更精准、时效性更强的回答。 其工作原理为:当用户发出提问时,AI 应用通过向量检索、关键词匹配等方式,从外部知识库或数据源中搜索相关信息,再把检索到的信息作为上下文提供给大模型,让大模型基于补充的信息进行回答。技术流程: 用户提问→问题向量化→向量数据库相似度检索→拼接上下文提示词→模型生成答案。

图片来源:ailydoseofds

Function Calling(函数调用) 拓展了模型执行动作的能力,解决纯文本交互的局限性,即模型解析用户意图后生成结构化指令,调用预定义外部函数或 API(如发送邮件、查询天气)。其工作原理为:当用户发出提问时,应用会将集成的函数列表作为上下文发送给大模型。大模型根据用户输入判断应调用的函数,并生成相应的调用参数。随后,应用执行该函数并将结果发送给大模型,作为补充信息供其生成最终的总结或回答。技术流程:用户指令→ 模型识别需调用的函数→ 生成参数化调用指令→ 外部系统执行→ 返回结果至模型→ 生成用户响应。

图片来源:ailydoseofds

但不同的 API 需要封装成不同的方法,且参数确定后,后期变更困难,很难在不同的平台灵活复用。  而我们可以认为,MCP 是在 Function Calling 的基础上做了进一步的抽象,目的是让应用更加简单、高效、安全地对接外部资源,更好地为大模型补充上下文信息。总结起来就是,MCP 把大模型运行环境称作 MCP Client,也就是 MCP 客户端,同时,把外部函数运行环境称作 MCP Server,也就是 MCP 服务器,然后,统一 MCP 客户端和服务器的运行规范,并且要求 MCP 客户端和服务器之间,也统一按照某个既定的提示词模板进行通信。

MCP vs Function Calling 对比:

综上,RAG与Function Calling 互补,前者用于知识检索,后者用于执行操作,二者均可提升模型实用性,但目标维度不同,且存在难集成,扩展性差的问题,开发者往往需为不同模型重复实现工具调用逻辑。

MCP 则是对两者的整合与标准的规范化:

  • 标准化接口:MCP 为 RAG 的检索源接入(如数据库、文档库)和 Function Calling 的工具调用(如 API 服务)提供统一接入规范,避免为每个工具开发定制化适配。 比如 MCP 工具的inputSchema可定义多个参数,通过required标记必传参数。大模型在解析用户提问时,会根据工具的描述和参数定义,自动解析并提供相应参数值来调用工具。这种参数化设计方式提高了工具调用的灵活性,降低了 Function Calling 的开发复杂度。
return Tool(
    name=self.name,
    description=self.description,
    inputSchema={
        "type""object",
        "properties": {
            "host": {"type""string""description""数据库主机地址"},
            "port": {"type""integer""description""数据库端口"},
            "user": {"type""string""description""数据库用户名"},
            "password": {"type""string""description""数据库密码"},
            "database": {"type""string""description""数据库名称"}
        },
        "required": ["host""port""user""password""database"]
    }
)
  • 能力扩展
    • RAG 通过 MCP 接入实时数据流(如证券行情),突破静态知识库限制;
    • Function Calling通过 MCP 调用异构工具(如 IoT 设备),无需依赖特定模型的支持。
  • 系统效率:MCP 降低开发复杂度(如开发者无需为不同模型重复实现工具调用逻辑),促进工具生态共享。

技术演进总结

  • RAG → Function Calling → MCP 代表了 AI 能力的三个重要维度:从静态知识检索到动态行动执行,再到标准化生态构建,标志着 AI 从知识增强到行动扩展再到生态标准化的发展趋势。
  • 在 AI Agent 架构中:RAG 充当知识中枢,Function Calling 为执行手段,MCP 则是连接内外的“神经枢纽”。
  • 未来意义:MCP 的开放性将加速工具互操作性,推动复杂任务(如多 Agent 协作)的规模化落地,成为 AI 基础设施的关键组件。

二、MCP 架构详解

了解 MCP 存在的价值后,我们还需要定位 MCP 在整个 AI 体系中所处的位置,如以 Agent 为例:

了解完其在生态中所处的位置后,本节将结合 Python 版 SDK 源码和开源 MCP for DB 项目解读如何运用 MCP 以此了解其架构原理及使用方法。MCP Python SDK 提供了一个分层架构,通过多种传输协议将 LLM 应用程序连接到 MCP 服务器,同时具有高级和低级开发 API。

参考项目地址:github.com/Eliot-Shen/…

Python SDK:github.com/modelcontex…

2.1MCP运行过程

在此之前,有必要对 MCP 整体运行有个宏观上的认知,如下图所示,首先,需用户在主机上配置 MCP 服务,比如借助 VSCode 插件 Cline,根据使用的协议配置好 JSON文件即可。然后,用户输入问题,客户端让大语言模型选择 MCP 工具,大模型选择好工具后,客户端寻求用户同意,然后再请求 MCP 服务器, MCP 服务器调用工具并将工具的结果返回给客户端,客户端将模型调用结果和用户的查询发送给大语言模型,大语言模型组织答案给用户。

可见,整个流程中最核心的部分就是Client 和 Server的交互,而在使用像 Cline 这种主机端软件时,整体感知如下图,Client 已经被嵌在 MCP Host 中,只需开发出对应的 MCP Server,在主机中配置好 JSON即可使用。

接下来将结合项目和 SDK 源码详细解读 MCP 架构原理。

2.2MCP运行原理

MCP Server作为 MCP 架构的核心部分,在提升AI应用性能方面发挥着不可替代的关键作用。其 Python 版 SDK 提供的框架图如下:

我们着重关注其提供的三大核心功能:资源@mcp.resource、工具@mcp.tool、提示词@mcp.prompt。而这三大核心功能之间的协作逻辑大致如下:

  • 资源为工具提供上下文:手动注入资源可增强模型对任务的理解(如提供参考文档)辅助其更准确调用工具。
  • 工具执行依赖资源输入:当工具操作外部数据时,如文件处理工具,可将指定文件 URI 作为工具参数输入。
  • 提示词封装工具与资源调用:复杂 Prompt 可预设工具调用顺序或资源使用规则,形成自动化工作流。
# 在工具中封装提示词模版存在的问题是该工具不一定能被LLM调用,导致不一定能达到预期效果,但使用cline这中客户端,服务端的提示词又不能被加载调用
async def run_tool(self, arguments: Dict[str, Any]) -> Sequence[TextContent]:
    prompt = f"""
            - Workflow:
              1. 解析用户输入的自然语言指令,提取关键信息,如表描述和查询条件。
              2. 判断是否跨库查询、是否明确指定了目标表名(例如是中文的描述、英文的描述,偏向语义化的描述则判断为未明确表名)
              3. 未明确指定目标表名则调用“get_table_name”工具,获取对应的表名。
              4. 调用“get_table_desc”工具,获取表的结构信息。
              5. 根据表结构信息和用户输入的查询条件,生成SQL查询语句并调用“execute_sql”工具,返回查询结果。
            - Examples:
              - 例子1:用户输入“查询用户表张三的数据”
                解析结果:表描述为“用户表”,查询条件为“张三”。
                判断结果:1.没有出现跨库的情况 2.未明确指定表名,当前为表的描述,需调用工具获取表名
                调用工具“get_table_name”:根据“用户表”描述获取表名,假设返回表名为“user_table”。
                调用工具“get_table_desc”:根据“user_table”获取表结构,假设表结构包含字段“id”、“name”、“age”。
                生成SQL查询语句:`SELECT * FROM user_table WHERE name = '张三';`
                调用工具“execute_sql”:根据生成的SQL,获取结果。
                查询结果:返回张三的相关数据。
            - task: 
              - 调用工具“get_table_name”,
              - 调用工具“get_table_desc”,
              - 调用工具“execute_sql”
              - 以markdown格式返回执行结果
            """
    
    return [TextContent(type="text", text=prompt)]

但出于安全考虑,大模型对资源/工具的访问能力受到限制:

  • 工具:支持自主调用。大模型通过解析服务端公开的工具描述,能主动发起工具调用请求 ,无需用户逐条指令干预。此能力依赖模型的 Function Calling 支持,否则需通过提示词工程实现。
  • 资源:禁止自主访问。资源始终由应用层或用户管控 ,模型仅能使用已注入的资源内容。避免模型随意访问敏感数据,保障安全性。

资源(Resources)

资源是指由 MCP Server 向客户端提供的数据实体,这些实体作为统一的信息载体,旨在扩展 AI 模型的数据访问边界,并支撑其对动态、结构化与非结构化数据的实时处理能力。类似于 REST API中的 GET 端点——提供数据,但不应执行大量计算或产生副作用。

资源代表任何可供 AI 模型读取的数据形式,是你向LLM 暴露数据的方式,涵盖:

  • 文件内容:包括文本文件、JSON、XML 等结构化文档,以及源代码、配置文件等文本数据(UTF-8 编码)。
  • 数据库记录:关系型或非关系型数据库查询结果(e.g. PostgreSQL或MySQL)。
  • 动态系统数据:包括实时日志、屏幕截图、多媒体(图像、视频)、传感器输出等二进制数据。

如 MCP-DB-GPT 项目中定义的访问本地 JSON 格式的日志数据资源接口如下:

@mcp.resource("logs://{session_id}/{limit}")
def get_query_logs(limit: str = "5", session_id: str = "anonymous") -> Dict[strAny]:
    """获取查询日志
    
    Args:
        limit: 可选参数,指定返回的日志数量,默认为5
        session_id: 可选参数,指定要获取的会话ID
    """
    try:
        limit_val = int(limit)
        if limit_val <= 0:
            return {"success"False"error""Limit must be a positive integer"}
        
        logs = query_logger.get_logs(session_id=session_id, limit=limit_val)
        total = query_logger.total_query_count(session_id=session_id)
        
        return {"success"True"logs": logs, "total_queries": total}
    except Exception as e:
        return {"success"False"error"str(e)}
  • 装饰器中资源类型以统一资源标识符(URI)为唯一寻址机制,格式为[协议]://[主机]/[路径](如 file://home/user/report.pdf 或 postgres://database/customers/schema)。此设计允许资源协议、主机与路径的灵活定制,支持跨本地与远程环境的无缝集成。

通过整合多模态数据(文本与二进制)资源使 AI 模型能访问私有或专属知识库(如企业内部文档)、实时外部 API 及系统动态信息,有效突破单一大模型数据孤岛。

工具(Tools)

工具是服务器向客户端暴露的可执行函数集合,用于拓展大型语言模型(LLM)的操作能力,使其突破纯文本生成的局限,实现对外部系统的主动交互。其本质就是函数抽象,通过JSON Schema 严格定义输入/输出参数结构(如天气查询需输入位置参数,输出结构化天气数据)。

如 MCP-DB-GPT 项目中定义的只读 SQL 查询工具接口如下:

@mcp.tool()
def query_data(sql: str, session_id: str = "anonymous") -> Dict[strAny]:
    """Execute read-only SQL queries"""
    logger.info(f"Executing query: {sql}")
    conn = get_connection()
    cursor = None
    try:
        # Create dictionary cursor
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        
        # Start read-only transaction
        cursor.execute("SET TRANSACTION READ ONLY")
        cursor.execute("START TRANSACTION")
        
        try:
            cursor.execute(sql)
            results = cursor.fetchall()
            conn.commit()
            
            # 记录成功查询
            log_query(operation=sql, success=True, session_id=session_id)
            
            # Convert results to serializable format
            return {
                "success"True,
                "results": results,
                "rowCount"len(results)
            }
        except Exception as e:
            conn.rollback()
            log_query(operation=sql, success=False, error=str(e), session_id=session_id)
            return {
                "success"False,
                "error"str(e)
            }
    finally:
        if cursor:
            cursor.close()
        conn.close()

客户端通过标准协议接口(tools/list发现工具、tools/call 调用工具)与服务器交互,形成机器可读的自动化操作链路。其安全控制机制采用 “模型控制 + 人类监督” 双轨制:

  • LLM 自主决定工具调用的必要性(如识别用户请求中的查询数据库表信息意图);
  • 每次执行需用户显式授权(如弹出确认框),确保数据隐私与操作合规。

提示词(Prompts)

提示词是服务器端预定义的可重用交互模板,用于标准化和引导大型语言模型(LLM)的任务执行。这些模板通过动态参数化设计,允许传入特定值(如任务变量或上下文数据)生成定制化指令,从而实现高效、一致的模型交互。其核心机制如下:

  • 结构化要素:每个提示模板包含唯一标识符、任务描述、参数列表(如输入变量)以及可选的资源引用(如外部文件或API数据)。这种结构确保指令的明确性和可扩展性,减少模型输出歧义。例如,在文本生成任务中,模板可能定义输出格式要求(如字数限制或响应风格),并动态整合用户输入数据。
  • 上下文引导:提示词嵌入上下文关联机制(如历史对话片段或外部资源引用),帮助模型理解任务背景。例如,在问答场景中,模板可引入相关数据源(如知识库),提升响应准确性和相关性。
  • 工作流支持:支持链式交互设计,允许多个提示模板组合以处理复杂任务(如多步骤分析或迭代优化)。同时,模板常作为用户界面元素(如斜杠命令)集成,增强可用性。
  • ⚠️:提示词模版需要与客户端联动,即服务端定义好之后,在与客户端交互时,客户端获取服务器端动态填充好的提示词模版再发给大模型,大模型按照提示词要求进行回答。如果嵌在工具中触发条件往往是被动的

如MCP-DB-GPT 项目中服务端定义好的提示词接口如下:

@mcp.prompt()
def generate_db_gpt_prompt() -> str:
    """Generate a prompt for LLM to interact with database."""
    # 获取数据库表列表
    tables_info = get_tables()
    database_name = tables_info["database"]
    tables = tables_info["tables"]
    
    # 获取所有表的描述信息
    table_definitions = []
    for table in tables:
        table_desc = get_table_description(table)
        if table_desc.get("success"):
            table_definitions.append(table_desc["table_definition"])
    return DB_GPT_SYSTEM_PROMPT.format(
        database_name=database_name,
        table_definitions="\n".join(table_definitions),
    )

DB_GPT_SYSTEM_PROMPT 即是预先编写好的提示词模板,当你动态的获取参数后进行替换即可。一个简易的提示词模板如下:

Baseline_SYSTEM_PROMPT = """
请根据用户选择的数据库和该库的所有可用表结构定义来回答用户问题.
数据库名:
    {database_name}
表结构定义:
    {table_definitions}


约束:
    1. 请根据用户问题理解用户意图,使用给出表结构定义创建一个语法正确的mysql sql。
    2. 将查询限制为最多10000个结果。
    3. 只能使用表结构信息中提供的表来生成 sql。
    4. 请检查SQL的正确性。
    5. 分析基于现有表结构和元数据信息,估算用户提供的 DQL 语句的索引推荐策略,并返回给用户explain执行结果


用户问题:
    {user_question}


请按照以下JSON格式回复:
{{
    "thoughts": "分析思路",
    "sql": "SQL查询语句",
    "explain": "优化后的DQL语句执行结果"
}}
"""

然后客户端建立通信连接后获取相关的资源、工具和提示词模版:

    # methods will go here
    async def connect_to_server(self, server_script_path: str):
        """Connect to an MCP server
        
        Args:
            server_script_path: Path to the server script (.py or .js)
        """
        # try:
        is_python = server_script_path.endswith('.py')
        is_js = server_script_path.endswith('.js')
        if not (is_python or is_js):
            raise ValueError("Server script must be a .py or .js file")
        
        command = "python" if is_python else "node"
        server_params = StdioServerParameters(
            command=command,
            args=[server_script_path],
            env=None
        )
        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
        await self.session.initialize()
        print(f"Session_id: {self.session_id}")
        
        # List available tools
        response = await self.session.list_tools()
        tools = response.tools
        print("\nConnected to server with tools:", [tool.name for tool in tools])
        
        # List available resources
        resources_response = await self.session.list_resources()
        if resources_response and resources_response.resources:
            print("Available resources:", [resource.uri for resource in resources_response.resources])
        else:
            print("Available resources templates: ['logs']")


        prompts = await self.session.list_prompts()
        if prompts and prompts.prompts:
            print("Available prompts:", [prompt.name for prompt in prompts.prompts])
        else:
            print("No available prompts found.")

MCP Client

MCP Client 在整个模型交互过程中则起着至关重要的桥梁作用,连接着 LLM 与 MCP Server。其Python版 SDK 提供的框架图如下:

此处,我们先不看他的通信机制,结合 MCP-DB-GPT 项目仅关注 ClientSession 是如何与服务器层交互的。从宏观上看,客户端与服务器端的消息流大致如下:

而在 MCP-DB-GPT项目中定义了一个集成阿里通义千问大模型接口的 MCPClient类:

class MCPClient:
    def __init__(self):
        # Initialize session and client objects
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.llm = TongYiAPI()
        self.session_id = str(uuid.uuid4())
        self.use_few_shot = True
        self.conversation_history = FEW_SHOT_EXAMPLES if self.use_few_shot else []
    
    # methods will go here
    async def connect_to_server(self, server_script_path: str):
        """Connect to an MCP server
    
    async def get_query_logs(self, limit: int = 5) -> str:
        """获取查询日志"""
    
    async def get_schema(self, table_names: Optional[List[str]] = None) -> str:
        """获取数据库结构信息"""


    async def process_query(self, query: str) -> str:
        """使用通义千问处理数据库相关查询"""

客户端就是通过ClientSession对象与服务器交互,主要包括初始化连接、工具调用和资源访问:

  • 连接建立
    • 在connect_to_server方法中,客户端基于服务器脚本路径(如 Python 或 Node.js 脚本)初始化StdioServerParameters。
    • 使用stdio_client建立标准输入输出(Stdio)传输通道,创建ClientSession对象。调用session.initialize()初始会话,生成唯一会话 ID(session_id),用于加密通信和日志跟踪。
    • 随后通过session.list_tools()获取可用工具列表(如query_data、get_schema),并通过session.list_resources()列出可访问资源(如日志)。此阶段实现协议中的“工具发现”阶段,确保客户端了解服务器功能。
  • 工具调用过程
    • 当执行具体操作时(如执行 SQL 或获取 Schema),客户端使用session.call_tool(tool_name, params)方法发送 JSON-RPC 请求。 
      • 请求结构:以get_schema为例,参数包括session_id和可选table_names,序列化为 JSON 格式。
      • 服务器响应:服务器执行工具(如查询数据库结构),返回JSON-RPC响应。响应包含content字段(如数据库结构信息),客户端解析 JSON 以提取结果。
    • 错误处理:若响应包含success: false或error字段(如无法获取Schema),客户端返回错误信息。
  • 资源访问机制
    • 通过session.read_resource(uri)访问资源(如日志)。日志 URI 格式为logs://{session_id}/{limit},服务器返回结构化的日志数据(JSON 格式)。
    • 安全性:所有交互依赖会话级加密(session_id),并通过权限验证(用户需显式授权敏感操作)。

典型案例运用

MCP-DB-GPT 项目的一大亮点在于,其实现了结合提示词后,如何借助大模型解析自然语言生成 SQL 语句再与服务器进行交互并返回查询结果的完整过程。该功能能让初学者一步感知到 MCP 的工作流程以及它的灵活与强大。

通过LLM解析自然语言生成SQL语句的流程:在process_query方法中,LLM(通义千问 API)用于生成结构化工具调用(如 SQL 语句)。核心代码实现如下:

async def process_query(self, query: str) -> str:
    """使用通义千问处理数据库相关查询"""
    try:
        # 调用服务器层的提示词方法
        prompt = await self.session.get_prompt("generate_db_gpt_prompt")
        prompt = prompt.messages[0].content.text
        # 将封装好的提示词投喂给大模型
        llm_response = self.llm.chat(system_prompt=prompt, content=query, response_format="json_object",
                                     conversation_history=self.conversation_history)
        response_data = json.loads(llm_response)
  • 客户端通过session.get_prompt("generate_db_gpt_prompt")获取预定义提示模板(prompt),该模板描述可用工具(如query_data)和任务规范,优化 LLM 对查询的理解和工具的调用。
  • LLM 基于提示和对话历史,执行“意图解析”
    • 分析自然语言,匹配工具(如自动选择query_data工具)。
    • 输出 JSON 包含sql字段(生成的 SQL 语句)、direct_response(当无需 SQL)或thoughts(推理过程)。
    • 示例:查询“金额最高的订单”可能生成"sql": "SELECT * FROM orders ORDER BY amount DESC LIMIT 1"。
  • 对话更新:用户查询和 LLM 响应添加到conversation_history,保持上下文一致性。

生成 SQL 后与服务器层的二次交互:在process_query中,如果 LLM 响应包含sql字段(如"sql":"SELECT * FROM users"),客户端自动调用session.call_tool("query_data", params)。

# 如果有SQL查询,执行它
if response_data.get("sql"):
    # 执行SQL查询
    query_result = await self.session.call_tool("query_data", {
        "sql": response_data["sql"],
        "session_id": self.session_id
    })
    # 构建最终响应
    final_response = {
        "thoughts": response_data["thoughts"],
        "sql": response_data["sql"],
        "display_type": response_data.get("display_type""Table"),
        "results": json.loads(query_result.content[0].text) if query_result.content[0].text else None
    }
    
    return json.dumps(final_response, ensure_ascii=False, indent=2)
  • 所有查询(包括 LLM 生成的 SQL)通过write_resource记录到日志资源,URI 为logs://{session_id}/{limit},支持后续审计。

此交互完成 MCP 的“执行-响应”闭环:SQL 作为工具调用的参数,服务器执行后返回结构化数据,客户端整合为最终响应。

三、底层通信原理

3.1协议层:JSON-RPC 2.0基础

MCP 的核心消息格式采用 JSON-RPC 2.0 协议,这是一个轻量级的 RPC 框架,用于结构化请求、响应和通知。在 Python SDK 中:

  • 消息结构:每条消息都是 JSON 对象,包含 method(方法名,如 tool_execute)、params(参数)和 id(请求 ID)。
class JSONRPCRequest(Request[dict[strAny] | Nonestr]):
    """A request that expects a response."""
    jsonrpc: Literal["2.0"]
    id: RequestId
    method: str
    params: dict[strAny] | None = None
  • 交互类型
    • 请求(JSONRPCRequest) :客户端(如 AI 应用)发送操作指令(如执行工具)。
    • 响应(JSONRPCResponse) :服务器返回结果。
    • 错误响应(JSONRPCError):服务器返回错误信息。
    • 通知(JSONRPCNotification) :用于异步事件(如服务器主动推送上下文更新),无需响应。
  • 优势:JSON-RPC 2.0 的标准化确保跨平台兼容性,源码中通过 JsonRPCRequest和JsonRPCResponse 类实现解析和验证,减少消息解析开销。如在 STDIO 传输中,消息的序列化和反序列化过程如下:

发送消息时:消息被序列化为 JSON 并添加换行符。

try:
    async with write_stream_reader:
        async for session_message in write_stream_reader:
            json = session_message.message.model_dump_json(by_alias=True, exclude_none=True)
            await process.stdin.send(
                (json + "\n").encode(
                    encoding=server.encoding,
                    errors=server.encoding_error_handler,
                )
            )
except anyio.ClosedResourceError:
    await anyio.lowlevel.checkpoint()

接收消息时:从输入流读取行,解析为JSON-RPC消息 。

try:
    message = types.JSONRPCMessage.model_validate_json(line)
except Exception as exc:
    await read_stream_writer.send(exc)
    continue


session_message = SessionMessage(message)
await read_stream_writer.send(session_message)

3.2传输层:双向通信实现

MCP Python SDK 提供了多种传输机制,每种机制都针对不同的部署场景和通信模式进行了优化。所有传输机制都抽象为一个基于流的通用接口,同时支持特定于协议的功能。

Stdio传输

通过标准输入/输出(stdin/stdout)流进行同步或异步通信。源码中读取和写入流由如下函数实现:

  • 读取流:MemoryObjectReceiveStream[SessionMessage | Exception]-从服务器标准输出接收消息
  • 写入流:MemoryObjectSendStream[SessionMessage]- 将消息发送到服务器标准输入

其适用于本地进程间通信(如 IDE 插件),低延迟但仅限单机。 关键代码包括stdin_reader()和stdout_writer方法,负责后台处理双向通信。其通信流程大致为以下七步

  1. 客户端以子进程的方式启动服务器
  2. 客户端往服务器的 stdin 写入消息
  3. 服务器从自身的 stdin 读取消息
  4. 服务端往自身的 stdout 写入消息
  5. 客户端从服务器的 stdout 读取消息
  6. 客户端终止子进程,关闭服务器的 stdin
  7. 服务器关闭自身的 stdout

  • ⚠️⚠️⚠️:当客户端调用mcp.tool() 装饰的函数时,SDK 内部封装请求为 JSON-RPC,通过 stdio 发送给服务器进程,服务端通过装饰器装饰的函数在建立连接后被调用时,会自动进行注入并转换成 JSON格式的数据与客户端进行自动交互。

服务端代码参考:

async def run_stdio():
    """运行标准输入输出模式的服务器
    
    使用标准输入输出流(stdio)运行服务器,主要用于命令行交互模式
    
    Raises:
        Exception: 当服务器运行出错时抛出异常
    """
    from mcp.server.stdio import stdio_server
    
    logger.info("启动标准输入输出(stdio)模式服务器")
    
    try:
        # 初始化资源
        await initialize_global_resources()
        
        async with stdio_server() as (read_stream, write_stream):
            try:
                logger.debug("初始化流式传输接口")
                await app.run(
                    read_stream,
                    write_stream,
                    app.create_initialization_options()
                )
                logger.info("标准输入输出模式服务结束")
            except Exception as e:
                logger.critical(f"标准输入输出模式服务器错误: {str(e)}")
                logger.exception("服务异常终止")
                raise
    finally:
        # 关闭资源
        await close_global_resources()

Main 函数中调用:

try:
    if mode == "stdio":
        asyncio.run(run_stdio())

配置 Cline 的JSON文件即可访问:

"mcp_db": {
      "timeout": 60,
      "type": "stdio",
      "command": "uv",
      "args": [
        "--directory",
        "/Users/admin/Downloads/Codes/MCP/mcp_for_db/src/",
        "run",
        "-m",
        "server.mcp.server_mysql",
        "--mode",
        "stdio"
      ],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "root",
        "MYSQL_PASSWORD": "password",
        "MYSQL_DATABASE": "mcp_db",
        "MYSQL_ROLE": "admin",
        "PYTHONPATH": "/Users/admin/Downloads/Codes/MCP/MCP-DB/"
      }
}

SSE 传输

SSE 传输使用服务器发送事件 (SSE) 传输服务器到客户端的消息,并使用 HTTP POST 请求传输客户端到服务器的消息,提供基于 HTTP 的通信。其本质是双向模拟,即 SSE 本质为单向,SDK 通过“请求/响应”对模拟双向通信(客户端发送 HTTP POST 请求携带JSON-RPC,服务器返回 SSE 流)。其通信流程也可概括为七步:

  1. 客户端向服务器的 /sse 端点发送请求(一般是 GET 请求),建立 SSE 连接;
  2. 服务器给客户端返回一个包含消息端点地址的事件消息;
  3. 客户端给消息端点发送消息;
  4. 服务器给客户端响应消息已接收状态码;
  5. 服务器给双方建立的 SSE 连接推送事件消息;
  6. 客户端从 SSE 连接读取服务器发送的事件消息;
  7. 客户端关闭 SSE 连接。

服务器端:SseServerTransport为服务器端 SSE 传输实现提供了两个主要的 ASGI 应用程序。

客户端:sse_client函数提供客户端 SSE 传输实现。

二者通信的消息格式如下:每条 SSE 消息以 data: 前缀携带 JSON-RPC 负载,客户端监听事件流。

# 服务端
logger.debug("Starting SSE writer")
async with sse_stream_writer, write_stream_reader:
    await sse_stream_writer.send({"event""endpoint""data": client_post_uri_data})
    logger.debug(f"Sent endpoint event: {client_post_uri_data}")
    
    async for session_message in write_stream_reader:
        logger.debug(f"Sending message via SSE: {session_message}")
        await sse_stream_writer.send(
            {
                "event""message",
                "data": session_message.message.model_dump_json(by_alias=True, exclude_none=True),
            }
        )

其中涉及两种主要事件类型:

  • endpoint事件:在连接建立时发送,告知客户端 POST 消息的端点 URL。
  • message事件:传输实际的 JSON-RPC 消息。

客户端通过 sse_reader 函数处理接收到的 SSE 事件:

  • endpoint事件处理:验证端点 URL 的安全性,确保与连接源匹配。
match sse.event:
    case "endpoint":
        endpoint_url = urljoin(url, sse.data)
        logger.debug(f"Received endpoint URL: {endpoint_url}")
        
        url_parsed = urlparse(url)
        endpoint_parsed = urlparse(endpoint_url)
        if (
            url_parsed.netloc != endpoint_parsed.netloc
            or url_parsed.scheme != endpoint_parsed.scheme
        ):
            error_msg = (
                "Endpoint origin does not match " f"connection origin: {endpoint_url}"
            )
            logger.error(error_msg)
            raise ValueError(error_msg)
        
        task_status.started(endpoint_url)
  • message事件处理:解析 JSON-RPC 消息并转换为 SessionMessage。
case "message":
    try:
        message = types.JSONRPCMessage.model_validate_json(  # noqa: E501
            sse.data
        )
        logger.debug(f"Received server message: {message}")
    except Exception as exc:
        logger.error(f"Error parsing server message: {exc}")
        await read_stream_writer.send(exc)
        continue
    
    session_message = SessionMessage(message)
    await read_stream_writer.send(session_message)

适用场景:远程或云端部署,支持高并发和实时更新(如工具调用的结果推送)。错误处理包括连接超时重试(源码中 retry 机制)。

服务端代码参考

def run_sse():
    """运行SSE(Server-Sent Events)模式的服务器
    
    启动一个支持SSE的Web服务器,允许客户端通过HTTP长连接接收服务器推送的消息
    服务器默认监听0.0.0.0:9000
    """
    logger.info("启动SSE(Server-Sent Events)模式服务器")
    sse = SseServerTransport("/messages/")
    
    async def handle_sse(request):
        """处理SSE连接请求
        
        Args:
            request: HTTP请求对象
        """
        logger.info(f"新的SSE连接 [client={request.client}]")
        async with sse.connect_sse(
                request.scope, request.receive, request.send
        ) as streams:
            try:
                await app.run(streams[0], streams[1], app.create_initialization_options())
            except Exception as e:
                logger.error(f"SSE连接处理异常: {str(e)}")
                raise
        logger.info(f"SSE连接断开 [client={request.client}]")
        return Response(status_code=204)
    
    @contextlib.asynccontextmanager
    async def lifespan(app: Starlette) -> AsyncIterator[None]:
        """SSE应用的生命周期管理"""
        try:
            # 初始化资源
            await initialize_global_resources()
            yield
        finally:
            # 关闭资源
            await close_global_resources()
    
    starlette_app = Starlette(
        debug=True,
        routes=[
            Route("/sse", endpoint=handle_sse),
            Mount("/messages/", app=sse.handle_post_message)
        ],
        lifespan=lifespan
    )
    
    logger.info("SSE服务器启动中 [host=0.0.0.0, port=9000]")
    # 创建配置并运行
    config = uvicorn.Config(
        app=starlette_app,
        host="0.0.0.0",
        port=9000,
        loop="asyncio",
        log_config=None  # 禁用uvicorn默认日志配置
    )
    
    server = uvicorn.Server(config)
    server.run()

Main函数中调用

try:
    if mode == "sse":
        run_sse()

配置 Cline 的 JSON文件即可访问:

"mysql_mcp_server": {
  "disabled": true,
  "timeout": 60,
  "type": "sse",
  "url": "http://localhost:9000/sse"
}

3.3通信工作流程

MCP通信遵循 JSON-RPC 2.0模式,主要包含三个消息类别:

同时,理解 MCP 连接生命周期可以帮助我们更好地开发 MCP 服务器和 AI 应用。MCP 连接生命周期跟 TCP 的三次握手、四次挥手有点类似,也要经历建立连接、交换消息、断开连接等阶段。

接下来,以 StreamableHTTP 机制为例,分析通信工作流程。StreamableHTTP 传输机制实现了基于 HTTP 的双向通信,结合了 HTTP POST 请求和 Server-Sent Events (SSE) 流来提供完整的客户端-服务器通信解决方案。

整体架构流程

StreamableHTTP 通信机制包含会话管理、双向消息传输和可选的事件重放功能:

  • 客户端传输初始化
    • 客户端通过 streamablehttp_client 函数建立连接。
    • 核心组件 StreamableHTTPTransport 负责管理会话状态和消息路由。
  • 服务器端会话管理
    • 服务器端使用 StreamableHTTPSessionManager 管理多个并发会话。支持有状态和无状态两种模式:
    • 有状态模式:维护会话状态,支持连接恢复。
    • 无状态模式:每个请求创建新的传输实例。

通信工作流程详解

初始化和会话建立

  • 初始化请求:客户端发送 initialize 方法的 POST 请求。
  • 会话 ID 分配:服务器生成唯一会话 ID 并通过 mcp-session-id 头返回。
def _maybe_extract_session_id_from_response(
    self,
    response: httpx.Response,
) -> None:
    """Extract and store session ID from response headers."""
    new_session_id = response.headers.get(MCP_SESSION_ID)
    if new_session_id:
        self.session_id = new_session_id
        logger.info(f"Received session ID: {self.session_id}")

双向消息传输

客户端到服务器(POST 请求):客户端的post_writer 方法处理出站消息。

async def post_writer(
    self,
    client: httpx.AsyncClient,
    write_stream_reader: StreamReader,
    read_stream_writer: StreamWriter,
    write_stream: MemoryObjectSendStream[SessionMessage],
    start_get_stream: Callable[[], None],
    tg: TaskGroup,
) -> None:
    """Handle writing requests to the server."""
  • 消息序列化:将 JSON-RPC 消息序列化为 HTTP POST 请求体。
  • 请求处理:根据消息类型选择处理方式 - 普通请求或恢复请求。
async def handle_request_async():
    if is_resumption:
        await self._handle_resumption_request(ctx)
    else:
        await self._handle_post_request(ctx)
# If this is a request, start a new task to handle it
if isinstance(message.root, JSONRPCRequest):
    tg.start_soon(handle_request_async)
else:
    await handle_request_async()
  • 响应处理:支持 JSON 响应和 SSE 流响应两种模式。

服务器到客户端(SSE 流)

服务器端通过不同的 HTTP 方法处理消息:

  • POST 请求处理:接收客户端消息并通过 SSE 或 JSON 响应
if self.is_json_response_enabled:
    # Process the message
    metadata = ServerMessageMetadata(request_context=request)
    session_message = SessionMessage(message, metadata=metadata)
    await writer.send(session_message)
    try:
        # Process messages from the request-specific stream
        # We need to collect all messages until we get a response
        pass
    finally:
        await self._clean_up_memory_streams(request_id)
else:
    # Create SSE stream
    sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[dict[strstr]](0)
    
    async def sse_writer():
        # Get the request ID from the incoming request message
  • GET 请求处理:建立独立的 SSE 流用于服务器主动推送 streamable_http.py:511-601
async def _handle_get_request(self, request: Request, send: Send) -> None:
    """
    Handle GET request to establish SSE.
    
    This allows the server to communicate to the client without the client
    first sending data via HTTP POST. The server can send JSON-RPC requests
    and notifications on this stream.
    """

实际使用示例

从测试代码中可以看到完整的使用流程:

@pytest.mark.anyio
async def test_streamablehttp_client_basic_connection(basic_server, basic_server_url):
    """Test basic client connection with initialization."""
    async with streamablehttp_client(f"{basic_server_url}/mcp"as (
        read_stream,
        write_stream,
        _,
    ):
        async with ClientSession(
            read_stream,
            write_stream,
        ) as session:
            # Test initialization
            result = await session.initialize()
            assert isinstance(result, InitializeResult)
            assert result.serverInfo.name == SERVER_NAME
  1. 使用 streamablehttp_client 建立连接
  2. 通过 ClientSession 进行初始化
  3. 执行各种 MCP 操作(工具调用、资源访问等)

3.4各协议对比分析

MCP vs REST API

MCP vs WebSocket

四、项目初始化&实战解析

本节,将使用 uv 快速搭建 MCP 服务,然后结合 DW-DBA-MCP 实战项目进行介绍。

4.1环境安装

官方推荐使用 uv 进行虚拟环境及依赖的管理。uv 的安装可参考:docs.astral.sh/uv/#highlig…

# macos
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env


(base) Dewu-GK234XWXCT:~ admin$ uv --version
uv 0.7.13 (62ed17b23 2025-06-12)

同时,需要注意⚠️:SDK 需要 Python 3.10 或更高版本,支持 Python 3.10 至 3.13。

  • 创建虚拟环境:
# 初始化虚拟环境
uv init MCP-DB


# 切换目录
cd MCP-DB


# 安装mcp client依赖
uv add "mcp[cli]"


# 使用 uv 运行 mcp 命令
uv run mcp -help

4.2DW-DBA-MCP实战解析

本项目旨在为数据库侧开发 MCP Server。同时,考虑到高可扩展性,项目采用微服务架构进行设计开发,这也便于与 Nacos MCP Server 进行集成,客户端配置Nacos MCP Server 服务,LLM 即可通过该网关高效路由到合适工具;考虑到易用性,通过封装 MCP Client 和 MCP Server,提供 FastAPI 接口处理用户的提问。项目目录结构如下:

DW-DBA-MCP/
├── Dockerfile
├── LICENSE
├── README.md   
├── datas                          # 存放项目日志文件
│   ├── files                           # 存放工具执行的 SQL 语句
│   ├── logs                            # 存放日志文件
│   └── version   
├── mcp_for_db
│   ├── __init__.py
│   ├── client                    # 自建客户端
│   │   ├── __init__.py
│   │   ├── api.py                     # FastAPI 服务
│   │   └── client.py                  # MCP Client
│   ├── debug
│   │   ├── __init__.py
│   │   └── mcp_logger.py              # 记录 MCP Client 与 MCP Server 通信数据,用于白盒解析 MCP 通信协议
│   ├── envs
│   │   ├── common.env           # 多服务环境变量配置文件
│   │   ├── dify.env
│   │   └── mysql.env
│   ├── server
│   │   ├── __init__.py
│   │   ├── cli
│   │   │   ├── __init__.py
│   │   │   ├── dify_cli.py     # cli 方式启动 dify 服务
│   │   │   ├── mysql_cli.py    # cli 方式启动 mysql 服务
│   │   │   └── server.py
│   │   ├── common
│   │   │   ├── __init__.py
│   │   │   ├── base            # 公共的资源、工具和提示词自动注册和发现的基类包
│   │   │   ├── prompts.py      # 存放提示词模版
│   │   │   └── tools.py        # 存放工具描述
│   │   ├── core
│   │   │   ├── __init__.py
│   │   │   ├── base_server.py       # 微服务基类
│   │   │   ├── config_manager.py    # 环境变量配置器
│   │   │   ├── env_distribute.py    # stdio通信机制下多服务环境变量分发器
│   │   │   └── service_manager.py   # 多服务管理器
│   │   ├── server_dify              # dify 服务实现包
│   │   │   ├── __init__.py
│   │   │   ├── config
│   │   │   ├── dify_server.py      # dify 服务实现类
│   │   │   └── tools
│   │   ├── server_mysql             # mysql 服务实现包
│   │   │   ├── __init__.py
│   │   │   ├── config
│   │   │   ├── mysql_server.py     # mysql 服务实现类
│   │   │   ├── prompts
│   │   │   ├── resources
│   │   │   └── tools
│   │   └── shared
│   │       ├── __init__.py
│   │       ├── oauth
│   │       ├── security    # SQL鉴权
│   │       ├── templates
│   │       └── utils
│   └── test
├── pyproject.toml
├── requirements.txt
└── uv.lock

项目设计思路

本项目原先参考于开源项目:github.com/wenb1n-dev/…

针对如何借助 Low-Level 接口自动注册和发现资源、工具和提示词,接下来则以我们扩展封装的资源为例进行介绍。

在多服务基类脚本base_server.py中只需展示和读取资源即可,对应的类会自动将资源进行注册和读取。

async def setup_server(self):
    """设置服务器路由"""
    if self.server_setup_completed:
        self.logger.debug("服务器路由已设置,跳过重复设置")
        return
    
    self.logger.info("开始设置服务器路由")
    
    # 注册资源处理器
    @self.server.list_resources()
    async def handle_list_resources() -> List[Resource]:
        try:
            registry = self.get_resource_registry()
            if registry is None:
                self.logger.warning("资源注册表未初始化,返回空列表")
                return []
            
            if hasattr(registry, 'get_all_resources'):
                if asyncio.iscoroutinefunction(registry.get_all_resources):
                    return await registry.get_all_resources()
                else:
                    return registry.get_all_resources()
            return []
        except Exception as e:
            self.logger.error(f"获取资源列表失败: {str(e)}", exc_info=True)
            return []
    
    @self.server.read_resource()
    async def handle_read_resource(uri: AnyUrl) -> str:
        try:
            self.logger.info(f"开始读取资源: {uri}")
            registry = self.get_resource_registry()
            if registry is None:
                raise ValueError("资源注册表未初始化")
            
            if hasattr(registry, 'get_resource'):
                if asyncio.iscoroutinefunction(registry.get_resource):
                    content = await registry.get_resource(uri)
                else:
                    content = registry.get_resource(uri)
            else:
                content = None
            
            if content is None:
                content = "null"
            self.logger.info(f"资源 {uri} 读取成功,内容长度: {len(content)}")
            return content
        except Exception as e:
            self.logger.error(f"读取资源失败: {str(e)}", exc_info=True)
            raise

资源注册类:



class ResourceRegistry:
    """资源注册表,用于管理所有资源实例"""
    _resources: ClassVar[Dict[str'BaseResource']] = {}
    
    @classmethod
    def register(cls, resource_class: Type['BaseResource']):
        """注册资源实例"""
        resource = resource_class()
        logger.info(f"注册资源: {resource.name} (URI: {resource.uri})")
        cls._resources[str(resource.uri)] = resource
    
    @classmethod
    def register_instance(cls, resource: 'BaseResource'):
        """手动注册资源实例"""
        uri_str = str(resource.uri)
        logger.info(f"注册资源实例: {resource.name} (URI: {uri_str})")
        cls._resources[uri_str] = resource
    
    @classmethod
    async def get_resource(cls, uri: AnyUrl) -> str:
        """获取资源内容"""
        logger.info(f"请求资源: {uri}")
        parsed = urlparse(str(uri))
        uri_str = f"{parsed.scheme}://{parsed.netloc}/{parsed.path}"
        path_parts = parsed.path.strip('/').split('/')
        
        if not path_parts or not path_parts[0]:
            raise ValueError(f"无效的URI格式: {uri_str},未指定表名")
        
        # 优先尝试精确匹配
        for resource in cls._resources.values():
            if str(resource.uri) == uri_str:
                return await resource.read_resource(uri)
        
        # 尝试后缀匹配
        for resource in cls._resources.values():
            if str(resource.uri).endswith(path_parts[0]):
                return await resource.read_resource(uri)
        
        logger.error(f"未找到资源: {uri},已注册资源: {[r.uri for k, r in cls._resources.items()]}")
        raise ValueError(f"未注册的资源: {uri}")
    
    @classmethod
    async def get_all_resources(cls) -> List[Resource]:
        """获取所有资源的描述"""
        result = []
        # 创建资源副本避免在迭代过程中修改原字典:扫描库时还会注册表资源
        resources_copy = list(cls._resources.values())
        for resource in resources_copy:
            try:
                logger.info(f"获取 {resource.name} 的资源描述")
                descriptions = await resource.get_resource_descriptions()
                result.extend(descriptions)
                logger.debug(f"{resource.name} 提供了 {len(descriptions)} 个资源描述")
            except Exception as e:
                logger.error(f"获取 {resource.name} 的描述失败: {str(e)}", exc_info=True)
        return result

封装后的资源基类:主要是借助__init_subclass__方法自动注册

class BaseResource:
    """资源基类"""
    name: str = ""
    description: str = ""
    uri: AnyUrl
    mimeType: str = "text/plain"
    auto_register: bool = True
    
    def __init_subclass__(cls, **kwargs):
        """子类初始化时自动注册到资源注册表"""
        super().__init_subclass__(**kwargs)
        if cls.auto_register and cls.uri is not None:  # 只注册有 uri 的资源
            ResourceRegistry.register(cls)
    
    async def get_resource_descriptions(self) -> List[Resource]:
        """获取资源描述,子类必须实现"""
        raise NotImplementedError
    
    async def read_resource(self, uri: AnyUrl) -> str:
        """读取资源内容,子类必须实现"""
        raise NotImplementedError

实现 MySQL 资源类:值得注意的是此处我们在设计时迫于上面的机制又设计了表资源类TableResource

class TableResource(BaseResource):
    """代表具体表资源的类"""
    auto_register: bool = False
    
    TABLE_EXISTS_QUERY = """
        SELECT COUNT(*) AS table_exists
        FROM information_schema.tables
        WHERE table_schema = %s AND table_name = %s
    """
    
    COLUMN_METADATA_QUERY = """
        SELECT COLUMN_NAME, DATA_TYPE
        FROM information_schema.columns
        WHERE table_schema = %s AND table_name = %s
        ORDER BY ORDINAL_POSITION
    """
    
    def __init__(self, db_name: str, table_name: str, description: str):
        super().__init__()
        self.db_name = db_name
        self.table_name = table_name
        self.name = f"table: {table_name}"
        self.uri = AnyUrl(f"mysql://{db_name}/{table_name}")
        self.description = description
        self.mimeType = "text/csv"
    
    async def get_resource_descriptions(self) -> List[Resource]:
        """返回数据库表资源的描述:已返回"""
        return []
    
    async def read_resource(self, uri: AnyUrl) -> str:
        """安全读取数据库表数据为CSV格式(带列类型信息)"""
        logger.info(f"开始读取资源: {uri}")
        try:
            # 安全解析表名
            table_name = extract_table_name(uri)
            logger.info(f"准备查询表: {table_name}")
            
            # 获取列元数据(用于优化CSV生成)
            column_metadata = await self.get_table_metadata(table_name)
            
            async with get_current_database_manager().get_connection() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cursor:
                    # 使用参数化查询避免SQL注入
                    safe_query = _build_safe_query(table_name)
                    await cursor.execute(safe_query)
                    
                    # 直接获取列名
                    columns = [col[0for col in cursor.description]
                    rows = await cursor.fetchall()
                    
                    logger.info(f"获取到 {len(rows)} 行数据")
                    
                    # 使用优化的CSV生成
                    return generate_csv(columns, rows, column_metadata)
        
        except Exception as e:
            logger.error(f"读取资源失败: {str(e)}", exc_info=True)
            raise
    
    async def get_table_metadata(self, table_name: str) -> List[tuple]:
        """获取表列名和数据类型"""
        db_name = get_current_database_manager().get_current_config()["database"]
        
        async with get_current_database_manager().get_connection() as conn:
            async with conn.cursor(aiomysql.DictCursor) as cursor:
                # 首先验证表存在
                await cursor.execute(self.TABLE_EXISTS_QUERY, (db_name, table_name))
                exists = await cursor.fetchone()
                
                if not exists or not exists['table_exists']:
                    raise ValueError(f"表 '{table_name}' 在数据库 '{db_name}' 中不存在")
                
                # 获取列元数据
                await cursor.execute(self.COLUMN_METADATA_QUERY, (db_name, table_name))
                metadata = [(row['COLUMN_NAME'], row['DATA_TYPE']) for row in await cursor.fetchall()]
                
                return metadata




class MySQLResource(BaseResource):
    """MySQL数据库资源实现"""
    name = "MySQL数据库"
    uri = AnyUrl(f"mysql://localhost/default")
    description = "提供对MySQL数据库表的访问与查询"
    mimeType = "text/csv"
    auto_register = True
    
    # 重用这些常量查询
    TABLE_QUERY = """
        SELECT TABLE_NAME AS table_name,
               TABLE_COMMENT AS table_comment,
               TABLE_ROWS AS estimated_rows
        FROM information_schema.tables
        WHERE table_schema = %s
    """
    
    def __init__(self):
        """初始化资源管理"""
        super().__init__()
        self.cache = {}  # 查询结果缓存
    
    async def get_resource_descriptions(self) -> List[Resource]:
        """获取数据库表资源描述(带缓存机制)"""
        logger.info("获取数据库资源描述")
        
        db_manager = get_current_database_manager()
        if db_manager is None:
            logger.error("无法获取数据库管理器,上下文未设置?")
            return []
        
        db_name = db_manager.get_current_config().get("database")
        if not db_name:
            logger.error("数据库配置中未指定数据库名称")
            return []
        
        # 使用缓存避免重复查询
        if 'table_descriptions' in self.cache:
            logger.debug("使用缓存的表描述")
            return self.cache['table_descriptions']
        
        try:
            async with db_manager.get_connection() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cursor:
                    await cursor.execute(self.TABLE_QUERY, (db_name,))
                    tables = await cursor.fetchall()
                    logger.info(f"发现 {len(tables)} 个数据库表")
                    
                    resources = []
                    for table in tables:
                        table_name = table['table_name']
                        
                        # 添加表行数统计
                        description = table['table_comment'or f"{table_name} 表"
                        if table['estimated_rows']:
                            description += f" (~{table['estimated_rows']}行)"
                        
                        # 创建表资源
                        table_resource = TableResource(db_name, table_name, description)
                        
                        # 手动注册表资源实例
                        ResourceRegistry.register_instance(table_resource)
                        
                        # 创建资源描述对象
                        resource_desc = Resource(
                            uri=table_resource.uri,
                            name=table_resource.name,
                            mimeType=table_resource.mimeType,
                            description=table_resource.description
                        )
                        resources.append(resource_desc)
                    
                    # 缓存结果
                    self.cache['table_descriptions'] = resources
                    logger.info(f"创建了 {len(resources)} 个表资源描述")
                    return resources
        
        except Exception as e:
            logger.error(f"获取资源描述失败: {str(e)}", exc_info=True)
            return []
    
    async def read_resource(self, uri: AnyUrl) -> str:
        """读取根资源内容 - 返回数据库信息"""
        return json.dumps({
            "name": self.name,
            "uri": self.uri,
            "description": self.description,
            "type""database_root"
        })

想实现其他资源,就编写对应的脚本,然后将包加入到对应的__init__.py脚本中:

from .db_resource import MySQLResource, TableResource


__all__ = [    "MySQLResource",    "TableResource",]

这样代码具有较高可扩展性,组织结构也很清晰。当然,也会有其他更好的实现方式。

效果展示

MCP Server 主要是通过工具暴露数据给LLMs,故基于上述的设计思路,实现起来相对简单且专一,主要就是实现工具所对应的 SQL 语句编写和对应的提示词即可,鉴于篇幅和代码量,此处不再展示源码,仅提供历史测试效果图。

查询表中数据

在 Cline 中配置好阿里通义千问大模型 API-KEY 后,进行提问: 

⚠️:阿里通义千问大模型配置可参考:help.aliyun.com/zh/model-st…

随后,大模型开始解析执行任务:

发现解析错了,开始自动矫正: 

OK,现在看起来就对多了,开始执行工具运行指令并返回结果:

最终执行结果如下:

其他的比如:查询某表中告警信息。此处给出了明确的库表信息,回答的就很精准。

慢SQL优化

大模型在执行一些工具之后,给出了回答:

并最终给出了如下预期效果:

高危操作验证

在执行高危 SQL 语句前,会拦截并作解析,判断是否与预先允许的操作一致,不一致则不放行,模型无法操作数据库,报错终止任务。目前权限限定为查询操作 DQL

当想更新表中数据时:

自建客户端提问

实现自定义客户端时可调用服务端提示词进行任务编排,提高工具调用准确度和规避一连串的客户端continue操作,通过请求实现的FastAPI接口可直接运行出结果。

当前数据库基本信息,以及包含哪些表,同时用户表有哪些字段。

线上部署测试效果

我们在 MCP 应用市场中上架了一个版本的 DW-DBA-MCP 服务,在 VSCode 中安装 EP-Copilot 插件即可安装使用该服务。

然后在聊天界面中选择agent模式即可让 LLMs 选择合适的工具处理您的提问(注意为服务配置环境变量):

五、未来规划

AI4DB(AI for Database)领域,AI 技术正颠覆传统数据库的运维与调优逻辑,推动其从依赖人工介入的被动响应模式,全面迈向可自主诊断、智能调优、故障自愈的全链路智能自治新阶段,大幅降低运维成本的同时提升了数据库系统的稳定性与运行效率。

而在DB4AI(Database for AI)方向,适配 AI 场景的数据库解决方案已实现关键突破:不仅具备高性能向量检索、复杂分析计算及强事务一致性等核心能力,还能原生支持文本、图像等多模态数据的一体化存储与管理,为 AI 模型训练与推理提供了高效、可靠的数据基座。

在技术格局剧变的背景下,DBA 的角色定位也迎来重构。传统意义上,DBA 即 Database Administrator(数据库管理员),核心聚焦于数据库的日常运维与技术保障;但随着 AI 技术的井喷式发展,DBA 已突破单一运维属性,可进阶为Data Business Architect(数据业务架构师)—— 借助 AI 工具与能力,DBA 能从海量数据中挖掘潜藏价值,打通数据与业务的链路,实现技术能力向业务价值的转化。

面向未来,DBA 团队将持续强化两大核心能力:一是筑牢数据安全防线,构建全周期数据安全治理体系;二是夯实工程化落地能力,推动智能技术与业务场景的深度融合。以此为基础,充分释放 DBA 在数据库 “智能自治运维” 与 “全域数据价值挖掘” 领域的双重价值,为企业数字化与智能化转型提供坚实的数据支撑。

六、资源推荐

资源 内容
MCP Server 社区仓库推荐 - github.com/modelcontex… github.com/punkpeye/aw…
当前支持 MCP 协议的客户端应用 modelcontextprotocol.io/clients
MCP 市场 - ModelScope: modelscope.cn/mcp
百炼 MCP 市场: bailian.console.aliyun.com/?tab=mcp#/m…

参考资料:

[1] 一文带你 "看见" MCP 的过程,彻底理解 MCP 的概念(developer.aliyun.com/article/166…

[2] 100行代码讲透MCP原理(ai.programnotes.cn/p/100%E8%A1…

往期回顾

1. 项目性能优化实践:深入FMP算法原理探索|得物技术

2. Dragonboat统一存储LogDB实现分析|得物技术

3. 从数字到版面:得物数据产品里数字格式化的那些事

4. 一文解析得物自建 Redis 最新技术演进

5. Golang HTTP请求超时与重试:构建高可靠网络请求|得物技术

文 /少晖、洪兆

关注得物技术,每周更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

❌