news 2026/9/30 9:55:59

UE5狂暴敌人AI完整实战:状态机+行为树

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UE5狂暴敌人AI完整实战:状态机+行为树

最近在做 UE5 战斗 AI 时,“狂暴敌人”这个需求让我折腾了好一阵子。表面上看只是几个状态来回切换,但真正把行为树搭起来之后才发现,状态切不过去、分支中断、黑板数据不同步的问题一个接一个。后来我把“战斗状态机”和“行为树”两者的职责边界重新梳理了一遍,整个战斗 AI 才稳定下来。这篇文章就是一次完整复盘,从底层概念到实战构建都会讲到,包含可直接参考的 C++ 类示例和蓝图行为树搭建思路,适合正在做 UE5 战斗系统的开发者,也适合刚接触行为树的同学。

1. 背景与核心概念

1.1 什么是“狂暴敌人”AI

“狂暴敌人”是战斗类游戏中很常见的敌人设计,通常表现为:当生命值低于某个阈值,或者受到一定次数攻击后,敌人会进入一种更激进的状态,移动速度上升、攻击频率变快、伤害变高,甚至解锁新的技能。

从 AI 实现的角度看,它不是一个单纯的行为树分支,而是整个 AI 行为框架需要支持“运行时状态变化”。也就是说,AI 不能只靠一两个条件判断写死,它需要一套机制来感知自身状态、环境状态,并在不同状态下切换不同的行为策略。

用 UE5 来实现,一般有三种思路:

  • 纯蓝图状态机:用枚举变量 + 分支判断实现。
  • 纯行为树:把状态用黑板键表示,行为树里大量使用装饰器做条件分流。
  • 状态机 + 行为树:状态机负责状态切换,行为树负责当前状态下的行为执行。

第三种是项目实战中性价比最高的方案,也是本文重点。

1.2 行为树是什么

行为树是一种树状结构的决策系统,阅读和执行顺序很直观。树的根节点一般是Root,往下挂Selector、Sequence这样的组合节点,最终落到Task节点上。

行为树的执行逻辑可以简单理解为:

  • Selector:从上往下找第一个能执行的分支。
  • Sequence:从上往下顺序执行所有子节点,任意一个失败则整条序列失败。
  • Task:真正执行动作,例如移动、攻击、等待。
  • Decorator:装饰器,附加在节点上,控制该分支是否允许执行。
  • Service:服务,在节点执行期间不断更新外部数据,例如刷新玩家位置。

行为树在 UE5 中的最大优点是“可视化、可配置”,美术和策划也能看懂一部分逻辑,方便调参。

1.3 状态机是什么

状态机,全称有限状态机(FSM),核心思想是“在任一时刻,对象只能处于有限个状态中的一个,并且只能从当前状态切换到允许的相邻状态”。

战斗 AI 中常见的状态有:

  • 待机 Idle
  • 巡逻 Patrol
  • 追踪 Chase
  • 攻击 Attack
  • 狂暴 Berserk

状态机擅长处理“状态切换条件”,但一旦状态多起来,嵌套的 if-else 会非常难维护。所以需要行为树来承接“每个状态下具体怎么做”的执行逻辑。

1.4 为什么要状态机 + 行为树结合

状态机解决“何时切换状态”的问题,行为树解决“当前状态下做什么”的问题。

如果只用状态机,行为逻辑全写在状态内部,状态一多,代码很容易膨胀,而且每个状态内部的分支条件会让调试变得困难。如果只用行为树,虽然也可以用黑板键模拟状态,但状态切换的边界不够清晰,尤其是遇到“狂暴不可逆”“受伤后强制追击”这类强约束条件时,行为树分支会变得臃肿。

把两者结合后,职责非常明确:

  • EnemyCharacter 维护当前状态,并处理状态切换逻辑。
  • AI Controller 负责运行行为树。
  • 行为树根据黑板上的状态值,决定执行哪条分支。
  • 狂暴状态通过修改黑板键和数值属性,让行为树和动画系统同步感知。

2. 环境准备与版本说明

2.1 UE5 版本与模块准备

本文示例基于 UE5.1 之后的通用特性编写,UE5.0 到 UE5.4 的大部分 API 和编辑器操作基本一致,如果你的项目版本不同,注意路径和节点名称可能出现细微差异。

