Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pac
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tools
pac
Commits
f9716ee1
Commit
f9716ee1
authored
Oct 23, 2026
by
luoqi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore(ai): normalize DeepSeek Flash model and pricing
parent
6e099cc1
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
118 additions
and
30 deletions
+118
-30
apps/pac-docs/content/docs/algorithms/overview.mdx
+1
-1
apps/pac-docs/content/docs/algorithms/script-generation.mdx
+1
-1
apps/pac-docs/content/docs/deployment.mdx
+2
-1
apps/pac-service/.env.example
+6
-5
apps/pac-service/src/cli/ai-gen-script.cli.ts
+3
-3
apps/pac-service/src/config/ai-models.ts
+6
-0
apps/pac-service/src/config/configuration.ts
+11
-7
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/deep/calls.ts
+3
-3
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/stable/stable.call.ts
+1
-1
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/standard/standard.call.ts
+1
-1
apps/pac-service/src/modules/ai/calls/draft-wecom-script/calls.ts
+1
-1
apps/pac-service/src/modules/ai/core/ai-provider.service.ts
+5
-4
apps/pac-service/src/modules/ai/core/cost.ts
+7
-1
apps/pac-service/src/modules/ai/orchestrators/plan-script.orchestrator.ts
+1
-1
apps/pac-service/tests/deepseek-model-config.spec.ts
+69
-0
No files found.
apps/pac-docs/content/docs/algorithms/overview.mdx
View file @
f9716ee1
...
@@ -153,7 +153,7 @@ icon: Sparkles
...
@@ -153,7 +153,7 @@ icon: Sparkles
| **禁词 + 不承诺** | 不许"保证治好" / 报价 / 写死时间(诊所没排班接口,时间只能给"示例") |
| **禁词 + 不承诺** | 不许"保证治好" / 报价 / 写死时间(诊所没排班接口,时间只能给"示例") |
| **失败降级** | 大模型出错或违规 → 退回安全模板,客服永远有东西可用 |
| **失败降级** | 大模型出错或违规 → 退回安全模板,客服永远有东西可用 |
> 📐 **真理源**:提示词组装 `skill-composer.ts` + `skills/*/SKILL.md`(按 frontmatter 声明式匹配);安全规则**单一源** `safety-rules.ts`(机器闸 + prompt 同引,输出后扫禁词 / 加粗时间 / 临床码,命中即兜底);模型默认 `deepseek-
v4-
flash`;每次落 `agent_invocations`(档位 / 模型 / token / cost / 用了哪些块)。话术块是 MD 文件,**运营可改文案不动代码**。
> 📐 **真理源**:提示词组装 `skill-composer.ts` + `skills/*/SKILL.md`(按 frontmatter 声明式匹配);安全规则**单一源** `safety-rules.ts`(机器闸 + prompt 同引,输出后扫禁词 / 加粗时间 / 临床码,命中即兜底);模型默认 `deepseek-flash`;每次落 `agent_invocations`(档位 / 模型 / token / cost / 用了哪些块)。话术块是 MD 文件,**运营可改文案不动代码**。
> **本质**:话术不是"一个大模板",而是按患者特征拼装 + 大模型润色 + 安全兜底。
> **本质**:话术不是"一个大模板",而是按患者特征拼装 + 大模型润色 + 安全兜底。
...
...
apps/pac-docs/content/docs/algorithms/script-generation.mdx
View file @
f9716ee1
...
@@ -79,7 +79,7 @@ system prompt = `base common`(`shared/skills/_base/common.md`,三档共用:角
...
@@ -79,7 +79,7 @@ system prompt = `base common`(`shared/skills/_base/common.md`,三档共用:角
## provider 与落账
## provider 与落账
- **模型**:三档默认 `deepseek-
v4-
flash`(`?model=` 可覆盖)。
- **模型**:三档默认 `deepseek-flash`(`?model=` 可覆盖)。
- **落账**:每次经 `AiCallRunner` → `invocation-recorder` 两段式落 `agent_invocations`(tier 嵌在 `promptVersion`、`modelProvider`/`modelName`、token、`costYuan`、source);深度档额外 `attachJudge` 落自检质量分。
- **落账**:每次经 `AiCallRunner` → `invocation-recorder` 两段式落 `agent_invocations`(tier 嵌在 `promptVersion`、`modelProvider`/`modelName`、token、`costYuan`、source);深度档额外 `attachJudge` 落自检质量分。
- **写库**:`plan_scripts` upsert(`content` Markdown、`status='ready'`、`source`、`agentInvocationId`)。
- **写库**:`plan_scripts` upsert(`content` Markdown、`status='ready'`、`source`、`agentInvocationId`)。
...
...
apps/pac-docs/content/docs/deployment.mdx
View file @
f9716ee1
...
@@ -85,7 +85,8 @@ DW_CLICKHOUSE_PASSWORD=<密码> # env 名须等于 manifest 的
...
@@ -85,7 +85,8 @@ DW_CLICKHOUSE_PASSWORD=<密码> # env 名须等于 manifest 的
# ─── AI(DeepSeek)───
# ─── AI(DeepSeek)───
DEEPSEEK_API_KEY=<key>
DEEPSEEK_API_KEY=<key>
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_BASE_URL=https://api.deepseek.com
AI_DEFAULT_MODEL=deepseek-v4-flash
# 旧 deepseek-v4-flash 配置兼容归一;Flash 成本按高峰价估算,非实际账单
AI_DEFAULT_MODEL=deepseek-flash
# ─── 定时任务 ───
# ─── 定时任务 ───
# 注:增量 / 滞后监控 / 兜底扫描的代码默认是"永不"哨兵,必须显式设 env 才会调度
# 注:增量 / 滞后监控 / 兜底扫描的代码默认是"永不"哨兵,必须显式设 env 才会调度
...
...
apps/pac-service/.env.example
View file @
f9716ee1
...
@@ -84,10 +84,10 @@ DEEPSEEK_API_KEY=
...
@@ -84,10 +84,10 @@ DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_BASE_URL=https://api.deepseek.com
# 默认主力模型
# 默认主力模型
# local: deepseek-
v4-
flash (省钱开发)
# local: deepseek-flash (省钱开发)
# staging: deepseek-
v4-
flash
# staging: deepseek-flash
# production: deepseek-
v4-pro (质量优先
)
# production: deepseek-
flash (Pro 可单独评估后配置
)
AI_DEFAULT_MODEL=deepseek-
v4-
flash
AI_DEFAULT_MODEL=deepseek-flash
AI_REQUEST_TIMEOUT_SEC=60
AI_REQUEST_TIMEOUT_SEC=60
...
@@ -107,7 +107,8 @@ GEMINI_API_KEY=
...
@@ -107,7 +107,8 @@ GEMINI_API_KEY=
# GEMINI_LIVE_MODEL=gemini-3.1-flash-live-preview
# GEMINI_LIVE_MODEL=gemini-3.1-flash-live-preview
# 价格表(¥/M tokens)— 调价时改这里重启;不配走默认
# 价格表(¥/M tokens)— 调价时改这里重启;不配走默认
# AI_PRICE_TABLE_JSON={"deepseek-v4-pro":{"inHit":0.5,"inMiss":3.6,"out":25},"deepseek-v4-flash":{"inHit":0.07,"inMiss":0.5,"out":2}}
# Flash 默认按官方人民币高峰价保守估算,空闲时段实际为一半;自定义表会整体替换默认表。
# AI_PRICE_TABLE_JSON={"deepseek-flash":{"inHit":0.04,"inMiss":2,"out":8}}
# ─── CORS / iframe ───────────────────────────────────────────────────
# ─── CORS / iframe ───────────────────────────────────────────────────
...
...
apps/pac-service/src/cli/ai-gen-script.cli.ts
View file @
f9716ee1
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
* pnpm ai:gen-script -- --pid=F1001 # 用 patient externalId(自动取该 patient active plan)
* pnpm ai:gen-script -- --pid=F1001 # 用 patient externalId(自动取该 patient active plan)
* pnpm ai:gen-script -- --pid=F1001 --bust-cache # 跳过缓存重新调
* pnpm ai:gen-script -- --pid=F1001 --bust-cache # 跳过缓存重新调
* pnpm ai:gen-script -- --pid=F1001 --dry-run # 不写 PlanScript 表
* pnpm ai:gen-script -- --pid=F1001 --dry-run # 不写 PlanScript 表
* pnpm ai:gen-script -- --pid=F1001 --model=deepseek-
v4-
flash # 切模型
* pnpm ai:gen-script -- --pid=F1001 --model=deepseek-flash # 切模型
*
*
* 输入完全确定(不重新 pull 患者数据),仅用于验证 prompt + model 改动效果。
* 输入完全确定(不重新 pull 患者数据),仅用于验证 prompt + model 改动效果。
*/
*/
...
@@ -61,7 +61,7 @@ Usage:
...
@@ -61,7 +61,7 @@ Usage:
pnpm ai:gen-script -- --pid=<patient-external-id>
pnpm ai:gen-script -- --pid=<patient-external-id>
pnpm ai:gen-script -- --pid=F1001 --bust-cache # 跳过缓存,真实调 LLM
pnpm ai:gen-script -- --pid=F1001 --bust-cache # 跳过缓存,真实调 LLM
pnpm ai:gen-script -- --pid=F1001 --dry-run # 不写 PlanScript
pnpm ai:gen-script -- --pid=F1001 --dry-run # 不写 PlanScript
pnpm ai:gen-script -- --pid=F1001 --model=deepseek-
v4-
flash
pnpm ai:gen-script -- --pid=F1001 --model=deepseek-flash
Options:
Options:
--plan-id=<uuid> 直接指定 plan id
--plan-id=<uuid> 直接指定 plan id
...
@@ -70,7 +70,7 @@ Options:
...
@@ -70,7 +70,7 @@ Options:
--tenant=<str> 默认 t_demo
--tenant=<str> 默认 t_demo
--bust-cache 强制重跑(不走 prompt cache)
--bust-cache 强制重跑(不走 prompt cache)
--dry-run 只生成不写库
--dry-run 只生成不写库
--model=<id> 覆盖默认模型(deepseek-v4-pro / deepseek-
v4-
flash)
--model=<id> 覆盖默认模型(deepseek-v4-pro / deepseek-flash)
`
);
`
);
}
}
...
...
apps/pac-service/src/config/ai-models.ts
0 → 100644
View file @
f9716ee1
/** 官方保留的旧 Flash 别名;统一请求名和调用记录,不改历史数据。 */
export
function
canonicalModelId
(
modelId
:
string
):
string
{
return
modelId
===
'deepseek-v4-flash'
||
modelId
===
'deepseek-v4-flash-vision-exp'
?
'deepseek-flash'
:
modelId
;
}
apps/pac-service/src/config/configuration.ts
View file @
f9716ee1
import
{
canonicalModelId
}
from
'./ai-models'
;
export
interface
AppConfig
{
export
interface
AppConfig
{
nodeEnv
:
'development'
|
'test'
|
'production'
;
nodeEnv
:
'development'
|
'test'
|
'production'
;
port
:
number
;
port
:
number
;
...
@@ -15,7 +17,7 @@ export interface AppConfig {
...
@@ -15,7 +17,7 @@ export interface AppConfig {
deepseekApiKey
:
string
;
deepseekApiKey
:
string
;
/// DeepSeek base URL(默认官方;私有 / 镜像 endpoint 时覆盖)
/// DeepSeek base URL(默认官方;私有 / 镜像 endpoint 时覆盖)
deepseekBaseUrl
:
string
;
deepseekBaseUrl
:
string
;
/// 默认主力模型(deepseek-v4-pro / deepseek-
v4-
flash)
/// 默认主力模型(deepseek-v4-pro / deepseek-flash)
defaultModel
:
string
;
defaultModel
:
string
;
/// Gemini API key(Google AI Studio;海外需可达 generativelanguage.googleapis.com)
/// Gemini API key(Google AI Studio;海外需可达 generativelanguage.googleapis.com)
geminiApiKey
:
string
;
geminiApiKey
:
string
;
...
@@ -75,7 +77,7 @@ export function loadConfig(): AppConfig {
...
@@ -75,7 +77,7 @@ export function loadConfig(): AppConfig {
ai
:
{
ai
:
{
deepseekApiKey
:
process
.
env
.
DEEPSEEK_API_KEY
??
''
,
deepseekApiKey
:
process
.
env
.
DEEPSEEK_API_KEY
??
''
,
deepseekBaseUrl
:
process
.
env
.
DEEPSEEK_BASE_URL
??
'https://api.deepseek.com'
,
deepseekBaseUrl
:
process
.
env
.
DEEPSEEK_BASE_URL
??
'https://api.deepseek.com'
,
defaultModel
:
process
.
env
.
AI_DEFAULT_MODEL
??
'deepseek-v4-flash'
,
defaultModel
:
canonicalModelId
(
process
.
env
.
AI_DEFAULT_MODEL
??
'deepseek-flash'
)
,
geminiApiKey
:
process
.
env
.
GEMINI_API_KEY
??
''
,
geminiApiKey
:
process
.
env
.
GEMINI_API_KEY
??
''
,
geminiBaseUrl
:
process
.
env
.
GEMINI_BASE_URL
??
''
,
geminiBaseUrl
:
process
.
env
.
GEMINI_BASE_URL
??
''
,
geminiDefaultModel
:
process
.
env
.
GEMINI_DEFAULT_MODEL
??
'gemini-3.5-flash'
,
geminiDefaultModel
:
process
.
env
.
GEMINI_DEFAULT_MODEL
??
'gemini-3.5-flash'
,
...
@@ -108,8 +110,8 @@ export function loadConfig(): AppConfig {
...
@@ -108,8 +110,8 @@ export function loadConfig(): AppConfig {
/**
/**
* AI 价格表 — 从 env JSON 读,失败兜底默认值。
* AI 价格表 — 从 env JSON 读,失败兜底默认值。
*
*
* 单位:¥/M tokens
(已按汇率换算,DeepSeek 公开价 USD * 7.2)
。
* 单位:¥/M tokens
。DeepSeek Flash 使用官方人民币高峰价作保守估算,非实际账单
。
* inHit: 输入命中 prompt cache 的部分
(便宜 ~7x)
* inHit: 输入命中 prompt cache 的部分
* inMiss: 输入未命中(全价)
* inMiss: 输入未命中(全价)
* out: 输出(含 reasoning,DeepSeek 统一计费)
* out: 输出(含 reasoning,DeepSeek 统一计费)
*
*
...
@@ -120,14 +122,16 @@ export function loadConfig(): AppConfig {
...
@@ -120,14 +122,16 @@ export function loadConfig(): AppConfig {
* 4. 新调用按新价计
* 4. 新调用按新价计
*/
*/
function
parsePriceTable
(
raw
:
string
|
undefined
):
Record
<
string
,
{
inHit
:
number
;
inMiss
:
number
;
out
:
number
}
>
{
function
parsePriceTable
(
raw
:
string
|
undefined
):
Record
<
string
,
{
inHit
:
number
;
inMiss
:
number
;
out
:
number
}
>
{
//
⚠️ 价格核对于 2026-06(官方页):¥/M tokens。
美元价按 ×7.2 折 RMB。
//
除下方单独更新的 Flash 外,其他价格沿用 2026-06 配置:¥/M tokens,
美元价按 ×7.2 折 RMB。
// 用各家**标准价**(不含限时促销,促销到期不会少报);要按实付/促销算 → 用 AI_PRICE_TABLE_JSON 覆盖。
// 用各家**标准价**(不含限时促销,促销到期不会少报);要按实付/促销算 → 用 AI_PRICE_TABLE_JSON 覆盖。
// DeepSeek:api-docs.deepseek.com/quick_start/pricing(V4-pro $0.003625/0.435/0.87、V4-flash $0.0028/0.14/0.28)
// Flash 于 2026-10-23 单独核对官方人民币价:高峰 0.04/2/8,空闲为一半。
// https://api-docs.deepseek.com/zh-cn/quick_start/pricing/
// 仍用固定单价保守估算,不模拟节假日峰谷账单;其他模型价格保持原配置。
// Gemini 3.5 Flash:$0.15/1.5/9.0(cache/in/out)
// Gemini 3.5 Flash:$0.15/1.5/9.0(cache/in/out)
// Qwen3.7-Max(百炼,RMB 直接):缓存输入 1.2 / 输入 12 / 输出 36;现有限时 5 折 = 6/18(促销价走 env)
// Qwen3.7-Max(百炼,RMB 直接):缓存输入 1.2 / 输入 12 / 输出 36;现有限时 5 折 = 6/18(促销价走 env)
const
DEFAULT
=
{
const
DEFAULT
=
{
'deepseek-v4-pro'
:
{
inHit
:
0.03
,
inMiss
:
3.13
,
out
:
6.26
},
'deepseek-v4-pro'
:
{
inHit
:
0.03
,
inMiss
:
3.13
,
out
:
6.26
},
'deepseek-
v4-flash'
:
{
inHit
:
0.02
,
inMiss
:
1.01
,
out
:
2.02
},
'deepseek-
flash'
:
{
inHit
:
0.04
,
inMiss
:
2
,
out
:
8
},
'gemini-3.5-flash'
:
{
inHit
:
1.08
,
inMiss
:
10.8
,
out
:
64.8
},
'gemini-3.5-flash'
:
{
inHit
:
1.08
,
inMiss
:
10.8
,
out
:
64.8
},
'gemini-2.5-flash'
:
{
inHit
:
1.08
,
inMiss
:
10.8
,
out
:
64.8
},
'gemini-2.5-flash'
:
{
inHit
:
1.08
,
inMiss
:
10.8
,
out
:
64.8
},
// Qwen3.7-Max 是旗舰最贵档(不是便宜模型):标准价 ¥/M 直接填
// Qwen3.7-Max 是旗舰最贵档(不是便宜模型):标准价 ¥/M 直接填
...
...
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/deep/calls.ts
View file @
f9716ee1
...
@@ -56,7 +56,7 @@ export class DeepPlanCall implements AiCall<ScriptContext, DeepPlanZ> {
...
@@ -56,7 +56,7 @@ export class DeepPlanCall implements AiCall<ScriptContext, DeepPlanZ> {
readonly
kind
=
'script'
as
const
;
readonly
kind
=
'script'
as
const
;
readonly
callKey
=
'draft_plan_script_plan'
;
readonly
callKey
=
'draft_plan_script_plan'
;
readonly
promptVersion
=
'draft_plan_script@2026-06-16-deep-plan-v9'
;
// v9: 去掉钦定段式(给自由度),只留从果推因/层层递进原则;v8: 从果推因+后果不吓唬+3-6段
readonly
promptVersion
=
'draft_plan_script@2026-06-16-deep-plan-v9'
;
// v9: 去掉钦定段式(给自由度),只留从果推因/层层递进原则;v8: 从果推因+后果不吓唬+3-6段
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
DeepPlanSchema
;
readonly
outputSchema
=
DeepPlanSchema
;
buildPrompt
(
ctx
:
ScriptContext
)
{
buildPrompt
(
ctx
:
ScriptContext
)
{
return
{
system
:
PLAN_SYSTEM
,
prompt
:
buildPlanPrompt
(
ctx
)
};
return
{
system
:
PLAN_SYSTEM
,
prompt
:
buildPlanPrompt
(
ctx
)
};
...
@@ -68,7 +68,7 @@ export class DeepWriteCall implements AiCall<DeepWriteInput, DeepWriteZ> {
...
@@ -68,7 +68,7 @@ export class DeepWriteCall implements AiCall<DeepWriteInput, DeepWriteZ> {
readonly
kind
=
'script'
as
const
;
readonly
kind
=
'script'
as
const
;
readonly
callKey
=
'draft_plan_script_write'
;
readonly
callKey
=
'draft_plan_script_write'
;
readonly
promptVersion
=
'draft_plan_script@2026-07-29-deep-write-v19'
;
// v19: 自报家门改「我是{诊断医生}医生的助理X」(业务口径:非客服身份)。v18: 低龄约束(<18 不主推种植)+ 替牙期(≤12)说「早期矫治」。v17: 高龄硬约束(≥75 缺牙禁推种植/手术)。v16: 自报家门去人名 → 占位【回访客服】(渲染期按登录人回填,修串名 bug)。v15: 去掉钦定段式(给自由度);v14: 禁内部代码/术语(K08等)说大白话
readonly
promptVersion
=
'draft_plan_script@2026-07-29-deep-write-v19'
;
// v19: 自报家门改「我是{诊断医生}医生的助理X」(业务口径:非客服身份)。v18: 低龄约束(<18 不主推种植)+ 替牙期(≤12)说「早期矫治」。v17: 高龄硬约束(≥75 缺牙禁推种植/手术)。v16: 自报家门去人名 → 占位【回访客服】(渲染期按登录人回填,修串名 bug)。v15: 去掉钦定段式(给自由度);v14: 禁内部代码/术语(K08等)说大白话
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
DeepWriteSchema
;
readonly
outputSchema
=
DeepWriteSchema
;
constructor
(
private
readonly
skillRegistry
:
DraftPlanScriptSkillRegistry
)
{}
constructor
(
private
readonly
skillRegistry
:
DraftPlanScriptSkillRegistry
)
{}
buildPrompt
(
input
:
DeepWriteInput
)
{
buildPrompt
(
input
:
DeepWriteInput
)
{
...
@@ -86,7 +86,7 @@ export class DeepVerifyCall implements AiCall<DeepVerifyInput, DeepVerifyZ> {
...
@@ -86,7 +86,7 @@ export class DeepVerifyCall implements AiCall<DeepVerifyInput, DeepVerifyZ> {
readonly
kind
=
'judge'
as
const
;
readonly
kind
=
'judge'
as
const
;
readonly
callKey
=
'draft_plan_script_verify'
;
readonly
callKey
=
'draft_plan_script_verify'
;
readonly
promptVersion
=
'draft_plan_script@2026-06-16-deep-verify-v10'
;
// v10: 递进检查去钦定段式(给自由度);v9: 加④患者听得懂(无代码/术语/含糊)
readonly
promptVersion
=
'draft_plan_script@2026-06-16-deep-verify-v10'
;
// v10: 递进检查去钦定段式(给自由度);v9: 加④患者听得懂(无代码/术语/含糊)
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
DeepVerifySchema
;
readonly
outputSchema
=
DeepVerifySchema
;
buildPrompt
(
input
:
DeepVerifyInput
)
{
buildPrompt
(
input
:
DeepVerifyInput
)
{
return
{
system
:
VERIFY_SYSTEM
,
prompt
:
buildVerifyPrompt
(
input
)
};
return
{
system
:
VERIFY_SYSTEM
,
prompt
:
buildVerifyPrompt
(
input
)
};
...
...
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/stable/stable.call.ts
View file @
f9716ee1
...
@@ -77,7 +77,7 @@ export class DraftPlanScriptCall
...
@@ -77,7 +77,7 @@ export class DraftPlanScriptCall
readonly
kind
=
'script'
as
const
;
readonly
kind
=
'script'
as
const
;
readonly
callKey
=
'draft_plan_script'
;
readonly
callKey
=
'draft_plan_script'
;
readonly
promptVersion
=
DRAFT_PLAN_SCRIPT_PROMPT_VERSION
;
readonly
promptVersion
=
DRAFT_PLAN_SCRIPT_PROMPT_VERSION
;
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
DraftPlanScriptSchema
;
readonly
outputSchema
=
DraftPlanScriptSchema
;
readonly
safetyRules
=
safetyRules
;
readonly
safetyRules
=
safetyRules
;
...
...
apps/pac-service/src/modules/ai/calls/draft-plan-script/tiers/standard/standard.call.ts
View file @
f9716ee1
...
@@ -30,7 +30,7 @@ export class StandardScriptCall implements AiCall<DraftPlanScriptInput, DeepDraf
...
@@ -30,7 +30,7 @@ export class StandardScriptCall implements AiCall<DraftPlanScriptInput, DeepDraf
readonly
kind
=
'script'
as
const
;
readonly
kind
=
'script'
as
const
;
readonly
callKey
=
'draft_plan_script'
;
readonly
callKey
=
'draft_plan_script'
;
readonly
promptVersion
=
STANDARD_PROMPT_VERSION
;
readonly
promptVersion
=
STANDARD_PROMPT_VERSION
;
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
StandardScriptSchema
;
readonly
outputSchema
=
StandardScriptSchema
;
// 显式标 SafetyRule<DeepDraft>,让 runner.run 的 TOutput 推成 DeepDraft(否则会被结构型窄化丢 tone)
// 显式标 SafetyRule<DeepDraft>,让 runner.run 的 TOutput 推成 DeepDraft(否则会被结构型窄化丢 tone)
readonly
safetyRules
:
ReadonlyArray
<
SafetyRule
<
DeepDraft
>>
=
SCRIPT_SAFETY_RULES_SECTIONS
;
readonly
safetyRules
:
ReadonlyArray
<
SafetyRule
<
DeepDraft
>>
=
SCRIPT_SAFETY_RULES_SECTIONS
;
...
...
apps/pac-service/src/modules/ai/calls/draft-wecom-script/calls.ts
View file @
f9716ee1
...
@@ -52,7 +52,7 @@ export class WecomWriteCall implements AiCall<WecomWriteInput, WecomWriteZ> {
...
@@ -52,7 +52,7 @@ export class WecomWriteCall implements AiCall<WecomWriteInput, WecomWriteZ> {
readonly
callKey
=
'draft_wecom_script_write'
;
readonly
callKey
=
'draft_wecom_script_write'
;
// ⚠️ 换 promptVersion 会让缓存整体失效(这是想要的):流程和形态都变了,旧缓存不该再命中
// ⚠️ 换 promptVersion 会让缓存整体失效(这是想要的):流程和形态都变了,旧缓存不该再命中
readonly
promptVersion
=
'draft_wecom_script@2026-08-05-single-v2'
;
readonly
promptVersion
=
'draft_wecom_script@2026-08-05-single-v2'
;
readonly
defaultModelId
=
'deepseek-
v4-
flash'
;
readonly
defaultModelId
=
'deepseek-flash'
;
readonly
outputSchema
=
WecomWriteSchema
;
readonly
outputSchema
=
WecomWriteSchema
;
constructor
(
private
readonly
skillRegistry
:
DraftPlanScriptSkillRegistry
)
{}
constructor
(
private
readonly
skillRegistry
:
DraftPlanScriptSkillRegistry
)
{}
buildPrompt
(
input
:
WecomWriteInput
)
{
buildPrompt
(
input
:
WecomWriteInput
)
{
...
...
apps/pac-service/src/modules/ai/core/ai-provider.service.ts
View file @
f9716ee1
...
@@ -5,6 +5,7 @@ import { createGoogleGenerativeAI, type GoogleGenerativeAIProvider } from '@ai-s
...
@@ -5,6 +5,7 @@ import { createGoogleGenerativeAI, type GoogleGenerativeAIProvider } from '@ai-s
import
{
createOpenAICompatible
,
type
OpenAICompatibleProvider
}
from
'@ai-sdk/openai-compatible'
;
import
{
createOpenAICompatible
,
type
OpenAICompatibleProvider
}
from
'@ai-sdk/openai-compatible'
;
import
type
{
LanguageModel
}
from
'ai'
;
import
type
{
LanguageModel
}
from
'ai'
;
import
type
{
AppConfig
}
from
'../../../config/configuration'
;
import
type
{
AppConfig
}
from
'../../../config/configuration'
;
import
{
canonicalModelId
}
from
'../../../config/ai-models'
;
/**
/**
* AiProviderService — 模型工厂。
* AiProviderService — 模型工厂。
...
@@ -31,7 +32,7 @@ export class AiProviderService {
...
@@ -31,7 +32,7 @@ export class AiProviderService {
constructor
(
private
readonly
config
:
ConfigService
<
AppConfig
,
true
>
)
{
constructor
(
private
readonly
config
:
ConfigService
<
AppConfig
,
true
>
)
{
const
ai
=
this
.
config
.
get
(
'ai'
,
{
infer
:
true
});
const
ai
=
this
.
config
.
get
(
'ai'
,
{
infer
:
true
});
this
.
defaultModel
=
ai
.
defaultModel
;
this
.
defaultModel
=
canonicalModelId
(
ai
.
defaultModel
)
;
this
.
geminiDefaultModel
=
ai
.
geminiDefaultModel
;
this
.
geminiDefaultModel
=
ai
.
geminiDefaultModel
;
this
.
qwenDefaultModel
=
ai
.
qwenDefaultModel
;
this
.
qwenDefaultModel
=
ai
.
qwenDefaultModel
;
...
@@ -105,14 +106,14 @@ export class AiProviderService {
...
@@ -105,14 +106,14 @@ export class AiProviderService {
/**
/**
* 解析 modelId → vendor 实例 + 规范化具体 modelId(落账 modelName / 计费 priceTable 用规范化后的)。
* 解析 modelId → vendor 实例 + 规范化具体 modelId(落账 modelName / 计费 priceTable 用规范化后的)。
* 命名约定:
* 命名约定:
* "deepseek-v4-pro" / "deepseek-
v4-
flash" / "deepseek"(裸键)→ deepseek
* "deepseek-v4-pro" / "deepseek-flash" / "deepseek"(裸键)→ deepseek
* "gemini-2.5-flash" / "gemini"(裸键) → gemini
* "gemini-2.5-flash" / "gemini"(裸键) → gemini
* "qwen-max"(将来) → qwen
* "qwen-max"(将来) → qwen
* 裸键(无 "-" 版本)→ 解析到 config 配的默认型号;
带版本 → 原样直传 SDK
。
* 裸键(无 "-" 版本)→ 解析到 config 配的默认型号;
具体型号 → 直传 SDK(旧 Flash 别名归一为 deepseek-flash)
。
*/
*/
resolve
(
modelId
:
string
):
{
model
:
LanguageModel
;
provider
:
string
;
modelId
:
string
}
{
resolve
(
modelId
:
string
):
{
model
:
LanguageModel
;
provider
:
string
;
modelId
:
string
}
{
if
(
modelId
===
'deepseek'
||
modelId
.
startsWith
(
'deepseek-'
))
{
if
(
modelId
===
'deepseek'
||
modelId
.
startsWith
(
'deepseek-'
))
{
const
canonical
=
modelId
===
'deepseek'
?
this
.
defaultModel
:
modelId
;
const
canonical
=
canonicalModelId
(
modelId
===
'deepseek'
?
this
.
defaultModel
:
modelId
)
;
return
{
model
:
this
.
deepseek
(
canonical
),
provider
:
'deepseek'
,
modelId
:
canonical
};
return
{
model
:
this
.
deepseek
(
canonical
),
provider
:
'deepseek'
,
modelId
:
canonical
};
}
}
if
(
modelId
===
'gemini'
||
modelId
.
startsWith
(
'gemini-'
))
{
if
(
modelId
===
'gemini'
||
modelId
.
startsWith
(
'gemini-'
))
{
...
...
apps/pac-service/src/modules/ai/core/cost.ts
View file @
f9716ee1
import
{
canonicalModelId
}
from
'../../../config/ai-models'
;
/**
/**
* 成本估算 —— 纯函数,`AiCallRunner` 与助手共用**同一份**口径。
* 成本估算 —— 纯函数,`AiCallRunner` 与助手共用**同一份**口径。
*
*
...
@@ -30,7 +32,11 @@ export function estimateCostYuan(
...
@@ -30,7 +32,11 @@ export function estimateCostYuan(
completionTokens
:
number
,
completionTokens
:
number
,
cachedInputTokens
=
0
,
cachedInputTokens
=
0
,
):
CostResult
{
):
CostResult
{
const
p
=
priceTable
[
modelId
];
// 新旧 Flash 名使用同一计价;兼容部署时仍按旧名称配置的自定义价目表。
const
canonical
=
canonicalModelId
(
modelId
);
const
p
=
priceTable
[
canonical
]
??
priceTable
[
modelId
]
??
(
canonical
===
'deepseek-flash'
?
priceTable
[
'deepseek-v4-flash'
]
??
priceTable
[
'deepseek-v4-flash-vision-exp'
]
:
undefined
);
const
price
=
p
??
priceTable
[
'deepseek-v4-pro'
]
??
FALLBACK_PRICE
;
const
price
=
p
??
priceTable
[
'deepseek-v4-pro'
]
??
FALLBACK_PRICE
;
// 防御:cached > prompt 不该发生,clamp
// 防御:cached > prompt 不该发生,clamp
const
hit
=
Math
.
min
(
Math
.
max
(
0
,
cachedInputTokens
),
Math
.
max
(
0
,
promptTokens
));
const
hit
=
Math
.
min
(
Math
.
max
(
0
,
cachedInputTokens
),
Math
.
max
(
0
,
promptTokens
));
...
...
apps/pac-service/src/modules/ai/orchestrators/plan-script.orchestrator.ts
View file @
f9716ee1
...
@@ -270,7 +270,7 @@ export class PlanScriptOrchestrator {
...
@@ -270,7 +270,7 @@ export class PlanScriptOrchestrator {
yield
{
yield
{
type
:
'start'
,
type
:
'start'
,
invocationId
:
''
,
invocationId
:
''
,
modelId
:
options
.
modelIdOverride
??
'deepseek-
v4-
flash'
,
modelId
:
options
.
modelIdOverride
??
'deepseek-flash'
,
promptVersion
:
'draft_plan_script@deep-pipeline'
,
promptVersion
:
'draft_plan_script@deep-pipeline'
,
};
};
let
r
:
DeepScriptResult
;
let
r
:
DeepScriptResult
;
...
...
apps/pac-service/tests/deepseek-model-config.spec.ts
0 → 100644
View file @
f9716ee1
import
{
loadConfig
}
from
'../src/config/configuration'
;
import
{
AiProviderService
}
from
'../src/modules/ai/core/ai-provider.service'
;
import
{
estimateCostYuan
}
from
'../src/modules/ai/core/cost'
;
import
{
ConfigService
}
from
'@nestjs/config'
;
import
type
{
AppConfig
}
from
'../src/config/configuration'
;
jest
.
mock
(
'@ai-sdk/deepseek'
,
()
=>
({
createDeepSeek
:
()
=>
(
id
:
string
)
=>
({
modelId
:
id
})
}));
jest
.
mock
(
'@ai-sdk/google'
,
()
=>
({
createGoogleGenerativeAI
:
()
=>
(
id
:
string
)
=>
({
modelId
:
id
})
}));
jest
.
mock
(
'@ai-sdk/openai-compatible'
,
()
=>
({
createOpenAICompatible
:
()
=>
(
id
:
string
)
=>
({
modelId
:
id
})
}));
describe
(
'DeepSeek Flash 型号与计价迁移'
,
()
=>
{
const
originalEnv
=
process
.
env
;
beforeEach
(()
=>
{
process
.
env
=
{
DATABASE_URL
:
'postgresql://unused'
,
REDIS_URL
:
'redis://unused'
,
JWT_SECRET
:
'test-only'
,
JWT_REFRESH_SECRET
:
'test-only'
,
DEEPSEEK_API_KEY
:
'test-only'
,
GEMINI_API_KEY
:
'test-only'
,
QWEN_API_KEY
:
'test-only'
,
};
});
afterEach
(()
=>
{
process
.
env
=
originalEnv
;
});
function
provider
()
{
const
config
=
loadConfig
();
return
new
AiProviderService
(
new
ConfigService
<
AppConfig
,
true
>
(
config
));
}
it
(
'默认配置、SDK 请求和落账型号一致'
,
()
=>
{
expect
(
loadConfig
().
ai
.
defaultModel
).
toBe
(
'deepseek-flash'
);
const
resolved
=
provider
().
resolve
(
'deepseek'
);
expect
(
resolved
.
modelId
).
toBe
(
'deepseek-flash'
);
expect
(
resolved
.
model
).
toMatchObject
({
modelId
:
'deepseek-flash'
});
expect
(
resolved
.
provider
).
toBe
(
'deepseek'
);
});
it
.
each
([
'deepseek-v4-flash'
,
'deepseek-v4-flash-vision-exp'
])(
'兼容环境变量和显式旧别名 %s'
,
alias
=>
{
process
.
env
.
AI_DEFAULT_MODEL
=
alias
;
expect
(
loadConfig
().
ai
.
defaultModel
).
toBe
(
'deepseek-flash'
);
expect
(
provider
().
getDefaultModelId
()).
toBe
(
'deepseek-flash'
);
expect
(
provider
().
resolve
(
alias
).
model
).
toMatchObject
({
modelId
:
'deepseek-flash'
});
});
it
(
'保留显式 Pro 配置与其他供应商路由'
,
()
=>
{
process
.
env
.
AI_DEFAULT_MODEL
=
'deepseek-v4-pro'
;
expect
(
provider
().
resolve
(
'deepseek'
).
modelId
).
toBe
(
'deepseek-v4-pro'
);
expect
(
provider
().
resolve
(
'qwen'
).
provider
).
toBe
(
'qwen'
);
expect
(
provider
().
resolve
(
'gemini'
).
provider
).
toBe
(
'gemini'
);
});
it
.
each
([
'deepseek-flash'
,
'deepseek-v4-flash'
,
'deepseek-v4-flash-vision-exp'
])(
'%s 按 Flash 价估算,不误落 Pro 兜底'
,
model
=>
{
const
table
=
loadConfig
().
ai
.
priceTable
;
expect
(
estimateCostYuan
(
table
,
model
,
1
_000_000
,
1
_000_000
,
500
_000
))
.
toEqual
({
yuan
:
9.02
,
priceMissing
:
false
});
});
it
(
'沿用旧名称的自定义价格,不悄悄替换用户配置'
,
()
=>
{
process
.
env
.
AI_PRICE_TABLE_JSON
=
JSON
.
stringify
({
'deepseek-v4-flash'
:
{
inHit
:
1
,
inMiss
:
2
,
out
:
3
}
});
expect
(
estimateCostYuan
(
loadConfig
().
ai
.
priceTable
,
'deepseek-flash'
,
1
_000_000
,
1
_000_000
))
.
toEqual
({
yuan
:
5
,
priceMissing
:
false
});
});
it
(
'自定义价格同时有新旧名称时,以新名称为准'
,
()
=>
{
const
table
=
{
'deepseek-flash'
:
{
inHit
:
1
,
inMiss
:
2
,
out
:
3
},
'deepseek-v4-flash'
:
{
inHit
:
7
,
inMiss
:
8
,
out
:
9
},
};
expect
(
estimateCostYuan
(
table
,
'deepseek-v4-flash'
,
1
_000_000
,
1
_000_000
))
.
toEqual
({
yuan
:
5
,
priceMissing
:
false
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment