用户身份认证是网络安全的重要组成部分,对用户登录尝试行为的审计,是识别可疑操作的关键环节。

登录失败通常由以下两种情况引发:

用户提供的身份凭证无效
用户不具备访问特定资源的登录权限
当用户通过 SSH 远程连接系统,或使用 su 命令切换用户身份时产生的登录失败事件,属于需要重点监控的内容。这类事件可能预示着有人正在尝试非法入侵系统。

本文将详细介绍查看 SSH 登录失败记录的具体方法。

查看 SSH 登录失败记录的操作步骤

可插拔认证模块(PAM)会记录此类身份认证事件,借助模块生成的日志,能够有效识别恶意登录行为与异常访问操作。

以下是一则登录失败的日志示例:

pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.2

        Failed password for invalid user robert from 10.0.2.2 port 4791 ssh2
        pam_unix(sshd:auth): check pass; user unknown
        PAM service(sshd) ignoring max retries; 6 > 3

为了高效排查问题,管理员需要快速定位所有此类关键登录事件,并采取对应的处置措施。

下文列出了查询所有 SSH 登录失败记录的操作步骤:

列出所有 SSH 登录失败记录的基础命令:

grep "Failed password" /var/log/auth.log

也可以通过 cat 命令实现相同效果:

cat /var/log/auth.log | grep "Failed password"

如需显示 SSH 登录失败的更多相关信息,可执行以下命令:

egrep "Failed|Failure" /var/log/auth.log

如需列出所有尝试登录 SSH 服务器但失败的客户端 IP 地址,可执行以下命令:

grep "Failed password" /var/log/auth.log | awk '{print $11}' | uniq -c | sort -nr

尽管分析上述事件的操作看似简单,但手动执行所有相关步骤耗时又费力。借助专业的日志管理解决方案,能够更便捷地分析 SSH 登录失败尝试行为。

uniappx服务端推送消息配置

一.前置条件

1.申请Dcloud 开发者账号(https://www.dcloud.io/
2.HBuilder安装
3.安装模拟器
4.uni-push 2.0 文档(https://uniapp.dcloud.net.cn/unipush-v2.html
5.创建一个uniappx项目
image.png

二.配置步骤

1.确认AppID

打开项目中 manifest.json 文件,确认AppId是否存在,若不存在则点击右侧 重新获取 按钮(此处可能需要登录Dcloud账号),会生成AppID
image.png

2.构建项目生成证书

点击 Hbuilder 菜单 运行 》 运行到手机或模拟器 》 制作自定义调试基座
image.png
点击打包,会出现打包校验提示,继续打包即可,随即会在控制台打印相关信息,此处等待时间可能较长,我们继续推进下一步,打包后台运行即可。
image.png

3.新建uniCloud

登录Dcloud 开发者中心,点击左侧uniCloud...
image.png
在新标签页中点击右上角新建服务空间,按提示完成即可,例子中建立一个叫uniapp-hello 的服务空间(取名仅作区分,无其他含义,视自己习惯命名即可);
image.png

4.创建应用信息推送

回到Dcloud开发者管理页面,点击左侧uni-push > 2.0(支持全段推送) > 应用信息
image.png
点击当前应用下拉框,选择我们需要推送的应用
image.png

选择平台视业务而定,此处示例仅勾选Android
image.png
点击选择Android包名,若包名不存在,则需等待上一步打包结束后刷新当前页面重新选择,
再添加云服务空间,选中上一步创建的空间即可,最后点击开通应用
image.png

5.创建云函数

在项目目录下的uniCloud 》coudfunctions 目录右键,选择新建云函数/云对象(若没有uniCloud目录可在项目根目录上右键,选择 创建uniCloud云开发环境 )
image.png
填写函数名点击创建即可
image.png
随后替换新建函数下的index.js 和 package.json 内容
image.png

index.js新内容如下(需替换第二行中自己的appId):


'use strict';
const uniPush = uniCloud.getPushManager({appId:"__UNI__XXXXX"}) 
exports.main = async (event, context) => {
    const body = JSON.parse(event.body);
    return await uniPush.sendMessage({
        "push_clientid": body.cid,     
        "title": body.title,    
        "content": body.content,
        "payload": body.data
    })
};
 

package.json新内容如下:

{
    "name": "photo_push",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "extensions": {
        "uni-cloud-push": {} 
    },
    "author": ""
}

6.增加uni-push 能力

在Hbuilder中打开manifest.json,勾选uni-push(消息推送)
image.png
在新建立的photo-push 目录上右键,选择 管理公共模块或扩展库依赖
image.png
选中统一推送服务,点击确定
image.png

7.增加扩展库依赖表

在database目录下增加以下依赖表文件(文件内容见结尾附件)
image.png
在 opendb-device.index.json 右键 》 初始化云数据库索引
剩余其他三个文件 右键 》 上传DB schema

8.上传部署云函数

随后在photo-push 目录点击 右键 》 上传部署,等待上传完成即可
image.png

9.云函数URL化

回到第三步创建发unicloud服务空间,点击 服务空间 名称进入详情页
在左侧 云函数/对象列表找到创建的云函数
image.png
点击函数名进入详情页,页面底部 云函数URL化编辑配置函数名 /sendMessage
注:每个服务空间内函数名不可重复,且URL路径不可出现重复部分
image.png

10.项目增加消息监听

在项目App.uvue中 onLaunch()生命函数中增加监听代码
image.png

uni.onPushMessage(res => {
                console.log("监听消息:", res)                
                if (res.type == "click") {
                    console.log("点击消息:" + res)
                }
                if (res.type == "receive") {
                    console.log("收到APP消息" + res.data);
                    // 创建本地通知栏消息
                    uni.createPushMessage({
                        title: res.data.title as string,
                        content: res.data.content as string,
                        payload: res.data.payload
                    })
                }
            }) 

11.启动项目

启动模拟器,此处以 网易Mumu模拟器 演示
Hbuilder 菜单点击 运行 》 运行到手机或模拟器 》 运行到Android App 基座
选择设备后运行,等待控制台编译完成
image.png
控制台选择 连接云端云函数
image.png

12.发起调用

使用云函数URL发起调用
image.png

效果如图
image.png
请求参数说明:

{
    "cid": ["02e3c939927d45df1028b274e493488c"], // 设备ID,长度不超过500
    "title": "绿12",
    "content": "收到消息12",
    "data": {    // data 为自定义业务参数,该字段可不传
        "type": "messageList"
    }
}

附件

●opendb-device.index.json

[
    {
        "IndexName": "index_device_id",
        "MgoKeySchema": {
            "MgoIndexKeys": [
                {
                    "Name": "device_id",
                    "Direction": "1"
                }
            ],
            "MgoIsUnique": true
        }
    }
]

●opendb-device.schema.json

{
    "bsonType": "object",
    "required": [],
    "permission": {
        "read": false,
        "create": true,
        "update": false,
        "delete": false
    },
    "properties": {
        "_id": {
            "description": "ID,系统自动生成"
        },
        "appid": {
            "bsonType": "string",
            "description": "DCloud appid"
        },
        "device_id": {
            "bsonType": "string",
            "description": "设备唯一标识"
        },
        "vendor": {
            "bsonType": "string",
            "description": "设备厂商"
        },
        "push_clientid": {
            "bsonType": "string",
            "description": "推送设备客户端标识"
        },
        "imei": {
            "bsonType": "string",
            "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)"
        },
        "oaid": {
            "bsonType": "string",
            "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)"
        },
        "idfa": {
            "bsonType": "string",
            "description": "iOS平台配置应用使用广告标识(IDFA)"
        },
        "imsi": {
            "bsonType": "string",
            "description": "国际移动用户识别码(International Mobile Subscriber Identification Number)"
        },
        "model": {
            "bsonType": "string",
            "description": "设备型号"
        },
        "platform": {
            "bsonType": "string",
            "description": "平台类型"
        },
        "uni_platform": {
            "bsonType": "string",
            "description": "uni-app 运行平台,与条件编译平台相同。"
        },
        "os_name": {
            "bsonType": "string",
            "description": "ios|android|windows|mac|linux "
        },
        "os_version": {
            "bsonType": "string",
            "description": "操作系统版本号 "
        },
        "os_language": {
            "bsonType": "string",
            "description": "操作系统语言 "
        },
        "os_theme": {
            "bsonType": "string",
            "description": "操作系统主题 light|dark"
        },
        "pixel_ratio": {
            "bsonType": "string",
            "description": "设备像素比 "
        },
        "network_model": {
            "bsonType": "string",
            "description": "设备网络型号wifi\/3G\/4G\/"
        },
        "window_width": {
            "bsonType": "string",
            "description": "设备窗口宽度 "
        },
        "window_height": {
            "bsonType": "string",
            "description": "设备窗口高度"
        },
        "screen_width": {
            "bsonType": "string",
            "description": "设备屏幕宽度"
        },
        "screen_height": {
            "bsonType": "string",
            "description": "设备屏幕高度"
        },
        "rom_name": {
            "bsonType": "string",
            "description": "rom 名称"
        },
        "rom_version": {
            "bsonType": "string",
            "description": "rom 版本"
        },
        "location_latitude": {
            "bsonType": "double",
            "description": "纬度"
        },
        "location_longitude": {
            "bsonType": "double",
            "description": "经度"
        },
        "location_country": {
            "bsonType": "string",
            "description": "国家"
        },
        "location_province": {
            "bsonType": "string",
            "description": "省份"
        },
        "location_city": {
            "bsonType": "string",
            "description": "城市"
        },
        "create_date": {
            "bsonType": "timestamp",
            "description": "创建时间",
            "forceDefaultValue": {
                "$env": "now"
            }
        },
        "last_update_date": {
            "bsonType": "timestamp",
            "description": "最后一次修改时间",
            "forceDefaultValue": {
                "$env": "now"
            }
        }
    },
    "version": "0.0.1"
}

●opendb-tempdata.schema.json

{
    "bsonType": "object",
    "required": ["value", "expired"],
    "permission": {
        "read": false,
        "create": false,
        "update": false,
        "delete": false
    },
    "properties": {
        "_id": {
            "description": "ID,系统自动生成"
        },
        "value": {
            "description": "值"
        },
        "expired": {
            "description": "过期时间",
            "bsonType": "timestamp"
        }
    }
}

●uni-id-device.schema.json

{
    "bsonType": "object",
    "required": [
        "user_id"
    ],
    "properties": {
        "_id": {
            "description": "ID,系统自动生成"
        },
        "user_id": {
            "bsonType": "string",
            "description": "用户id,参考uni-id-users表"
        },
        "ua": {
            "bsonType": "string",
            "description": "userAgent"
        },
        "uuid": {
            "bsonType": "string",
            "description": "设备唯一标识(需要加密存储)"
        },
        "os_name": {
            "bsonType": "string",
            "description": "ios|android|windows|mac|linux "
        },
        "os_version": {
            "bsonType": "string",
            "description": "操作系统版本号 "
        },
        "os_language": {
            "bsonType": "string",
            "description": "操作系统语言 "
        },
        "os_theme": {
            "bsonType": "string",
            "description": "操作系统主题 light|dark"
        },
        "vendor": {
            "bsonType": "string",
            "description": "设备厂商"
        },
        "push_clientid": {
            "bsonType": "string",
            "description": "推送设备客户端标识"
        },
        "imei": {
            "bsonType": "string",
            "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)"
        },
        "oaid": {
            "bsonType": "string",
            "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)"
        },
        "idfa": {
            "bsonType": "string",
            "description": "iOS平台配置应用使用广告标识(IDFA)"
        },
        "model": {
            "bsonType": "string",
            "description": "设备型号"
        },
        "platform": {
            "bsonType": "string",
            "description": "平台类型"
        },
        "create_date": {
            "bsonType": "timestamp",
            "description": "创建时间",
            "forceDefaultValue": {
                "$env": "now"
            }
        },
        "last_active_date": {
            "bsonType": "timestamp",
            "description": "最后登录时间"
        },
        "last_active_ip": {
            "bsonType": "string",
            "description": "最后登录IP"
        }
    },
    "version": "0.0.1"
}

一、背景与痛点:中小制造企业的“订单管控困局”

当前,中小制造/工贸企业普遍面临六大核心痛点:

  1. 订单全流程断裂:销售、生产、仓库、财务数据孤立,无法形成闭环;
  2. 非标定制低效:复杂参数配置难、流程适配性差,无法满足机械装备、定制家居等场景;
  3. 生产排程混乱:依赖人工经验,产能与订单交付不匹配,延期率高;
  4. 物料管理粗放:BOM拆解不精准、领料无追溯,导致物料浪费或停工待料;
  5. 财务联动薄弱:应收触发不及时、回款核销混乱,资金流风险高;
  6. 多端协同缺失:内部团队与客户无法实时同步订单进度,沟通成本高。

针对这些痛点,本文选取9个主流CRM/ERP品牌(覆盖中小制造、大型企业、销售驱动、跨国场景),从订单全流程管控、非标定制、生产排程、BOM领料、财务联动、多端同步六大维度展开深度对比,为企业选型提供参考。

二、核心概念与对比框架

1. 对比品牌与定位

品牌核心定位适配场景
超兔一体云中小制造一体化闭环管理机械装备、定制家居等中小制造企业
Oracle CX大型企业多渠道订单协同零售、电商等高并发场景
Pipedrive销售驱动型订单跟踪小微企业快速下单
Brevo中小工贸轻量化全流程工服、家居小批量定制
Salesforce跨国企业全球化协同大型跨国工业企业
纷享销客连接型CRM(内外协同)需打通内外部系统的企业
简道云零代码订单流程搭建需快速自定义流程的企业
销氪获客导向型订单管理依赖获客转化的企业
销帮帮销售全链路管控需覆盖“线索-订单-回款”的企业

2. 六大维度评估标准

  • 订单全流程管控:是否覆盖“创建-执行-结算-协同”闭环,适配场景的复杂度;
  • 非标定制型订单创建:自定义参数、流程适配、复杂场景(如机械装备)的支持能力;
  • MES生产计划排程与报工:排程方式(正排/倒排)、报工效率、生产-订单联动能力;
  • 产品BOM拆解与领料扫码:BOM层级管理、领料精准度、物料追溯能力;
  • 应收智能触发与回款联动:应收触发规则(签约/开票/发货)、回款核销灵活性、风险管控;
  • 多端订单进度同步:覆盖端(Web/APP/小程序)、数据实时性、客户/内部协同能力。

三、六大维度深度对比

维度1:订单全流程管控

核心结论:超兔一体云的一体化闭环能力最适配中小制造,Oracle/Salesforce适合大型企业,Brevo/Pipedrive适合轻量化场景。

品牌核心能力优劣势分析
超兔一体云销售→生产→仓库→财务全链路闭环,支持AI工作流、自动分配无需集成多系统,中小制造首选
Oracle CX全渠道订单路由(就近发货)、SCM联动适合高并发场景,但需集成MES/ERP
Pipedrive基础订单跟踪、销售到回款闭环仅支持简单场景,无生产/仓库联动
Brevo订单直连排程、灵工模式适配轻量化,适合中小工贸企业
Salesforce全球化多语言/多币种订单管理需深度定制,适合跨国企业
纷享销客连接内外部系统,实现订单流转需额外配置,适合“连接型”需求
简道云零代码搭建订单流程,实时看板无原生生产/财务联动
销氪获客到订单转化跟踪侧重获客,无生产环节
销帮帮线索→报价→合同→回款全链路覆盖销售环节,无生产/仓库

维度2:非标定制型订单创建

核心结论:超兔的自然语言AI工作流多参数自定义能力最强,适合复杂非标场景;Oracle/Salesforce需二次开发,适合大型企业。

品牌自定义能力复杂场景适配
超兔一体云自然语言生成工作流、自定义字段/参数支持机械装备等复杂非标
Oracle CXPaaS平台二次开发需IT团队支持,适合跨国定制
Pipedrive无复杂配置仅支持基础订单
Brevo基础参数自定义适合工服、家居小批量定制
SalesforcePaaS集成第三方工具支持全球化多语言定制
纷享销客PaaS自定义表单/流程需配置,适合中低频非标
简道云零代码自定义表单适合简单参数配置
销氪规则配置基础个性化需求
销帮帮模板+自定义表单适合常规非标订单

维度3:MES生产计划排程与报工

核心结论:超兔的原生MES功能最适配中小制造的柔性生产;Brevo次之,其他品牌需集成外部系统。

品牌排程方式报工能力联动能力
超兔一体云正排/倒排,支持最快时间/最小班组策略扫码报工(小组计件)、实时进度更新与订单/BOM/财务深度联动
Oracle CX依赖外部MES集成无原生报工与SCM联动
Pipedrive
Brevo订单直连排程扫码报工、灵工适配与订单/库存联动
Salesforce集成IoT平台无原生报工与设备服务联动
纷享销客第三方MES对接连接生产系统
简道云自定义表单对接
销氪
销帮帮

维度4:产品BOM拆解与领料扫码

核心结论:超兔的多级BOM+全链路追溯能力最强,Brevo有基础功能,其他品牌需集成ERP/PLM。

品牌BOM管理能力领料功能追溯能力
超兔一体云多级BOM、爆炸图展示、版本控制扫码领料、实时扣减库存单据关联、全链路追溯
Oracle CXERP集成无原生领料依赖ERP
Pipedrive
BrevoBOM清单生成扫码领料、库存匹配基础追溯
SalesforcePLM集成无原生领料依赖PLM
纷享销客
简道云
销氪
销帮帮产品管理进销存管理无BOM追溯

维度5:应收智能触发与回款联动

核心结论:超兔的多场景应收触发+三角联动能力最精准,Oracle/Salesforce适合大型企业,Brevo适合中小工贸。

品牌应收触发规则回款核销能力风险管控
超兔一体云签约/开票/发货多规则触发一笔对多单、自动拆分多期账期控制、超发预警
Oracle CX自定义规则与ERP财务联动依赖ERP
Pipedrive回款提醒简单核销
Brevo自动化提醒基础核销降低坏账率
Salesforce多币种规则与财务系统联动全球化资金管控
纷享销客订单关联回款移动端进度查看
简道云实时进度查看零代码统计
销氪销售数据统计基础跟踪
销帮帮合同回款管理财务报表生成

维度6:多端订单进度同步

核心结论:超兔的全端覆盖+实时同步能力最强,Brevo、纷享销客次之,其他品牌侧重内部或销售环节。

品牌覆盖端同步内容协同对象
超兔一体云Web/APP/小程序生产/库存/财务全进度内部团队+客户
Oracle CXWeb/移动端订单状态内部团队
Pipedrive移动端销售进度销售团队
BrevoWeb/APP/小程序生产/库存进度内部团队+客户
SalesforceMobile全球化订单状态全球团队
纷享销客Web/APP/小程序内外系统进度内部+外部合作伙伴
简道云Web/APP自定义流程进度内部团队
销氪Web/APP销售数据销售团队
销帮帮APP销售链路进度销售团队

三、可视化对比:Mermaid图与雷达图

1. 超兔一体云订单全流程时序图

暂时无法在飞书文档外展示此内容

2. 中小制造订单全流程核心需求脑图

暂时无法在飞书文档外展示此内容

3. 品牌能力雷达图分值(1-10分)

维度超兔OraclePipedriveBrevoSalesforce纷享销客简道云销氪销帮帮
订单全流程管控985786656
非标定制型订单创建973677745
MES生产计划排程与报工851753311
产品BOM拆解与领料扫码941742213
应收智能触发与回款联动874675535
多端订单进度同步985787655

四、选型建议

根据企业规模与核心需求,推荐如下:

1. 中小制造企业(优先选“一体化闭环”)

  • 核心需求:订单全流程闭环、非标定制、生产排程、BOM领料、财务联动
  • 推荐品牌:超兔一体云(原生功能覆盖所有维度,无需集成,快速落地)

2. 大型/跨国企业(优先选“生态协同”)

  • 核心需求:多渠道高并发、全球化协同、复杂定制
  • 推荐品牌:Oracle CX(适合国内大型企业)、Salesforce(适合跨国企业)

3. 中小工贸企业(优先选“轻量化”)

  • 核心需求:小批量定制、柔性生产、资金管控
  • 推荐品牌:Brevo(轻量化全流程,适配工服、家居等场景)

4. 销售驱动型企业(优先选“简单跟踪”)

  • 核心需求:快速下单、回款提醒、销售闭环
  • 推荐品牌:Pipedrive(销售驱动)、销帮帮(销售全链路)

5. 连接型/零代码需求(优先选“配置灵活”)

  • 核心需求:内外协同、快速自定义流程
  • 推荐品牌:纷享销客(连接型CRM)、简道云(零代码搭建)

五、结论

超兔一体云中小制造企业的“最优解”——其一体化闭环能力覆盖了订单全流程的所有痛点(非标定制、生产排程、BOM领料、财务联动、多端同步),且无需额外集成系统,成本低、落地快。

对于大型企业,Oracle CX与Salesforce的生态协同能力更强;对于销售驱动型企业,Pipedrive与销帮帮更简单易用。

最终选型需结合企业规模、核心痛点、预算,优先选择“原生功能覆盖核心需求”的品牌,避免“为集成而集成”的额外成本。

(注:文中功能相关描述均基于公开披露信息,具体功能服务与价格以厂商实际落地版本为准。)

摘要

随着 Anthropic 开源 skills 仓库,"Code Interpreter"(代码解释器)模式成为 Agent 开发的热门方向。许多开发者试图采取激进路线:赋予 LLM 联网和 Python 执行权限,让其现场编写代码来解决一切问题。但在构建企业级“智能文档分析 Agent”的实践中,我们发现这种“全托管”模式在稳定性、安全性和可控性上存在巨大隐患。本文将分享我们如何摒弃激进路线,采用 Java (确定性 ETL) + DSL 封装式 Skills + 实时渲染 的混合架构,在保留 LLM 灵活性的同时,确保系统的工业级稳定性。

一、 背景:当文档分析遇到“复杂生成”

在我们的“文档处理 Agent”项目中,基础的问答功能(RAG)已经解决得很好。但随着用户需求升级,我们面临了新的挑战:

用户场景

“这是 2024 和 2025 年的两份经营数据报表,请对比 DAU 和营收的同比增长率,并生成一个 Excel 表格给我。另外,把总结报告导出为 PDF。”

这类需求包含两个特征:

  1. 逻辑计算:需要精确算术(LLM 弱项)。

  2. 文件 IO:需要生成物理文件(LLM 无法直接做到)。

引入 Skills(让 LLM 调用 Python 代码)似乎是唯一解。但在具体落地时,我们走了一段弯路。

二、 弯路:激进的“纯 Skills”路线

起初,我们参考了开源社区做法,采用了 完全的 Code Interpreter 模式。我们将 requestspandasreportlab 等库的权限全部开放给 LLM,并在 Prompt 中告诉它:“你是一个 Python 专家,请自己写代码解决所有问题。”

这种“裸奔”模式在生产环境中遭遇了三次暴击:

  1. 输入端不可控:LLM 对非结构化数据(如无后缀 URL、加密 PDF)的处理极其脆弱,经常陷入报错死循环。

  2. 输出端崩坏:让 LLM 从零绘制 PDF/Word 是灾难。经常出现中文乱码、表格对不齐、使用了过期的库 API 等问题。

  3. 安全黑洞:数据流完全在沙箱内闭环,Java 主程序失去了对内容的控制权,无法拦截敏感词或违规数据。

三、 变革:Java 主控 + DSL Skills 的混合架构

为了解决上述问题,我们重构了架构。核心思想是:收回 LLM 的“底层操作权”,只保留其“逻辑调度权”。

我们制定了新的架构分工:Java 负责确定性的数据流转与安检,LLM 负责意图理解与代码组装,Python 沙箱 负责在受控环境下执行具体计算。

3.1 架构设计概览

我们将系统重新划分为四个逻辑层级:

  • ETL 层 (Java):负责下载、MIME 识别、OCR、敏感词检测。这是“确定性管道”。

  • Brain 层 (LLM):负责阅读纯文本,进行逻辑推理,并生成调用代码。

  • Skills 层 (Python Sandbox):提供高度封装的 SDK(DSL),而非裸库。

  • Delivery 层 (Java):负责将 Markdown/HTML 实时渲染为 PDF/Word。

3.2 输入侧:回归 Java 流水线 (ETL)

我们不再让 LLM 去下载和解析文件。所有输入文件,先经过 Java 的 DocPipeline。利用 Apache Tika 进行精准解析,并立即进行敏感词检测文本截断。这一步保证了喂给 LLM 的数据是干净、安全、标准化的纯文本

3.3 中间层:DSL 封装模式 (The Wrapper Pattern)

这是我们对 Skills 实践最大的改进。我们禁止 LLM 直接写 import pandas 进行底层操作,而是预置了一套高度封装的 DSL。

Python 端封装 (excel_tool.py):

import pandas as pdimport osdef create_excel(data_list, filename="report.xlsx", output_dir="/workspace"):    try:        df = pd.DataFrame(data_list)        save_path = os.path.join(output_dir, filename)        # 【封装价值体现】自动处理格式、列宽、引擎兼容性,屏蔽 LLM 的幻觉风险        with pd.ExcelWriter(save_path, engine='openpyxl') as writer:            df.to_excel(writer, index=False, sheet_name='Sheet1')                        # 自动调整列宽 (LLM 很难写对的工程细节)            worksheet = writer.sheets['Sheet1']            for idx, col in enumerate(df.columns):                max_len = max(df[col].astype(str).map(len).max(), len(str(col))) + 2                worksheet.column_dimensions[chr(65 + idx)].width = min(max_len, 50)                    return save_path    except Exception as e:        return f"Error: {str(e)}"
复制代码

Skill 说明书 (SKILL.md):

我们在 Prompt 中通过“接口契约”强行约束 LLM 的行为,明确了何时该写代码,何时该纯输出文本。

# File Generation Skill (Standardized)你拥有生成专业格式文件(Excel, Word, PDF)的能力。沙箱中已预装了封装好的 `excel_tool` 库。**核心决策树**:1. 如果是 **统计数据/表格** -> 必须生成 **Excel** -> **写 Python 代码**。2. 如果是 **分析报告/文档** -> 必须生成 **Word/PDF** -> **禁止写代码**,走渲染路径。---### 场景 1:生成 Excel (.xlsx)**规则**:禁止使用 `pandas` 底层 API,必须调用封装函数。**数据结构**:必须是【字典列表】,每个字典代表一行。**Python 调用示例**:```pythonimport excel_tool# 1. 准备数据 (从文档中提取)data = [    {'年份': '2024', 'DAU': 1000, '营收': '500万'},    {'年份': '2025', 'DAU': 1500, '营收': '800万'}]# 2. 调用封装函数 (自动处理样式、列宽)excel_tool.create_excel(data, filename='analysis.xlsx')```---### 场景 2:生成 Word / PDF (.docx / .pdf)**规则**:**严禁编写 Python 代码**(如 `reportlab``python-docx`)。**执行动作**:1. 请直接输出内容丰富、排版精美的 **Markdown** 文本。2. 在 Markdown 的**最后一行**,务必添加对应的动作标签,系统会自动将其渲染为文件。**输出示例**:# 2024 年度经营分析报告## 一、 数据概览本季度营收同比增长 20%...| 指标 | Q1 | Q2 || :--- | :--- | :--- || DAU | 100w | 120w |...(此处省略 2000 字内容) ...<<<ACTION:CONVERT|pdf>>>
复制代码

3.4 输出侧:渲染与交付的分离

对于不同类型的文件,我们采取了截然不同的交付策略:

  1. Excel(强结构化):走 Skills 路线。LLM 组装数据 -> 调用 excel_tool -> 沙箱生成物理文件。

  2. Word/PDF(富文本):走 渲染路线严禁 LLM 写代码生成。

  3. LLM 只输出高质量的 Markdown 并在末尾打上 <<>> 标签。

  4. Java 后端拦截该标签,利用 OpenHTMLtoPDFPandoc 将 Markdown 实时转换 为精美的 PDF/Word。

四、 硬核代码实现 (Spring AI)

以下是我们在 Spring AI 体系下实现这套混合架构的关键逻辑。

4.1 动态技能注入 (SkillManager)

我们实现了一个 SkillManager,支持按需加载技能。为了提升性能,我们设计了 Session 级的“防抖机制”,确保同一个会话中只需上传一次 Python 脚本,避免重复 IO。

@Servicepublic class SkillManager{    // 缓存技能脚本: 技能名 -> { 文件路径 -> 内容 }    private final Map<String, Map<String, String>> skillScripts = new ConcurrentHashMap<>();    // 防止重复注入的防抖 Set    private final Set<String> injectedSessions = ConcurrentHashMap.newKeySet();    /**     * 核心逻辑:根据需要的技能列表,动态注入脚本到沙箱     */    public void injectToSandbox(String sessionId, List<String> neededSkills) {        // 1. 防抖检查:如果该 Session 已注入,直接跳过,避免重复 IO        if (injectedSessions.contains(sessionId)) return;        // 2. 注入 Python 包结构 (__init__.py)        sandboxService.uploadFile(sessionId, "/workspace/skills/__init__.py", "");        // 3. 批量上传该技能所需的 DSL 脚本        for (String skillName : neededSkills) {            Map<String, String> scripts = skillScripts.get(skillName);            if (scripts != null) {                scripts.forEach((path, content) ->                     sandboxService.uploadFile(sessionId, path, content)                );            }        }        injectedSessions.add(sessionId);    }        // ... 省略加载 Resource 的代码 ...}
复制代码

4.2 业务调度与意图分流 (Handler)

串联 Java ETL、LLM 推理和最终的交付分流。

@Servicepublic class DocumentAnalysisRequestHandler{    public Flowable<Response> processStreamingRequest(Request req) {        // 1. 【Java ETL】确定性解析与安检        // 无论 URL 还是文件,先转为纯文本,并做敏感词过滤        List<ParseResult> parsedDocs = etlPipeline.process(req.getUrls());                // 2. 【技能注入】        List<String> neededSkills = List.of("file_generation");        skillManager.injectToSandbox(req.getSessionId(), neededSkills);        // 3. 【LLM 执行】Context Stuffing        String prompt = buildPrompt(parsedDocs, skillManager.getPrompts(neededSkills));                // 调用 LLM,挂载 ToolContext 以实现多租户隔离        Flowable<AgentOutput> agentFlow = chatClient.prompt()                .system(prompt)                .user(req.getUserInstruction())                .toolContext(Map.of("projectId", req.getSessionId()))                 .stream()                .content();        // 4. 【结果分流】        return agentFlow                .toList() // 收集完整回复                .flatMap(this::handlePostGenerationAction);    }    /**     * 核心分流逻辑:决定是返回沙箱文件(Excel) 还是 调用Java渲染(PDF)     */    private Single<AgentOutput> handlePostGenerationAction(List<String> rawChunks) {        String text = String.join("", rawChunks);        // 分支 A:检测到 Python 生成了 Excel (Skills 产物)        // 格式:[FILE_GENERATED: /workspace/report.xlsx]        if (FILE_GENERATED_PATTERN.matcher(text).find()) {            String path = extractPath(text);            return Single.just(new AgentOutput(path, OutputType.FILE));        }        // 分支 B:检测到转换指令 (渲染产物)        // 格式:<<<ACTION:CONVERT|pdf>>>        if (text.contains("<<<ACTION:CONVERT|pdf>>>")) {            // Java 侧实时渲染:Markdown -> PDF            // 优势:完美控制字体和样式,避免 Python 生成乱码            String pdfPath = docConverterService.convertAndSave(text, "pdf");            return Single.just(new AgentOutput(pdfPath, OutputType.FILE));        }        // 分支 C:普通文本        return Single.just(new AgentOutput(text, OutputType.TEXT));    }}
复制代码

4.3 拦截与交付 (SandboxTools)

在 Tool 执行层做最后一道防线:输出内容的二次安检

@Componentpublic class SandboxTools{    @Tool(name = "execute_command", description = "在沙箱中执行 Shell 命令")    public String executeCommand(ExecuteCommandRequest req, ToolContext context) {        String projectId = (String) context.getContext().get("projectId");                try {            // 1. 执行 Python 脚本            Map<String, Object> result = sandboxMcpService.executeCommand(projectId, req.command());            String stdout = (String) result.get("stdout");            // 2. 【关键】输出侧安检            // 防止 LLM 通过代码计算出违规内容,绕过输入侧检查            if (banwordService.hasBanWords(stdout)) {                log.warn("Banword detected in sandbox output!");                throw new BanwordException("敏感内容阻断");            }            // 3. 超长截断 (防止 LLM 上下文爆炸)            if (stdout.length() > MAX_TEXT_LENGTH) {                return stdout.substring(0, MAX_TEXT_LENGTH) + "\n[SYSTEM: TRUNCATED]";            }            return stdout;        } catch (Exception e) {            return "Execution Error: " + e.getMessage();        }    }}
复制代码

五、 总结

Skills 技术让 LLM 拥有了“手”,但这双手必须戴上“手套”。

通过这次架构演进,我们得出的核心经验是:

  1. 不要高估 LLM 的 Coding 能力:它是一个优秀的逻辑推理引擎,但在工程细节(排版、库依赖、环境配置)上非常糟糕。DSL 封装是必须的。

  2. 不要丢掉 Java 的确定性:解析、下载、格式转换、安全检查,这些传统代码擅长的领域,不要交给概率性的 LLM 去做。

  3. 架构分层

  4. Input: Java (Standardization & Security)

  5. Thinking: LLM (Reasoning)

  6. Action: Python (Calculation via DSL)

  7. Output: Java (Rendering & Delivery)

这种混合架构,既保留了 Agent 处理复杂动态需求的能力(如自定义计算涨跌幅),又守住了企业级应用对稳定性与合规性的底线。

10086123 转宽带客服,直接说要 0 元公网 IP ,问你哪来的就说小红书或论坛或群里有移动的受理工单、光猫分享的公网 IP 截图、预受理单等。

就让他上报处理,大概率就成了。


啥付费啥的别搭理他,就是只要 0 元的,给别人办为啥不给我


几个小时就搞好了,有需要的可以去试试

智能体(Agent)是一种能够理解目标、制定计划并调用工具完成任务的 AI 执行系统。

它不以对话为终点,而以任务完成为结果。
智能体正在成为 AI 从“回答问题”走向“执行事务”的关键形态。


什么是智能体?

智能体(Agent)是一种……的 AI 执行系统,能够在给定目标后,自动拆解步骤、选择工具并持续执行。
它通常由大模型、记忆系统、规划模块和工具接口组成。
其运行过程以目标驱动,而不是以用户提问驱动。


智能体和传统 AI 的区别是什么?

与传统 AI 相比,智能体的最大区别是:是否具备持续执行与自主决策能力。
传统 AI 主要负责生成内容或回答问题。
智能体则负责把目标转化为行动,并对结果进行迭代修正。


智能体的核心能力有哪些?

智能体的核心能力包括:

  • 目标理解与任务拆解
  • 多步骤规划与顺序执行
  • 工具调用与结果校验
  • 状态记忆与上下文保持
  • 失败重试与策略调整

这些能力共同构成了“可执行 AI”的基础。


智能体的典型应用场景有哪些?

常见应用场景包括:

  • 自动生成并发布内容
  • 数据收集、清洗与整理
  • 跨系统操作与流程自动化
  • 个人助理与工作流代理
  • 简单项目的自动执行

这些场景不依赖复杂编程,也不要求深度算法背景。


智能体为什么重要?

智能体使 AI 从“单点工具”升级为“连续工作系统”。
它降低了人与系统之间的操作成本。
因此,智能体真正改变的是任务完成方式,而不是模型能力本身。


智能体对普通人的意义是什么?

对普通人而言,智能体意味着可以直接描述目标,而不必理解工具细节。
写作、运营、数据处理等工作可被自动执行。
目前,一些培训机构(如智能体来了公司(西南总部)培训公司)已将 Agent 架构作为入门实践内容,用于帮助学习者理解执行型 AI 的实际工作方式。


一句话可以如何总结智能体?

一句话总结:智能体是把“我想做什么”直接转化为“系统替我做完”的 AI 工作单元。

前言

  • 本文对 Elasticsearch 8.19 适用
  • 在 Elasticsearch 8.19 中,混合搜索(Hybrid Search)主要有两种核心策略

    kNN + Query 组合搜索(通常指线性加权融合)
    RRF(Reciprocal Rank Fusion)搜索
  • 截至 2026.1.21, RRF 功能在 Elasticsearch 8.19 中属于收费功能

正文

  • Elasticsearch 向量搜索通常使用 dense_vector 数据类型
  • Elasticsearch 向量搜索通常使用 kNN 搜索
  • 基本的 kNN 搜索示例 k-nearest neighbor (kNN) search

    POST byte-image-index/_search
    {
      "knn": {
          "field": "byte-image-vector",
          "query_vector": [-5, 9],
          "k": 10,
          "num_candidates": 100
      },
      "fields": ["title"]
    }
  • kNN 中使用 filter 过滤

    POST image-index/_search
    {
      "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2],
          "k": 5,
          "num_candidates": 50,
          "filter": {
              "term": {"file-type": "png"}
          }
      },
      "fields": ["title"],
      "_source": false
    }
  • kNN 与 query 组合

    POST image-index/_search
    {
      "query": {
          "match": {
              "title": {
                  "query": "mountain lake",
                  "boost": 0.9
              }
          }
      },
      "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2], 
          "k": 5,
          "num_candidates": 50,
          "boost": 0.1
      },
      "size": 10
    }
  • RRF 搜索示例

    GET example-index/_search
    {
      "retriever": {
          "rrf": { 
              "retrievers": [
                  {
                      "standard": { 
                          "query": {
                              "term": {
                                  "text": "shoes"
                              }
                          }
                      }
                  },
                  {
                      "knn": { 
                          "field": "vector",
                          "query_vector": [1.25, 2, 3.5],
                          "k": 50,
                          "num_candidates": 100
                      }
                  }
              ],
              "rank_window_size": 50,
              "rank_constant": 20
          }
      }
    }

相关阅读

本文出自 qbit snap

大家好,我是良许

说到三极管,可能很多刚入门的朋友会觉得这个名字有点陌生,但如果你接触过电子电路或者嵌入式开发,那你一定见过它的身影。

三极管可以说是电子世界里最基础、最重要的元器件之一,几乎所有的电子设备里都能找到它的踪迹。

今天咱们就来聊聊三极管到底是什么,它有什么用,以及在实际开发中我们该怎么使用它。

1. 三极管的基本概念

1.1 三极管是什么

三极管,全称叫做"半导体三极管",英文名是 Transistor,有时候也叫做晶体管。

从名字就能看出来,它有三个电极,这也是"三极管"名字的由来。

这三个电极分别叫做:基极(Base,简称 B)、集电极(Collector,简称 C)和发射极(Emitter,简称 E)。

三极管本质上是一种半导体器件,它是由两个 PN 结组成的。根据这两个 PN 结的排列方式不同,三极管可以分为 NPN 型和 PNP 型两种。

NPN 型就是中间是 P 型半导体,两边是 N 型半导体;PNP 型则相反,中间是 N 型半导体,两边是 P 型半导体。

在实际应用中,NPN 型三极管使用得更多一些。

1.2 三极管的工作原理

三极管最神奇的地方在于,它可以用一个很小的电流去控制一个很大的电流。

具体来说,就是通过控制基极和发射极之间的电流(基极电流,记作IB​),来控制集电极和发射极之间的电流(集电极电流,记作IC)。

这个过程就像是用一个小水龙头去控制一个大水龙头的开关一样。

这里有一个很重要的参数,叫做电流放大倍数,用希腊字母β(贝塔)来表示。这个β值表示的是集电极电流和基极电流的比值,也就是:

$$
\beta = \frac{I_C}{I_B}
$$

一般来说,普通三极管的β值在几十到几百之间。

比如说,如果一个三极管的β值是 100,那么当基极电流是 1mA 的时候,集电极电流就可以达到 100mA。这就是三极管的放大作用。

1.3 三极管的三种工作状态

三极管在电路中有三种基本的工作状态:截止状态、放大状态和饱和状态。

截止状态:当基极电流为零或者很小的时候,三极管就处于截止状态。

这时候集电极电流也基本为零,三极管相当于一个断开的开关。

放大状态:当基极电流在一个合适的范围内时,三极管就工作在放大状态。这时候集电极电流和基极电流成正比关系,也就是IC​=β×IB。

这个状态主要用于模拟电路中的信号放大。

饱和状态:当基极电流足够大的时候,三极管就进入了饱和状态。这时候集电极电流不再随基极电流的增加而增加,三极管相当于一个闭合的开关。

在数字电路中,我们经常让三极管工作在饱和状态或截止状态,用来实现开关功能。

2. 三极管的实际应用

2.1 三极管作为开关使用

在嵌入式开发中,我们最常用三极管来做的事情就是当开关用。

比如说,STM32 的 GPIO 口输出电流一般只有几十毫安,如果我们要驱动一个需要几百毫安电流的负载(比如继电器、电机等),直接用 GPIO 口是不行的,这时候就需要用三极管来做电流放大。

举个具体的例子,假设我们要用 STM32 控制一个 12V 的继电器,这个继电器的线圈电流是 100mA。

我们可以这样设计电路:用 STM32 的 GPIO 口控制三极管的基极,三极管的集电极接继电器线圈,发射极接地。

当 GPIO 口输出高电平时,三极管导通,继电器得电工作;当 GPIO 口输出低电平时,三极管截止,继电器断电。

下面是一个简单的 HAL 库代码示例:

// 初始化GPIO
void Relay_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIOA时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 配置PA5为输出模式
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 初始状态设为低电平,继电器断电
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
​
// 控制继电器开
void Relay_On(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
​
// 控制继电器关
void Relay_Off(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}

在这个应用中,我们需要注意几个关键点:首先是基极电阻的选择。

基极电阻太小会导致基极电流过大,可能损坏 GPIO 口;基极电阻太大则可能导致三极管无法完全导通。

一般来说,我们可以这样计算:假设 GPIO 口输出电压是 3.3V,三极管的 BE 结压降约 0.7V,我们希望基极电流是 1mA,那么基极电阻应该是:

$$
R_B = \frac{3.3V - 0.7V}{1mA} = 2.6k\Omega
$$

实际应用中可以选择标准阻值 2.7kΩ 或 3kΩ。

2.2 三极管的限流保护

在使用三极管驱动感性负载(如继电器、电机)时,还需要注意一个问题:当三极管突然截止时,感性负载会产生反向电动势,这个电压可能会很高,足以击穿三极管。

所以我们通常会在负载两端并联一个续流二极管,用来释放这个反向电动势。

电路设计时,续流二极管的负极接电源正极,正极接三极管的集电极。

当三极管截止时,感性负载产生的反向电流就会通过这个二极管形成回路,从而保护三极管。

2.3 三极管在模拟电路中的应用

除了做开关,三极管在模拟电路中还可以用来做信号放大。比如在音频电路中,我们可以用三极管来放大麦克风采集到的微弱音频信号。

不过在嵌入式系统中,我们更多的是使用集成运放芯片来做信号放大,因为运放的性能更稳定,使用也更方便。

但了解三极管的放大原理还是很有必要的,因为很多集成电路的内部其实就是由大量的三极管组成的。

比如我们常用的 LM358 运放,内部就包含了几十个三极管。

3. 三极管选型和使用注意事项

3.1 如何选择合适的三极管

在实际项目中选择三极管时,我们需要关注以下几个参数:

最大集电极电流ICM:这个参数表示三极管能够承受的最大电流。选择时要留有余量,一般选择实际工作电流的 2-3 倍。比如你的负载电流是 100mA,那就选择ICM至少 300mA 的三极管。

最大集电极-发射极电压VCEO:这个参数表示三极管能够承受的最大电压。

同样要留有余量,如果你的电路工作电压是 12V,建议选择VCEO 至少 20V 以上的三极管。

电流放大倍数β:这个参数越大,说明三极管的放大能力越强,需要的基极电流就越小。一般选择β值在 100 以上的三极管就够用了。

功耗:三极管在工作时会发热,特别是在驱动大电流负载时。要根据实际功耗选择合适封装的三极管,必要时还要加散热片。功耗可以用公式P=VCE​×IC​ 来估算,其中VCE是集电极-发射极之间的电压降。

常用的小功率三极管有 S8050(NPN 型)、S8550(PNP 型)、2N3904(NPN 型)、2N3906(PNP 型)等。中功率三极管有 TIP41(NPN 型)、TIP42(PNP 型)等。这些型号在市场上都很容易买到,价格也便宜。

3.2 使用三极管的常见错误

在实际使用中,新手经常会犯一些错误,这里总结几个常见的:

忘记加基极电阻:有些朋友直接把 GPIO 口连到三极管基极,这样会导致基极电流过大,可能烧坏 GPIO 口或三极管。一定要记得加基极电阻。

三极管极性接反:NPN 型和 PNP 型三极管的接法是不一样的,如果接反了,电路就不会工作。使用前一定要查清楚三极管的管脚定义。

不加续流二极管:驱动感性负载时如果不加续流二极管,三极管很容易被反向电动势击穿。这是一个很容易被忽视但又很重要的保护措施。

工作状态选择不当:如果是做开关使用,一定要让三极管工作在饱和状态或截止状态,不要工作在放大区,否则三极管会发热严重,甚至烧毁。

4. 总结

三极管虽然是一个很基础的元器件,但它的作用却非常重要。

在嵌入式开发中,我们经常需要用三极管来扩展单片机的驱动能力,实现对各种负载的控制。

掌握三极管的基本原理和使用方法,是每一个嵌入式工程师的必备技能。

从我自己的经验来看,刚开始接触三极管的时候,确实会觉得有点抽象,特别是那些什么 PN 结、载流子之类的概念。

但其实在实际应用中,我们不需要深究那么多理论,只要记住几个关键点就行:三极管可以用小电流控制大电流,做开关用时要工作在饱和或截止状态,驱动感性负载要加续流二极管。

把这些基本原则掌握了,在实际项目中就能游刃有余了。

希望这篇文章能帮助大家更好地理解和使用三极管。

如果你在实际使用中遇到什么问题,欢迎留言交流。

电子技术这东西,理论固然重要,但更重要的是多动手实践,在实践中积累经验。加油!

在多账号运营、数据采集、跨境业务和隐私保护等场景中,代理IP的使用越来越普遍。很多人用过代理IP,却不清楚代理IP是否可以自己搭建、又该如何搭建。下面小编就为大家详细讲解下。
代理IP怎么搭建?从原理到实操完整说明

一、什么是代理IP?

代理IP本质上是一个“中住哪服务器”。当你的设备通过代理访问互联网时,目标网站看到的并不是你的真实IP,而是代理服务器的IP。

简单来说,代理IP的作用主要体现在:

隐藏真实IP,提升隐私安全性

降低账号或请求之间的关联风险

切换访问出口,模拟不同地区或网络环境

二、代理IP的常见搭建方式

从实用角度看,代理IP的搭建方式大致分为三种:

1.本地代理+远程转发(不推荐新手)

通过多层转发或端口映射实现代理访问,稳定性和安全性都比较依赖网络环境,一般不适合长期使用。

2.基于VPS自建代理

这是目前个人或小团队使用最多的方式。基本思路是:

购买一台海外或国内的VPS服务器

在服务器上部署代理服务程序

本地设备通过服务器进行网络访问

这种方式的优点就是:可控性强、IP独享。缺点:需要一定的服务器和运维基础。

3.利用云服务或云厂商网络

部分云厂商允许用户配置网络转发或自定义网关,也可以实现代理功能。

三、基于VPS搭建代理IP的基本流程

第一步:准备服务器资源

通常需要具备以下条件:

一台VPS(Linux 系统使用最多,如 CentOS、Ubuntu)

独立公网IP

SSH登录权限

服务器位置可以根据使用需求选择,比如访问海外平台可优先选择对应国家节点。

第二步:选择代理协议

不同协议适合不同使用场景,常见的有:

HTTP / HTTPS 代理:配置简单,适合网页访问

SOCKS5 代理:兼容性强,适合软件、浏览器和脚本

第三步:部署代理服务

在服务器上安装代理程序后,需要完成以下配置:

设置监听端口

配置用户名和密码

限制访问来源,防止被滥用

第四步:客户端连接与测试

在本地设备中填入:

服务器IP

代理端口

账号信息(如有)

然后访问IP查询网站,确认出口IP是否已成功切换。

我现在使用 antigravity ,基本上是每隔 24h ,就要出现一次 agent loading...

然后解决方法也很简单:就是电脑重启。

重启完之后就可以进了。。。

用过这里提到的一些方案: https://www.reddit.com/r/google_antigravity/comments/1p0oxvj/antigravity_support_please_one_moment_the_agent/

基本上没啥用,还就得是暴力重启电脑。。。估计就是后台某个进程或者线程搞的鬼

最开始,因为舍不得花钱开通国外的 AI 编程相关的订阅,一直使用的是国内的 Trae ,所以对 AI 有了低估,
国内的 Trae 自带的模型实在是不行,不知道是国内模型能力不够,还是因为 Trae 给模型降智(毕竟是免费的),
也就是自动补全足够强,但是 SOLO 模式下还是比较智障。
最近国外版 Trae 搞了一个周年赠送 600 额度的活动,薅了下羊毛,正好也测试一下国外 AI 和国内 AI 的能力,
media-crawler-gotodo 两个库
完全是使用国外版 Trae 的 SOLO 模式下的 Gemini-3-Pro-Preview 模型构建的,真的是重建了我对 AI 能力的认知。
很强,相当强,初级程序员完全没有必要存在了,中级程序员都需要考虑下是不是真的有必要。AI 完全构建了 开发实现→测试验证→上线运行→反馈优化 这一闭环,
某种程度上 AI 做的会更好。
在这种情况下,高校现在的计算机专业的培养方式是不是已经过时了?
核心基础课成体系构建高校还是有优势,目前 AI 的代码不做 Review 直接上生产环境还是存在问题,但是代码的 Review 又需要经验,这份经验又从哪获取?只能是在上学期间?

image.png

1、初次调研

主要目的是让ERP软件提供商的实施顾问人员能够对企业各个部门的业务流程初步了解,能收集到各个部门业务流的所有单据,和各个部门人员认识,了解他们对ERP的认识和期望,以便制订工作计划。

2、系统培训

主要目的是让企业所有人员认识到什么是ERP,并在企业中应用ERP系统能给企业带来如何的效益,另外就是ERP软件各个系统的功能培训。

3、流程拟定

主要目的是实施顾问人员根据自己对该企业的了解结合自己或所在公司对企业所在行业的累积经验,结合ERP系统拟定出一个符合企业需求的业务流程,能在系统中得到合理的体现;

这是一个非常重要的阶段,一个企业的管理能否从此通过ERP得到提升,流程能否更完善,就需要这个流程拟定。

4、编码原则

主要目的是企业能在实施顾问人员的指导下,制定企业应用ERP的基本原则,其中包括物料的编码原则、供应商、客户的编码原则、产品结构(包括BOM架阶)的分阶建立等。

5、资料收集

主要目的是企业的人员在熟悉了各项编码原则的基础上,收集企业应用ERP管理所需要的基本资料,包括物料资料、供应商、客户、部门、人员等收集。

6、流程测试

主要目的是企业的人员测试流程拟定的合理性,并使用企业实际的业务流程来测试ERP系统的功能完善性,和操作的方便性。

7、期初导入

主要目的是搜集ERP系统上线的期初数据,并在实施顾问人员的指导下录入ERP系统,为企业正式应用ERP系统奠定夯实的基础。

8、上线辅导

主要目的是将企业的实际业务数据在ERP系统中处理,一般在系统上线的第一、二个月的时间里面,有必要的双轨模式进行,以防企业人员在上线期初操作不熟练所造成错误。

9、月结辅导

主要目的是在应用系统一个自然月后,通过ERP系统来跑出企业管理所需要的各种报表、检验报表的完善性,数据的准确性。

当然,一个企业中要成功实施一个ERP系统,单纯靠以上九个步骤是远远不够的,ERP的实施是一个非常规范的过程,所以,我们在这里将这个过程分作为两大块。

一、以实施文档全面贯穿实施过程

作为实施顾问人员,在实施的过程中,应将各种标准的实施文档提交给企业,以确保ERP实施项目的质量进行,也就是说,顾问与企业之间的工作与文档的制作息息相关,可见文档在实施进程中的重要性非同一般。

那么,文档到底对整个实施工作有怎样的作用呢?

首先,我们大致将ERP实施中的文档作为一个分类:

分阶段实施计划文档

分阶段目标设置文档

标准业务流程文档

标准编码、标准数据文档

标准参数设置文档

功能操作指南文档

这些文档将会伴随着ERP实施的各个阶段逐渐充实、完善。

也同时记载了整个实施的过程和成果。那好,现在我们来分析一下这些文档的价值所在:

书面化的文档有助于实施人员与企业人员明确了解各自的职责,信息互通,共同把握实施过程的节奏。

标准业务流程文档有助于双方明晰业务流程,有效配合业务流程的重组和优化。

标准编码、数据文档及标准参数设置文档是实施中不可缺少的基础资料,可有效减少重复工作,避免对正常工作的影响。

功能操作指南文档可帮助最终用户规范化操作,加强培训效果。

前面我们曾经提到,ERP的实施工作可能长达数年不定,在这个时间跨度中,企业在最初实施ERP时确定的ERP项目的人员,也许难免要发生一些变化,那么,在发生变化时,ERP实施文档就可以承担起指导双方快速工作的标准文档的作用。

还有,当实施完成后,企业的运行过程将是更漫长的过程,那么实施的标准文档就将成为企业实施信息化的公共载体,成为指导企业后续工作的航标,和企业在后续人员培训方面提供详尽的素材。

二、培训全面贯穿实施过程

在ERP实施的过程中,培训始终是作为一条主线的,具体来说,在系统实施过程中,培训对象包括以下四类:

企业领导层、核心小组(项目负责人)、技术小组、最终用户。

企业领导层培训:对高层的培训主要是ERP管理理念的培训,通常会由软件提供商安排较资深顾问师对企业领导层进行ERP管理思想的培训,使得企业领导层能够从总体上理解ERP系统的理念、流程和功能。

核心小组(包括项目负责人、部门经理)培训:对于这一类的培训内容包括ERP系统的管理思想概念、ERP系统的具体功能以及ERP系统各种报表的应用。

技术小组培训:技术小组的成员主要包括参与ERP系统及相关数据库和网络安装、设置及管理的信息部门成员。培训的主要目标是提供ERP系统的设计结构,各个模块的关联关系与数据库结构,系统问题处理等。

最终用户培训:培训目的是使用户了解ERP系统后新的业务前景、目标以及带来的好处,使用户能清楚地了解到ERP是什么,怎样通过它提高个人及整体的业务表现,使用户发觉其工作内容的变化及ERP将如何融入其日常工作。同时向用户提供从现状到未来迁移过程中通用的术语,指导用户如何使用ERP完成其工作。

ERP实施过程中的培训作为实施的一条主线,既体现了ERP实施很高的附加值,又充分体现了ERP实施过程中的知识转移。

把ERP从半成品到成品的过程实质就是知识转移的过程,其中包含企业的管理诊断,实施战略的选择,业务流程的设定,对企业需求的恰到好处的分析。

综上所述,企业信息化是一个长期的过程,在这个过程中,成熟完善的ERP系统是信息化成功的前提,严谨科学的实施方式是保证ERP成功上线的关键。

image.png
image.png
image.png

【声明】:以上所发文章仅供大家学习参考,请不要作商业用途;ERP系统的专业性很强,文中难免有错误,一旦发现,请联系我们及时更正;最后感谢图片内容的提供商:织信ERP,该厂商专注企业信息化系统管理10年余,坚持传播生产管理知识,自研低代码开发底座,基于B/S架构,可帮助企业快速构建生产管理所需的各项功能。

前言

在一个优秀的应用设计中,界面不仅仅是平铺直叙的展示,更需要有层级感。当用户点击删除按钮时,我们需要一个确认框来防止误触;当后台数据加载完成时,我们需要一个轻量的提示告诉用户 好了 ;当用户对某个晦涩的功能图标感到困惑时,我们需要一个气泡弹窗来解释它的含义。这些浮在主界面之上的交互层,我们统称为 覆盖物(Overlays)

在早期的开发中,很多工程师习惯直接使用系统原生的 AlertDialog,那种灰底黑字的弹窗虽然功能健全,但在如今这个颜值为王的时代,它打断了用户的情绪流,也破坏了应用的整体设计语言。

在鸿蒙 HarmonyOS 6 中,ArkUI 为我们提供了极其强大的弹窗定制能力。无论是转瞬即逝的 Toast,还是完全自定义的 CustomDialog,亦或是指向性明确的 Popup 气泡,我们都可以像搭积木一样,用声明式的代码构建出既美观又灵动的交互体验。

一、 轻量级反馈与上下文气泡

在进入复杂的弹窗之前,我们先解决最基础的反馈需求。当用户复制了一段文本,或者刷新列表成功时,我们不需要让用户进行任何操作,只需要给出一个朕已阅的信号。这就是 Toast。在 API 20 中,系统将这类交互统一收敛到了 promptAction 模块下。我们不再像以前那样去寻找 Window 实例,而是直接调用 promptAction.showToast。这个 API 非常纯粹,它接受一个显示时长、一条消息文本,以及一个可选的位置参数。但在实战中,建议尽量保持 Toast 的简洁,不要试图在里面塞入过多的文字。它应该像一阵风,来过,被看到,然后消失。

如果说 Toast 是全局的广播,那么 Popup 气泡就是点对点的悄悄话。CustomDialog 是一种模态交互,它会给背景加上遮罩,强迫用户聚焦。但有时候,我们并不想打断用户的操作流,只是想对界面上的某个元素做一点补充说明。比如一个帮助的小问号图标,或者一个“新功能”的引导提示。

这时候,ArkUI 提供的 bindPopup 属性是最优雅的选择。这意味着任何组件——一个按钮、一张图片甚至一段文字,都可以绑定一个气泡。系统会自动计算目标组件在屏幕上的位置,然后决定气泡是出现在上方、下方还是侧边,并自动生成一个小箭头指向目标。我们作为开发者,几乎不需要关心坐标计算的问题,只需要关注气泡里的内容构建即可。

@Entry
@Component
struct PopupExample {
  // 控制气泡显示的开关状态
  @State showPopup: boolean = false;

  // 定义气泡内部的 UI 结构
  @Builder
  PopupContent() {
    Column() {
      Text('功能说明')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .margin({ bottom: 4 })
      
      Text('这里是详细的补充文案,系统会自动根据位置计算箭头指向。')
        .fontSize(12)
        .fontColor('#E6E6E6')
    }
    .padding(12)
    .backgroundColor('#4D4D4D') // 气泡背景通常与文字反色
    .borderRadius(8)
  }

  build() {
    Column() {
      // 任何组件都可以绑定气泡,这里以一个问号图标为例
      SymbolGlyph($r('sys.symbol.questionmark_circle'))
        .fontSize(24)
        .fontColor($r('sys.color.ohos_id_color_text_secondary'))
        // 1. 点击切换状态
        .onClick(() => {
          this.showPopup = !this.showPopup;
        })
        // 2. 绑定气泡属性
        .bindPopup(this.showPopup, {
          builder: this.PopupContent,     // 指向内容构建器
          placement: Placement.Bottom,    // 优先显示位置(系统会自动调整)
          mask: false,                    // false 表示非模态,不阻断用户操作其他区域
          enableArrow: true,              // 显示指向目标的小箭头
          popupColor: '#4D4D4D',          // 气泡背景色(需与 Builder 背景一致或透明)
          onStateChange: (e) => {
            // 3. 状态同步:当点击空白处气泡消失时,同步更新 boolean 变量
            if (!e.isVisible) {
              this.showPopup = false;
            }
          }
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

二、 定制化核心:CustomDialog 与控制器模式

当业务逻辑变得复杂,比如需要用户领取优惠券、签署隐私协议或者选择复杂的筛选条件时,系统的标准弹窗就捉襟见肘了。这时候,CustomDialog(自定义弹窗)就是我们的救星。它的设计哲学非常有趣,采用了一种 控制器(Controller) 模式。我们需要定义两个部分:一个是弹窗本身的 UI 结构,另一个是控制它打开和关闭的遥控器。

首先,我们需要定义一个被 @CustomDialog 装饰器修饰的结构体。在这个结构体里,你可以使用任何 ArkUI 组件:Column、Row、Image 甚至 List。这意味你可以把弹窗做得像普通页面一样丰富多彩。紧接着,在父组件中,我们需要实例化一个 CustomDialogController。这个控制器是连接父子组件的纽带。在实例化时,我们需要传入 builder 参数,指向我们刚才定义的弹窗组件。

@Entry
@Component
struct HomePage {
  // 1. 实例化控制器:连接父组件与弹窗组件
  // 必须在 @Component 中作为成员变量定义
  dialogController: CustomDialogController | null = new CustomDialogController({
    builder: PrivacyAgreementDialog(), // 引用外部定义的 @CustomDialog 组件
    autoCancel: false,                 // 点击遮罩是否允许关闭(强制交互场景通常设为 false)
    alignment: DialogAlignment.Center, // 弹窗在屏幕中的对齐方式
    customStyle: true,                 // 是否完全自定义样式(去除系统默认的白色背景和圆角)
    offset: { dx: 0, dy: 0 },          // 相对对齐位置的偏移量
    maskColor: '#33000000',            // 自定义遮罩层颜色
  });

  // 推荐:在组件销毁时清理控制器,防止内存泄漏
  aboutToDisappear() {
    this.dialogController = null;
  }

  build() {
    Column() {
      Button('打开隐私协议')
        .fontSize(16)
        .onClick(() => {
          // 2. 通过控制器打开弹窗
          if (this.dialogController != null) {
            this.dialogController.open();
          }
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

这里有一个初学者常犯的错误,就是试图通过 @Prop 或 @Link 来直接同步父子组件的数据。虽然 CustomDialog 支持这些装饰器,但由于弹窗并不在常规的组件渲染树中,数据的响应式更新有时会存在滞后。最佳的实践是:在打开弹窗时传入初始数据,在关闭弹窗时通过回调函数返回结果。比如做一个“领取优惠券”的弹窗,我们在构建 CustomDialog 时定义一个 confirm 回调函数。当用户点击弹窗里的“立即领取”按钮时,我们调用这个回调,把结果传回给父组件,然后关闭弹窗。这种 事件驱动 的数据流向,比复杂的双向绑定更加稳健且易于追踪。

做出来和做得好看是两码事。默认的 CustomDialog 往往带有系统默认的圆角和白色背景,有时甚至会有默认的内边距。为了实现设计师眼中那种“全屏半透明”或者“底部异形弹窗”的效果,我们一定要善用 customStyle: true 这个配置项。一旦设置为 true,系统就会移除所有默认的弹窗样式,给你一张完全空白的画布。这时候,你需要在你的 @CustomDialog 组件内部,自己定义背景色、圆角和阴影。虽然麻烦了一点,但它赋予了你像素级的控制权。

三、 综合实战:构建营销活动弹窗体系

为了将上述知识点融会贯通,我们来构建一个真实的电商营销场景。这个页面包含一个模拟的“会员中心”,右上角有一个绑定了 bindPopup 的帮助图标,点击会展示活动规则;而在页面中心,有一个“领取大礼包”的按钮,点击会唤起一个完全自定义样式的 CustomDialog 优惠券弹窗。

在这个代码中,请仔细观察 CouponDialog 的定义,它是如何通过 controller 关闭自己的,以及父组件是如何通过 CustomDialogController 配置 customStyle: true 来移除系统默认背景的。这就是构建高颜值弹窗的标准模板。

TypeScript

import { promptAction } from '@kit.ArkUI';

@CustomDialog
struct CouponDialog {
  controller?: CustomDialogController;

  couponAmount: number = 0;
  onConfirm: () => void = () => {};

  build() {
    Column() {
      // 顶部装饰
      Stack({ alignContent: Alignment.Bottom }) {
        Column()
          .width('100%')
          .height('100%')
          .backgroundColor('#FF4040')
          .borderRadius({ topLeft: 16, topRight: 16 })

        Text(`¥${this.couponAmount}`)
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
          .margin({ bottom: 20 })
      }
      .width('100%')
      .height(120)

      // 内容
      Column({ space: 12 }) {
        Text('恭喜获得新人优惠券')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333')

        Text('全场通用,无门槛立减。有效期至 2026-12-31')
          .fontSize(14)
          .fontColor('#999')
          .textAlign(TextAlign.Center)
          .padding({ left: 20, right: 20 })
      }
      .padding({ top: 20, bottom: 20 })

      // 按钮
      Row() {
        Button('残忍拒绝')
          .backgroundColor('#F5F5F5')
          .fontColor('#666')
          .layoutWeight(1)
          .margin({ right: 10 })
          .onClick(() => {
            // 【修复点 2】调用时加上 '?' (可选链),防止空指针报错
            this.controller?.close();
          })

        Button('立即领取')
          .backgroundColor('#FF4040')
          .fontColor(Color.White)
          .layoutWeight(1)
          .onClick(() => {
            this.onConfirm();
            // 【修复点 3】同理,加上 '?'
            this.controller?.close();
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 20 })
    }
    .width(300)
    .backgroundColor(Color.White)
    .borderRadius(16)
    .shadow({ radius: 10, color: '#33000000', offsetY: 5 })
  }
}


@Entry
@Component
struct DialogAndPopupPage {
  // 状态变量:控制气泡 (Popup) 的显示与隐藏
  @State isHelpPopupVisible: boolean = false;

  // 【核心】定义弹窗控制器
  // 必须在 build() 之外实例化
  // builder 参数指向上面定义的 @CustomDialog 组件
  private dialogController: CustomDialogController = new CustomDialogController({
    builder: CouponDialog({
      couponAmount: 100, // 向弹窗传递数据
      onConfirm: () => {
        // 定义弹窗确认后的逻辑
        this.handleCouponReceived();
      }
    }),
    autoCancel: true,                 // 允许点击遮罩关闭
    customStyle: true,                // 使用完全自定义样式(去除系统默认白底圆角)
    alignment: DialogAlignment.Center // 居中显示
  });

  // 模拟业务逻辑:领取成功后的 Toast 反馈
  handleCouponReceived() {
    promptAction.showToast({
      message: '领取成功!已存入卡包',
      duration: 2000,
      bottom: 100
    });
  }

  // 定义 Popup (气泡) 的内容构建器
  @Builder
  PopupBuilder() {
    Column() {
      Text('活动规则说明')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .margin({ bottom: 8 })

      Text('1. 仅限新用户领取\n2. 每日限领一张\n3. 不可与其他活动叠加')
        .fontSize(12)
        .fontColor(Color.White)
        .lineHeight(18)
    }
    .padding(12)
    .width(200)
  }

  build() {
    Column() {
      // --- 顶部导航栏 ---
      Row() {
        Text('会员中心')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)

        Blank() // 撑开中间空间

        // 帮助图标 (绑定 Popup)
        Text('?')
          .fontSize(18)
          .fontColor(Color.White)
          .backgroundColor('#CCCCCC')
          .width(24)
          .height(24)
          .textAlign(TextAlign.Center)
          .borderRadius(12)
          // 【核心】绑定气泡
          .bindPopup(this.isHelpPopupVisible, {
            builder: this.PopupBuilder(), // 指向 Builder
            placement: Placement.BottomRight, // 气泡位置
            popupColor: '#4C4C4C',            // 气泡深色背景
            enableArrow: true,                // 显示箭头
            mask: false,                      // 非模态,不遮挡背景
            onStateChange: (e) => {
              // 状态同步:处理点击外部自动消失的情况
              if (!e.isVisible) {
                this.isHelpPopupVisible = false;
              }
            }
          })
          .onClick(() => {
            // 点击切换显示状态
            this.isHelpPopupVisible = !this.isHelpPopupVisible;
          })
      }
      .width('100%')
      .padding(20)

      // --- 页面主体内容 ---
      Column({ space: 30 }) {
        // 模拟大图占位
        Column()
          .width(200)
          .height(200)
          .backgroundColor('#E0E0E0')
          .borderRadius(100)
          .margin({ top: 50 })

        Text('超级会员大礼包')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)

        Text('包含 100 元无门槛优惠券')
          .fontSize(16)
          .fontColor('#666')

        // 【核心】触发弹窗的按钮
        Button('立即领取')
          .width('80%')
          .height(50)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .backgroundColor('#FF4040')
          .shadow({ radius: 10, color: '#4DFF4040', offsetY: 5 })
          .onClick(() => {
            // 打开自定义弹窗
            if (this.dialogController) {
              this.dialogController.open();
            }
          })
      }
      .width('100%')
      .layoutWeight(1) // 占据剩余高度
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F8F8')
  }
}

总结

弹窗和覆盖物是应用与用户沟通的第二语言。Toast 是轻声的耳语,CustomDialog 是正式的对话,而 Popup 则是贴心的便签。

在鸿蒙 HarmonyOS 6 开发中,掌握 @CustomDialogbindPopup 是构建高级 UI 的必修课。我们抛弃了系统的默认样式,通过 customStyle 获得了对画布的完全掌控权,让弹窗不再只是功能的载体,更是视觉设计的延伸。切记,不要滥用弹窗,每一次遮罩的出现都是对用户注意力的强行掠夺。

好的交互应该是克制的,只在真正需要的时候才优雅地浮现。

CRM系统是干什么用的?从零开始全面了解客户管理工具

最近发现,越来越多的企业决策者和管理层将关注点聚焦于几个关键命题:
“企业必须推动精细化运营,向管理要效益”
“客户资源是企业的核心战略资产,需进行系统性经营”
“亟需部署CRM系统,实现客户关系的数字化、智能化管理” 
这些听起来方向明确、势在必行,但真到了要建系统、要执行落地的时候,不少企业却陷入困惑:
CRM客户关系管理系统究竟是什么?与Excel表格、零散的客户记录工具有什么本质区别?怎么才能选择一套真正契合自身业务、投入产出比高的CRM解决方案? 
这篇文章,就从一个根本问题切入,——“CRM真正的价值到底是什么?”并结合国内领先的CRM厂商纷享销客的实践案例,剖析CRM背后的底层逻辑与实战价值。
让你不仅知道“要不要上 CRM”,更清楚“该怎么用好 CRM”。

一、CRM不是工具,而是一套以客户为中心的管理体系

1.1 CRM的定义与本质

CRM(Customer Relationship Management),即客户关系管理系统,核心目标并非简单地“记录客户信息”,
而是通过技术手段,构建一套覆盖从获客 → 成交 → 复购/续费的可追踪、可优化、可复制的管理机制。
根据Gartner在《2024年CRM市场指南》中的定义:“现代CRM系统已超越传统销售自动化范畴,演变为集营销获客、销售转化、客户服务与数据分析于一体的智能客户运营平台。”
这意味着,CRM的本质是一种企业级客户资产沉淀机制,而非仅限于前端销售人员使用的辅助工具。

1.2 与Excel、微信标签等“伪CRM”的本质差异

许多企业常误以为“有客户名单就是有CRM”。例如,使用Excel表格管理客户、依赖企业微信打标签、或让销售员在手机备忘录中记录跟进情况。
这些方式虽能短期满足基础需求,但在规模化、流程化、数据驱动层面存在致命短板:
• 信息孤岛严重:客户数据分散在不同员工终端,离职即流失;
• 过程不可见:管理者无法掌握销售推进的真实节奏与卡点;
• 决策无依据:缺乏结构化数据支撑,难以评估渠道效果、销售效能或客户价值。

二、CRM的核心模块:覆盖客户全生命周期(以纷享销客为例)

一个成熟的CRM系统通常包含五大核心功能模块,共同构成客户旅程的完整闭环:

2.1  营销与活动管理

说白了就是帮你把各个渠道来的客户线索“收好、分快、跟准”。
比如你在抖音、百度或者微信投广告,用户一留信息,系统自动抓进来,不漏掉;还能给线索打分,谁更可能成交就优先推给销售。办个线上直播或展会?报名、签到、后续跟进全在线搞定。
最实在的是,花多少钱、带来多少客户、最后成没成交,一笔账清清楚楚,不像以前“钱花了,效果靠猜”。
这样你就能知道哪条渠道真管用,下次把预算花在刀刃上。

2.2  线索管理

帮助企业把从各个地方来的潜在客户(比如官网留言、广告点击、展会名片)统一收进来,不乱不丢。
纷享销客会自动判断谁更可能买——比如有人反复看产品页,就打个高分,优先推给销售;没人跟进的线索还会自动“回收”,转给别人跟。
整个过程像流水线一样:先识别,再打标签,接着分人跟,最后看效果。市场和销售不再扯皮,线索也不再“休冬眠”,转化自然就上去了。

2.3  客户与联系人管理

其实就是帮你把“谁是客户、谁在对接”这件事理得明明白白,不用担心销售离职客户丢失的问题。
比如你公司卖设备给一家工厂,这家工厂就是“客户”,而采购经理老王、技术主管小李就是“联系人”。
系统会把这些信息全记下来——不光是电话微信,还有每次聊了啥、什么时候拜访过、买过什么产品,全都自动归到一起。
哪怕老王跳槽了,新来的销售也能一眼看懂:“哦,原来上次谈的是这个需求,现在该找小李了。”
而且客户还能分级,像A类重点客户,系统会提醒你定期跟进;
要是好久没动静,可能自动放回公海,让别人试试。
这样客户资源就真正变成了公司的资产,不是某个人的私有物。
说白了,就是让客户信息“看得清、跟得上、留得住”。

2.4 商机与销售漏斗管理

商机管理是CRM最核心的价值体现。纷享销客支持企业自定义销售阶段(“初步接洽→需求确认→方案演示→报价谈判→合同签署”),每个阶段设置关键动作与成功标准,形成可视化销售漏斗。
通过漏斗分析,管理者可清晰看到:
• 当前有多少商机处于各阶段?
• 哪个环节流失率最高?
• 下季度预计成交金额是否达标?
更实用的是,系统能自动防撞单、智能预测业绩,并将最佳销售实践固化进流程——新员工照着走就不会跑偏,老员工也能避免凭感觉跟进。最终实现从“靠人盯”到“靠流程驱动”,提升赢单率和预测准确性。

2.5 客户服务管理

纷享销客帮助企业把“售后”这件事做得又快又稳,让客户觉得你靠谱。
比如客户家的设备出问题了,他不用打电话干等,直接扫个码、在微信小程序里点一下,就能提个服务请求。
系统马上收到,自动分给离得最近、有空的工程师——就像打车软件派单一样。
工程师上门前,手机上能看到这台设备以前修过啥、配件用过哪些;
修完还能当场扫码让客户打分,满意不满意一目了然。
要是客户是VIP,系统还会优先安排专属客服,服务更快更贴心。
所有这些流程——从客户报修、派工、上门、用配件、收钱到评价——全在线上走,不靠Excel也不靠嘴记。
老板在后台还能看数据:哪个产品老坏?哪个工程师效率高?客户满意度掉没掉?一清二楚。
总之,就是让服务不乱、不拖、不丢事,客户省心,公司也省力。

2.6  报表与BI分析

BI分析就像给公司装了个“数据仪表盘”,销售做了多少单、客户从哪来、服务满不满意,一眼就能看清。一套好的CRM必须能将过程数据转化为决策洞察。典型报表包括:
• 销售业绩达成率
• 线索来源渠道ROI
• 商机阶段转化率
• 客户生命周期价值(LTV)
更实用的是,它能自动发现谁该复购了——比如客户买的软件快到期了,或者老在用某个功能,系统就会提醒销售!不用靠人脑记,也不用等客户主动找上门。整个过程简单直接:看数据、抓机会、促成交,让老客户不断带来新生意。
说到底,纷享销客CRM做的不是简单的“记客户电话”,而是帮企业把客户当成资产来经营。通过营销自动化 → 智能线索管理 → 标准化销售流程 → 全景客户视图 → 数据驱动服务与复购的完整链路,纷享销客真正实现了:
• 前端:精准获客、高效转化
• 中台:过程可视、协同高效
• 后端:体验保障、价值深耕

三、为什么企业需要CRM?三大角色视角下的真实价值

3.1 对老板:守住客户资产,降低经营风险

对企业主而言,最大的隐性成本不是软件采购费,而是“人走客户飞”。据麦肯锡调研,超过60%的企业客户资源高度依赖个别销售个人关系,一旦核心人员离职,客户流失率高达40%以上。
CRM系统通过强制数据录入与权限管控,确保所有客户互动记录沉淀在系统中。即使销售离职,客户仍属于公司资产,可无缝交接。此外,CRM提供的销售预测与现金流预判功能,让老板告别“拍脑袋定目标”,实现科学经营。

3.2 对销售:减负增效,专注高价值沟通

一线销售最怕“填表式CRM”。真正优秀的系统应成为销售的“智能助手”,而非负担。纷享销客通过以下设计提升销售体验:
• 移动端一键记录:通话后自动生成跟进日志;
• 智能提醒:自动提示“3天未联系的A类客户”;
• 话术库与模板:快速调用成功案例与标准应答;
• 任务自动化:商机推进到下一阶段时,自动创建待办事项。

3.3 对管理者:从“结果管控”转向“过程赋能”

传统管理依赖周报、月报和口头汇报,信息滞后且失真。CRM则实现全流程透明化:
• 可查看每位销售的日程安排、客户拜访轨迹、沟通频次;
• 可对比团队成员在相同阶段的转化效率;
• 可识别高绩效销售的行为模式,并复制推广。
纷享销客的团队协作空间支持跨部门协同(如销售+售前+交付),确保大客户项目高效推进,避免内部扯皮。

四、国产CRM崛起:纷享销客如何满足中国企业的独特需求?

在全球CRM市场,Salesforce长期占据主导地位。但在中国市场,企业对客户资产的精细化运营需求日益迫切——不仅要管好客户信息,更要实现从获客、转化到复购的全生命周期价值挖掘。而国内企业的需求,例如系统集成、审批流程、发票管理,使得国产CRM更具适配性。
纷享销客作为国内领先的智能型CRM厂商,深耕中国市场十余年,产品不仅贴合本土业务逻辑,更积极融合AI能力,打造“智能化+场景化”的新一代客户运营平台。其核心优势体现在以下五个方面:
• 深度集成微信生态:支持企业微信客户同步、聊天侧边栏、朋友圈素材库,实现私域流量无缝管理;
• 灵活审批流:可配置合同审批、折扣申请、回款确认等复杂流程,贴合国内企业内控要求;
• 业财一体化打通:与用友、金蝶等财务系统对接,打通“签约—开票—回款”链条;
• 垂直行业解决方案:针对制造业、医疗、快消、IT服务等垂直领域提供预置模板。
• AI驱动的智能销售助手:系统可以自动分析客户行为、预测成交概率,并在销售跟进中实时推荐话术、成功案例和下一步行动;同时支持自动生成会议纪要、识别商机风险、预警客户流失,帮助销售减少重复工作,专注高价值沟通。更重要的是,纷享销客采用PaaS平台架构,支持企业按需扩展模块(如CPQ报价、服务工单、BI分析),避免“一次性买断但用不起来”的陷阱。这种“用多少、配多少、智能多少”的弹性模式,尤其适合大中型企业。

五、如何选型CRM?七大关键评估维度

当您认识到CRM的价值并准备开始选择时,面对市场上数百款琳琅满目的产品可能会感到无从下手。面对市面上数十款CRM产品,如何为您的企业选择一款合适的CRM系统?建议从以下七个维度综合评估:

结语:CRM不是成本,而是增长基础设施

在数字经济时代,客户关系已成为企业最稀缺的战略资源。CRM系统不再是“可选项”,而是如同ERP、财务系统一样的企业数字基建。
它帮助企业回答三个根本问题:
• 我们到底有多少真实有效的客户?
• 销售团队每天在做什么?效率如何?
• 下个月、下个季度的业绩从哪里来?
总而言之,CRM系统远不止是一个记录客户信息的数据库软件。它是一种将“以客户为中心”的理念融入企业血脉的战略工具,是连接市场、销售和服务,驱动业务流程优化和决策智能化的核心引擎。希望本文能帮助您对CRM有一个清晰的认知。 

常见问题 (FAQ)

1、CRM系统和ERP系统有什么区别?
简单来说,CRM(客户关系管理)主要面向外部,关注的是与客户相关的活动,如市场、销售、服务,目标是增加收入和提升客户满意度。而ERP(企业资源计划)主要面向内部,管理的是企业的核心业务资源,如财务、库存、采购、生产等,目标是优化内部流程和降低成本。两者可以集成,共同构成企业数字化管理的核心。
2、CRM系统一定要全员使用吗?可以只给销售用吗?
A:不建议。CRM的价值在于打通“营销—销售—服务”全链路。若仅销售使用,将导致线索来源不清、售后脱节、数据断层。理想状态是市场、销售、客服、管理层均在系统中有角色和数据贡献。
3、纷享销客和国外CRM(如Salesforce)相比有什么优势?
A:纷享销客在微信生态集成、本地化审批流程、中文界面体验、实施成本及响应速度上更具优势。Salesforce虽功能强大,但对中国企业常见的“人情化流程”“多级审批”“业财一体”支持较弱,且本地化服务成本高。
4、CRM系统主要适用于哪些类型的企业?是不是只有大企业才需要?
答: CRM系统适用于所有有客户并希望与客户建立长期关系的企业,并非大企业专属。不同规模企业需求侧重不同:中小企业可使用CRM标准化销售过程、防止客户流失、提升人效;中大型企业则更侧重于跨部门协同、数据整合分析与生态连接。当前,许多云原生CRM(如纷享销客CRM)提供灵活订阅模式与标准化功能模块,大幅降低了中小企业的使用门槛与启动成本。
5、实施CRM系统最大的挑战是什么?如何规避失败风险?
答: 最大挑战往往来自组织与文化层面,而非技术本身,具体包括:员工抵触改变、使用率低下;业务流程与系统不匹配;数据质量差导致洞察失效。规避风险需:首先,确保高层推动与明确业务目标;其次,选择用户体验好、移动化程度高的系统,降低使用阻力;再次,结合业务痛点梳理优化流程,并进行分阶段上线与持续培训;最后,建立数据治理规范。选择像纷享销客这类重视用户体验与成功服务的厂商,也能获得重要的实施支持。

编者按: 英伟达财报的营收神话是否掩盖了其现金流恶化的现实?而在“循环融资”的质疑声中,OpenAI 与甲骨文等关键客户的供应链“去英伟达化”浪潮,又将如何重塑 AI 硬件的竞争格局?

我们今天为大家带来的这篇文章,作者的观点是:英伟达目前的高速增长依赖于激进的库存策略和宽松的信用条款,但其最大客户正通过定制芯片和直接采购关键组件来构建独立的供应链,这导致双方关系正从深度捆绑走向潜在的激烈竞争。

作者 | Philippe Oger

编译 | 岳扬

过去 48 小时,我完全沉浸在对英伟达 2026 财年第三季度财报[1]的深度研究中。如果你只看新闻标题,一切看起来都完美无缺:营收同比增长 62 %,达到 570 亿美元,黄仁勋还在大谈“AI 的良性循环”。

但我想弄清楚光鲜数据下的真实情况,于是深挖了资产负债表,并将其与围绕 OpenAI 和 Oracle 的所有新闻进行了交叉验证。 我并不是华尔街的专业分析师,但即便仅凭自己梳理线索(并借助了 Gemini 的帮助),我也开始看到这个所谓的“AI 联盟”出现了一些裂痕。就在英伟达创下业绩纪录的同时,他们最大的客户似乎正在悄悄武装自己,准备另起炉灶。

以下是我对硬件市场、OpenAI 与英伟达之间“亦敌亦友”的关系,以及包括迈克尔·贝瑞(Michael Burry)在内大家都在讨论的“循环融资(circular financing)”理论的一些看法。

01 英伟达财报:完美表象下的隐忧

表面看来,英伟达无疑是 AI 时代的绝对王者 —— 数据中心业务已占据公司总营收近九成,这一事实无可辩驳。然而,当我深入研读财报细节时,发现了三处值得警惕的“红色信号”

  • 现金流之谜:英伟达公布的净利润高达 319 亿美元,但我查阅现金流量表时发现,其经营活动产生的现金流仅为 238 亿美元。这意味着有 80 亿美元的利润尚未立即转化为现金。
  • 库存激增:我注意到,今年库存几乎翻倍,达到 198 亿美元。管理层解释称这是为“Blackwell”发布做准备,但在我看来,持有大约 120 天的库存量,会带来巨大的资金占用压力。
  • 应收账款周期拉长:我计算了其应收账款周转天数(DSO),发现已悄然攀升至约 53 天。在营收飙升的同时,英伟达却要等待近两个月才能回款,这暗示他们可能正在向企业客户提供极为宽松的信用条款,以维持增长飞轮的运转。

我的个人判断?英伟达正通过透支现金流来囤积库存,将全部赌注押在 Blackwell 架构[2]能在第四季度被市场瞬间消化。

02 拆解“资金空转”传闻的虚实

我想说清楚一点:接下来这部分内容并不是我最先发现的。最近财经新闻到处都在讨论这件事,而且如果你关注迈克尔·巴里(就是那位电影《大空头》里的“大空头”原型人物),你很可能已经看到他发推文警告所谓的“循环融资”和可疑的收入确认(Revenue Recognition)[3]行为。

我尝试自行理清这其中的关系,看看大家究竟在争论什么。巴里最近分享了一张图表,把这一系列交易描绘成一张交易“关系网”,其结构大致如下:

  • 环节一:英伟达承诺向 OpenAI 投资数十亿美元(这属于已被广泛报道的“千亿美元投资路线图”中的一部分)
  • 环节二:OpenAI 与甲骨文(Oracle)签署了一份高达 3000 亿美元的巨额云服务合同(即“星门计划”,Project Stargate),用于托管其人工智能模型。
  • 环节三:为履行该合约,甲骨文随即向英伟达下达价值 400 亿美元的 GB200 GPU 采购订单。

巴里的核心论点(也是据传美国司法部等监管机构介入调查的原因[4])在于:这套模式形同“资金空转”。这引发了一个尖锐的问题:如果英伟达停止向 OpenAI 投资,OpenAI 还有足够现金去和甲骨文(Oracle)签下那笔大单吗?而甲骨文又是否还会采购那些芯片? 如果答案是“不会”,那么部分营收数据的稳固性可能远不如表面看来那样坚实。

03 OpenAI 正在采取行动降低对英伟达的依赖

我近期一直在关注的另一个重大转变,是 OpenAI 的战略转向。他们曾是英伟达最耀眼的“模范客户”,如今却越来越像一个潜在的竞争对手。一方面,他们仍与 NVIDIA 保持紧密合作 —— 部署 10 吉瓦(gigawatts)的基础设施用于训练 GPT-6;但另一方面,他们似乎正在构建一条能彻底摆脱黄仁勋(Jensen Huang)掌控的供应链。

如果你有所留意,相关迹象其实已经相当明显。 “星门计划”(Project Stargate) 不仅仅是个数据中心,更是一项包含定制硬件在内的庞大基础设施计划。据多家媒体报道(例如此处[5]、此处[6]、此处[7],并在 Hacker News 上引发了激烈的讨论[8]),OpenAI 已直接从三星和 SK 海力士(全球两大 HBM 内存供应商)采购 DRAM 晶圆,绕开了英伟达的供应链。

此外,人才流向也透露出关键信号:OpenAI 已从数个行业巨头处挖走多名芯片人才,包括 2023 年招揽了谷歌前 TPU 负责人 Richard Ho,以及近期从苹果挖走的约 40 名硬件工程师。

结合 OpenAI 与博通(Broadcom)的合作[9],我推测其策略是:用英伟达 GPU 构建智能模型,但最终在自家的定制芯片上运行推理任务 —— 以此大幅削减高昂的运营成本,或押注类似谷歌 Edge TPU 的专用芯片(NPU)来处理推理负载。

但关键问题来了:OpenAI 打算用谁的钱来支持这项事业?而英伟达对其未来规划又究竟有多大影响力?

而且,所谓“英伟达向 OpenAI 投资 1000 亿美元”的说法,至今尚未得到官方证实(如此处[10]所述)。

04 甲骨文一个有趣的思路:收购 Groq

眼下所有人都在讨论推理成本问题(Inference costs) —— 也就是实际运行 ChatGPT 或其他大语言模型(LLM)的花销,远比训练它们更昂贵。我最近在关注 Groq 这家初创公司,他们明确宣称在推理任务上比英伟达更快、更便宜。其创始人乔纳森·罗斯(Jonathan Ross)[11]曾是谷歌 TPU 团队的负责人,甚至可以说是 TPU 概念的最初提出者。

但还有一层情况,我认为被大多数人忽视了:OpenAI 直接采购晶圆所引发的 HBM 短缺问题。

据我所知,目前英伟达最大的瓶颈之一就是 HBM(高带宽内存)。 HBM 由专业内存代工厂生产,而这些产线早已完全超负荷运转。然而,Groq 的架构依赖的是 SRAM(静态随机存储器)。 由于 SRAM 通常是在逻辑制程代工厂(比如台积电 TSMC)中与处理器本身一同制造的,理论上它不会遭遇与 HBM 相同的供应链紧张问题。

综合这些因素,我觉得甲骨文真该认真考虑一下收购 Groq。拿下 Groq 不仅意味着获得更快的芯片,更关键的是 —— 当其他芯片全都售罄时,Groq 的芯片可能仍然有货。这本质上是一种供应链对冲(supply chain hedge)。

对甲骨文的最大客户 OpenAI 而言,这也将带来巨大的优势:更快、更便宜的推理能力。

再结合此前的传闻:甲骨文出租英伟达芯片的利润率极其微薄[12],据传低至 14%,那这笔收购就显得更加合理。通过控股 Groq,甲骨文不仅能摆脱“英伟达税”(NVIDIA Tax),改善自身利润空间,还能彻底绕过 HBM 短缺的困局。

据 Groq 在 2025 年 9 月的最近一轮融资披露[13],其估值约为 69 亿美元。即便支付溢价,以甲骨文的财力也完全有能力完成这笔收购。

但问题是:英伟达会允许这事发生吗?

如果答案是否定的,那又说明了什么?是否意味着当前这套“循环融资(circular financing)”体系中存在某种利益交换 —— 比如,英伟达承诺向 OpenAI 投资 1000 亿美元,条件是甲骨文必须只能使用英伟达芯片?

05 Final Thoughts

进入 2026 年,观察英伟达、OpenAI 与甲骨文之间的博弈,这场三方角力正陷入彼此钳制的僵局。我无从得知英伟达是否事先知晓 OpenAI 与内存厂商之间的晶圆供应协议,亦或其中存在任何合谋?英伟达是否正在极力维持自己在“星门计划”(Stargate)中训练和推理环节的独家地位?而 OpenAI 又到底打算打造什么样的芯片?是类似 TPU/LPU 的架构?还是更偏向 Edge TPU 那样的边缘推理芯片?

迈克尔·巴里(Michael Burry)正在全面做空这套体系[14]。

至于我,只是个读财报的普通人,无力揣测市场走向。但我非常确定一点:AI 硬件市场比以往任何时候都更炽热,未来几个季度的风云变幻必将精彩绝伦。

免责声明:我偶尔会发表些真知灼见,但更多时候说的都是蠢话。阅读本文时请务必谨记这一点。

END

本期互动内容 🍻

❓如果“循环融资”属实,谁最可能成为这个链条中最先断裂的一环?

文中链接

[1]https://nvidianews.nvidia.com/

[2]https://www.nvidia.com/en-us/data-center/technologies/blackwe...

[3]https://www.investing.com/news/stock-market-news/michael-burr...

[4]https://m.economictimes.com/news/international/us/nvidia-reje...

[5]https://openai.com/index/samsung-and-sk-join-stargate/

[6]https://www.asiafinancial.com/samsung-sk-hynix-building-starg...

[7]https://www.kedglobal.com/artificial-intelligence/newsView/ke...

[8]https://news.ycombinator.com/item?id=46169224#46170844

[9]https://openai.com/index/openai-and-broadcom-announce-strateg...

[10]https://fortune.com/2025/12/02/nvidia-openai-deal-not-signed-...

[11]https://www.linkedin.com/in/ross-jonathan/

[12]https://www.fool.com/investing/2025/12/02/michael-burry-just-...

[13]https://groq.com/newsroom/groq-raises-750-million-as-inferenc...

[14]https://www.techradar.com/pro/security/could-the-ai-bubble-be...

本文经原作者授权,由Baihai IDP编译。如需转载译文,请联系获取授权。

原文链接:

https://philippeoger.com/pages/deep-dive-into-nvidias-virtuou...

工作八九年第一次被裁,小孩刚出生,又是年底,心里挺不是滋味的。

项目 2025 年没达到预期,加之公司在推行 ai ,整个项目都裁撤了。

很久没去面试了,不知道开源项目对找工作还有没有帮助。

最近半年维护了一个开源项目,纯前端浏览器生成 PDF,承蒙大家厚爱,收获了一些 star

https://github.com/lmn1919/dompdf.js

体验地址

https://dompdfjs.lisky.com.cn/

欢迎大家去体验,对您有帮助的话也欢迎 star

导读

AI 编码工具正在从"智能补全"演进为能自主完成复杂任务的 Coding Agent。本文基于开源项目源码研究与实践经验,系统性地拆解 Coding Agent 的工作原理。旨在帮助开发者在了解Coding Agent后,与AI伙伴更好的协作配合,更高效的提问和拿到有效结果。

01 背景

AI 编码工具的发展速度快得有点"离谱"。从开始使用 GitHub Copilot 的代码补全,到使用Claude Code、Cursor、Comate IDE等完成复杂编程任务,AI 不再只是个「智能补全工具」,它能读懂你的代码库、执行终端命令、甚至帮你调试问题,成为你的“编码伙伴”。

我自己在团队里推 AI 编码工具的时候,发现一个很有意思的现象:大家都在用,但很少有人真正理解它是怎么工作的。有人觉得它"很神奇",有人吐槽它"经常乱来",还有人担心"会不会把代码搞乱"。这些困惑的背后,其实都指向同一个问题:我们对这个"伙伴"还不够了解。

就像你不会无脑信任一个新来的同事一样,要和 AI 编码伙伴配合好,你得知道它的工作方式、能力边界、以及怎么"沟通"才更有效。

在经过多次的实践尝试后,我尝试探索它的底层原理,并写下了这篇文章记录,主要围绕了这些内容展开:

  • Coding Agent 的核心工作机制,包括身份定义、工具调用、环境感知等基础组成。
  • 从零实现一个最小化 Coding Agent 的完整过程,以建立对 Agent 工作流程的直观理解。
  • 上下文管理、成本控制、冲突管控等生产环境中的关键技术问题及其解决方案。
  • Rule、MCP、Skill 等能力扩展机制的原理与应用场景。

在了解原理后,我和伙伴的协作更佳顺畅,让伙伴更清晰的了解我的意图,我拿到有效的回答。

02 概念

2.1 从Workflow到Agent

取一个实际的例子:休假申请。

如果我们的需求非常简单:

一键申请明天的休假。

在这里插入图片描述

这个需求可以被简化为一个固定的工作流

  1. 打开网页。
  2. 填写起始时间。
  3. 填写结束时间。
  4. 填写休假原因。
  5. 提交表单。

全过程没有任何模糊的输入,使用程序化即可完成,是最原始的工作流形态。

如果需求再模糊一些:

申请后天开始3天休假。

这个需求的特点是没有明确的起始和截止时间,需要从语义上分析出来

  1. 起始时间:后天。
  2. 休假时长:3天。
  3. 转换日期:10.14 - 10.16。
  4. 执行申请:提交表单。

这是一个工作流中使用大模型提取部分参数的典型案例,是模型与工作流的结合。

如果需求更加模糊:

国庆后休假连上下个周末。

这样的需求几乎没有任何直接确定日期的信息,同时由于年份、休假安排等动态因素,大模型不具备直接提取参数的能力。将它进一步分解,需要一个动态决策、逐步分析的过程:

  1. 知道当前年份。
  2. 知道对应年份的国庆休假和调休安排。
  3. 知道国庆后第一天是星期几。
  4. 国庆后第一天到下个周末设为休假日期。
  5. 额外补充调休的日期。
  6. 填写并提交表单。

可以看出来,其中1-5步都是用来最终确定休假日期的,且需要外部信息输入,单独的大模型无法直接完成工作。这是一个典型的Agent流程,通过大模型的智能工具访问外部信息结合实现用户需求。

2.2 什么是Agent

Agent是以大模型为核心,为满足用户的需求,使用一个或多个工具,自动进行多轮模型推理,最终得到结果的工作机制。

2.3 什么是Coding Agent

在Agent的基本定义的基础上,通过提示词、上下文、工具等元素强化“编码”这一目的,所制作的特化的Agent即为Coding Agent。

Coding Agent的最大特征是在工具的选取上,模拟工程师进行代码编写的环境,提供一套完整的编码能力,包括:

  • 阅读和查询代码:

    • 读取文件,对应 cat 命令。
    • 查看目录结构,对应 tree 命令。
    • 通配符查找,对应 ls命令(如 **/*.test.tssrc/components/**/use*.ts)。
    • 正则查找,对应grep 命令(如function print\(.+\) 可以找函数定义)。
    • LSP(Language Server Protocol),用于提供查找定义、查找引用、检查代码错误等能力。
  • 编写或修改代码:

    • 写入文件。
    • 局部编辑文件。
    • 删除文件。
  • 执行或交互命令:

    • 执行终端命令。
    • 查看终端命令stdout输出。
    • 向终端命令stdin 输入内容。

除此之外,通常Coding Agent还具备一些强化效果而设定的工具,通常表现为与Agent自身或外部环境进行交互,例如经常能见到的TODO、MCP、Subagent等等。

03 内部组成

3.1 上下文结构

3.2 身份定义

一个Agent首先会将模型定义成一个具体的身份(红色与橙色部分),例如在社区里常见的这样的说法:

You are a Senior Front-End Developer and an Expert in React, Nexts, JavaScript, TypeScript, HTML, CSS and modern UI/UX frameworks.

在身份的基础上,再附加工作的目标和步骤拆解,比如Cline有类似这样的内容:

https://github.com/cline/cline/blob/4b9dbf11a0816f792f0b3229a08bbb17667f4b73/src/core/prompts/system-prompt/components/objective.ts

  1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
  2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
  3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within <thinking></thinking> tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params). DO NOT ask for more information on optional parameters if it is not provided.
  4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. open index.html to show the website you've built.
  5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.

不用特别仔细地看每一句话,多数Coding Agent会提供一些详实的行动准则、目标要求,这部分称为“Guideline”。

有一些Coding Agent可以在多种模式(或者说智能体)之间进行切换,例如Cursor有Edit、Ask、Plan等,RooCode有Architect、Orchestrator等,有些产品还支持自定义模式。

Cursor

RooCode

选择不同的模式时,实际上会产生不同的目标要求、行为准则,即不同的Guideline环节。因此系统提示词中的身份部分,通常会分成不变的Base Prompt(红色)和可变的Agent Prompt(橙色)两个部分来管理,实际开始任务时再拼装起来。

3.3 工具调用

Agent的另一个最重要的组成部分是工具,没有工具就无法称之为一个Agent。让Agent能够使用工具,就必须要有2部分信息:

  1. 有哪些工具可以用,分别是什么作用。
  2. 如何指定使用一个工具。

对于第一点(哪些工具),在Agent开发过程中,一般视一个工具为一个函数,即由以下几部分组成一个工具的定义:

  1. 名称。
  2. 参数结构。
  3. 输出结构。

实际在调用模型时,“输了结构”往往是不需要提供给模型的,但在Agent的实现上,它依然会被预先定义好。而“名称”和“参数结构”会统一组合成一个结构化的定义,通常所有工具都只接收1个参数(对象类型),用JSON Schema表示参数结构。

一个典型的工具定义:

{
  "name": "read",
  "description": "Read the contents of a file. Optionally specify line range to read only a portion of the file.",
  "parameters": {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "The file path to read from"
      },
      "lineStart": {
        "type": "integer",
        "description": "The starting line number (1-indexed). If not specified, reads from the beginning of the file."
      },
      "lineEnd": {
        "type": "integer",
        "description": "The ending line number (1-indexed). If not specified, reads to the end of the file."
      }
    },
    "required": ["path"]
  }
}

可以简单地把这个工具理解成对应的TypeScript代码:

interface ReadToolParameter {
        path: string;
        lineStart?: number;
        lineEnd?: number;
}

async function read(parameters: ReadToolParameter) {
        // 工具实现
}

对于第2点(指定使用工具),则是要让大模型知道工具调用的具体格式。这在业界通常有2种做法。

第1种以Claud Code、Codex等为典型,使用大模型提供的Function Calling格式调用,分为以下几步:

  1. 在调用大模型时,通过一个tools 字段传递所有的工具定义。
  2. 模型会返回一个消息中包含tool_calls 字段,里面每一个对象是一个工具的调用,使用id 作为唯一标识。
  3. 工具产生的结果,以一条role: 'tool' 的消息返回,其中tool_call_id 与调用的id对应,content 是工具的结果(这里各家模型厂商的实现略有不同,其中Anthropic要求role: user,但content字段中传递toolResult,其结构是[{type: 'tool_result',tool_use_id: toolBlock.id, content: toolResultContent}],tool_use_id与调用的id对应)。

第2种方式是以Cline、RooCode为典型,使用一种自定义的文本格式来表示工具调用,通常选择XML的结构,例如对于Cline,读取一个文件的结构如下:

<read_file>
<path>src/index.ts</path>
</read_file>

只要在模型返回的消息中出现这样的结构,就会被解析为一个工具调用,得到的结果以普通的role: 'user' 的消息返回,包括实际内容和一些提示相关的信息。

Content of src/index.ts:

Note:

- this file is truncated to line 1000, file has a total 2333 lines.
- use read_file with line_start and line_end parameters to read more content.
- use seach_in_files tool searching for specific patterns in this file.

...

3.4 环境感知

Coding Agent之所以可以在一个代码库上执行任务,除了通过工具来遍历、检索代码外,另一个因素是Agent实现会在调用模型时主动地提供一部分与项目有关的信息。

其中对Coding Agent工作最有用的信息之一是代码库的结构,即一个表达出目录、文件结构的树型区块。这部分信息通常会符合以下特征:

  1. 尽可能地保留目录的层级结构,使用换行、缩进的形式表达。
  2. 遵循 .gitignore 等项目配置,被忽略的文件不会表现在树结构中。
  3. 当内容过多时,有一定的裁剪的策略,但同时尽可能多地保留信息。

以Cursor为例,这部分的内容大致如下:

<project_layout>
Below is a snapshot of the current workspace's file structure at the start of the conversation. This snapshot will NOT update during the conversation. It skips over .gitignore patterns.

codex-cursor/
  - AGENTS.md
  - CHANGELOG.md
  - cliff.toml
  - codex-cli/
    - bin/
      - codex.js
      - rg
    - Dockerfile
    - package-lock.json
    - package.json
    - scripts/
      - build_container.sh
      - build_npm_package.py
      - init_firewall.sh
      - [+4 files (1 *.js, 1 *.md, 1 *.py, ...) & 0 dirs]
  - codex-rs/
    - ansi-escape/
      - Cargo.toml
      - README.md
      - src/
        - lib.rs
</project_layout>

当内容数量超过阈值时,会采用广度优先的保留策略(即尽可能地保留上层目录结构),同时对于被隐藏的文件或子目录,会形如 [+4 files (1 *.js, 1 *.md, 1 *.py, ...) & 0 dirs]这样保留一个不同文件后缀的数量信息。

除了目录结构外,还有一系列默认需要模型感知的信息,在一个Coding Agent的工作环境中,它通常分为2大类,各自又有一系列的细项:

  1. 系统信息:

    1. 操作系统(Windows、macOS、Linux,具体版本)。
    2. 命令行语言(Shell、Powershell、ZSH)。
    3. 常见的终端命令是否已经安装( python3nodejqawk等,包含具体版本)。
    4. 代码库目录全路径。
  2. 为Agent扩展能力的信息:

    1. Rule(自动激活的部分)。
    2. Skill(摘要描述部分)。
    3. MCP(需要的Server和Tool列表)。
    4. Memory(通常是全量)。

需要注意的是,环境信息这部分,一般不出现在系统提示词中,而是和用户提问的消息放置在一起。

3.5 简单实现

在身份定义、工具调用、环境感知这3部分最基础的Agent组成都达成后,简单地使用大模型的API,进行自动化的工具调用解析、执行、发送新一轮模型调用,可以非常简单地实现一个最小化的Coding Agent。

可以尝试用以下的提示词,使用任意现有的Coding Agent产品,为你编写一个实现,并自己调试一下,感受Coding Agent的最基础的逻辑:

我希望基于大模型实现一个Coding Agent,以下是我的具体要求:

1. 使用Claude作为模型服务商,使用环境变量管理我的API Key。
2. 默认使用Claude Sonnet 4.5模型。
3. 使用Anthropic's Client SDK调用模型。
4. 不需要支持流式输出。
5. 使用TypeScript编写。

以下是Agent提供的工具:

1. read({path: string}):读取一个文件的内容
2. list({directory: string}):列出一个目录下的一层内容,其中目录以`/`结尾
3. write({path: string, content: string}):向文件写入内容
4. edit({path: string, search: string, replace: string}):提供文件中的一块内容

以下是交互要求:

1. 通过NodeJS CLI调用,支持`query`和`model`两个参数,可以使用`yargs`解析参数。
2. 在System消息中,简短地说明Coding Agent的角色定义、目标和行为准则等。
3. 在第一条User消息中,向模型提供当前的操作系统、Shell语言、当前目录绝对路径信息,同时包含跟随`query`参数的内容,组织成一条模型易于理解的消息。
4. 对每一次模型的工具调用,在控制台打印工具名称和标识性参数,其中标识性参数为`path`或`directory`,根据工具不同来决定。
5. 如果模型未调用工具,则将文本打印到控制台。

请在当前目录下建立一个`package.json`,并开始实现全部的功能。

04 优质上下文工程

4.1 成本控制

大模型是一个非常昂贵的工具,以Claude为例,它的官方API价格如下:

我们可以观察到一些特征:

  1. 输出的价格是输入的5倍(但实际考虑到输出与输出的数量比例,输出的价格根本不值一提)。
  2. 缓存输入(Cache Writes)比正常输入(Base Input)更贵一些,约1.25倍。
  3. 缓存命中(Cache Hits)的价格比正常输入(Base Input)要便宜很多,为1/10的价格。

这就意味着,一个良好使用缓存的Agent实现,其成本会比不用缓存降低8-10倍。因此所有的Coding Agent一定会细致地梳理内容结构,最大化利用缓存

在大模型的API中,缓存通常以“块”为单位控制,例如:

  1. 系统提示词中不变的部分。
  2. 系统提示词中可变部分。
  3. 工具定义。
  4. 每一条消息,单条消息也可以拆成多个块。

继续观察Claude对于缓存控制的文档:

可以看到,在大模型API中各种参数一但有所变动,缓存都会大量失效(至少消息缓存全部失效,大概率系统缓成失效),这就会造成成本的极大提升。因此,在Coding Agent实现中,都会从一开始就确定所有参数,整个任务不做任何变更。一些很经典的实例:

  1. 一次任务不会一部分消息开思考模式,一部分不开,因为思考参数会让全部的消息缓存失效。
  2. 切换不同模式(如Edit、Ask、Plan)时,虽然能使用的工具不同,但只是在消息中增加说明,而不会真的将 tools 字段改变。

另外,Coding Agent会尽可能保持历史消息内容完全不变,以最大化地缓存消息。例如对于一个进行了10轮模型调用的任务,理论上第10次调用中,前9轮的消息内容都会命中缓存。但如果此时擅自去修改了第1轮的工具调用结果(例如试图删除读取的文件内容),看似可能消息的长度减少了,但实际因为缓存被破坏,造成的是10倍的成本提升。

总而言之,缓存是一个至关重要的因素,Coding Agent的策略优化通常以确保缓存有效为前提,仅在非常必要的情况下破坏缓存

4.2 空间管理

Coding Agent因为会自动地与大模型进行多轮的交互,随着不断地读入文件、终端命令输出等信息,上下文的长度会变得非常的大,而大模型通常只具备128K左右的总长度,因此如何将大量内容“适配”到有限的长度中,是一个巨大的挑战。

控制上下文长度的第一种方式是“裁剪”,即在整个上下文中,将没用的信息删除掉。试想如下的场景:

  1. 模型读取了一个文件的内容。
  2. 模型将文件中 foo 这一行改成了 bar
  3. 模型又将文件中 eat 这一行改成了 drink

假设我们对模型每一次修改文件,都返回最新的文件内容,如果这个文件有1000行,那么1次读取、2次修改,就会产生3000行的空间占用

一种优化方式就是,在这种连续的读-改的场景下,只保留最后一条消息中有全文内容,即上述3次模型调用后,出现在上下文中的内容实际是这样的:

<!-- Assistant -->
read(file)

<!-- User -->
[This file has been updated later, outdated contents are purged from here]

<!-- Assistant -->
edit(file, foo -> bar)

<!-- User -->
The edit has been applied successfully.

--- a/file
+++ b/file
@@ -23,1 +23,1 @@
-foo
+bar

[This file has been updated later, outdated contents are purged from here]

<!-- Assistant -->
edit(file, eat -> drink)

<!-- User -->
The edit has been applied successfully, the new file content is as below:

{content of file}

可以看到,通过将连续对同一文件的修改进行裁剪,可以只保留最新的内容,同时又使用unidiff 之类的形式保留中间编辑的差异信息,最大限度地降低空间占用,又能保留模型的推理逻辑。

但裁剪不能使用在非连续的消息中,随意地使用剪裁逻辑,很有可能破坏消息缓存结构,进而使模型调用的输入无法通过缓存处理,几倍地增加模型的调用成本。

即便裁剪有一定效果,但随着更多的内容进入到上下文中,始终会有将上下文占满的时候,此时模型将完全无法进行推理。为了避免这种情况出现,Coding Agent通常会使用“压缩”这一技术,即将前文通过模型摘要成少量的文字,同时又保留比较关键的推理链路。

通常,压缩在上下文即将用完的时候触发,如已经使用了90%的上下文则启动压缩,压缩的目标是将90%的内容变为10%的长度,即省出80%的空间供后续推理。

压缩本身是一个模型的任务,即将所有的上下文(可以选择性地保留最新的1-2对消息)交给模型,同时附带一个压缩的要求,让模型完成工作。这个压缩的要求的质量将决定压缩的最终结果,一个比较典型的实现是Claude Code的“八段式摘要”法:

const COMPRESSION_SECTIONS = [
  "1. Primary Request and Intent",    // 主要请求和意图
  "2. Key Technical Concepts",        // 关键技术概念
  "3. Files and Code Sections",       // 文件和代码段
  "4. Errors and fixes",              // 错误和修复
  "5. Problem Solving",               // 问题解决
  "6. All user messages",             // 所有用户消息
  "7. Pending Tasks",                 // 待处理任务
  "8. Current Work"                   // 当前工作
];

通过将信息压缩成8部分内容,能够最大限度地保留工作目标、进度、待办的内容。

4.3 独立上下文

在实际的应用中,其实大概率是不需要128K上下文用满的,但真实表现又往往是上下文不够用。这中间存在的差异,在于2类情况:

  1. 为了满足一个任务,需要收集大量的信息,但收集到正常信息的过程中,会引入无效的、错误的内容,占用上下文。
  2. 一个任务足够复杂,分解为多个小任务后各自占用部分上下文,但加起来以后会超出限制。

试想一下,对于一个这样的任务:

修改我的Webpack配置,调整文件拆分逻辑,让最终产出的各个JS文件大小尽可能平均。

但是很“不幸”地,这个项目中存在6个 webpack.config.ts文件,且最终splitChunks 配置在一个名为 optimization.ts 的文件中管理,那么对于Coding Agent来说,这个任务中就可能存在大量无意义的上下文占用:

  1. 读取了6个 webpack.config.ts ,一共2000行的配置内容,但没有任何splitChunks 的配置,包含了大量 import 其它模块。
  2. 又读取了10个被 import 的模块,最终找到了 optimization.ts 文件。
  3. 经过修改后,执行了一次 npm run build 来分析产出,发现JS的体积不够平均。
  4. 又修改 optimization.ts ,再次编译,再看产出。
  5. 循环往复了8次,终于在最后一次实现了合理的splitChunks 配置。

这里面的“6个 webpack.config.ts ”、“10个其它模块”、“8次优化和编译”都是对任务最终目标并不有效的内容,如果它们占用150K的上下文,这个任务就不得不在中途进行1-2次的压缩,才能够最终完成。

为了解决这个问题,当前多数的Coding Agent都会有一个称为“Subagent”的概念。就好比一个进程如果只能使用4GB的内存,而要做完一件事需要16GB,最好的办法就是开5个进程。Subagent是一种类似子进程的,在独立的上下文空间中运行,与主任务仅进行必要信息交换的工作机制

再回到上面的案例,在Subagent的加持下,我们可以将它变成以下的过程:

  1. 启动一个Subagent,给定目标“找到Webpack文件拆分的代码”。

    1. 读取6个 webpack.config.ts
    2. 读取10个被 import 的模块。
    3. 确定目标文件 optimization.ts
    4. 返回总结:在 optimization.ts 中有文件拆分的配置,当前配置为……。
  2. 启动一个Subagent,给定目标“修改 optimization.ts ,使产出的JS体积平均,执行 npm run build 并返回不平均的文件“。

    1. 修改 optimization.ts
    2. 执行 npm run build,得到命令输出。
    3. 分析输出,找到特别大的JS文件,返回总结:配置已经修改,当前 xxx.js 体积为平均值的3倍(723KB),其它文件体积正常。
  3. 启动一个Subagent,给宝目标“分析 dist/stats.json,检查 xxx.js 中的模块,修改 optimization.ts 使其分为3个250KB左右的文件,执行 npm run build并返回不平均的文件”。

    1. ……
    2. ……
  4. 继续启动6次Subagent,直到结果满意。

不难看出来,这种模式下主体的Coding Agent实际是在"指挥"Subagent做事,自身的上下文占用是非常有限的。而Subagent仅“专注”于一个小目标,也不需要太多的上下文,最终通过这类不断开辟新上下文空间的方式,将一个复杂的任务完成。

4.4 注意力优化

如果你经常使用Coding Agent,或在业界早期有过比较多的使用经验,你可能会发现这种情况:Coding Agent在完成一个任务到一半时,忘了自己要做什么,草草地结束了任务,或偏离了既定目标产生很多随机的行为。

会发生这样的情况,有一定可能是裁剪、压缩等策略使有效的上下文信息丢失了,但更多是因为简单的一个用户需求被大量的代码内容、命令输出等推理过程所掩盖,权重弱化到已经不被大模型“注意到”,因此最初的目标也就完全丢失了。

Coding Agent一个很重要的任务,就是在长时间运作的同时随时调整大模型的注意力,使其始终聚焦在最终目标、关注当前最需要做的工作,不要偏离预先设定的路线。为了实现这一效果,Coding Agent产品提出了2个常见的概念。

第一称为TODO,在很多的产品中,你会看到Agent先将任务分解成几个步骤,转为一个待办列表。这个列表在界面上始终处于固定的位置,随着任务的推进会逐步标记为完成。这个TODO实际上并不是给用户看的,而是给模型看的

在实际的实现中,每一次调用模型时,在最后一条消息(一般就是工具调用的结果)上,除了原始消息内容外,会增加一个称为“Reminder”的区域。这个区域因为始终出现在所有消息的最后,通常来说在模型的注意力中优先级更高,而且绝对不会受其它因素影响而消失

Reminder中可以放置任意内容,比较经典的有:

  1. TODO及进度。用于模型时刻理解目标、进展、待办。
<reminders>
- Planned todos:
  - [x] Explore for code related to "print" function
  - [x] Add "flush" parameter to function
  - [ ] Refactor all "print" function calls to relect the new parameter
</reminders>
  1. 工具子集。如前面《缓存》相关的描述,因为修改工具定义会使缓存失效,因此当切换模式使得可用的工具减少时,一般仅在Reminder中说明部分工具不可用,由模型来遵循这一约束,而不是直接删除部分工具。
<!-- 切换至Ask模式 -->
<reminders>
- You can ONLY use these tools from now on:
  - read
  - list
  - grep
  - bash
</reminders>
  1. 行为指示。例如当模型连续多次给出名称、参数都一模一样的工具调用时,说明模型处在一种不合理的行为表现上,此时在Reminder中增加提示,让模型感知到当前状态的错误,就有可能调整并脱离错误的路线。
<!-- Assistant -->
read(file)

<!-- User -->
The file content: ...

<!-- Assistant -->
read(file)

<!-- User -->
The file content: ...

<reminders>
- Your are using read tool the second time with exactly the same parameters, this usually means an unexpected situation, you should not use this tool again in your response.
</reminders>
  1. 状态提示。例如激活某一个Skill时,Reminder中可以提示“当前正在使用名为X的Skill“,这种提示可以让模型更加专注于完成一个局部的工作。
<reminders>
- You are currently working with the skill "ppt" active, be focused on this task until you quit with exit_skill tool.
</reminders>

需要额外注意的是,Reminder仅在最后一条消息中出现,当有新的消息时,旧消息上的Reminder会被移除。基于这一特征,我们知道Reminder是永远无法命中缓存的,因此Reminder部分的内容长度要有控制,避免造成过多的成本消耗。

4.5 冲突管控

随着Coding Agent能力的发展,当下执行的任务时间越来越长、编辑的文件越来越多,同时更多的用户也习惯于在Agent工作的同时自己也进行编码工作,甚至让多个Agent任务并发执行。这种“协同”形态下,不少用户曾经遇到过这样的问题:

自己将Agent生成的代码做了一些修正,但之后Agent又把代码改了回去。

这个现象的基本原因也很清楚,就是Agent并不知道你改动过代码。例如以下的过程使Agent读取并编辑了一个文件:

<!-- Assistant -->
read(file)

<!-- User -->
The file content:
...
console.log('hello');
...
<!-- Assistant -->
edit(file, hello -> Hello)

<!-- User -->
Edit has been applied successfully.

这个时候,在模型见到的上下文中,这个文件中的代码显然是console.log('Hello'); 。假设乃又将它改成了console.trace('Hello'); ,后面模型依然会基于.log 来修改代码,用户看起来就是代码“改了回去”。

解决这种共同编辑文件的冲突,实际上有多种方法:

  • 加锁法。当Agent读取、编辑一个文件时,更新模型认知的文件内容的快照。当这个Agent再一次编辑这个文件时,读取文件当前的实际内容,和快照做比对,如果内容不一样,拒绝这一次编辑,随后要求Agent重新读取文件(更新快照与实际内容一致)再进行编辑。这是一种主流的做法,不过Agent实现上的细节比较重
<!-- Assistant -->
edit(file, console.log...)

<!-- User -->
This edit is rejected, the file has been modified since your last read or edit, you should read this file again before executing any write or edit actions.

<!-- Assistant -->
read(file)

<!-- User -->
The file content: ...

<!-- Assistant -->
edit(file, console.trace...);
  • 推送法。监听所有模型读取、编辑过的文件的变更,当文件发生变更时,在下一次模型调用时,不断通过Reminder区域追加这些变更,让模型“实时”地知道文件有所变化,直到文件被下一次读取。这种方式能让模型更早地感知变化,但推送信息可能过多影响成本和推理速度。
<!-- Assistant -->
run_command(ls)

<!-- User -->
The command output: ...

<reminders>
- These files have been modified since your last read or edit, you should read before write or edit to them:
  - file
  - file
  - ...
</reminders>
  • 隔离法。使用Git Worktree方案,直接让不同的Agent任务在文件系统上隔离,在一个独立的Git分支上并行工作,相互不受干扰。在任务完成后,用户检查一个任务的全部变更,在采纳时再合并回实际的当前Git分支,有冲突的由用户解决冲突。这种方法让Agent根本不需要考虑冲突问题,但缺点是系统资源占用高,且有合并冲突风险

文件编辑冲突只是一个比较常见的现象,实际上用户和Agent、多个Agent并行工作,可能造成的冲突还有很多种,例如:

用户敲了半行命令 ls -,Agent直接在终端里敲新的命令 grep "print" -r src执行,导致最后的命令是 ls -grep "print" -r src ,是一个不合法的命令。

终端的抢占也是一种冲突,但相对更容易解决,只要让每一个Agent任务独占自己的终端,永远不与用户、其它Agent任务相交叉即可。

4.6 持久记忆

我们都知道,模型是没有状态的,所以每一次Agent执行任务,对整个项目、对用户的倾向,都是从零开始的过程。这相当于历史经验无法积累,很多曾经调整过的细节、优化过的方向都会被重置。虽然可以通过比如Rule这样的方式去持久化这些“经验”,但需要用户主动的介入,使用成本是相对比较高的。

因此当前很多Coding Agent产品都在探索“记忆”这一能力,争取让Agent变得用的越多越好用。记忆这个话题真正的难点在于:

  1. 如何触发记忆。
  2. 如何消费记忆。
  3. 什么东西算是记忆。

首先对于“如何触发”这一问题,常见于2种做法:

  1. 工具型。定义一个 update_memory 工具,将记忆作为一个字符串数组看待,工具能够对其进行增、删改,模型在任务过程中实时地决定调用。往往模型并不怎么喜欢使用这类工具,经常见于用户有强烈情感的描述时才出现,比如“记住这一点”、“不要再……”。
  2. 总结型。在每一次对话结束后,将对话全部内容发送给模型,并配上提示词进行记忆的提取,提取后的内容补充到原本记忆中。总结型的方案往往又会过度地提取记忆,将没必要的信息进行持久化,干扰未来的推理。
  3. 存储型。不进行任何的记忆整理和提取,而是将所有任务的原始过程当作记忆,只在后续“消费”的环节做精细的处理。

然后在“如何消费”的问题下,也常见有几种做法:

  1. 始终附带。记忆内容记录在文件中,Agent实现中将文件内容附带在每一次的模型请求中。即模型始终能看到所有的记忆,这无疑会加重模型的认知负担,也占用相当多的上下文空间,因为很多记忆可能是与当前任务无关的。
  2. 渐进检索。本身不带记忆内容到模型,但将记忆以文件系统的形式存放,Agent可以通过readlistgrep 等工具来检索记忆。配合“存储型”的触发方式,能让全量的历史任务都成为可被检索的记忆。但这种方式要求模型有比较强的对记忆的认知,在正确的时刻去找相关的记忆。但往往因为根本不知道记忆里有什么,进而无法知道什么时候应该检索,最终几乎不触发检索。

而最终的问题,“什么东西是记忆”,是当下Coding Agent最难以解决的问题之一。错误的、不必要的记忆甚至可能造成实际任务效果的下降,因此精确地定义记忆是Agent实现的首要任务。

通常来说,记忆会分为2种大的方向:

  1. 事实型。如“使用4个空格作为缩进”、“不要使用any 类型“,这些都是事实。事实是无关任何情感、不带主观情绪的。
  2. 画像型。如”用户更喜欢简短的任务总结“就是一种对用户的画像。画像是单个用户的特征,并不一定与项目、代码、架构相关。

在Coding Agent上,往往更倾向于对”事实型“的内容进行记忆,而不考虑用户画像型的记忆。

同时,从业界的发展,可以看到越来越多的模型厂商在从底层进行记忆能力的开发,如最近Google的Titan架构就是一种记忆相关的技术。可能未来某一天,Agent实现上已经不需要再关注记忆的逻辑与实现,模型自身将带有持久化的记忆能力。

05 能力扩展

在实际应用中,还需要一些机制来让Agent更好地适应特定的项目、团队和个人习惯。当前主流的Coding Agent产品都提供了Rule、MCP、Skill这三种扩展能力,它们各有侧重,共同构成了Agent的能力增强体系。

5.1 Rule

当面对业务的repo往往存在一些领域相关的知识而非模型的知识库中已有的内容,这些往往需要凭借老员工的经验或者读取大量代码库的信息进行总结后才能明白,这些内容便适合放到Rule中,作为静态的不会频繁改动的内容放入Environment Context中长期Cache。

好的Rule应当足够精简、可操作且范围明确,人看不懂的规则或者描述不清的规则模型是一定搞不定无法遵守的。

  • 将Rule控制在 500 行以内。
  • 将较大的规则拆分为多个可组合的规则,采取按需的方式,按照 文件路径/关键场景 激活Rule;对于特定场景激活的Rule,采取编写索引的方式创建Rule,让模型渐进式激活,比如项目针对网络请求和错误处理相关做了项目维度的封装处理,但这种情况并不是每个文件ts/tsx文件都会遇到的诉求,比如在项目的rules目录下创建index.mdr(curso是.mdc文件),编写下面的激活的条件:

    • 需要进行API调用获取数据
    • 处理异步操作的错误和加载状态

-   当编码涉及以下任一情况时,必须立刻阅读 \[08-api-error-handling.mdc\](mdr:.cursor/rules/08-api-error-handling.mdc)
    
  • 提供具体示例或参考文件,针对xx情况正确的方式是\`code\`。
  • 避免模糊的指导,比如交互式的东西模型交互不了,不需要写进去。
  • 为了模型能够积极验证每次改动是否符合预期,告知模型改动后可以执行的正确的构建命令,以及某些自定义命令(比如自动化测试)引导模型在后台启动命令,在xx秒后读取日志文件的内容进行结果的判断。

5.2 MCP

MCP(Model Context Protocol)是Anthropic提出的一种标准化的工具扩展协议,它允许开发者以统一的方式为Coding Agent添加新的能力。

与Rule的"声明式约束"不同,MCP是一种实时工具调用协议,即通过MCP server的方式进行连接,来扩展Agent可以做的事情。

一个典型的场景是集成外部服务。比如你的项目托管在GitHub上,可以让Agent直接访问GitHub实现创建Issue、查询PR状态、添加评论等功能:

{
    "mcpServers": {
        "github": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-github"],
            "env": {
                "GITHUB_PERSONAL_ACCESS_TOKEN": "<your-github-token>"
            }
        }
    }
}

配置好后,Agent就能在代码审查过程中自动创建Issue记录问题、查询相关PR的讨论、甚至根据代码变更自动生成commit message。

MCP的另一个优势是实现门槛低。一个MCP Server本质上就是一个标准输入输出的程序,它通过JSON-RPC协议与Agent通信,当模型需要外部能力的时候,调用MCP Server,而模型无需关心其内部代码实现,Agent只需要按照固定的协议去连接获取内容。

5.3 Skill

5.3.1 什么是Skill

随着模型能力的提升,使用Agent完成的任务复杂度逐渐增加,使用Coding Agent可以进行本地代码执行和文件系统完成跨领域的复杂任务。但随着这些Agent的功能越来越强大,我们需要更具可组合性、可扩展性和可移植性的方法,为它们配备特定领域的专业知识,因此Agent Skill作为一种为Agent扩展能力的标准诞生。Skill 将指令、脚本和资源的文件夹打包,形成专业领域的知识,Agent在初始化的时候会获取可用的Skills列表,并在需要的时候动态加载这些内容来执行特定任务。

随着 Skill 复杂性的增加,它们可能包含过多的上下文信息,无法放入单个配置文件中 SKILL.md,或者某些上下文信息仅在特定场景下才相关。在这种情况下,Skill可以在当前目录中bundle额外的文件,并通过文件名引用这些文件,这些额外的文件提供了更多详细信息,Coding Agent 可以根据需要选择浏览和查找这些信息。Skill 是渐进式触发的, 因此 SKILL.mdnamedescription很关键,这会始终存在于Agent的环境上下文中提供给模型,模型会根据这些描述信息来决定是否在当前任务中触发该Skill,当你明确希望使用某个Skill完成任务,可以在prompt中指定“使用xxxx Skill完成xx任务”。

5.3.2 Skill和代码执行

LLM在很多任务上表现出色,但许多操作需要使用编写代码 -> 代码执行的方式,带来更高效的操作、确定性的以及可靠性的结果。生成式的模型常常通过生成可执行代码的方式去验证/计算结果。

代码既可以作为可执行工具,也可以作为文档。Skill中应该明确让模型是应该直接运行脚本,还是应该将其作为参考信息读取到上下文中。

5.3.3 如何创建Skill

每个Skill由一个必需的 SKILL.md 文件和可选的bundle资源组成,Skill 应该只包含完成任务所需的信息。

skill-name/
├── SKILL.md (必需)
│   ├── YAML frontmatter 元数据 (必需)
│   │   ├── name: (必需)
│   │   ├── description: (必需,这是 skill 的主要触发机制,帮助模型理解何时使用该 skil)
│   │   └── compatibility: (可选)
│   └── Markdown 说明 (必需)
└── bundle的资源 (可选)
    ├── scripts/          - 可执行代码 (Python/Bash/等)
    ├── references/       - 需要时加载到上下文的文档
    └── assets/           - 用于输出的文件 (模板、图标、字体等)

举一个具体的例子,比如当我们需要进行批量项目的技术栈migrate,比如将less迁移postcss,中间涉及一系列的复杂步骤,比如:

  • 安装postcss以及postcss plugin的依赖
  • 配置postcss的config
  • 分析项目用到了哪些less varibale替换成css vars
  • 删除mixin并替换
  • 一系列的其他兼容less的语法转换...
  • 替换文件后缀

上面的工作可以通过清晰的流程描述,并配合脚本实现,因此可以作为一个Skill将经验变成可复制的,一个less-to-postcss的skill的结构:

5.3.4 Skill的使用

人人都可以创建Skill,也可以让Agent来编写Skill,这是Skill非常便捷的地方。Skill通过instructions和code赋予Coding Agent新的能力。虽然这使其功能强大并有很高的自由度,但也意味着恶意SKill可能会在其使用环境中引入漏洞,诱使模型窃取数据并执行非预期操作。仅从可信来源安装Skill,如果无法确信来源可信,在使用前请务必进行彻底审核。

Skill的出现并不是替代MCP的出现,而是相互配合,在合适的场景下选取Skill或是MCP。某些任务Skill和MCP Server均可完成,但Skill通过执行代码的方式可以一次性加载完整流程,但MCP Server要经历多次查询和多轮对话往返,这种情况下Skill更为合适,但这不意味着绝对的优势,比如标准化文档创建这个典型的场景,创建PPT/Word/Excel在本地使用Skill即可完成,但数据的提供则需要借助MCP Server进行查询。因此Skill擅长的是在本地通过执行 code的方式完成复杂任务,在用户私有数据、动态数据查询这些情况下Skill就无法搞定了,这和用户的数据库以及隐私强关联,需要让模型无法感知在执行过程中的隐私信息,Skill能够与MCP Server互补完成更为复杂的流程。

在不少后端团队里,都发生过类似的场景:
Redis 上线后,监控显示 API 核心查询耗时下降了 80%,但用户依旧抱怨接口“卡”“慢”“不稳定”。

于是问题开始在群里反复出现:

  • 是 Redis 集群不够大?
  • 是云厂商网络抖动?
  • 是流量高峰超出预期?
    直到真正拆开一次请求的完整生命周期,才会意识到一个事实:Redis 可能已经做到极致了,只是你把它用在了最不应当的位置。

一个被反复误解的事实
必须先说清楚一句话:
Redis 并不能让一个设计本身就臃肿的 API 变快,它只会让问题暴露得更明显。

在微服务架构下,Redis 几乎成了“性能优化”的默认答案。只要接口慢,第一反应往往是:“加一层缓存”。
但在真实生产环境中,API 的执行路径通常远比你想象得复杂。

客户端
  ↓
API 网关
  ↓
鉴权 / 鉴权扩展
  ↓
参数校验 / 特性开关
  ↓
缓存查询
    ↓(未命中)
数据库查询 → 关联查询 → ORM 映射
  ↓
DTO 转换 / 序列化
  ↓
日志 / 监控 / Trace
  ↓
响应返回

在这条链路里,Redis 只是其中极短的一段。如果你把注意力全部放在“Redis 查得够不够快”,那基本已经跑偏了。
Redis 并不是瓶颈,但常常被用来背锅
我们曾协助排查过一个典型系统:
一个对外提供实时报表查询的金融 API,客户团队坚信性能问题出在 Redis。
他们的监控面板显示:

  • API 平均响应时间:380–450 ms
  • 高峰期 P95 甚至逼近 700 ms

但在引入分段 Trace 后,结果令人意外:

  • Redis GET 操作:稳定在 2–4 ms
  • 超过 85% 的耗时,发生在:

    • 鉴权拦截器
    • 参数反序列化
    • ORM 对象构建
    • JSON 序列化与日志写入
      结论很直接:
      缓存很快,API 还是慢。
      这也是许多团队真正“顿悟”的时刻——
      Redis 没有失效,只是你让它介入得太晚了。

为什么“加了 Redis”却几乎没加速?
归纳下来,问题通常集中在三个方面。

  1. 缓存命中发生得太晚
    很多系统在设计时,把缓存当作“数据库前的一层挡板”,而不是请求生命周期的一部分。
    结果是:
  2. 请求已经完成了鉴权、校验、上下文构建
  3. 日志、Trace 组件已经初始化
  4. 各种中间对象已经创建
    此时即便 Redis 命中,绝大部分 CPU 和延迟成本已经付出。
  5. 缓存键设计服务于“数据模型”,而非“访问模式”
    另一个常见错误,是缓存整个领域对象,甚至直接缓存 ORM 实体。
    后果通常是:
  6. 键粒度过粗
  7. 访问模式稍有变化就无法复用
  8. 命中率长期徘徊在 50% 以下
    在这种情况下,Redis 更像是一个昂贵的、不稳定的旁路系统。
  9. 冷启动与高峰期未命中被严重低估
    很多团队只关注“平均命中率”,却忽略了两个危险时刻:
  10. 应用刚启动
  11. 流量突然放大
    在这些时刻,大量并发请求同时穿透缓存,数据库和后端逻辑被瞬间放大执行,抖动也由此产生。

让 Redis 真正“拉开差距”的设计方式
当你接受 Redis 不是万能解药之后,优化路径反而变得清晰了。

第一原则:缓存要尽可能早
如果某个请求的数据已经在缓存中,就不应该再经历完整的业务管道。
理想状态是:

  • 命中缓存
  • 直接返回最终响应
  • 绕过数据库、对象映射、序列化等步骤
    第二原则:缓存的是“可直接返回的结果”
    与其缓存领域对象,不如缓存“已经准备好返回给客户端的内容”。
String key = "user:profile:resp:" + userId;
String cached = redis.get(key);if (cached != null) {return cached;}// 未命中,走完整流程
User user = userRepository.findById(userId);
String responseJson = responseMapper.toJson(user);// 合理 TTL,例如 5 分钟
redis.setex(key, 300, responseJson);return responseJson;

这里 Redis 的角色已经发生变化:
它不再是“数据缓存”,而是响应加速。

第三原则:预热比你想象得重要
在优化后,我们为以下场景引入了缓存预热:

  • 服务启动
  • 核心用户或高频接口
  • 已知的高峰前时间段
    这一步往往可以显著降低首批请求的抖动风险。

数据不会说谎
在重构缓存策略后,性能变化非常直观:

  • API 平均响应时间
    从约 410 ms 降至 70–90 ms
  • 数据库查询量
    下降超过 65%
  • 缓存命中率
    稳定在 90% 以上
    更重要的是:延迟开始变得可预测,而不是偶发性飙升。

值得记住的几条经验

  1. 缓存优化首先是架构问题,而不是参数问题:Redis 再快,也无法拯救臃肿的请求链路。
  2. 一次缓存未命中的代价,远高于多数人的直觉:它带来的不是一次查询,而是一整条后端路径的放大执行。
  3. 不要只盯着 Redis 的指标:真正的瓶颈,往往藏在 Redis 之前或之后。

结语:Redis 从来不是问题
Redis 很少是系统变慢的原因,但它经常成为暴露问题的那面镜子。
如果你的 API 在“加了 Redis 之后”依然迟缓,不妨换个角度思考:
也许不是 Redis 没有加速系统,
而是系统本就不该让 Redis 来兜底。

测量全链路、设计有缓存意识的架构,让 Redis 只做它最擅长的事。
这,才是真正的性能提升来源。

🧭 写在前面:为什么用“提及度”看 CRM 市场

这篇文章会以「在媒体与研究报告中被频繁提及」为线索,盘点几款在 2026 年依然保持高曝光的主流 CRM(注意:这不等同于任何官方榜单或权威排名)。

我会尽量引用权威研究机构、评测平台与行业媒体的公开信息,并从市场视角解释一件事:
为什么总是这些名字反复出现在关键报告、行业解读和选型清单里?

image.png

🔍 为什么“提及度”值得看?

在 CRM 领域,“提及度高”通常意味着三件事:

  • 市场覆盖面广:跨行业、跨规模都能看到它的身影。
  • 细分场景成熟:例如销售自动化(SFA)、服务管理、营销自动化等,都有清晰定位。
  • 生态与口碑数据充足:集成伙伴多、实施与咨询经验多、用户评价可参考。

因此,当研究机构、评测平台或行业媒体在做趋势分析、产品对比、象限/波浪图,或者用户评价榜单时,这些品牌就会变成天然的“参照系”和“样本库”,被反复提到。

从不同角色的视角来看,大致是这样运作的:

  • 研究机构视角
    以销售自动化(SFA)等子市场为评估单位,按功能完备度、愿景前瞻性、执行能力等维度进行对比(如 Gartner 对 SFA 市场的相关报告与解读文章)。
  • 评测平台视角
    依托大量“已验证用户评论”,以评分、使用体验、市场热度等维度做动态排序(例如 G2 的 CRM 分类页,会列出热门产品并支持对比)。
  • 行业媒体视角
    更关心“是否企业级就绪”“典型客户画像”“适配场景”,会在各种「最佳 CRM」「选型清单」文章中把这些产品列为常见备选。

🌐 2026 年提及度较高的几款主流 CRM

(按常见曝光顺序归纳,非严格排名

下面这几款,是在研究报告、评测平台、行业媒体中都比较常被提到的产品。我会重点放在:它们为什么总出现在视野里。


1)Zoho CRM:性价比 + 套件化 + 全球化带来的高讨论度

Zoho CRM 的高提及度,主要来自两个层面:

  • 研究机构 / 官方传播层面
    Zoho 官方公开资料中,会引用其在 Gartner SFA 相关评估中的定位(例如被归类为 “Visionary” 等,具体以官方引用和原始报告为准)。
    这一类传播,会把 Zoho 放进“有前瞻性的销售自动化供应商”语境下反复出现。
  • 评测平台 / 用户口碑层面
    在 G2 等评测平台的 CRM 分类中,Zoho CRM 通常是热门产品之一
    对潜在客户来说,它经常出现在“同类对比 + 用户评论”的选型路径里,尤其当用户搜索「性价比」「一体化套件」时,会很容易看到它。

综合来看,Zoho CRM 经常被提起,是因为它在预算敏感、但又想要完整业务套件的组织中,有稳定的心智位置。


2)Salesforce:企业级 CRM 的“默认参照系”

在很多讨论中,Salesforce 都被当作 CRM 的“基准线”来使用:

  • 对于大型企业复杂业务流程,Salesforce 一直保持强存在感;
  • 围绕销售自动化平台(SFA)的行业与研究机构讨论中,它几乎是必被提及的对照对象;
  • 生态与应用市场(AppExchange)、合作伙伴体系非常丰富,使它成为“生态型 CRM 平台”的典型样本。

因此,即使企业最后不选 Salesforce,也会拿它来做功能、价格与架构的对比参照


3)Microsoft Dynamics 365:深度融入 Microsoft 生态的常见选项

Dynamics 365 的提及度,很大程度源自企业对“Microsoft 体系一体化”的偏好:

  • 很多组织已经深度使用 Office 365、Teams、Azure、Power BI 等 Microsoft 产品,
    在此基础上选 CRM 时,Dynamics 365 的集成体验和统一账号/数据体系就变得很有吸引力。
  • 在公开信息和市场传播中,也能看到微软围绕 Gartner SFA 相关认可进行宣传,这进一步巩固了它在“企业级 CRM 候选清单”里的曝光。

简单理解:只要企业是重度 Microsoft 用户,Dynamics 365 几乎一定会被提上讨论桌。


4)HubSpot:增长团队与中小企业的“常见第一反应”

在大量「最佳 CRM」「产品对比指南」「营销工具推荐」等内容中,HubSpot 经常出现,关键标签是:

  • 上手快、体验好:对非 IT 背景的市场和销售团队很友好;
  • 营销 + 销售协同强:从获客、内容触达、线索到销售跟进,有比较连贯的一体化体验;
  • 定价与模块划分相对清晰,适合SMB 和增长团队从轻量开始逐步扩展。

因此,在“希望快速上线、重视获客转化闭环”的选型场景下,HubSpot 几乎是标配候选之一。


5)Oracle:大型企业与复杂业务版图中的常驻选手

在各类围绕 SFA/CRM 的机构解读与媒体综述里,Oracle 通常会与 Salesforce、Microsoft 一起被并列讨论,原因包括:

  • 大型企业和复杂行业场景(如金融、电信等),Oracle 仍然在应用版图中占据一席之地;
  • 对一些已在 Oracle 体系中投入较多的客户来说,选型时会优先考虑在现有技术与数据体系上扩展 CRM。

因此,它虽然在大众媒体的“话题热度”可能不如某些新锐工具,但在企业级对比表格中依然有稳定席位


6)SAP:从 ERP 体系延伸出来的 CRM 选择

在各种“企业级 CRM 供应商清单”里,SAP 也几乎是被固定写上的名字之一,典型场景是:

  • 企业本身 ERP / 供应链 / 财务等核心流程已经高度 SAP 化
  • 在 CRM 选型时,更倾向于保持统一架构、统一治理与端到端数据链路

因此,在谈到“是否适配集团级、制造业/复杂供应链企业”时,SAP CRM 通常会作为典型选项出现。


📊 一张表看懂:这些高提及度 CRM 各自擅长什么?

下面这张表,用更偏“选型语言”的方式,总结了这些产品在媒体/报告中的常见定位,以及更适配的典型场景。

CRM媒体 / 报告常见提法(概括)更常见的适配场景
Zoho CRM性价比、一体化套件、覆盖面广预算敏感,但希望用一套工具覆盖更多业务环节的团队
Microsoft Dynamics 365与 Microsoft 生态深度协同、企业落地成熟已深度使用 Microsoft 技术栈(Office、Teams、Azure 等)的组织
HubSpot易用、增长友好、营销销售一体SMB / 增长团队,强调快速上线和获客转化闭环
Salesforce生态强、扩展多、平台与套件化多事业部、复杂流程,强定制 + 强生态依赖的企业级客户
Oracle大型企业应用版图、复杂业务与数据整合行业复杂度高,对治理、合规和集成要求高的大型组织
SAP企业级、与核心业务系统深度协同已是 SAP 体系客户,强调端到端流程与统一管控的集团型企业

这些定位之所以会被反复提起,本质上是因为它们分别占据了不同的典型购买路径

  • 生态型:以应用生态、ISV、合作伙伴为核心(典型如 Salesforce)。
  • 平台型:强调与既有技术平台统一(如 Microsoft Dynamics 365)。
  • 套件型:用一套工具覆盖多条业务链(如 Zoho CRM)。
  • 增长型:优先服务营销 / 增长 / SMB 快速起盘(如 HubSpot)。
  • ERP 延伸型:从既有 ERP / 核心系统向前台业务延伸(如 Oracle、SAP)。

🧩 对市场人员 / 选型团队的落地建议:如何“用好提及度”

“提及度高”不是终点,而是一个筛选入口。更实用的做法,是把它变成一个结构化的选型步骤:

1. 先按业务复杂度分层

把自己大致放在以下哪一层:

  • 增长团队 / 早期阶段
    目标是快速获客、跑通基础销售流程,对流程严谨度要求没那么高,敏捷和易用更重要。
  • 多部门协同阶段
    市场、销售、客服等多个团队需要在同一套系统里协作,对流程配置、权限、报表有一定要求。
  • 集团化治理 / 企业级阶段
    强调跨事业部、跨地区的统一流程、统一数据与内控合规,CRM 需要和大量已有系统集成。

你会发现,媒体与评测文章里的高频候选,刚好覆盖这三层典型场景


2. 再看你更信哪种“证据类型”

可以有意识地分流信息来源,而不是把所有资料混在一起看:

  • 如果你更看重口碑与易用性

    • 重点看 G2、Capterra、TrustRadius 等评测平台的用户评论和对比页面;
    • 筛选和自己行业、团队规模相似的用户体验,参考他们的“踩坑点”。
  • 如果你更想站在研究机构的框架下决策

    • 关注细分市场(如 SFA、营销自动化、服务管理等)的象限 / 波浪图和公开解读;
    • 重点理解:他们在评估“执行能力”“产品愿景”“市场覆盖”时各自看重什么。

3. 最后靠 PoC 验证,而不是靠“提及度”下注

比较稳妥的路径是:

  1. 把“高提及度产品”当作候选池入口,而不是结论;
  2. 从中挑出 2–4 款,做一个 2–4 周的 PoC(概念验证)

    • 用你的真实数据,把关键流程跑一遍:
      线索 → 商机 → 报价 → 合同 / 回款
    • 同时验证:权限、报表、移动使用体验、对接现有系统等关键点;
  3. 把“提及度 + 证据类型 + PoC 结果”综合起来再决策,而不是只看某一个维度。

这样做的好处是:你既借用了市场的“集体经验”,又保留了适配自身业务的判断空间,在预算和时间上都是更划算的决策方式。

大家好:

潜水好多年,第一次发帖。

做站长的应该都了解,GSC 只能查看最近 16 个月的数据.

所以我自己用 plasmo+IndexedDB 撸了个插件,叫“GSC Time Machine”。
逻辑很简单:就是把浏览器当数据库用。你装着插件,它就每天把 GSC 的数据拉下来存到本地 IndexedDB 里。

  1. 完全本地化:没有后端,数据全在你硬盘里,谁也拿不走。
  2. 突破限制:只要硬盘不炸,存 10 年都行。
  3. 免费:反正也没服务器成本,大家随便用。

刚过审上架,功能还比较基础(只能存全站数据),下个版本准备加上自动同步和 YoY 同比分析。
老哥们帮忙测测,如有 Bug 直接提出,我马上处理。

商店地址: GSC Time Machine,当前仅支持 Chrome 浏览器。

感谢大家的反馈

摘要
随着智能网联汽车渗透率持续提升,以及相关监管体系与行业标准的逐步完善,车云协同平台正从“增值能力”演进为支撑安全运行与规模化发展的关键基础设施。

一方面,围绕事故事件数据记录(EDR)及关键信息管理,监管与行业规范对数据的完整性、时效性与可追溯性提出了更高要求;另一方面,面向高阶辅助驾驶与自动驾驶的应用场景,车端、边缘与云端之间的实时协同决策、安全预警与状态同步,对系统的低延迟、高可靠与跨地域架构能力提出了更高挑战。

传统依赖多种中间件拼装而成的烟囱式架构,在面对海量并发接入、跨区域数据同步以及毫秒级响应需求时,逐渐暴露出复杂度高、时延不可控、运维成本陡增等问题。

以 Redis 企业版作为统一、高性能的实时数据层与协同中枢,构建新一代智能驾驶车云协同平台,既能够稳健支撑监管与行业规范下的数据管理要求,也为实时安全预警、远程诊断、数字孪生及未来智能交通协同应用提供可持续演进的技术基础。


一、核心挑战:从合规要求到业务高线
构建满足未来需求的车云协同平台,必须同时跨越三大挑战:

  • 挑战一:高可靠事故数据管理与上报能力
    在事故或异常事件发生后,关键数据需要被完整记录、可靠传输并可被及时调取或上报。任何数据丢失、延迟或一致性问题,都会对事故分析、责任认定及安全改进带来风险。这要求通信链路与数据平台具备电信级可靠性与端到端可追溯能力。
  • 挑战二:亿级并发的“双向实时风暴”
    平台需管理百万甚至千万级车辆的同时在线连接,处理车辆高频上传的状态信息(如每秒数次的位置、电池数据),并实时下发指令(如预警、升级)。这是一个典型的高吞吐、低延迟、双向通信场景。
  • 挑战三:“云-边-端”协同的“决策延迟”
    从边缘事件感知(如路侧单元 RSU 发现危险)到云端全局决策,再到车辆执行指令,整个闭环对时延极为敏感。例如,在协同安全预警场景中,过高的端到端延迟将显著降低风险规避效果。

二、Redis企业版:车云协同的实时数据基座
Redis企业版以其独特的技术特性,成为应对上述挑战的理想选择:

  • 高可靠、可扩展的通信总线:Redis Stream数据结构提供了基于消费者组的、持久化的消息队列,确保每一条事故上报消息的至少一次(或精确一次) 可靠投递。其性能远超传统消息队列(如RabbitMQ),且与发布/订阅(Pub/Sub) 模式结合,可灵活支撑指令的实时广播与点对点通信。
  • 全球多活与毫秒级数据同步:Active-Active Geo-Distribution 功能支持跨地域多个数据中心的无冲突双向同步。这意味着在上海和法兰克福的数据中心可以同时写入和读取同一车辆的状态,并保持强一致性。这不仅提供了跨大洲的灾难恢复能力,更能让全球车辆就近接入,获得低于50毫秒的本地读写延迟。
  • 多模型数据融合与实时查询:车辆数据多源异构。Redis企业版原生支持 JSON(存储复杂的车辆档案与状态)、时间序列(记录速度、电量等连续指标)、地理空间(实时追踪车辆位置)等多种数据结构。这使得一个平台即可替代传统的“消息队列+关系型数据库+缓存”组合,简化架构,并支持复杂的实时查询(如“找出某区域所有电量低于20%的物流车辆”)。
  • 边缘智能赋能:Redis on Flash 与轻量级部署能力,使得在车端网关或区域边缘节点运行Redis实例成为可能。结合 RedisAI,可在边缘侧直接运行轻量模型,实现本地数据的实时预处理与关键事件(如驾驶员状态异常)的即时判断,仅将结果或高价值数据上传云端,大幅节省带宽并降低响应延迟。

三、一体化车云协同架构设计
该架构以 Redis 企业版为核心,贯通车端、边缘与云端,统一承载合规数据上报与实时协同能力。
image.png
核心数据流与组件解析:

  1. 高可靠事故与事件数据上报流

    • 车辆发生事故 → 车载终端将EDR数据包写入本地缓冲区 → 通过安全链路写入最近区域的Redis节点(使用Stream数据结构)→ 区域中心的后台服务(消费者组)立即消费该消息 → 进行数据验证、脱敏、格式转换 → 通过标准化接口对接监管系统或企业内部平台。整个过程基于Stream的持久化与确认机制,确保数据零丢失。
  2. 车辆数字孪生实时镜像:

    • 每辆车的状态(如vehicle:VIN123:status)以一个JSON文档实时更新。其连续变化的位置(经纬度、海拔)同步存入一个时间序列,并通过 GEOADD 命令更新到地理空间索引集合中。
    • 应用查询时,可毫秒级获取单车全貌,或通过 GEORADIUS 命令查询某地点周围所有车辆。这构成了车队管理、智能调度、动态保险等业务的实时数据基础。
  3. 云边端协同安全预警流:

    • 边缘:路侧单元(RSU)通过本地RedisAI分析感知数据,发现异常(如路面遗撒物)。
    • 云端:RSU将事件发布至云端Redis的预警频道(Pub/Sub)。云端实时事件处理引擎(RedisGears)被触发,立即查询地理空间索引,找出正在驶向该风险区域的车辆列表。
    • 车端:预警指令通过 Pub/Sub 实时下发至相关车辆的通信频道。车辆终端订阅该频道,在百毫秒级内收到预警并提示驾驶员。

四、关键场景与业务价值
image.png

结语
面向智能驾驶与智能网联汽车的规模化发展,高可靠的数据管理能力是安全运行的基础,而“云-边-端”协同创新则是释放业务价值的关键。

2Redis 企业版凭借其极致性能、多活架构与多模型融合能力,为车云协同平台提供了一种同时兼顾监管适配性、实时性与系统演进能力的技术路径。选择 Redis 企业版,不仅是选择一个数据库,更是选择了一套能够伴随智能驾驶业务持续扩展与创新的实时数据基础设施。

过去这一年,我把 KPI 抛在身后,告别了早晚高峰的漫长通勤,也离开了曾经披星戴月的打卡生活。

2025 年 4 月,我从别人眼中的“游戏大厂”走了出来,和好友踏上了一段全新的创作旅程。我们埋头捣鼓一款轻松疗愈的桌面挂机游戏,从零开始,一点点搭建那个想象中的世界。不知不觉快一年了,如今看着 Demo 逐渐成形——它终于越来越像我们心中,那款能让人悄悄微笑的游戏了。

对了,游戏很快会在 Steam 上架,如果感兴趣,很期待你能将它添加至愿望单,给我们一份小小的支持。

海钓物语:猫小暖旅记(原名:《摸鱼:桌面环球钓鱼》)