需要确保项目启用了以下模块:

  • AIModule:行为树、黑板、AI Controller 的基础模块。
  • NavigationSystem:AI 移动寻路必须的导航系统。
  • GameplayTasks:行为树任务节点的底层依赖,通常在 Build.cs 中一并加上。

打开项目的 Build.cs 文件,例如:

// Source/BerserkDemo/BerserkDemo.Build.cs public class BerserkDemo : ModuleRules { public BerserkDemo(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AIModule", "GameplayTasks", "NavigationSystem" }); } }

如果是蓝图项目,不需要手动改 Build.cs,但依然要在项目设置中保证默认的 AI 模块和导航网格相关功能可用。

2.2 编辑器准备

在 UE5 编辑器中,至少需要准备:

  • 一个第三人称模板或空白项目,方便放置玩家角色和敌人。
  • Content 目录下建好 AI 相关文件夹。
  • 场景中放置 NavMeshBounds 或 RecastNavMesh,并按P键显示导航网格范围。

建议目录结构如下:

Content/ ├── AI/ │ ├── Blackboards/ │ ├── BehaviorTrees/ │ ├── Controllers/ │ └── Tasks/ ├── Characters/ │ ├── BerserkEnemy/ ├── Level/ └── ...

3. 行为树核心机制拆解

3.1 Blackboard 黑板:AI 的公共记忆

黑板是行为树与外界通信的数据池。行为树节点可以从黑板读取数据,也可以向黑板写入数据。

强烈建议在这个阶段把黑板键类型定义清楚,不要混用:

黑板键类型作用
TargetActorObject保存玩家/目标的 Actor 引用
CurrentStateEnum当前 AI 状态,对应 EEnemyState
bIsBerserkBool是否处于狂暴状态
DistanceToTargetFloat与目标距离,供装饰器条件判断

在 C++ 中定义枚举时,建议使用UENUM(BlueprintType),这样蓝图和黑板编辑器都能直接使用该枚举类型。

3.2 行为树节点类型

行为树中的节点通常分为四类:

  • 组合节点 Composite
  • 任务节点 Task
  • 装饰器 Decorator
  • 服务节点 Service

在 UE5 编辑器中,右键行为树空白处即可创建节点。一个典型的狂暴敌人行为树结构如下:

Root └── Selector ├── [Decorator: bIsBerserk == True] Sequence │ ├── Service: UpdateTarget │ ├── Task: MoveTo TargetActor │ └── Task: BerserkAttack ├── [Decorator: DistanceToTarget < AttackRange] Sequence │ ├── Task: MoveTo TargetActor │ └── Task: NormalAttack ├── [Decorator: TargetActor != None] Task │ └── MoveTo TargetActor └── Task └── Wait / Patrol

3.3 Decorator 与状态切换的关键:Observer Aborts

行为树里最常见的坑是:状态已经变了,但行为树没有切换分支。

原因通常是 Decorator 没有设置好监听机制。UE5 的 Blackboard Decorator 需要在细节面板中配置Observer Aborts,可选值包括:

  • None:不主动中止,只能等当前分支自然结束。
  • Self:只中止自身节点。
  • Lower Priority:中止优先级更低的分支。
  • Both:同时中止自身和低优先级分支。

在狂暴敌人 AI 中,一般建议设置为Both,这样当bIsBerserk从 false 变为 true 时,行为树能立刻中止当前分支,重新进入狂暴分支。

4. 狂暴敌人整体战斗状态机设计

4.1 状态定义

先用枚举定义敌人状态:

UENUM(BlueprintType) enum class EEnemyState : uint8 { Idle UMETA(DisplayName = "Idle"), Chase UMETA(DisplayName = "Chase"), Attack UMETA(DisplayName = "Attack"), Berserk UMETA(DisplayName = "Berserk"), Dead UMETA(DisplayName = "Dead") };

不建议把 Patrol 放到状态枚举里,巡逻可以视为 Idle 状态下的一种行为,这样可以减少状态切换复杂度。

4.2 状态切换条件

狂暴敌人 AI 的状态切换条件可以整理成下表:

状态触发条件行为表现退出条件
IdleAI 出生后默认状态待机、缓慢巡逻感知到玩家或受伤
Chase发现玩家或丢失攻击距离向目标移动玩家死亡或拉到攻击距离
Attack距离小于攻击范围普通攻击循环玩家拉远距离
Berserk生命值比例低于阈值加速、高频率攻击、释放狂暴技能不可逆,直到死亡

