首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何为基于代理的模型实现基于规则的决策者?

如何为基于代理的模型实现基于规则的决策者?
EN

Software Engineering用户
提问于 2021-07-21 15:19:50
回答 2查看 424关注 0票数 1

在我试图开发的基于代理的模型中,我很难理解如何将基于规则的决策方法结合到基于代理的模型中。

代理的接口非常简单。

代码语言:javascript
复制
public interface IAgent
{
   public string ID { get; }

   public Action Percept(IPercept percept);
}

为了这个例子,让我们假设代理代表在一个大仓库内穿越道路的车辆,以便装卸他们的货物。他们的路线(从起点到代理目的地的道路顺序)由另一个代理,即主管指定。车辆代理的目标是遍历其指定路线,卸下货物,装载新的货物,接受主管指定的另一条路线,并重复这一过程。

车辆还必须注意可能发生的碰撞,例如在交叉点,并根据某些规则给予优先权(例如,运载最重货物的车辆具有优先权)。

据我所知,这是我想要构建的代理的内部结构:

所以车辆代理可以是:

代码语言:javascript
复制
public class Vehicle : IAgent
{
  public VehicleStateUpdater { get; set; }

  public RuleSet RuleSet { get; set; }

  public VehicleState State { get; set; }

  public Action Percept(IPercept percept)
  {
    VehicleStateUpdater.UpdateState(VehicleState, percept);
    Rule validRule = RuleSet.Match(VehicleState);
    VehicleStateUpdater.UpdateState(VehicleState, validRule);
    Action nextAction = validRule.GetAction();
    return nextAction;
  }
}

对于车辆代理人的内部状况,我正在考虑这样的事情:

代码语言:javascript
复制
public class VehicleState
{
  public Route Route { get; set; }

  public Cargo Cargo { get; set; }

  public Location CurrentLocation { get; set; }
}

对于此示例,必须为车辆代理实现3条规则。

  1. 如果另一辆车辆在代理人附近(例如少于50米),则货物最重的车辆有优先权,其他代理人必须保持其位置。
  2. 当代理到达目的地时,他们卸载货物,装载新的货物,并等待主管分配新的路线。
  3. 在任何特定的时刻,主管,无论出于什么原因,都可以发出命令,接收车辆必须遵守(保持位置或继续)。

VehicleStateUpdater必须考虑代理的当前状态、接收到的感知的类型,并相应地更改状态。因此,为了使状态反映出主管收到了例如命令,可以对其进行如下修改:

代码语言:javascript
复制
public class VehicleState
{
  public Route Route { get; set; }

  public Cargo Cargo { get; set; }

  public Location CurrentLocation { get; set; }

  // Additional Property
  public RadioCommand ActiveCommand { get; set; }
}

如果RadioCommand可以是值为None的枚举,请保持,继续。

但现在,如果另一辆车接近,我也必须在代理人的状态下登记。因此,我必须向VehicleState添加另一个属性。

代码语言:javascript
复制
public class VehicleState
{
  public Route Route { get; set; }

  public Cargo Cargo { get; set; }

  public Location CurrentLocation { get; set; }

  public RadioCommand ActiveCommand { get; set; }

  // Additional properties
  public bool IsAnotherVehicleApproaching { get; set; }

  public Location ApproachingVehicleLocation { get; set; }
}

这是我很难理解如何进行的地方,我有一种感觉,我没有真正遵循正确的方法。首先,我不知道如何使VehicleState类更加模块化和可扩展。第二,我不知道如何实现定义决策过程的基于规则的部分.我是否应该创建相互排斥的规则(这意味着每一种可能的状态必须不超过一条规则)?是否有一种设计方法允许我添加额外的规则,而不必来回地添加/修改VehicleState类的属性,以确保每种可能的Percept类型都可以由代理的内部状态处理?

我在“人工智能:现代方法手册”和其他资料中看到了这些例子,但现有的例子太简单,当需要设计更复杂的模型时,我无法“理解”这个概念。

