Files
zyt/server/config/prescription_ai.php

130 lines
6.4 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 处方库与诊单 AI 报告(Dify / OpenAI-compatible)
*
* qwen/openai 分别使用各自的服务端凭据。凭据禁止下发到客户端或写入日志。
*/
return [
'enable' => filter_var(
env('prescription_ai.ENABLE', env('prescription_ai.enable', false)),
FILTER_VALIDATE_BOOLEAN
),
'base_url' => rtrim((string) env(
'prescription_ai.BASE_URL',
env('prescription_ai.base_url', '')
), '/'),
'timeout' => (int) env(
'prescription_ai.TIMEOUT',
env('prescription_ai.timeout', 90)
),
// Clinical source files are mandatory: upload their bytes first instead of
// relying on the model server to reach the workstation's resource URLs.
'required_attachment_transport' => 'upload',
// A byte budget for additional evidence, not a claim about provider token capacity.
'required_attachment_input_bytes' => (int) env('prescription_ai.ATTACHMENT_INPUT_BYTES', 196608),
// Independent local text recognition supplements the unchanged image/model
// transport. Install the pinned offline worker before enabling on a host.
'required_attachment_ocr' => [
'enabled' => filter_var(env('prescription_ai.LOCAL_OCR_ENABLED', false), FILTER_VALIDATE_BOOLEAN),
'python' => (string) env('prescription_ai.LOCAL_OCR_PYTHON', ''),
'timeout_seconds' => (int) env('prescription_ai.LOCAL_OCR_TIMEOUT', 15),
'max_text_bytes' => 48000,
'threads' => 2,
'detection_limit' => 736,
],
'required_attachment_upload' => [
// Explicit aliases for this deployment's upload directory. Do not derive
// local-file access from an untrusted request Host or attachment URL.
'local_origins' => array_values(array_filter(array_map('trim', explode(',', (string) env(
'prescription_ai.REQUIRED_ATTACHMENT_LOCAL_ORIGINS',
'https://admin.zhenyangtang.com.cn,https://css.zhenyangtang.com.cn,http://admin.zhenyangtang.com.cn,http://css.zhenyangtang.com.cn'
))))),
'max_file_bytes' => (int) env('prescription_ai.REQUIRED_ATTACHMENT_MAX_BYTES', 20 * 1024 * 1024),
// Optional trusted server CA bundle for PHP installations without a system
// cURL certificate store; peer and hostname verification remain enabled.
'ca_file' => (string) env('prescription_ai.REQUIRED_ATTACHMENT_CA_FILE', ''),
// Optional server-owned DNS pins for remote attachment storage only. Exact
// hostname => nonempty public-IP array; origins/TLS checks still apply.
// Keep invalid JSON/root types as null so the uploader fails closed.
'resolved_hosts' => (static function () {
$decoded = json_decode((string) env('prescription_ai.REQUIRED_ATTACHMENT_RESOLVED_HOSTS', '{}'));
return $decoded instanceof \stdClass ? get_object_vars($decoded) : null;
})(),
],
// Reuse only evidence for an identical, freshly authorized source; 0 disables.
'context_compaction_cache_ttl' => (int) env('prescription_ai.CONTEXT_COMPACTION_CACHE_TTL', 300),
/**
* 单个应用请求的附件上限(默认 3),各模型可在 models 中覆盖。
* required_files 超限时在同应用分批提取全部可用附件,再结合完整正文综合;
* 确认缺失明确跳过,明确拒收逐项记录,鉴权/网络等不明确错误仍安全失败。
* 普通可选附件保留原清单策略。修改上游后核对 /parameters 的
* file_upload.number_limits 并同步此配置,避免不必要的分批。
*/
'max_files' => (int) env(
'prescription_ai.MAX_FILES',
env('prescription_ai.max_files', 3)
),
// Per-model staged analysis limits. Oversized semantic units and exhausted call budgets
// are explicit task errors; they never silently remove patient evidence or attachments.
'manual_analysis' => [
/**
* 单次提示词的字节上限(UTF-8 字节是 token 的保守上界)。调大可减少分片与压缩
* 轮次、显著缩短一份报告的总耗时;上游应用限制更严时会返回可重试的拒绝,
* 此时调小本值。
*/
'input_token_budget' => (int) env(
'prescription_ai.MANUAL_INPUT_TOKEN_BUDGET',
env('prescription_ai.manual_input_token_budget', 48000)
),
'max_calls_per_model' => (int) env(
'prescription_ai.MANUAL_MAX_CALLS_PER_MODEL',
env('prescription_ai.manual_max_calls_per_model', 128)
),
/**
* 医学研究对照模式:无论资料是否完整,两个模型都必须先各自独立开出候选处方,
* 再由服务端与人工方比较。缺口、假设与复核要求写入候选方的说明与风险提示,
* 不再以资料不足为由返回空方案。候选方仍不写回正式处方、审核或订单。
*/
'require_candidate' => filter_var(
env('prescription_ai.MANUAL_REQUIRE_CANDIDATE', true),
FILTER_VALIDATE_BOOLEAN
),
/**
* 后台分阶段分析的单次请求超时(秒,上限 300)。同步页面仍使用上面的 timeout;
* 该值必须明显小于任务租约 prescription_analysis.lease_seconds。
*/
'request_timeout' => (int) env(
'prescription_ai.MANUAL_REQUEST_TIMEOUT',
240
),
// 模型仍拒绝开方时,携带其拒绝理由重新追问的次数。
'candidate_insist_rounds' => (int) env(
'prescription_ai.MANUAL_CANDIDATE_INSIST_ROUNDS',
2
),
],
'models' => [
'qwen' => [
'name' => 'qwen3.6-35b',
'label' => '千问',
// 2026-10-23 已核对原应用发布后的 file_upload.number_limits = 10。
'max_files' => (int) env('prescription_ai.QWEN_MAX_FILES', 10),
'api_key' => (string) env(
'prescription_ai.QWEN_API_KEY',
env('prescription_ai.qwen_api_key', '')
),
],
'openai' => [
'name' => 'gpt-5.6-sol',
'label' => 'OpenAI',
// 该应用 file_upload.number_limits = 10:一次多带附件可显著减少往返次数
'max_files' => (int) env('prescription_ai.OPENAI_MAX_FILES', 10),
'api_key' => (string) env(
'prescription_ai.OPENAI_API_KEY',
env('prescription_ai.openai_api_key', '')
),
],
],
];