狂暴状态一般是不可逆状态,也就是一旦生命值低于阈值,不会再恢复到普通状态。这个设计符合大多数战斗中“狂怒”的行为语义。

4.3 狂暴状态的参数变化

普通状态下:

  • 移动速度:420
  • 攻击间隔:2.0 秒
  • 单次攻击伤害:20

狂暴状态下:

  • 移动速度:600
  • 攻击间隔:1.0 秒
  • 单次攻击伤害:30
  • 身体缩放:1.15 倍左右,并触发特效材质变化

这里的数值只是示例,要根据你的游戏平衡去调整。但注意,狂暴参数最好不要写死在行为树节点中,而是通过配置数据表或者暴露给蓝图调整。

5. 完整实战:构建一个狂暴敌人 AI

5.1 创建项目结构

我以一个名为BerserkDemo的项目为例,实际操作时替换成你的项目模块名。

项目结构如下:

BerserkDemo/ ├── Source/ │ └── BerserkDemo/ │ ├── BerserkDemo.Build.cs │ ├── BerserkDemoGameMode.h │ ├── BerserkEnemy.h / .cpp │ ├── BerserkAIController.h / .cpp │ └── BTTask_BerserkAttack.h / .cpp └── Content/ ├── AI/ │ ├── Blackboards/ │ └── BehaviorTrees/ └── Characters/

5.2 创建敌人角色类

在 C++ 中,敌人角色类继承自ACharacter,需要包含状态枚举、血量、狂暴阈值、伤害回调等。

// Source/BerserkDemo/BerserkEnemy.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BerserkEnemy.generated.h" UENUM(BlueprintType) enum class EEnemyState : uint8 { Idle UMETA(DisplayName = "Idle"), Chase UMETA(DisplayName = "Chase"), Attack UMETA(DisplayName = "Attack"), Berserk UMETA(DisplayName = "Berserk"), Dead UMETA(DisplayName = "Dead") }; UCLASS() class BERSERKDEMO_API ABerserkEnemy : public ACharacter { GENERATED_BODY() public: ABerserkEnemy(); virtual float TakeDamage( float DamageAmount, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override; UPROPERTY(BlueprintReadOnly, Category = "AI|State") EEnemyState CurrentState; UPROPERTY(BlueprintReadOnly, Category = "AI|State") bool bIsBerserk; void PerformBerserkAttack(); protected: virtual void BeginPlay() override; UPROPERTY(EditDefaultsOnly, Category = "AI|Config") float MaxHealth; UPROPERTY(VisibleInstanceOnly, Category = "AI|Config") float CurrentHealth; UPROPERTY(EditDefaultsOnly, Category = "AI|Config") float BerserkHealthPercent; void CheckAndEnterBerserk(); };

对应的 CPP 文件:

// Source/BerserkDemo/BerserkEnemy.cpp #include "BerserkEnemy.h" #include "AIController.h" #include "BehaviorTree/BlackboardComponent.h" #include "GameFramework/CharacterMovementComponent.h" ABerserkEnemy::ABerserkEnemy() { MaxHealth = 100.f; CurrentHealth = MaxHealth; BerserkHealthPercent = 0.3f; bIsBerserk = false; CurrentState = EEnemyState::Idle; } void ABerserkEnemy::BeginPlay() { Super::BeginPlay(); CurrentHealth = MaxHealth; } float ABerserkEnemy::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) { float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser); CurrentHealth = FMath::Max(0.f, CurrentHealth - ActualDamage); CheckAndEnterBerserk(); return ActualDamage; } void ABerserkEnemy::CheckAndEnterBerserk() { if (bIsBerserk || CurrentHealth <= 0.f) { return; } if (CurrentHealth / MaxHealth <= BerserkHealthPercent) { bIsBerserk = true; CurrentState = EEnemyState::Berserk; UCharacterMovementComponent* MoveComp = GetCharacterMovement(); if (MoveComp) { MoveComp->MaxWalkSpeed = 600.f; } AController* Controller = GetController(); AAIController* AIController = Cast<AAIController>(Controller); if (AIController) { UBlackboardComponent* Blackboard = AIController->GetBlackboardComponent(); if (Blackboard) { Blackboard->SetValueAsEnum("CurrentState", (uint8)CurrentState); Blackboard->SetValueAsBool("bIsBerserk", true); } } } } void ABerserkEnemy::PerformBerserkAttack() { // 在这里接入你的攻击系统,例如播放动画、发射碰撞体、结算伤害。 // 项目中如果用 Gameplay Ability System,建议通过 GAS 触发。 UE_LOG(LogTemp, Log, TEXT("[BerserkEnemy] Perform Berserk Attack!")); }

