无尘阁日记

无尘阁日记

Yii2 AI 配置助手开发全流程教程:从结构设计到接口实现
2025-04-07

这是一篇基于实际协作开发过程沉淀下来的教程,记录了从零构建一个可管理、可校验、可调用的企业级 AI 配置助手模块的完整流程。教程以 Yii2 框架为基础,前后端通过 UUID 管理配置记录,支持查询与更新,默认数据结构清晰,验证机制严密,整体风格清晰明了,可开箱即用。

✅ 一、设计目标

由我(作者)提出的核心目标如下:

  • 以企业为单位存储和管理 AI 能力配置信息(如调用频率、Token 限额、记忆能力等);

  • 配置结构采用 JSON 存储,字段有默认值且必须完整,要求接收前端传入数组;

  • 提供查询、更新两个接口,基于 UUID 定位唯一记录;

  • 保证配置的读取为结构化数组,存储前自动 encode,保存时自动验证字段完整性与格式;

  • 所有逻辑都聚焦在模型 HeadOfficeCapacityModel 内完成,接口仅做调度。


✅ 二、Mock 数据结构设计(AI 配置字段)

我们约定 ai_config 字段为 JSON,结构如下:

{
  "enabled": true,
  "request_interval_sec": 60,
  "token_limit": 100000,
  "token_used": 4300,
  "access_start_time": "2025-03-01 00:00:00",
  "access_end_time": "2025-12-31 23:59:59",
  "memory": {
    "enabled": true,
    "depth": 1,
    "cross_session": false
  },
  "sensitive_fuzzy_match": true,
  "suggested_keywords_enabled": true,
  "max_active_users": 100,
  "same_question_answer": true,
  "pre_defined_answer": true,
  "ai_answer_timeout": 86400
}

我们将这个结构转换为 PHP 注释,方便开发参考:

/**
 * @var array $ai_config 示例结构:
 * [
 *     'enabled' => bool,
 *     'request_interval_sec' => int,
 *     'token_limit' => int,
 *     'token_used' => int,
 *     'access_start_time' => string (Y-m-d H:i:s),
 *     'access_end_time' => string (Y-m-d H:i:s),
 *     'memory' => [
 *         'enabled' => bool,
 *         'depth' => int,
 *         'cross_session' => bool
 *     ],
 *     'sensitive_fuzzy_match' => bool,
 *     'suggested_keywords_enabled' => bool,
 *     'max_active_users' => int,
 *     'same_question_answer' => bool,
 *     'pre_defined_answer' => bool,
 *     'ai_answer_timeout' => int
 * ]
 */

✅ 三、模型内部处理逻辑(HeadOfficeCapacityModel)

1. 自动转换(在 afterFindbeforeSave 中处理)

public function afterFind()
{
    if (is_string($this->ai_config)) {
        $this->ai_config = Json::decode($this->ai_config);
    }
    parent::afterFind();
}

public function beforeSave($insert)
{
    if (is_array($this->ai_config)) {
        $this->validateAiConfig($this->ai_config);
        $this->ai_config = Json::encode($this->ai_config);
    }
    return parent::beforeSave($insert);
}

2. 配置校验逻辑(结构完整性 + 类型检查)

protected function validateAiConfig($config)
{
    if (!is_array($config)) {
        throw new InvalidArgumentException("AI配置必须是数组结构。");
    }

    $requiredKeys = [
        'enabled' => 'boolean',
        'request_interval_sec' => 'integer',
        'token_limit' => 'integer',
        'token_used' => 'integer',
        'access_start_time' => 'datetime',
        'access_end_time' => 'datetime',
        'memory' => 'array',
        'sensitive_fuzzy_match' => 'boolean',
        'suggested_keywords_enabled' => 'boolean',
        'max_active_users' => 'integer',
        'same_question_answer' => 'boolean',
        'pre_defined_answer' => 'boolean',
        'ai_answer_timeout' => 'integer'
    ];

    foreach ($requiredKeys as $key => $type) {
        if (!array_key_exists($key, $config)) {
            throw new InvalidArgumentException("缺少字段:{$key}");
        }

        $value = $config[$key];
        switch ($type) {
            case 'boolean': if (!is_bool($value)) throw new InvalidArgumentException("{$key} 应为布尔值。"); break;
            case 'integer': if (!is_int($value)) throw new InvalidArgumentException("{$key} 应为整数。"); break;
            case 'datetime': if (!strtotime($value)) throw new InvalidArgumentException("{$key} 应为合法时间字符串。"); break;
            case 'array': if (!is_array($value)) throw new InvalidArgumentException("{$key} 应为数组。"); break;
        }
    }

    foreach (['enabled' => 'boolean', 'depth' => 'integer', 'cross_session' => 'boolean'] as $key => $type) {
        if (!array_key_exists($key, $config['memory'])) {
            throw new InvalidArgumentException("memory 缺少字段:{$key}");
        }

        $value = $config['memory'][$key];
        switch ($type) {
            case 'boolean': if (!is_bool($value)) throw new InvalidArgumentException("memory.{$key} 应为布尔值。"); break;
            case 'integer': if (!is_int($value)) throw new InvalidArgumentException("memory.{$key} 应为整数。"); break;
        }
    }
    return true;
}

3. 查 / 改方法(基于 UUID)

public static function getAiConfigByUuid(string $uuid): ?array
{
    $model = static::findOne(['uuid' => $uuid]);
    return $model ? $model->ai_config : null;
}

public static function updateAiConfigByUuid(string $uuid, array $newConfig): bool
{
    $model = static::findOne(['uuid' => $uuid]);
    if (!$model) return false;
    $model->ai_config = $newConfig;
    return $model->save(false);
}

✅ 四、接口控制器实现(查询 + 更新)

我们采用 POST 方式,仅实现两个接口:

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['verbs'] = [
        'class' => \yii\filters\VerbFilter::class,
        'actions' => [
            'ai-config-view' => ['POST'],
            'ai-config-update' => ['POST'],
        ],
    ];
    return $behaviors;
}

查询接口

/**
 * @route   v1/ai-chat-manage/ai-config-view
 * @name    查询 AI 配置
 * @method  POST
 */
public function actionAiConfigView()
{
    $uuid = Yii::$app->request->post('uuid');
    if (empty($uuid)) {
        return $this->errorParam->paramValidity('UUID不能为空', 0);
    }
    $config = HeadOfficeCapacityModel::getAiConfigByUuid($uuid);
    return $config ? $this->errorParam->paramValidity($config, 1)
                   : $this->errorParam->paramValidity('找不到对应记录', 0);
}

更新接口(含异常捕获)

/**
 * @route   v1/ai-chat-manage/ai-config-update
 * @name    更新 AI 配置
 * @method  POST
 */
public function actionAiConfigUpdate()
{
    $uuid = Yii::$app->request->post('uuid');
    $aiConfig = Yii::$app->request->post('ai_config');

    if (empty($uuid)) {
        return $this->errorParam->paramValidity('UUID不能为空', 0);
    }
    if (!is_array($aiConfig)) {
        return $this->errorParam->paramValidity('ai_config 必须为数组', 0);
    }

    try {
        $success = HeadOfficeCapacityModel::updateAiConfigByUuid($uuid, $aiConfig);
        return $success ? $this->errorParam->paramValidity('更新成功', 1)
                        : $this->errorParam->paramValidity('更新失败,可能UUID无效或数据未变更', 0);
    } catch (\Throwable $e) {
        Yii::error("AI配置更新异常:{$e->getMessage()}", __METHOD__);
        return $this->errorParam->paramValidity("更新失败:{$e->getMessage()}", 0);
    }
}

✅ 五、总结:一套安全、标准、结构清晰的配置方案

这个 AI 配置助手模块具备以下优势:

  • JSON 配置统一集中管理,前端传数组、后端结构清晰;

  • 自动 encode/decode,无需业务方操心格式转换;

  • 严格字段验证,防止脏数据写入数据库;

  • 所有操作基于 UUID,支持 SaaS 多企业场景;

  • 接口统一、逻辑清晰、易于维护。

你只需将这套代码复制进你的项目中,即可直接运行并扩展。未来如需增设默认提示词、模型选择器、角色管理、权限开关等模块,只需在 ai_config 中扩展字段结构,并在验证器中同步添加规则即可。

欢迎在此基础上拓展属于你自己的 AI 模块治理体系。

by 楠哥 红尘炼心,知行一体。