无尘阁日记

无尘阁日记

🧠 企业级AI系统后端能力全链路实现:从权限配置到回答命中,从超时控制到标准响应
2025-04-10

本文记录了一个企业AI系统后台的构建全过程,涵盖配置驱动、请求控制、敏感词识别、回答复用、超时终止、统一响应结构等高复杂度场景的落地实践,适合后端开发、架构师、系统设计师参考与复用。

一、企业级AI能力配置体系设计

我们首先构建了一个高度可配置、精细可控的企业AI权限模型,存储在 HeadOfficeCapacityModelai_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}

注入全局参数

在 Controller 的 beforeAction() 中,我们统一读取并注入到 Yii::$app->params['ai_config'],确保后续模块能统一调用:

$config = HeadOfficeCapacityModel::getAiConfig($orgId);
Yii::$app->params['ai_config'] = $config;

二、请求频率控制与企业活跃人数上限校验

请求间隔控制

通过配置字段 request_interval_sec,结合 ai_messages 表中记录的上次提问时间,对每次提问做频率控制。

辅助方法:

public static function getLastAskTime($userId, $orgId): ?string

当前活跃用户统计

使用 UserHeadOfficeDetailModel 联表 User,统计 IsCurrent = 1IsLocked = 1 的用户数量,实现企业最大活跃用户数控制:

public static function checkActiveUserLimit($orgId): bool

三、敏感词识别逻辑支持“精确 + 模糊”双模式

配置项 sensitive_fuzzy_match 控制敏感词检测模式:

  • true:使用 stripos() 进行模糊匹配

  • false:使用 === 进行精确匹配

方法封装如下:

public static function hasSensitiveWord($text): bool

该方法动态读取配置,判断是否命中任一敏感词。场景包括问题审核、AI拒答策略触发等。


四、AI回答复用机制(缓存命中判断)

配置项 same_question_answer 用于判断是否开启回答缓存机制:

public static function isSameQuestionAnswerMatched(string $question): bool

我们同时引入了 pre_defined_answer 配置项,用于控制是否允许使用管理员预设答案。

判断逻辑如下(简化):

if (
    $match &&
    $config['same_question_answer'] &&
    (
        ($match->admin_answer_id && $config['pre_defined_answer']) ||
        ($match->ai_answer_id && !$match->admin_answer_id)
    )
)

该逻辑兼顾两类答案源(ADMIN/AI),并可灵活控制来源优先级。


五、历史答案过期机制(超时刷新)

为了避免长期使用过期的AI答案,我们设计了 ai_answer_timeout 超时时间策略:

public static function isAIAnswerExpired(?AiAnswers $answer): bool

该方法会判断该问题对应的历史AI答案的 updated_at 与当前时间的间隔,是否超过 ai_config 中的秒数配置(如 86400 秒 = 1 天),若超时则触发重新请求。


六、请求 AI 接口的超时控制与失败容错机制

我们封装了对外请求 AI 模型接口的统一方法:

public static function requestAiAnswer($question, &$modelId, $apiType = 'deepseek')

并实现以下关键能力:

请求超时控制

通过配置的 ai_answer_timeout 控制 HTTP 请求的最大等待时间(秒),通过 cURL 参数实现:

CURLOPT_TIMEOUT => $timeoutSec

异常捕获与分类日志

我们对请求异常进行了智能分类处理:

错误信息中包含分类标签
timeout【超时】
could not resolve【DNS错误】
connection refused【连接被拒绝】
其他【异常】

分类日志记录到:

/runtime/logs/ai_request_exception_YYYYMMDD.log

示例日志片段:

2025-04-10 23:55:10 【超时】发送问题失败:请帮我生成一份日报
异常信息:cURL error 28: Operation timed out after 30000ms...

七、标准化响应结构与客户端兼容优化

我们通过监听 Yii::$app->response->on(Response::EVENT_BEFORE_SEND) 实现响应格式统一:

$response->data = [
  'IsSuccess' => 0,
  'ErrMsg' => $response->data['message'] ?? '系统异常',
  'Data' => $route === 'v1/ai-chat/ask' ? (object)[] : []
];
$response->statusCode = 200; // 避免 Word 插件屏蔽 403/500 错误体

这确保了所有错误响应结构固定,且适配 Postman、Web 客户端、Word 插件等不同客户端解析行为。


八、其他重要封装与规范

  • 所有 AiAnswers 相关的匹配、记录引用、更新次数等操作均封装为模型方法,如:

    AiAnswerReferences::recordAnswerReference(...)
    AiSuggestedKeywordReferences::recordReference(...)
  • 所有参数校验均通过统一封装返回结构:

    ErrorCode::wordReturnData('提示信息', 0|1, $data);
  • 请求发起者来源、模型类型、预设 vs 历史来源都通过 answer_type 字段标记。


九、总结:这一天,我们构建了什么?

能力模块核心逻辑或方法说明
企业级AI功能权限控制ai_config + 全局注入控制访问、token、功能
请求频率限制getLastAskTime控制用户提交间隔
同步在线用户限制checkActiveUserLimit防止高并发压垮AI
敏感词过滤双模式hasSensitiveWord()区分模糊 / 精确匹配
历史回答缓存判断isSameQuestionAnswerMatched()同问同答策略
AI答案过期检测isAIAnswerExpired()控制内容新鲜度
接口调用容错封装requestAiAnswer()含超时与分类日志
标准响应统一输出beforeSend监听器适配不同客户端行为