说明:CheckAndEnterBerserk中的核心思路是“敌人自己判断狂暴条件,并同步更新黑板上键值”。这样行为树不用轮询血量,只要监听bIsBerserk变化即可。

5.3 创建 AI Controller 类

AI Controller 负责控制敌人 Pawn,并在 Possess 时运行行为树。

// Source/BerserkDemo/BerserkAIController.h #pragma once #include "CoreMinimal.h" #include "AIController.h" #include "BerserkAIController.generated.h" class UBehaviorTree; UCLASS() class BERSERKDEMO_API ABerserkAIController : public AAIController { GENERATED_BODY() public: ABerserkAIController(); protected: virtual void OnPossess(APawn* InPawn) override; UPROPERTY(EditDefaultsOnly, Category = "AI") TObjectPtr<UBehaviorTree> BehaviorTreeAsset; };
// Source/BerserkDemo/BerserkAIController.cpp #include "BerserkAIController.h" #include "BehaviorTree/BehaviorTree.h" void ABerserkAIController::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); if (BehaviorTreeAsset) { RunBehaviorTree(BehaviorTreeAsset); } }

5.4 配置黑板键

在 Content 中创建 Blackboard 资产,命名为BB_BerserkEnemy,然后添加以下键:

  • TargetActor:Object 类型
  • CurrentState:Enum 类型,枚举选择EEnemyState
  • bIsBerserk:Bool 类型

如果后续行为树需要根据距离切换攻击分支,建议再加一个 Float 键DistanceToTarget,由 Service 节点更新。

5.5 构建行为树

创建 Behavior Tree 资产,命名为BT_BerserkEnemy,并关联BB_BerserkEnemy。

行为树根节点下方挂一个Selector,根架构如下:

Root └── Selector ├── Berserk Branch ├── Attack Branch ├── Chase Branch └── Idle Branch

每个分支结构为:

  • Berserk Branch:
    • Decorator:Blackboard 判断bIsBerserk == true
    • Sequence:
      • Service:更新目标与距离
      • Task:MoveTo TargetActor
      • Task:自定义BTTask_BerserkAttack
  • Attack Branch:
    • Decorator:Blackboard 判断DistanceToTarget < AttackRange
    • Sequence:
      • Task:MoveTo TargetActor
      • Task:普通攻击
  • Chase Branch:
    • Decorator:Blackboard 判断TargetActor != None
    • Task:MoveTo TargetActor
  • Idle Branch:
    • Task:Wait,也可以挂巡逻任务

关键一步:Berserk Branch 的 Decorator 必须在细节面板中设置Observer Aborts = Both,否则bIsBerserk变化时行为树可能不会立刻切换。

5.6 自定义攻击任务节点

创建一个UBTTaskNode子类,命名为BTTask_BerserkAttack。

// Source/BerserkDemo/BTTask_BerserkAttack.h #pragma once #include "CoreMinimal.h" #include "BehaviorTree/BTTaskNode.h" #include "BTTask_BerserkAttack.generated.h" UCLASS() class BERSERKDEMO_API UBTTask_BerserkAttack : public UBTTaskNode { GENERATED_BODY() public: UBTTask_BerserkAttack(); protected: virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; };
// Source/BerserkDemo/BTTask_BerserkAttack.cpp #include "BTTask_BerserkAttack.h" #include "AIController.h" #include "BerserkEnemy.h" UBTTask_BerserkAttack::UBTTask_BerserkAttack() { NodeName = TEXT("Berserk Attack"); } EBTNodeResult::Type UBTTask_BerserkAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { AAIController* AIController = OwnerComp.GetAIOwner(); if (!AIController) { return EBTNodeResult::Failed; } ABerserkEnemy* Enemy = Cast<ABerserkEnemy>(AIController->GetPawn()); if (!Enemy) { return EBTNodeResult::Failed; } Enemy->PerformBerserkAttack(); return EBTNodeResult::Succeeded; }

5.7 运行验证