我试图纳入一项规则的一个例子:

代码语言:javascript
复制
public class HoldPositionCommandRule : IAgentRule<VehicleState>
{
    public int Priority { get; } = 0;

    public bool ConcludesTurn { get; } = false;


    public void Fire(IAgent agent, VehicleState state, IActionScheduler actionScheduler)
    {
        state.Navigator.IsMoving = false;
        //Use action scheduler to schedule subsequent actions...
    }

    public bool IsValid(VehicleState state)
    {
        bool isValid = state.RadioCommandHandler.HasBeenOrderedToHoldPosition;
        return isValid;
    }
}

代理决策者的样本,我也试着去实现。

代码语言:javascript
复制
public void Execute(IAgentMessage message,
                    IActionScheduler actionScheduler)
{
    _agentStateUpdater.Update(_state, message);
    Option<IAgentRule<TState>> validRule = _ruleMatcher.Match(_state);
    validRule.MatchSome(rule => rule.Fire(this, _state, actionScheduler));
}

如果有人能在执行基于规则的部分方面指出正确的方向,我将不胜感激。

我是用C#写的,但据我所知,它与我试图解决的更广泛的问题并没有真正的关系。

EN

回答 2

Software Engineering用户

回答已采纳

发布于 2021-07-29 16:56:52

这是我很难理解如何进行的地方,我有一种感觉,我没有真正遵循正确的方法。首先,我不知道如何使VehicleState类更加模块化和可扩展。

虽然这里有许多具体的问题,但我看到的是您在这里展示的代码中存在的许多一般性问题。你所经历的许多挣扎似乎与没有正确使用OO概念有关。创建接口和类的目的是管理复杂性。主要的问题是这里几乎没有封装,您的对象主要是“属性包”,即美化的地图/字典。让我们从您的最后一个示例VehicleState开始。首先,我不认为您需要VehicleStateVehicleStateUpdater on Vehicle。车辆对象已经具有状态,并且该状态的更新者应该是它自己。现在让我们来看一下VehicleState,我稍后再讨论这个问题:

代码语言:javascript
复制
public class VehicleState
{
  public Route Route { get; set; }

  public Cargo Cargo { get; set; }

  public Location CurrentLocation { get; set; }

  public RadioCommand ActiveCommand { get; set; }

  // Additional properties
  public bool IsAnotherVehicleApproaching { get; set; }

  public Location ApproachingVehicleLocation { get; set; }
}

这在功能上等价于带有类型的硬编码字典。而且,很多事情对我来说都没有意义。另一辆车是否正在接近并不是车辆状态的一部分。我希望这是Percept (perceptor)状态的一部分。接近车辆位置是接近车辆状态的一部分,它已经有一个位置作为其状态的一部分。

我不明白暴露Location的设置者有什么意义。更像update,它告诉车辆更新它的状态,比如Location,在这里是典型的。同样,货物设置器应该用LoadUnload方法代替。

RadioCommand似乎过于具体,您可能应该有一个Command接口和一个带有GetCommand方法的Radio类型。然后,HoldPositionCommandRule变成一个HoldPositionCommandCommandRule可能如下所示:

代码语言:javascript
复制
public interface IRule
{
    bool public canMove();

    // ...
}

public class Command
{
    List<IRule> Rules { get; set; }

    public addRule(IRule rule) {
       getRules.add(rule)
    }
}

然后你就可以拥有:

代码语言:javascript
复制
public class HoldPositionRule : IRule
{
    bool public canMove()
    {
        return false;
    }
}

我使用Python编写了一个小的工作示例。在任何人变得过于兴奋之前,我并不是把它作为有史以来最好的代码,也不是一个真正有趣的代理示例。我在这里的首要任务是保持简单和简短。为此,我已经消除了路线,并硬编码为一条直线30单位长。有两个规则:遵循至少2个单位的距离和保持位置。我把三辆车放在路上,当第一辆车(A)到达终点时,停止模拟。在每10个周期,领头卡车被给予一个‘保持’的命令,每3个,它被给予‘跟随’的命令。perceptor只能看到前面的车辆(或者旁边的车辆,在本例中不应该出现这种情况)。

