🔎 搜索体验优化:ABP vNext 的查询改写(Query Rewrite)与同义词治理


📚 目录

  • 🔎 搜索体验优化:ABP vNext 的查询改写(Query Rewrite)与同义词治理
    • 1. 背景与问题界定 🧩
    • 2. 总体架构(ABP 模块化 + 多租户 + 分层) 🏗️
      • 🗺️ 架构总览(Mermaid)
    • 3. 数据模型(PostgreSQL,含索引) 🧬
      • 🔗 租户关联(Mermaid ER)
    • 4. 改写管线(确定性顺序 + 可扩展) 🛠️
      • 🔄 管线运行示意
    • 5. 拼写纠错(SymSpell + BK-Tree) ✍️
    • 6. 同义词/别名治理(方向性 + 热更新) 🧠
    • 7. 查询计划 → Elasticsearch DSL(去 `_all`,更稳的 `bool`/`dis_max`) 🧪
    • 8. 点击反馈重排(轻量、弱监督、可回退) 🎯
      • 🔀 RRF 融合示意
    • 9. 多租户与缓存治理(ABP 最佳实践) 🧰
    • 10. 评测与观测 📊
    • 11. 一键 Compose 🧪
    • 12. 关键代码片段 🧩
    • 13. 同义词热更新流程(SOP) 🛡️
    • 15. 常见坑与规避 ⚠️


1. 背景与问题界定 🧩

痛点:零结果、弱相关、错拼(键邻/近音/形近)、品牌/部门别名不一致(“华为/HUAWEI/华爲”)、多叫法(“摄像头/相机/camera”)。
目标(示例):ZRR 下降 ≥ 30%(📉)、首条点击率 +10%(📈)、二次搜索率 -15%(📉)(以 AB 实验为准)。
约束:多租户隔离、可灰度、可回滚、可观测、可复现。


2. 总体架构(ABP 模块化 + 多租户 + 分层) 🏗️

  • 模块Search.QueryRewriteModuleDomain/Application/HttpApi/Infrastructure)。

  • 多租户ICurrentTenant 贯穿数据、缓存、指标;缓存键必须包含 TenantId;后台跨租户操作用 ICurrentTenant.Change(...)

  • 存储分层

    • 检索面:Elasticsearch(中文分词 + 同义词 + DSL)
    • 规则面:PostgreSQL(词典/规则/行为聚合)
    • 热数据:Redis(24h CTR / Dwell Top-K;ABP 官方 Redis 模块支持批量 SetManyAsync/GetManyAsync
  • 流程:改写(检索前)→ ES 查询 → 轻量行为重排(可选 RRF 融合多路召回)。

🗺️ 架构总览(Mermaid)

🛡️ 治理后台
ABP 应用层
词典/规则
在线行为
🧭 Rewrite Admin UI
🛠️ QueryRewritePipeline
Normalize→Spell→Synonym→Alias→Rule→Safety
🌐 HttpApi: /rewrite
🔎 ES Query
multi_match + tie_breaker
Elasticsearch
🎯 轻量重排
(CTR/Dwell)
🧑‍💻 用户/前端
✅ 最终结果
🗄️ PostgreSQL
⚡ Redis

3. 数据模型(PostgreSQL,含索引) 🧬

关键修正:spell_lexicon 主键改为 (tenant_id, term),避免多租户冲突;为热路径增加索引。

create table synonym_set (tenant_id uuid not null,group_id  uuid not null,terms     text[] not null,direction smallint not null, -- 0: 双向, 1: 单向 from→toboost     double precision not null default 1.0,version   int not null default 1,primary key (tenant_id, group_id)
);create table alias (tenant_id uuid not null,entity_type text not null,    -- brand/department/skukey text not null,aliases text[] not null,boost double precision not null default 0.8,primary key (tenant_id, entity_type, key)
);create table spell_lexicon (tenant_id uuid not null,term text not null,freq bigint not null default 0,flags int not null default 0,primary key (tenant_id, term)
);create table rewrite_rule (tenant_id uuid not null,id uuid not null,cond_json jsonb not null,     -- {"category":"phone"}action_json jsonb not null,   -- {"extend":["phone case","保护壳"]}weight double precision not null default 1.0,active_from timestamptz,active_to   timestamptz,primary key (tenant_id, id)
);create table click_log (tenant_id uuid not null,query text not null,doc_id text not null,clicked boolean not null,dwell_ms int,ts timestamptz not null default now()
);-- 索引建议
create index idx_syn_terms on synonym_set using gin (terms);
create index idx_alias_key on alias(tenant_id, entity_type, key);
create index idx_spell_term on spell_lexicon(tenant_id, term);
create index idx_click_q_ts on click_log(tenant_id, query, ts desc);

🔗 租户关联(Mermaid ER)

TENANTSYNONYM_SETALIASSPELL_LEXICONREWRITE_RULECLICK_LOGownsownsownsownsowns

4. 改写管线(确定性顺序 + 可扩展) 🛠️

不依赖 DI 注册顺序。为每个 Step 增加 Order,在管线里显式排序;或使用 .NET 8 Keyed Services 绑定“管线位点”。

public sealed record RewriteContext(string TenantId, string RawQuery, string? Category);
public sealed record Token(string Term, double Boost = 1.0, string Source = "orig");
public sealed record RewritePlan(string Original,IReadOnlyList<Token> Must, IReadOnlyList<Token> Should, IReadOnlyList<Token> Filters);public interface IRewriteStep {int Order { get; }Task InvokeAsync(RewriteContext ctx, RewritePlanBuilder plan, CancellationToken ct);
}public sealed class QueryRewritePipeline {private readonly IReadOnlyList<IRewriteStep> _steps;public QueryRewritePipeline(IEnumerable<IRewriteStep> steps)=> _steps = steps.OrderBy(s => s.Order).ToArray();public async Task<RewritePlan> ExecuteAsync(RewriteContext ctx, CancellationToken ct) {var b = new RewritePlanBuilder(ctx.RawQuery);foreach (var s in _steps) await s.InvokeAsync(ctx, b, ct);return b.Build();}
}

建议顺序

  1. NormalizeStep(10):全/半角、大小写、标点统一、停用词(按语种/租户)
  2. SpellCorrectStep(20):SymSpell(≤2 编辑距)+ 键邻错(可加拼音/形近)
  3. SynonymStep(30):双向同义/单向归一;标准词高权
  4. AliasStep(40):品牌/部门/SKU 标准化(单向)
  5. BusinessRuleStep(50):类目/意图触发扩展/过滤
  6. SafetyStep(60):黑白名单/敏感词过滤

🔄 管线运行示意

Raw Query
Normalize
Spell Correct
Synonym Expand
Alias Map
Business Rules
Safety
Rewrite Plan
Must / Should / Filters / Boost
ES DSL
multi_match / dis_max

5. 拼写纠错(SymSpell + BK-Tree) ✍️

  • 首选 SymSpell:对称删除,低延迟;支持复合词纠错(空格插/漏)。
  • 备选 BK-Tree + Levenshtein:适合小中词表 / 租户私有词。
  • 候选排序score = α * freqPrior + β * editSim + γ * clickPrior
  • 中文增强:拼音近音 / 形近字特征作为附加分。
public sealed class SpellCorrectStep : IRewriteStep
{public int Order => 20;private readonly SymSpell _sym;public SpellCorrectStep(SymSpell sym) => _sym = sym;public Task InvokeAsync(RewriteContext ctx, RewritePlanBuilder plan, CancellationToken ct){foreach (var term in plan.CurrentTerms()){var sug = _sym.Lookup(term, Verbosity.Top, maxEditDistance: 2);foreach (var s in sug.Take(3))plan.AddShould(new Token(s.Term, 0.75, "spell"));}return Task.CompletedTask;}
}

6. 同义词/别名治理(方向性 + 热更新) 🧠

  • 方向性:双向同义(手机移动电话);单向归一(华为手机HUAWEI)。
  • 权重:标准 1.0;别名/口语 0.6–0.8;冷门 0.3–0.5。
  • 冲突检测:同词多组 / 循环引用 / 相互否定。
  • ES 配置synonym_graph(多词同义)或 synonym仅用于 search analyzer 的过滤器可设 "updateable": true;ES 8.11+ 可用 Synonyms API

可运维的 search analyzer 示例

{"settings": {"analysis": {"filter": {"zh_syn": {"type": "synonym_graph","synonyms_path": "analysis/synonyms.txt","updateable": true}},"analyzer": {"zh_search": {"tokenizer": "standard","filter": [ "lowercase", "zh_syn" ]}}}},"mappings": {"properties": {"title":   { "type": "text", "analyzer": "ik_smart", "search_analyzer": "zh_search" },"content": { "type": "text", "analyzer": "ik_smart", "search_analyzer": "zh_search" }}}
}

热更新 SOP(文件法)

  1. 更新 analysis/synonyms.txt
  2. 分发到所有数据节点相同路径;
  3. POST /{index}/_reload_search_analyzers
  4. 清理 request cache(如启用);

过滤器需仅用于 search_analyzer,且 updateable:true

Synonyms API(8.11+)

  • 维护“同义词集”,索引引用;发布时 API 生效,便于审计/回滚。

中文分词插件

  • 官方 smartcn;社区 IKstconvert:需按 ES 版本安装,自定义镜像并做回归;注意许可证与兼容性。

7. 查询计划 → Elasticsearch DSL(去 _all,更稳的 bool/dis_max) 🧪

_all 自 ES 6 移除;推荐 multi_match 多字段检索(carets 权重,如 "title^2"),或 mapping 用 copy_to
中文较多、跨字段词项合并时可选 CrossFields;英文/短词可用 BestFields

.NET(Elastic.Clients.Elasticsearch v8)示例:原词(Must) + 改写(Should + MinimumShouldMatch + dis_max)

同时传入 CancellationToken;当改写项较多时,设置 .MinimumShouldMatch(1) 可降噪。

var resp = await _es.SearchAsync<MyDoc>(s => s.Index("docs").Size(20).TrackTotalHits(true).Query(q => q.Bool(b => b.Must(m => m.MultiMatch(mm => mm.Query(ctx.RawQuery).Fields(new[] { "title^2", "content" }).Type(TextQueryType.BestFields)   // 或 CrossFields 视语种/策略.TieBreaker(0.3))).Should(plan.Should.Select(t =>(Func<QueryDescriptor<MyDoc>, IQuery>)(sd => sd.DisMax(dx => dx.Queries(dq => dq.MultiMatch(mm => mm.Query(t.Term).Fields(new[] { "title^2", "content" }).Boost((float)t.Boost).Type(TextQueryType.BestFields)))))).ToArray()).MinimumShouldMatch(1) // 关键:至少命中一个改写项)), ct);

8. 点击反馈重排(轻量、弱监督、可回退) 🎯

信号:CTR、Dwell(停留)、跳出、二次搜索
原则:行为信号视为弱监督;设上限冷启回退;阈值/权重可配置。
键空间降基数:查询做归一化+哈希存储(例如 SHA-256 前 12 字符),降低高基数键风险。

private static string NormalizeQuery(string q)=> q.Trim().ToLowerInvariant(); // 可叠加全/半角、标点等
private static string Hash12(string s)
{using var sha = System.Security.Cryptography.SHA256.Create();var b = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(s));return BitConverter.ToString(b).Replace("-", "").Substring(0, 12).ToLowerInvariant();
}
private static string BehaviorKey(string tenantId, string q)=> $"behavior:{tenantId}:{Hash12(NormalizeQuery(q))}";async Task<double> BehaviorFactorAsync(string tenantId, string docId, string query, IConnectionMultiplexer mux,double lambda = 0.2, double maxBoost = 0.5, ILogger? logger = null)
{try{var db = mux.GetDatabase();var key = BehaviorKey(tenantId, query);var score = await db.SortedSetScoreAsync(key, docId).ConfigureAwait(false);return score is double s ? 1.0 + lambda * Math.Min(s, maxBoost) : 1.0; // 冷启回退}catch (Exception ex){logger?.LogWarning(ex, "Redis 行为分读取失败, 使用回退");return 1.0; // 异常回退}
}// 并发收集 + 应用
var factors = await Task.WhenAll(results.Select(async r =>(r.DocId, Factor: await BehaviorFactorAsync(ctx.TenantId, r.DocId, ctx.RawQuery, mux, logger:_logger))));
var factorMap = factors.ToDictionary(x => x.DocId, x => x.Factor);
foreach (var r in results) r.FinalScore = r.EsScore * factorMap[r.DocId];
results = results.OrderByDescending(x => x.FinalScore).ToList();

🔀 RRF 融合示意

向量召回
同义扩展
原词检索
docC rank1
docD rank2
docA rank3
docB rank1
docA rank2
docD rank3
docA rank1
docB rank2
docC rank3
RRF Fuser
融合后排序
(docA/docB/docC/docD)

9. 多租户与缓存治理(ABP 最佳实践) 🧰

[DependsOn(typeof(AbpAspNetCoreMvcModule),typeof(AbpDddApplicationModule),typeof(AbpCachingStackExchangeRedisModule) // 官方 Redis 模块
)]
public sealed class SearchQueryRewriteModule : AbpModule { /* ... */ }
  • 多租户上下文:统一用 ICurrentTenant;日志与 UI 默认不外泄 TenantId(除审计场景)。
  • 缓存键规范{env}:{tenantId}:{module}:{category}:{key};配置热更新需按租户清理键空间。
  • 批量缓存:优先 IDistributedCache<T>.SetManyAsync/GetManyAsync,减少 RTT。

10. 评测与观测 📊

  • 离线:ZRR、Recall@k、NDCG@k(小标注集)
  • 在线:首条点击率、二次搜索率、平均停留、改写命中率
  • 可视化:改写命中、词典版本/灰度进度、规则冲突、重排提升分布
  • 验收(示例目标,7 天):ZRR ≥ -30%;首条点击率 ≥ +10%;二次搜索率 ≥ -15%(租户/类目分桶 + 显著性检验)

📌 指标为示例目标,以实际 AB 实验结论为准。


11. 一键 Compose 🧪

services:es:image: docker.elastic.co/elasticsearch/elasticsearch:8.13.4environment:- discovery.type=single-node- xpack.security.enabled=false # ⚠️ 仅限本地演示!ports: ["9200:9200"]redis:image: redis:7ports: ["6379:6379"]postgres:image: postgres:16environment: ["POSTGRES_PASSWORD=pass"]ports: ["5432:5432"]rewrite:build: ./QueryRewriteModuleenvironment:- ConnectionStrings__Default=Host=postgres;Database=qrw;Username=postgres;Password=pass- Redis__Configuration=redis:6379- Elastic__Url=http://es:9200depends_on: [ es, redis, postgres ]ports: ["8080:8080"]admin:build: ./RewriteAdminUIports: ["5173:80"]

🔐 生产安全基线:开启 xpack.security、设置内建用户密码、TLS、最小权限账号(只读/写入分离),并配置监控与告警。
🧩 中文分词插件:smartcn/IK/stconvert 与 ES 版本严格匹配;建议自定义镜像并做 CI 回归。


12. 关键代码片段 🧩

Normalize(含全/半角)

public sealed class NormalizeStep : IRewriteStep
{public int Order => 10;public Task InvokeAsync(RewriteContext ctx, RewritePlanBuilder plan, CancellationToken ct){var q = plan.Original.Trim().Normalize(NormalizationForm.FormKC);q = ToHalfWidth(q).ToLowerInvariant();plan.ReplaceCurrent(q);return Task.CompletedTask;}private static string ToHalfWidth(string s) =>string.Concat(s.Select(c => c == '\u3000' ? ' ' :(c >= 0xFF01 && c <= 0xFF5E) ? (char)(c - 0xFEE0) : c));
}

同义词 Step(节选)

public sealed class SynonymStep : IRewriteStep {public int Order => 30;private readonly ISynonymStore _store;public SynonymStep(ISynonymStore store) => _store = store;public async Task InvokeAsync(RewriteContext ctx, RewritePlanBuilder plan, CancellationToken ct) {foreach (var term in plan.CurrentTerms()) {var syns = await _store.LookupAsync(ctx.TenantId, term, ct);foreach (var s in syns) plan.AddShould(new Token(s.Term, s.Boost, "syn"));}}
}

Elasticsearch 查询(Multi-match + dis_max + MinimumShouldMatch + CT)

var resp = await _es.SearchAsync<MyDoc>(s => s.Index("docs").Size(20).TrackTotalHits(true).Query(q => q.Bool(b => b.Must(m => m.MultiMatch(mm => mm.Query(ctx.RawQuery).Fields(new[] { "title^2", "content" }).Type(TextQueryType.BestFields) // 或 CrossFields.TieBreaker(0.3))).Should(plan.Should.Select(t =>(Func<QueryDescriptor<MyDoc>, IQuery>)(sd => sd.DisMax(dx => dx.Queries(dq => dq.MultiMatch(mm => mm.Query(t.Term).Fields(new[] { "title^2", "content" }).Boost((float)t.Boost).Type(TextQueryType.BestFields)))))).ToArray()).MinimumShouldMatch(1))), ct);

在线行为重排(并发收集 + 键降基数 + 异常回退)

// 见第 8 节完整实现

13. 同义词热更新流程(SOP) 🛡️

达标
不达标
⚙️ 前置条件
仅 search_analyzer + updateable:true
✏️ 编辑 synonyms.txt
🚚 分发到所有数据节点
🔄 POST /{index}/_reload_search_analyzers
🧹 清理 request cache (可选)
✅ 样例回归测试
📣 灰度/全量上线
↩️ 回滚上一版本

15. 常见坑与规避 ⚠️

  • 方向不当 → 召回污染:型号/品牌优先单向归一
  • 改写过度 → 泛召回:控制 Boost 与条件、必要时转 Should 而非 Must
  • 多租户串线 → 缓存键含租户;跨租户需显式上下文切换
  • 行为偏差 → CTR/Dwell 设上限,冷启回退;日志抽样
  • 中文分词插件 → 与 ES 版本严格匹配,容器镜像固定版本并做 CI
  • 生产安全 → 开启认证/TLS、最小权限、监控与告警

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94214.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94214.shtml
英文地址,请注明出处:http://en.pswp.cn/web/94214.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Text2API与Text2SQL深度对比:自然语言驱动的数据交互革命

在数字化浪潮中&#xff0c;如何让人机交互更加自然流畅&#xff1f;Text2API与Text2SQL技术应运而生&#xff0c;它们如同魔法般将自然语言转化为机器可执行的指令&#xff0c;让数据交互不再高不可攀。本文将深入剖析这两项技术的原理、优劣势及应用场景&#xff0c;带您领略…

数据可视化与分析平台设计与实现案例

数据可视化与分析平台设计与实现案例(python) 下面分享一个完整的 Flask 数据可视化与分析平台代码,包含所有必要的组件和功能。这个平台允许用户上传数据文件、进行基本的数据清洗、生成各种可视化图表以及查看基础统计分析结果。 产品设计 核心功能 数据上传与管理(支…

Kotlin-基础语法练习二

接上一篇博客 每个 Kotlin 程序都是由两种部分组成的&#xff1a; 1、表达式&#xff08;Expressions&#xff09;&#xff1a;用于计算值的部分&#xff0c;比如 2 3、函数调用、变量赋值等&#xff0c;它们通常会返回一个结果。2、语句&#xff08;Statements&#xff09;…

与Deepseek对话了解单片机基础知识

keil5里的c语言编程的程序烧录到单片机里具体过程是啥&#xff1f;如何能把机器语言转换为电路控制&#xff1f; 步骤 所在位置 核心工具 输入->输出 比喻 1. 编译 Keil5 (PC) 编译…

利用背景图片定位套打档案封面

某些表单设计起来比较复杂&#xff0c;或只有表单的空白图片资料。Nhdeep档案目录套打工具&#xff08;nhdeep官网www.nhdeep.com&#xff09;支持将已有的表单图片作为模版背景图片&#xff0c;然后使用文本框进行精准的位置定位&#xff0c;再进行文本替换。 背景图片定位套…

微信HOOK 实现自动下载视频

1、前言 在收发消息的接口中&#xff0c;图片和文件这类接口是相对容易自动下载&#xff0c;但是视频的下载是需要手动点击的&#xff0c;并且只有这一种下载方式&#xff0c;实现自动化也比较困难&#xff0c;一些项目的开发中&#xff0c;需要自动下载收到的视频并保存&#…

【GPT入门】第57课 详解 LLamaFactory 与 XTuner 实现大模型多卡分布式训练的方案与实践

【GPT入门】第57课 大模型多卡计算1. 理论2.LLamaFacotory实践3. xtuner3.1 介绍3.1 安装3.2 xtuner训练3.4 训练后格式转换3.5 合并基础模型与lora模型3.6 参数说明3.7 训练过程主观检验1. 理论 deepspeed的三种训练方式 zero-1&#xff0c;优化器状态分片。的优势体现在多卡…

部队多媒体信息发布系统:赋能 IPTV 与电教化,加速军营信息化变革

在科技飞速发展的当下&#xff0c;部队的信息化建设也在不断推进。多媒体信息发布系统作为一种创新的技术手段&#xff0c;正逐步融入部队的各个领域&#xff0c;为部队的现代化建设注入强大动力。​在部队 IPTV 方面&#xff0c;多媒体信息发布系统展现出卓越的性能。它打破了…

FTP/TCP上传下载文件

封装C风格地ftplib为ftp.c和ftp.h文件&#xff1a;cftplient类&#xff08;主要成员变量&#xff1a;文件大小、文件修改时间、主要成员函数&#xff1a;get函数&#xff08;远程文件名、本地文件名、核对文件时间&#xff09;、put函数&#xff08;本地文件名、服务端文件名、…

DeepSeek V3.1深度解析:一个模型两种思维,迈向Agent时代的第一步!

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录一、什么是DeepSeek V3.1&#xff1f;为什么这么火&#x1f680;1. 发布时间线回顾2.…

VsCode 便携版(绿色版)下载及配置

下载 VsCode 便携版&#xff0c;并确保所有配置和扩展都保存在一起&#xff0c;实现真正的“绿色版”效果 核心步骤概览 核心原理是在 VSCode 的主程序目录下创建一个名为 data 的文件夹&#xff0c;VSCode 启动时如果检测到这个文件夹&#xff0c;就会自动切换到便携模式&am…

使用VLLM部署大模型embedding/chat 的API

模型下载&#xff1a;一般通过modelscope提供的方式进行下载&#xff0c;速度更快&#xff0c;huggingface下模型即便开启了魔法也还是很慢&#xff0c;对于9B以上的模型都是至少15G的。 比如需要下载qwen3-embedding-8b的模型&#xff0c;可以通过提供的一段代码自动进行下载到…

Blender模型动画导入到UE5

UE5支持直接导入FBX文件&#xff0c;但在实际应用中笔者发现&#xff1a;刚开始使用的是UE5.3&#xff0c;在UE5.3中直接将.fbx文件拖入UE中导入后是一个个的零件&#xff0c;后来使用了datasmith插件等其他办法&#xff0c;怎么都没有达到想要的效果。后面升级UE5.4以后&#…

Promise详解:Promise解决ajax回调嵌套问题

目录 一、Promise是什么 二、回调地狱 三、Promise解决回调地狱的原理 四、promaise实例 一、Promise是什么 1、主要用于异步计算 2、可以将异步操作队列化&#xff0c;按照期望的顺序执行&#xff0c;返回符合预期的结果 4、可以在对象之间传递和操作promise&#xff0c…

【Kubernetes知识点】Pod调度和ConfigMaps

目录 1.如何将特定Pod调度到指定的节点&#xff1f; 2.什么是节点的亲和性&#xff1f; 3.什么是污点&#xff0c;它的主要用途是什么&#xff1f; 4.解释ConfigMap的作用。 5.Secret和ConfigMap相比较有哪些优点。 6.解释ResourceQuota的作用 1.如何将特定Pod调度到指定…

火车头使用Post方法采集Ajax页面教程

前面有写过一篇瀑布流的采集方法&#xff0c;今天在添加一个POST方法来采集Ajax刷新页面的教程。 之前的文章请看&#xff1a;火车头采集动态加载Ajax数据&#xff08;无分页瀑布流网站&#xff09; 如果遇到POST方法来架子Ajax数据&#xff0c;这和我之前写的是两个类型&…

【学习记录】structuredClone,URLSearchParams,groupBy

structuredClone() 可以进行深拷贝&#xff0c;这里有详细讲解&#xff1a;Window&#xff1a;structuredClone() 方法 当需要处理包含嵌套对象或数组的复杂数据结构时&#xff0c;建议使用 structuredClone() 来保护原始数据。 举例&#xff1a;别再用 … 扩展运算符了&#x…

30条AI编程指令

大家好&#xff0c;小机又来分享AI了。 前言&#xff1a; 凌晨三点&#xff0c;你还在像素级对齐那个永远对不齐的按钮&#xff1b;刚写完的API文档&#xff0c;产品经理一句"需求变了" 让你瞬间崩溃&#xff1b;更扎心的是&#xff0c;实习生用AI十分钟搞定了你要…

AI+虚拟仿真:以科技之光照亮希望的田野

在乡村振兴与农业现代化的全新征程中&#xff0c;农林专业人才肩负着科技赋能土地、守护绿色发展的重任。然而&#xff0c;现有的教育模式却越发不适应农业人才的培养需求。“AI虚拟仿真”正在为农业现代化人才建设提供创新的技术引擎。市场风口与政策红据统计&#xff0c;2024…