在编辑器中进行以下操作:

  1. 将敌人蓝图或 C++ 类的AIControllerClass设置为BerserkAIController。
  2. 将BerserkAIController的BehaviorTreeAsset设置为BT_BerserkEnemy。
  3. 在场景中放置NavMeshBoundsVolume,并按P键查看导航网格是否覆盖地面。
  4. 运行游戏,控制玩家角色靠近敌人。
  5. 用武器或控制台命令对敌人造成伤害,观察敌人状态是否切换。

验证要点:

  • 正常状态下敌人会追向玩家并攻击。
  • 当生命值降到 30% 以下时,移动速度明显提高,攻击动画频率变快。
  • 行为树调试窗口中,应从普通分支跳到 Berserk 分支。

6. 常见问题与排查思路

问题现象常见原因解决思路
敌人站在原地不动场景没有导航网格,或 NavMesh 没有覆盖到敌人位置检查P键下的绿色导航范围,调整 NavMeshBoundsVolume
行为树完全不执行AI Controller 未设置,或 OnPossess 中没有调用 RunBehaviorTree确认 Pawn 的 AIControllerClass 已配置,并检查 BehaviorTreeAsset 是否有效
黑板键报错设置值与黑板键类型不匹配检查SetValueAsEnum、SetValueAsBool与黑板中键的类型是否一致
状态切到狂暴后行为树没有反应Decorator 没有监听黑板键变化将对应 Decorator 的 Observer Aborts 设置为 Both
敌人追踪时走走停停目标类型不对,或 MoveTo 每次都在拿旧位置使用 Service 高频更新 TargetActor 黑板键,建议频率设置为 0.2 - 0.5 秒
狂暴后速度没有生效CharacterMovement 的 MaxWalkSpeed 被动画或 Ability 覆盖检查是否有其他系统在覆盖移动速度,并用日志打印实际速度
Task 返回 Failed 后整条分支卡死Sequence 中某个 Task 快速失败,又没有 Fallback 分支给高风险 Task 加 Decorator 条件,或对失败分类,区分可重试和不可重试的失败

7. 最佳实践与工程建议

7.1 职责边界不要混

状态机负责“状态切换”,行为树负责“状态内执行”。不要在行为树里写太多状态切换逻辑,也不要让 Character 类去直接驱动行为树节点。正确的数据流是:

受伤事件 -> Character 判断状态 -> 更新黑板 -> 行为树感知变化 -> 切换分支

7.2 使用 Service 更新高频数据

玩家位置、目标距离这类数据不适合在 Task 中一次性获取,因为 AI 需要持续更新。建议使用 Service 节点,在行为树分支执行期间不断刷新黑板键。

比如:

// 自定义 BTS_UpdateTarget 的 TickNode 示意 void UBTS_UpdateTarget::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) { Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds); AAIController* AIController = OwnerComp.GetAIOwner(); if (!AIController) { return; } APawn* TargetPawn = nullptr; // 通过 AI 感知组件或简单距离探测获取玩家 // 将目标写入 TargetActorKey UBlackboardComponent* Blackboard = OwnerComp.GetBlackboardComponent(); if (Blackboard) { Blackboard->SetValueAsObject(TargetKey.SelectedKeyName, TargetPawn); if (TargetPawn) { const float Distance = FVector::Distance(GetOwner()->GetActorLocation(), TargetPawn->GetActorLocation()); Blackboard->SetValueAsFloat(DistanceToTargetKey.SelectedKeyName, Distance); } } }

7.3 狂暴参数集中配置

不要把一个敌人的血量、速度、狂暴阈值散落到行为树节点中。建议使用DataTable、PrimaryDataAsset或UDeveloperSettings管理,方便批量调整和做变种敌人。

7.4 降低轮询开销

大量 AI 如果用 Service 每帧计算距离,性能会明显下降。实际项目中可以通过 AI 感知组件AIPerception来触发视野感知和伤害感知,状态切换由事件驱动,而不是每帧轮询。

7.5 调试建议

推荐使用 UE5 行为树调试功能,运行游戏后选中 AI Controller,可以查看当前行为树停留在哪个节点。

常用手段:

  • 在 Task 和 Service 中打印关键状态日志。
  • 在 Character 的TakeDamage中断言黑板上键值是否正确。
  • 用可视化辅助把当前CurrentState绘制在屏幕上。

8. 总结与下一步学习路线

这一段内容并没有太多需要往深扩展的理论,重点是你在动手前,先把状态机与行为树的边界想清楚。

本文核心要点可以浓缩成三点:

  • 状态机负责定义“狂暴不可逆”这类强状态规则。
  • 行为树负责当前状态下的攻击、追踪、待机行为。
  • 黑板是状态机和行为树之间的通信桥梁,Decorator 的 Observer Aborts 是状态切换能否立即生效的关键。

如果再往下学,建议按这个顺序进阶:

  • AI 感知:使用AIPerception组件替代手动轮询玩家位置。
  • EQS 环境查询:解决“狂暴敌人优先选择哪个目标”或“跳到哪个位置”等更复杂的决策问题。
  • GAS 技能系统:把攻击、伤害、减速等效果交给 Gameplay Ability 管理。
  • MassAI:如果你的游戏有大量 AI 单位,可以考虑 UE5 的 Mass 框架优化性能。

建议先做一个最简单的“巡逻敌人”,再逐步加入状态机和狂暴分支。等这套结构跑通之后,你会发现后续加入飞行敌人、远程敌人、Boss 技能都只是在这个骨架上不断填充分支节点而已。如果这篇文章对你有帮助,可以收藏备用,等真正开始调 AI 时再对照排查。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 9:54:55

SPSS Modeler实战指南:业务可解释建模与决策流水线落地

1. 这不是“SPSS Modeler”软件教程&#xff0c;而是一份十年实战者写给真实业务场景的建模手记你搜“SPSS Modeler”&#xff0c;跳出的大多是“下载破解版”“安装教程”“聚类分析步骤”——这些内容像说明书&#xff0c;能让你点开软件、跑通流程&#xff0c;但解决不了你坐…

作者头像 李华
网站建设 2026/9/30 9:54:51

半年账单狂翻十倍吓坏财务,美国大厂悄悄把脏活累活踢给平价模型

半年账单狂翻十倍吓坏财务&#xff0c;美国大厂悄悄把脏活累活踢给平价模型 想象一下&#xff0c;你开了一家生意红火的连锁餐厅&#xff0c;后厨原本请了一批身价极高的米其林大厨。起初你觉得贵有贵的道理&#xff0c;名厨出手&#xff0c;做出来的招牌大菜确实惊艳。但几个月…

作者头像 李华
网站建设 2026/9/30 9:54:29

多Agent编排与生产级落地:AgentScope架构设计与实践解析

一个多月前接了个内部知识库问答的项目&#xff0c;各种Agent框架翻了一圈&#xff0c;最后把AgentScope装进了生产环境。今天写这篇文章&#xff0c;就是想认认真真推荐一下这个系统——尤其如果你也在做多Agent编排&#xff0c;想让不同的LLM服务在同一个框架里稳定协作&…

作者头像 李华
网站建设 2026/9/30 9:53:40

数码产品越放越便宜的定律失效了,连停产旧手机都在偷偷加价卖

数码产品越放越便宜的定律失效了&#xff0c;连停产旧手机都在偷偷加价卖 如果你打算趁着降价换一部旧款手机&#xff0c;或者买一台便宜笔记本&#xff0c;最近可能会遇到一件怪事&#xff1a;老款不仅没打折&#xff0c;反而悄悄涨价了。 很多人习惯了数码产品每年贬值的常理…

作者头像 李华
网站建设 2026/9/30 9:53:33

LLM Wiki实战:用Markdown+YAML构建可审计、可追溯、可演进的知识库

1. 这不是又一篇“RAG入门指南”&#xff0c;而是一份LLM Wiki项目实操手记你点开这篇&#xff0c;大概率正被三件事困扰&#xff1a;第一&#xff0c;手头有一堆PDF、Word、内部文档、会议纪要、产品手册&#xff0c;想让大模型“真正懂”它们&#xff0c;而不是泛泛而谈&…

作者头像 李华
网站建设 2026/9/30 9:52:53

实对称矩阵特征值为实数的三种证明与代码验证

对称矩阵的特征值为实数&#xff0c;这条结论几乎每个学过线性代数的人都在课本上见过&#xff0c;但真正能在白纸上把它从头到尾推干净的人并不多。它不是一个孤立的习题&#xff0c;而是谱定理的第一步&#xff0c;是主成分分析、二次型标准化、结构力学模态分析、协方差矩阵…

作者头像 李华