1- // J4 service tuning: draft (beta) usable → config change persisted → released version usable after deploy.
1+ // J4 service tuning: draft (beta) usable → config change persisted → released version carries the change after deploy.
22// create + service create (search, initial draft/beta) → search --agent-version beta recalls (hard)
3- // → service update --description → service get asserts the change (hard) → deploy → released search recalls (hard).
3+ // → service update --description/--temperature (scalar merge path) → get (beta) asserts (hard)
4+ // → service update --config-file tweaking kb_search_configs (whole-replace path) → get (beta) asserts
5+ // the nested change AND that the replace kept the scalar tuning (hard) → deploy
6+ // → get (released version) asserts both changes landed in the published version (hard) → released search recalls (hard).
7+ import { mkdtempSync , writeFileSync } from "node:fs" ;
8+ import { tmpdir } from "node:os" ;
9+ import { join } from "node:path" ;
410import { describe , expect , test } from "vite-plus/test" ;
511import { isKbAdminE2EReady , parseStdoutJson } from "../../helpers.ts" ;
612import { JOURNEY_J4_ROUTES } from "../../topic-routes.ts" ;
713import {
814 cleanupKbFixture ,
915 createJourneyReporter ,
1016 createKbWithDocs ,
17+ nodesRecallMarker ,
1118 patchSearchServiceRetrievalConfig ,
1219 pollUntil ,
1320 uniqueMarker ,
@@ -17,7 +24,7 @@ import {
1724describe . skipIf ( ! isKbAdminE2EReady ( ) ) ( "journey J4: 问答服务调优 (live, 自清理)" , ( ) => {
1825 const workspaceId = process . env . BAILIAN_WORKSPACE_ID ! ;
1926
20- test ( "draft 可用 → update 落库 → deploy → 正式版可用 " , async ( ) => {
27+ test ( "draft 可用 → update 落库 → deploy → 正式版详情携带修改且可用 " , async ( ) => {
2128 const reporter = createJourneyReporter ( import . meta. url ) ;
2229 const marker = uniqueMarker ( "j4" ) ;
2330 const fixture : Partial < KbFixture > = { } ;
@@ -71,25 +78,33 @@ describe.skipIf(!isKbAdminE2EReady())("journey J4: 问答服务调优 (live, 自
7178 ] ;
7279 const betaPoll = await pollUntil (
7380 ( ) => reporter . runStep ( "search (beta)" , JOURNEY_J4_ROUTES , betaSearchArgs ) ,
74- ( run ) => run . exitCode === 0 && run . stdout . includes ( marker ) ,
81+ ( run ) => run . exitCode === 0 && nodesRecallMarker ( run . stdout , marker ) ,
7582 { timeoutMs : 180_000 , intervalMs : 15_000 } ,
7683 ) ;
7784 reporter . recordNote ( `beta search 轮询 ${ betaPoll . attempts } 次` ) ;
7885 expect ( betaPoll . satisfied , `search(beta) 未召回标记词 ${ marker } ` ) . toBe ( true ) ;
7986
80- // 3) update the description (top-level scalar, valid for the search scene too) → get asserts persistence (hard)
87+ // 3) tune the draft: description (top-level) + temperature (config-level scalar,
88+ // read-merge-write keeps the backfilled retrieval params) → get asserts both (hard)
8189 const newDescription = `journey j4 tuned at ${ Date . now ( ) } ` ;
82- const updateRun = await reporter . runStep ( "service update --description" , JOURNEY_J4_ROUTES , [
83- "knowledge" ,
84- "service" ,
85- "update" ,
86- "--agent-id" ,
87- agentId ,
88- "--description" ,
89- newDescription ,
90- "--workspace-id" ,
91- workspaceId ,
92- ] ) ;
90+ const tunedTemperature = 0.55 ;
91+ const updateRun = await reporter . runStep (
92+ "service update --description --temperature" ,
93+ JOURNEY_J4_ROUTES ,
94+ [
95+ "knowledge" ,
96+ "service" ,
97+ "update" ,
98+ "--agent-id" ,
99+ agentId ,
100+ "--description" ,
101+ newDescription ,
102+ "--temperature" ,
103+ String ( tunedTemperature ) ,
104+ "--workspace-id" ,
105+ workspaceId ,
106+ ] ,
107+ ) ;
93108 expect ( updateRun . exitCode , updateRun . stderr ) . toBe ( 0 ) ;
94109
95110 const getRun = await reporter . runStep ( "service get (beta)" , JOURNEY_J4_ROUTES , [
@@ -106,10 +121,88 @@ describe.skipIf(!isKbAdminE2EReady())("journey J4: 问答服务调优 (live, 自
106121 "json" ,
107122 ] ) ;
108123 expect ( getRun . exitCode , getRun . stderr ) . toBe ( 0 ) ;
109- const getData = parseStdoutJson < { data ?: { agent_desc ?: string } } > ( getRun . stdout ) ;
124+ const getData = parseStdoutJson < {
125+ data ?: {
126+ agent_desc ?: string ;
127+ agent_details ?: Array < {
128+ agent_config ?: { temperature ?: number } & Record < string , unknown > ;
129+ } > ;
130+ } ;
131+ } > ( getRun . stdout ) ;
110132 expect ( getData . data ?. agent_desc ) . toBe ( newDescription ) ;
133+ expect ( getData . data ?. agent_details ?. [ 0 ] ?. agent_config ?. temperature ) . toBe ( tunedTemperature ) ;
134+
135+ // 3.5) complex nested tuning via --config-file (whole-replace path, distinct
136+ // from the scalar merge path above): read the current beta config, tweak
137+ // kb_search_configs, write it back — then assert the nested change landed
138+ // AND the replace kept the scalar tuning intact
139+ const tunedDenseTopK = 66 ; // distinctive value, still recall-safe (loose top-k)
140+ const betaConfig = getData . data ?. agent_details ?. [ 0 ] ?. agent_config as
141+ | ( { kb_search_configs ?: Array < Record < string , unknown > > } & Record < string , unknown > )
142+ | undefined ;
143+ expect ( betaConfig ?. kb_search_configs ?. length , "beta 配置应含 kb_search_configs" ) . toBeTruthy ( ) ;
144+ for ( const kbConfig of betaConfig ! . kb_search_configs ! ) {
145+ kbConfig . dense_similarity_top_k = tunedDenseTopK ;
146+ }
147+ const configDir = mkdtempSync ( join ( tmpdir ( ) , "j4-config-" ) ) ;
148+ const configFile = join ( configDir , "agent-config.json" ) ;
149+ writeFileSync ( configFile , JSON . stringify ( betaConfig ) ) ;
150+ const configUpdateRun = await reporter . runStep (
151+ "service update --config-file (kb_search_configs)" ,
152+ JOURNEY_J4_ROUTES ,
153+ [
154+ "knowledge" ,
155+ "service" ,
156+ "update" ,
157+ "--agent-id" ,
158+ agentId ,
159+ "--config-file" ,
160+ configFile ,
161+ "--workspace-id" ,
162+ workspaceId ,
163+ ] ,
164+ ) ;
165+ expect ( configUpdateRun . exitCode , configUpdateRun . stderr ) . toBe ( 0 ) ;
166+
167+ const betaAfterConfigRun = await reporter . runStep (
168+ "service get (beta, after config-file)" ,
169+ JOURNEY_J4_ROUTES ,
170+ [
171+ "knowledge" ,
172+ "service" ,
173+ "get" ,
174+ "--agent-id" ,
175+ agentId ,
176+ "--agent-version" ,
177+ "beta" ,
178+ "--workspace-id" ,
179+ workspaceId ,
180+ "--output" ,
181+ "json" ,
182+ ] ,
183+ ) ;
184+ expect ( betaAfterConfigRun . exitCode , betaAfterConfigRun . stderr ) . toBe ( 0 ) ;
185+ const betaAfterConfig = parseStdoutJson < {
186+ data ?: {
187+ agent_details ?: Array < {
188+ agent_config ?: {
189+ temperature ?: number ;
190+ kb_search_configs ?: Array < { dense_similarity_top_k ?: number } > ;
191+ } ;
192+ } > ;
193+ } ;
194+ } > ( betaAfterConfigRun . stdout ) . data ?. agent_details ?. [ 0 ] ?. agent_config ;
195+ expect (
196+ betaAfterConfig ?. kb_search_configs ?. [ 0 ] ?. dense_similarity_top_k ,
197+ "kb_search_configs 嵌套修改未落库" ,
198+ ) . toBe ( tunedDenseTopK ) ;
199+ expect ( betaAfterConfig ?. temperature , "config-file 整体替换不应冲掉已调优的 temperature" ) . toBe (
200+ tunedTemperature ,
201+ ) ;
111202
112- // 4) deploy → released search (without --agent-version) recalls (hard)
203+ // 4) deploy → the published version's detail must carry the tuned config (hard):
204+ // search alone only proves the released service responds, not that the
205+ // change actually shipped
113206 const deployRun = await reporter . runStep ( "service deploy" , JOURNEY_J4_ROUTES , [
114207 "knowledge" ,
115208 "service" ,
@@ -122,8 +215,52 @@ describe.skipIf(!isKbAdminE2EReady())("journey J4: 问答服务调优 (live, 自
122215 "--quiet" ,
123216 ] ) ;
124217 expect ( deployRun . exitCode , deployRun . stderr ) . toBe ( 0 ) ;
125- reporter . recordNote ( `deploy 版本号: ${ deployRun . stdout . trim ( ) . split ( "\n" ) . pop ( ) ?? "?" } ` ) ;
218+ const deployedVersion = deployRun . stdout . trim ( ) . split ( "\n" ) . pop ( ) ?? "" ;
219+ expect ( deployedVersion , "deploy 应输出新版本号" ) . toBeTruthy ( ) ;
220+ reporter . recordNote ( `deploy 版本号: ${ deployedVersion } ` ) ;
221+
222+ const releasedGetRun = await reporter . runStep (
223+ `service get (released v${ deployedVersion } )` ,
224+ JOURNEY_J4_ROUTES ,
225+ [
226+ "knowledge" ,
227+ "service" ,
228+ "get" ,
229+ "--agent-id" ,
230+ agentId ,
231+ "--agent-version" ,
232+ deployedVersion ,
233+ "--workspace-id" ,
234+ workspaceId ,
235+ "--output" ,
236+ "json" ,
237+ ] ,
238+ ) ;
239+ expect ( releasedGetRun . exitCode , releasedGetRun . stderr ) . toBe ( 0 ) ;
240+ const releasedData = parseStdoutJson < {
241+ data ?: {
242+ agent_details ?: Array < {
243+ agent_version ?: string ;
244+ agent_config ?: {
245+ temperature ?: number ;
246+ kb_search_configs ?: Array < { dense_similarity_top_k ?: number } > ;
247+ } ;
248+ } > ;
249+ } ;
250+ } > ( releasedGetRun . stdout ) ;
251+ const releasedDetail = releasedData . data ?. agent_details ?. find (
252+ ( detail ) => detail . agent_version === deployedVersion ,
253+ ) ;
254+ expect ( releasedDetail , `get 应返回已发布版本 ${ deployedVersion } 的详情` ) . toBeTruthy ( ) ;
255+ expect ( releasedDetail ?. agent_config ?. temperature , "调优的 temperature 未进入正式版配置" ) . toBe (
256+ tunedTemperature ,
257+ ) ;
258+ expect (
259+ releasedDetail ?. agent_config ?. kb_search_configs ?. [ 0 ] ?. dense_similarity_top_k ,
260+ "调优的 kb_search_configs 未进入正式版配置" ,
261+ ) . toBe ( tunedDenseTopK ) ;
126262
263+ // 5) released search (without --agent-version) recalls (hard)
127264 const releasedPoll = await pollUntil (
128265 ( ) =>
129266 reporter . runStep ( "search (released)" , JOURNEY_J4_ROUTES , [
@@ -138,7 +275,7 @@ describe.skipIf(!isKbAdminE2EReady())("journey J4: 问答服务调优 (live, 自
138275 "--output" ,
139276 "json" ,
140277 ] ) ,
141- ( run ) => run . exitCode === 0 && run . stdout . includes ( marker ) ,
278+ ( run ) => run . exitCode === 0 && nodesRecallMarker ( run . stdout , marker ) ,
142279 { timeoutMs : 120_000 , intervalMs : 15_000 } ,
143280 ) ;
144281 reporter . recordNote ( `正式版 search 轮询 ${ releasedPoll . attempts } 次` ) ;
0 commit comments