代码语言:javascript
复制
class HoldPositionRule:
    def can_move(self, distance_to_next):
        return False


class FollowingRule:
    def can_move(self, distance_to_next):
        return distance_to_next is None or distance_to_next > 2


HOLD = HoldPositionRule()
FOLLOW = FollowingRule()


class Command:
    def __init__(self, *rules):
        self._rules = rules

    def can_move(self, distance_to_next):
        for rule in self._rules:
            if not rule.can_move(distance_to_next):
                return False

        return True


class Radio:
    def __init__(self):
        self.command = Command(FOLLOW)


class Location:
    def __init__(self, position):
        self.position = position

    def update(self, rate):
        self.position += rate

    def arrived(self):
        return self.position >= 30

    def __repr__(self):
        return str(self.position)

    def distance(self, other):
        return other.position - self.position


def distance(a, b):
    return a.location.distance(b.location)


class Environment:
    def __init__(self):
        self.vehicles = []

    def add(self, vehicle):
        self.vehicles.append(vehicle)

    def visible(self, perceptor):
        return [v for v in self.vehicles if distance(perceptor, v) >= 0]


ENVIRONMENT = Environment()


class Perceptor:
    def __init__(self, vehicle):
        self.vehicle = vehicle
        self.location = vehicle.location
        self.visible = []

    def update(self):
        self.visible = [v for v in ENVIRONMENT.visible(self) if v is not self.vehicle]


class Vehicle:
    def __init__(self, id, location):
        self.id = id
        self.radio = Radio()
        self.location = location
        self.perceptor = Perceptor(self)
        self.rate = 1

        ENVIRONMENT.add(self)

    def arrived(self):
        return self.location.arrived()

    def look(self):
        self.perceptor.update()

    def update(self):
        visible = [distance(self, v) for v in self.perceptor.visible]
        distance_to_next = min(visible) if len(visible) else None

        if self.radio.command.can_move(distance_to_next):
            self.location.update(self.rate)

    def __repr__(self):
        return f"{self.id}: {self.location}"


truckA = Vehicle("A", Location(0))
truckB = Vehicle("B", Location(-1))
truckC = Vehicle("C", Location(-2))

vehicles = [truckA, truckB, truckC]

for cycle in range(100):
    if truckA.arrived():
        break

    for v in vehicles:
        v.look()

    if cycle % 10 == 0:
        truckB.radio.command = Command(HOLD)
    elif cycle % 3 == 0:
        truckB.radio.command = Command(FOLLOW)

    for v in vehicles:
        v.update()

    print(cycle, vehicles)

这可能更简单,您想要做的事情,但如果您开始沿着这些路线,并使它发挥作用,您可以修改它变得更加复杂。最具挑战性的两个部分可能是perceptor如何提取环境细节,以及如何根据perceptor的输出应用规则。这在很大程度上取决于仿真的复杂性。我建议只在不足的时候开始简单地重新工作。

票数 1
EN

Software Engineering用户

发布于 2021-07-29 14:10:42

我不知道如何使VehicleState类更加模块化和可扩展。(...)是否有一种设计方法允许我添加额外的规则,而不必来回地添加/修改VehicleState类的属性,以确保每种可能的Percept类型都可以由代理的内部状态处理?

因此,您需要类似于面向插件的体系结构,可以添加新组件并相互通信,而不需要修改“核心”?实现这一目标的最简单的方法是实现代理本地第二集装箱。(在C#中,内建实现应该足够了。)

代码语言:javascript
复制
class RadioCommunicationState
{
    public RadioCommand CurrentCommand { get; set; }
}

class RadioCommandPerceptor : IPerceptor
{
    public RadioCommandPerceptor(IRemoteRadioCommandSource source, RadioCommunicationState radioState) { /*...*/ }
    public void Update() => _radioState.CurrentCommand = _source.RecentCommands.LastOrDefault();
}

class HoldPositionRule : IRule
{
    public HoldPositionRule(RadioCommunicationState radioState) { /*...*/ }
    public float PriorityScale { get; set; } = 42.314f; // for fine-tuning, read it from the config
    public float RateSelf() => (_radioState.CurrentCommand == RadioCommand.Hold) ? 1f : 0f;
    public void Apply() => _vehicleControlsState.DesiredSpeed = 0;
}

class VehicleAgent : IAgent
{
    public void Update()
    {
        foreach (var perceptor in _perceptors)
            perceptor.Update();
        var rule = _rules.Max(r => r.RateSelf() * r.PriorityScale);
        rule.Apply();
    }
}

class Program
{
    static VehicleAgent BuildVehicleAgent()
    {
        var collection = new ServiceCollection();
        collection.AddSingleton<RadioCommunicationState>();
        collection.AddSingleton<RadioCommandPerceptor>();
        collection.AddSingleton<HoldPositionRule>();
        collection.AddSingleton<VehicleAgent>();
        // ...
        return collection.BuildServiceProvider().GetService<VehicleAgent>();
    }
}

(如果您正在使用ML,请注意扩展ML模型有新的投入和产出通常需要完全重新培训。如果出于性能原因这是不可接受的,请选择/设计一个支持“持续学习”的模型。)

我不知道如何实现定义决策过程的基于规则的部分。我是否应该创建相互排斥的规则(这意味着每一种可能的状态必须不超过一条规则)?

FSM-like方法(即每1种状态有1条规则)是简单和可组合的。多个FSM可以并行使用以提供更好的灵活性,但它只对不需要协调的完全独立特性来说是有意义的。对于需要子步骤和协调的复杂规则,只需使用行为树;它还消除了对调度程序的需求。

如果有另一种可能让车辆代理人改变他们的路线,我会在IRouteAssignmentCommandSource中注射RouteAssignmentCommandPerceptor吗?

IRemoteRadioCommandSource只是一个抽象概念,可以在我的示例中插入一些漏洞。命令从何而来并不重要。但我想所有的命令都是通过同一个无线电频道发出的,所以把所有的命令放到同一个源中更有意义。

当我们在这里的时候,让我们改变perceptor,通过中间状态来解释一个命令。这样,就可以在不触及规则的情况下增加更多的感知者。

代码语言:javascript
复制
class TemporaryStopIntentState
{
    public DateTime Target { get; init; }
}

class HoldPositionCommand
{
    public DateTime ReleaseTime { get; init; }
}

class RadioCommandPerceptor : IPerceptor
{
    /* ... */
    public void Update()
    {
        if (_source.PendingCommand is HoldCommand command)
            _temporaryStopIntentState.Target = command.ReleaseTime;
    }
}

class HoldPositionRule : IRule
{
    /* ... */
    public float RateSelf() => (_temporaryStopIntentState.Target > DateTime.Now) ? 1f : 0f;
}

此外,在有限状态机方法中,一次只能选择一条规则并应用,对吗?因此,在我们的示例中,如果两个规则都是“有效的”(同时存在RadioCommand.Hold和NewRoute ),那么其中一个规则将在第一个更新周期中发生,而另一个规则将在下一个周期中发生。我理解得对吗?

这一直是重量最高的规则。你要仔细调整规则,以确保没有僵局,否则行为将取决于月亮的相位。但是,您不需要依赖于单一的FSM,我们更喜欢它们,因为它们是便宜的和可组合的。

命令是令人困惑的,因为它们更类似于事件(“我看到了一只猫”),而不是可感知的状态(“真,我现在看到了红灯”)。将命令处理移动到感知器中(如上面的代码所示)可以解决这一概念上的偏差。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/430453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档