@roll-agent/smart-reply-agent 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,14 @@
1
- import { type LanguageModel, type LanguageModelUsage } from "ai";
1
+ import { type FlexibleSchema, type LanguageModel, type LanguageModelUsage } from "ai";
2
2
  import { z } from "zod";
3
3
  import { type AppError } from "../errors/index.ts";
4
4
  export interface SafeGenerateObjectOptions<T> {
5
5
  model: LanguageModel;
6
6
  schema: z.ZodType<T>;
7
+ outputSchema?: FlexibleSchema<T> | undefined;
7
8
  schemaName?: string | undefined;
8
9
  system?: string | undefined;
9
10
  prompt: string;
11
+ transformOutput?: ((value: unknown) => unknown | Promise<unknown>) | undefined;
10
12
  onError?: ((error: AppError, rawText?: string) => void) | undefined;
11
13
  }
12
14
  export interface SafeGenerateObjectSuccess<T> {
@@ -20,6 +22,16 @@ export interface SafeGenerateObjectFailure {
20
22
  rawText?: string | undefined;
21
23
  }
22
24
  export type SafeGenerateObjectResult<T> = SafeGenerateObjectSuccess<T> | SafeGenerateObjectFailure;
25
+ /**
26
+ * 按 JSON Schema type 声明不支持的关键词。
27
+ * key = JSON Schema type 名称("array" | "number" | "integer" | "string" 等),
28
+ * value = 该类型下需要剥离的关键词列表。
29
+ * "integer" 会自动合并 "number" 的规则,无需重复声明。
30
+ */
31
+ export interface StructuredOutputCompatibilityOptions {
32
+ unsupportedKeywordsByType?: Readonly<Record<string, readonly string[]>> | undefined;
33
+ }
34
+ export declare function createStructuredOutputCompatibilitySchema<T>(schema: FlexibleSchema<T>, options?: StructuredOutputCompatibilityOptions): FlexibleSchema<T>;
23
35
  export declare function safeGenerateObject<T>(options: SafeGenerateObjectOptions<T>): Promise<SafeGenerateObjectResult<T>>;
24
36
  export interface SafeGenerateTextOptions {
25
37
  model: LanguageModel;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{defineAgent as o}from"@roll-agent/sdk";import{generateReply as r}from"./tools/generate-reply.js";import{syncBrandData as t}from"./tools/sync-brand-data.js";const e=o({name:"smart-reply-agent",tools:[r,t]});e.listen().catch(o=>{console.error("Fatal error:",o),process.exit(1)});
1
+ import{defineAgent as e}from"@roll-agent/sdk";import{defineTool as t}from"@roll-agent/sdk";import{z as n}from"zod";import{z as r}from"zod";import{z as a}from"zod";var o=a.object({lat:a.number(),lng:a.number()}),i=r.object({name:r.string().optional(),position:r.string().optional(),expectedPosition:r.string().optional(),communicationPosition:r.string().optional(),age:r.string().optional(),gender:r.string().optional(),experience:r.string().optional(),education:r.string().optional(),expectedSalary:r.string().optional(),expectedLocation:r.string().optional(),jobAddress:r.string().optional(),height:r.string().optional(),weight:r.string().optional(),healthCertificate:r.boolean().optional(),activeTime:r.string().optional(),info:r.array(r.string()).optional(),fullText:r.string().optional()}),s=r.object({base:r.number(),range:r.string().optional(),bonus:r.string().optional(),memo:r.string(),scenarioSummary:r.string().optional(),settlementCycle:r.string().optional()}),l=r.object({items:r.array(r.string()),promotion:r.string().optional()}),u=r.object({requiredDays:r.array(r.number().min(1).max(7)).optional(),minimumDays:r.number().min(0).optional(),description:r.string()}),c=r.enum(["fixed","flexible","rotating","on_call"]),d=r.object({punctualityRequired:r.boolean(),lateToleranceMinutes:r.number().min(0),attendanceTracking:r.enum(["strict","flexible","none"]),makeupShiftsAllowed:r.boolean()}),m=r.object({slot:r.string(),maxCapacity:r.number().min(0),currentBooked:r.number().min(0),isAvailable:r.boolean(),priority:r.enum(["high","medium","low"])}),p=r.object({canSwapShifts:r.boolean(),advanceNoticeHours:r.number().min(0),partTimeAllowed:r.boolean(),weekendRequired:r.boolean(),holidayRequired:r.boolean()}),g=r.object({minAge:r.number().nullable().optional(),maxAge:r.number().nullable().optional(),genderRequirement:r.string().nullable().optional(),education:r.string().nullable().optional(),healthCertificate:r.string().nullable().optional()}),f=r.object({id:r.string(),name:r.string(),brandId:r.string().optional(),brandName:r.string().optional(),projectId:r.string().optional(),projectName:r.string().optional(),timeSlots:r.array(r.string()),salary:s,workHours:r.string(),benefits:l,requirements:r.array(r.string()),urgent:r.boolean(),scheduleType:c,attendancePolicy:d,availableSlots:r.array(m),schedulingFlexibility:p,minHoursPerWeek:r.number().min(0).optional(),maxHoursPerWeek:r.number().min(0).optional(),attendanceRequirement:u.optional(),hiringRequirements:g.optional(),description:r.string().optional()}),y=r.object({id:r.string(),brandId:r.string(),name:r.string(),city:r.string().optional(),location:r.string(),district:r.string(),subarea:r.string(),coordinates:o,positions:r.array(f)}),b=r.object({defaultBrandId:r.string().optional(),syncedAt:r.string().optional(),source:r.string().optional()}),h=r.object({id:r.string(),name:r.string(),aliases:r.array(r.string()).optional(),stores:r.array(y)}),S=r.object({meta:b,brands:r.array(h)});import{existsSync as N,readFileSync as I,writeFileSync as T}from"node:fs";import{dirname as A,join as _}from"node:path";import{z as E}from"zod";var R=E.enum(["trust_building","private_channel","qualify_candidate","job_consultation","interview_scheduling","onboard_followup"]),O=E.enum(["public","private"]),L=E.enum(["minimal","focused"]),v=E.enum(["salary","schedule","location","policy","requirements","availability"]),w={trust_building:{description:"初次接触,建立信任并了解求职意向",transitionSignal:"候选人表达明确兴趣或开始询问具体岗位信息",applicableChannels:["public","private"]},private_channel:{description:"引导用户从公域平台(如BOSS直聘/鱼泡)转入微信私聊",transitionSignal:"候选人有继续深入了解的意愿,适合引导到私域",applicableChannels:["public"]},qualify_candidate:{description:"轻量确认候选人的关键匹配信息,避免审查式盘问",transitionSignal:"候选人表达求职意向后,需要核实基本资格",applicableChannels:["public","private"]},job_consultation:{description:"回答岗位相关问题(薪资、排班、地点等)并提升兴趣",transitionSignal:"候选人主动询问岗位细节",applicableChannels:["public","private"]},interview_scheduling:{description:"推动面试预约,确认时间和到店安排",transitionSignal:"候选人核心问题已解答,准备推进面试",applicableChannels:["public","private"]},onboard_followup:{description:"促进到岗并保持回访",transitionSignal:"候选人确认上岗安排",applicableChannels:["public","private"]}},k=E.enum(["stores","location","salary","schedule","policy","availability","requirements","interview","wechat","none"]),M=E.enum(["insurance_promise_risk","age_sensitive","confrontation_emotion","urgency_high","qualification_mismatch"]),D={stores:["location"],location:["location"],salary:["salary"],schedule:["schedule"],policy:["policy"],availability:["availability"],requirements:["requirements"],interview:[],wechat:[],none:[]},x=E.object({mentionedBrand:E.string().nullable(),city:E.string().nullable(),mentionedLocations:E.array(E.object({location:E.string(),confidence:E.number().min(0).max(1)})).nullable(),mentionedDistricts:E.array(E.object({district:E.string(),confidence:E.number().min(0).max(1)})).max(10).nullable(),specificAge:E.number().nullable(),hasUrgency:E.boolean().nullable(),preferredSchedule:E.string().nullable()}),C=E.object({stage:R,subGoals:E.array(E.string()).max(2),needs:E.array(k).max(8),primaryNeed:k,riskFlags:E.array(M).max(6),confidence:E.number().min(0).max(1),extractedInfo:x,reasoningText:E.string()}),U=E.object({description:E.string().optional(),primaryGoal:E.string(),successCriteria:E.array(E.string()),ctaStrategy:E.preprocess(e=>Array.isArray(e)?e.join("\n"):e,E.string()),disallowedActions:E.array(E.string()).optional()}),$=E.object({tone:E.string(),warmth:E.string(),humor:E.string(),length:E.enum(["short","medium","long"]),questionStyle:E.string(),empathyStrategy:E.string(),addressStyle:E.string(),professionalIdentity:E.string(),companyBackground:E.string()}),j=E.object({name:E.string(),industryBackground:E.string(),jargon:E.array(E.string()),styleKeywords:E.array(E.string()),tabooPhrases:E.array(E.string()),guidance:E.array(E.string())}),W=E.object({id:E.string(),rule:E.string(),severity:E.enum(["high","medium","low"])}),F=E.object({rules:E.array(W)}),P=E.object({mode:E.enum(["strict","balanced","open"]),verifiableClaimTypes:E.array(E.string()),fallbackBehavior:E.enum(["generic_answer","ask_followup","handoff"]),forbiddenWhenMissingFacts:E.array(E.string())}),B={maxQuestionsByMode:{minimal:1,focused:2},blockedAuditPhrases:["是否满足","是否符合","基本入职要求","先确认资格","年龄是否符合"],blockFirstTurnSpecificFacts:!0},G=E.object({maxQuestionsByMode:E.object({minimal:E.number().int().min(0),focused:E.number().int().min(0)}),blockedAuditPhrases:E.array(E.string()),blockFirstTurnSpecificFacts:E.boolean()}),q=E.object({enabled:E.boolean().default(!0),revealRange:E.boolean().default(!1),failStrategy:E.string().default("礼貌说明不匹配,避免承诺"),unknownStrategy:E.string().default("先核实年龄或资格条件"),passStrategy:E.string().default("确认匹配后推进下一步"),allowRedirect:E.boolean().default(!0),redirectPriority:E.enum(["low","medium","high"]).default("medium")}),H=E.object({age:q.default({enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"})}).default({age:{enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"}}),K=E.object({trust_building:U,private_channel:U.optional(),qualify_candidate:U,job_consultation:U,interview_scheduling:U,onboard_followup:U}).transform(e=>({...e,private_channel:e.private_channel??e.trust_building})),V=E.object({stageGoals:K,persona:$,industryVoices:E.record(E.string(),j),defaultIndustryVoiceId:E.string(),hardConstraints:F,factGate:P,qualificationPolicy:H,outputGuards:G.default(B)}),Y={stageGoals:{trust_building:{description:"初次接触,建立信任并了解求职意向",primaryGoal:"建立信任并了解求职意向",successCriteria:["候选人愿意继续沟通"],ctaStrategy:"用轻量提问引导需求细化",disallowedActions:["过早承诺具体待遇"]},private_channel:{description:"引导用户从公域平台(如BOSS直聘/鱼泡)转入微信私聊",primaryGoal:"推动进入私域沟通",successCriteria:["候选人愿意交换联系方式"],ctaStrategy:"说明后续沟通效率与资料同步价值",disallowedActions:["强迫式要微信"]},qualify_candidate:{description:"轻量确认候选人的关键匹配信息,避免审查式盘问",primaryGoal:"确认一个关键匹配信息并保持继续沟通意愿",successCriteria:["明确一个关键匹配信息","候选人愿意继续沟通"],ctaStrategy:"先回应关切,再顺带确认一个最关键条件",disallowedActions:["连续盘问多个资格条件","直接否定候选人"]},job_consultation:{description:"回答岗位相关问题(薪资、排班、地点等)并提升兴趣",primaryGoal:"回答岗位问题并提升兴趣",successCriteria:["候选人对岗位保持兴趣"],ctaStrategy:"先答核心问题,再给下一步建议",disallowedActions:["编造数字或政策"]},interview_scheduling:{description:"推动面试预约,确认时间和到店安排",primaryGoal:"推动面试预约",successCriteria:["候选人给出可面试时间"],ctaStrategy:"给出明确时间选项并确认",disallowedActions:["不确认候选人可到店性"]},onboard_followup:{description:"促进到岗并保持回访",primaryGoal:"促进到岗并保持回访",successCriteria:["候选人确认上岗安排"],ctaStrategy:"明确下一步动作与提醒",disallowedActions:["承诺不确定资源"]}},persona:{tone:"口语化",warmth:"高",humor:"低",length:"short",questionStyle:"单轮一个关键问题",empathyStrategy:"先认可关切再给建议",addressStyle:"使用你",professionalIdentity:"资深招聘专员",companyBackground:"连锁餐饮招聘"},industryVoices:{default:{name:"餐饮连锁招聘",industryBackground:"门店密集、排班灵活、强调稳定出勤",jargon:["排班","到岗","门店","班次"],styleKeywords:["直接","清晰","可信"],tabooPhrases:["包过","绝对","随便都行"],guidance:["先解决顾虑,再推动下一步"]}},defaultIndustryVoiceId:"default",hardConstraints:{rules:[{id:"no-fabrication",rule:"不得编造门店、薪资、排班、福利等事实信息",severity:"high"},{id:"no-insurance-promise",rule:"兼职场景不得承诺五险一金",severity:"high"},{id:"age-sensitive",rule:"年龄敏感问题使用合规话术,不暴露内部筛选线",severity:"high"}]},factGate:{mode:"strict",verifiableClaimTypes:["salary","location","schedule","policy","availability"],fallbackBehavior:"generic_answer",forbiddenWhenMissingFacts:["具体数字","具体门店承诺","明确福利承诺"]},qualificationPolicy:{age:{enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"}},outputGuards:B};function z(e){let t=e;for(;;){if(N(_(t,"package.json")))return t;const n=A(t);if(n===t)throw new Error(`Could not locate package root from ${e}`);t=n}}var J=_(z(import.meta.dirname),"data"),Q=_(J,"brand-config.json"),Z=_(J,"reply-policy.json"),X=3e5,ee=null,te=null;function ne(e){return Date.now()-e<X}function re(){if(ee&&ne(ee.loadedAt))return ee.data;const e=I(Q,"utf-8"),t=S.parse(JSON.parse(e));return ee={data:t,loadedAt:Date.now()},t}function ae(e){T(Q,JSON.stringify(e,null,2),"utf-8"),ee={data:e,loadedAt:Date.now()}}function oe(){if(te&&ne(te.loadedAt))return te.data;try{const e=I(Z,"utf-8"),t=V.parse(JSON.parse(e));return te={data:t,loadedAt:Date.now()},t}catch{return Y}}import{createAnthropic as ie}from"@ai-sdk/anthropic";import{createAlibaba as se}from"@ai-sdk/alibaba";import{createProviderRegistry as le}from"ai";import{createOpenAICompatible as ue}from"@ai-sdk/openai-compatible";import{createDeepSeek as ce}from"@ai-sdk/deepseek";import{createGoogleGenerativeAI as de}from"@ai-sdk/google";import{createOpenAI as me}from"@ai-sdk/openai";var pe=!1;function ge(e){pe=e}function fe(...e){pe||console.error(...e)}var ye=process.env.SMART_REPLY_PROXY_BASE_URL,be="https://apic1.ohmycdn.com/v1",he=ye||be,Se={anthropic:process.env.ANTHROPIC_BASE_URL||he,openai:process.env.OPENAI_BASE_URL||he,ohmygpt:process.env.OHMYGPT_BASE_URL||he,moonshotai:process.env.MOONSHOT_BASE_URL||"https://api.moonshot.cn/v1",deepseek:process.env.DEEPSEEK_BASE_URL||"https://api.deepseek.com",qwen:process.env.QWEN_BASE_URL||"https://dashscope.aliyuncs.com/compatible-mode/v1",google:process.env.GOOGLE_BASE_URL||"https://generativelanguage.googleapis.com/v1beta"},Ne={anthropic:{name:"Anthropic",baseURL:Se.anthropic,description:"Anthropic Claude"},openai:{name:"OpenAI",baseURL:Se.openai,description:"OpenAI GPT"},ohmygpt:{name:"OhMyGPT",baseURL:Se.ohmygpt,description:"OhMyGPT"},moonshotai:{name:"MoonshotAI",baseURL:Se.moonshotai,description:"MoonshotAI"},deepseek:{name:"DeepSeek",baseURL:Se.deepseek,description:"DeepSeek"},qwen:{name:"Qwen",baseURL:Se.qwen,description:"Qwen"},google:{name:"Google",baseURL:Se.google,description:"Google Gemini"}},Ie={chatModel:"anthropic/claude-haiku-4-5",classifyModel:process.env.SMART_REPLY_CLASSIFY_MODEL||"openai/gpt-5-mini",replyModel:process.env.SMART_REPLY_REPLY_MODEL||"openai/gpt-5.4"};function Te(){return process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||""}function Ae(e){const t=me({apiKey:e.apiKey||"",...void 0!==e.baseURL?{baseURL:e.baseURL}:{}});return new Proxy(t,{get(e,n){if("languageModel"===n)return e=>t.chat(e);if("chat"===n||"completion"===n)return t[n];if("embeddingModel"===n||"imageModel"===n){return t[n]||void 0}return t[n]}})}function _e(e){const t=Te();return le({anthropic:ie({apiKey:t,baseURL:e.anthropic?.baseURL||Se.anthropic}),openai:Ae({apiKey:t,baseURL:e.openai?.baseURL||Se.openai}),ohmygpt:ue({name:"ohmygpt",baseURL:e.ohmygpt?.baseURL||Se.ohmygpt,apiKey:t}),moonshotai:ue({name:"moonshotai",baseURL:e.moonshotai?.baseURL||Se.moonshotai,apiKey:process.env.MOONSHOT_API_KEY||""}),deepseek:ce({baseURL:e.deepseek?.baseURL||Se.deepseek,apiKey:process.env.DEEPSEEK_API_KEY||""}),google:de({apiKey:process.env.GEMINI_API_KEY||"",baseURL:e.google?.baseURL||Se.google}),qwen:se({apiKey:process.env.DASHSCOPE_API_KEY||process.env.ALIBABA_API_KEY||"",baseURL:e.qwen?.baseURL||Se.qwen})},{separator:"/"})}var Ee=null,Re=null;function Oe(e){const t=JSON.stringify(e);return Ee&&Re===t||(Ee=_e(e),Re=t,fe("[DYNAMIC REGISTRY] 创建新的动态registry,配置哈希:",t.substring(0,16)+"...")),Ee}function Le(e){return e.brands.flatMap(e=>e.stores)}function ve(e){return e.brands.map(e=>e.name)}function we(e,t){if(t)return e.brands.find(e=>e.name===t)}function ke(e){if(e.meta.defaultBrandId){const t=e.brands.find(t=>t.id===e.meta.defaultBrandId);if(t)return t}return e.brands[0]}function Me(e){return ke(e)?.name??""}function De(e,t){const n=t?we(e,t)?.stores??[]:Le(e);return n.find(e=>"string"==typeof e.city&&e.city.trim().length>0)?.city}import{z as xe}from"zod";import{setTimeout as Ce,clearTimeout as Ue}from"node:timers";import{performance as $e}from"node:perf_hooks";import{asSchema as je,generateText as We,jsonSchema as Fe,NoObjectGeneratedError as Pe,Output as Be}from"ai";import"zod";var Ge={CONFIG:"CONFIG",AUTH:"AUTH",NETWORK:"NETWORK",LLM:"LLM",VALIDATION:"VALIDATION",BUSINESS:"BUSINESS",SYSTEM:"SYSTEM"},qe={LLM_UNAUTHORIZED:"LLM_UNAUTHORIZED",LLM_MODEL_NOT_FOUND:"LLM_MODEL_NOT_FOUND",LLM_RATE_LIMITED:"LLM_RATE_LIMITED",LLM_TIMEOUT:"LLM_TIMEOUT",LLM_GENERATION_FAILED:"LLM_GENERATION_FAILED",LLM_RESPONSE_PARSE_ERROR:"LLM_RESPONSE_PARSE_ERROR",CONFIG_NOT_FOUND:"CONFIG_NOT_FOUND",CONFIG_INVALID:"CONFIG_INVALID",CONFIG_MISSING_FIELD:"CONFIG_MISSING_FIELD",CONFIG_LOAD_FAILED:"CONFIG_LOAD_FAILED",NETWORK_TIMEOUT:"NETWORK_TIMEOUT",NETWORK_CONNECTION_FAILED:"NETWORK_CONNECTION_FAILED",NETWORK_HTTP_ERROR:"NETWORK_HTTP_ERROR",NETWORK_DNS_FAILED:"NETWORK_DNS_FAILED",AUTH_UNAUTHORIZED:"AUTH_UNAUTHORIZED",AUTH_FORBIDDEN:"AUTH_FORBIDDEN",AUTH_TOKEN_EXPIRED:"AUTH_TOKEN_EXPIRED",AUTH_TOKEN_INVALID:"AUTH_TOKEN_INVALID",VALIDATION_INVALID_INPUT:"VALIDATION_INVALID_INPUT",VALIDATION_MISSING_REQUIRED:"VALIDATION_MISSING_REQUIRED",VALIDATION_FORMAT_ERROR:"VALIDATION_FORMAT_ERROR",VALIDATION_SCHEMA_ERROR:"VALIDATION_SCHEMA_ERROR",BUSINESS_RULE_VIOLATION:"BUSINESS_RULE_VIOLATION",BUSINESS_RESOURCE_NOT_FOUND:"BUSINESS_RESOURCE_NOT_FOUND",BUSINESS_RESOURCE_EXISTS:"BUSINESS_RESOURCE_EXISTS",BUSINESS_OPERATION_NOT_ALLOWED:"BUSINESS_OPERATION_NOT_ALLOWED",SYSTEM_INTERNAL:"SYSTEM_INTERNAL",SYSTEM_DEPENDENCY_FAILED:"SYSTEM_DEPENDENCY_FAILED",SYSTEM_RESOURCE_UNAVAILABLE:"SYSTEM_RESOURCE_UNAVAILABLE",SYSTEM_UNKNOWN:"SYSTEM_UNKNOWN"},He={[qe.LLM_UNAUTHORIZED]:Ge.LLM,[qe.LLM_MODEL_NOT_FOUND]:Ge.LLM,[qe.LLM_RATE_LIMITED]:Ge.LLM,[qe.LLM_TIMEOUT]:Ge.LLM,[qe.LLM_GENERATION_FAILED]:Ge.LLM,[qe.LLM_RESPONSE_PARSE_ERROR]:Ge.LLM,[qe.CONFIG_NOT_FOUND]:Ge.CONFIG,[qe.CONFIG_INVALID]:Ge.CONFIG,[qe.CONFIG_MISSING_FIELD]:Ge.CONFIG,[qe.CONFIG_LOAD_FAILED]:Ge.CONFIG,[qe.NETWORK_TIMEOUT]:Ge.NETWORK,[qe.NETWORK_CONNECTION_FAILED]:Ge.NETWORK,[qe.NETWORK_HTTP_ERROR]:Ge.NETWORK,[qe.NETWORK_DNS_FAILED]:Ge.NETWORK,[qe.AUTH_UNAUTHORIZED]:Ge.AUTH,[qe.AUTH_FORBIDDEN]:Ge.AUTH,[qe.AUTH_TOKEN_EXPIRED]:Ge.AUTH,[qe.AUTH_TOKEN_INVALID]:Ge.AUTH,[qe.VALIDATION_INVALID_INPUT]:Ge.VALIDATION,[qe.VALIDATION_MISSING_REQUIRED]:Ge.VALIDATION,[qe.VALIDATION_FORMAT_ERROR]:Ge.VALIDATION,[qe.VALIDATION_SCHEMA_ERROR]:Ge.VALIDATION,[qe.BUSINESS_RULE_VIOLATION]:Ge.BUSINESS,[qe.BUSINESS_RESOURCE_NOT_FOUND]:Ge.BUSINESS,[qe.BUSINESS_RESOURCE_EXISTS]:Ge.BUSINESS,[qe.BUSINESS_OPERATION_NOT_ALLOWED]:Ge.BUSINESS,[qe.SYSTEM_INTERNAL]:Ge.SYSTEM,[qe.SYSTEM_DEPENDENCY_FAILED]:Ge.SYSTEM,[qe.SYSTEM_RESOURCE_UNAVAILABLE]:Ge.SYSTEM,[qe.SYSTEM_UNKNOWN]:Ge.SYSTEM},Ke={[qe.LLM_UNAUTHORIZED]:"AI 服务认证失败,请检查配置",[qe.LLM_MODEL_NOT_FOUND]:"所选模型暂时不可用,请尝试其他模型",[qe.LLM_RATE_LIMITED]:"请求过于频繁,请稍后重试",[qe.LLM_TIMEOUT]:"AI 响应超时,请稍后重试",[qe.LLM_GENERATION_FAILED]:"内容生成失败,请稍后重试",[qe.LLM_RESPONSE_PARSE_ERROR]:"AI 响应格式异常,请重试",[qe.CONFIG_NOT_FOUND]:"配置数据未找到,请先进行初始化",[qe.CONFIG_INVALID]:"配置格式无效,请检查配置",[qe.CONFIG_MISSING_FIELD]:"配置缺少必需字段",[qe.CONFIG_LOAD_FAILED]:"配置加载失败,请重试",[qe.NETWORK_TIMEOUT]:"网络请求超时,请检查网络连接",[qe.NETWORK_CONNECTION_FAILED]:"网络连接失败,请检查网络",[qe.NETWORK_HTTP_ERROR]:"服务器返回错误,请稍后重试",[qe.NETWORK_DNS_FAILED]:"域名解析失败,请检查网络",[qe.AUTH_UNAUTHORIZED]:"请先登录",[qe.AUTH_FORBIDDEN]:"您没有权限执行此操作",[qe.AUTH_TOKEN_EXPIRED]:"登录已过期,请重新登录",[qe.AUTH_TOKEN_INVALID]:"认证信息无效,请重新登录",[qe.VALIDATION_INVALID_INPUT]:"输入参数无效",[qe.VALIDATION_MISSING_REQUIRED]:"缺少必需参数",[qe.VALIDATION_FORMAT_ERROR]:"数据格式错误",[qe.VALIDATION_SCHEMA_ERROR]:"数据验证失败",[qe.BUSINESS_RULE_VIOLATION]:"操作违反业务规则",[qe.BUSINESS_RESOURCE_NOT_FOUND]:"请求的资源不存在",[qe.BUSINESS_RESOURCE_EXISTS]:"资源已存在",[qe.BUSINESS_OPERATION_NOT_ALLOWED]:"当前操作不被允许",[qe.SYSTEM_INTERNAL]:"系统内部错误,请稍后重试",[qe.SYSTEM_DEPENDENCY_FAILED]:"依赖服务异常,请稍后重试",[qe.SYSTEM_RESOURCE_UNAVAILABLE]:"系统资源不可用,请稍后重试",[qe.SYSTEM_UNKNOWN]:"发生未知错误,请稍后重试"};function Ve(e){return He[e]}function Ye(e){return Ke[e]}var ze=class _AppError extends Error{code;category;userMessage;details;cause;timestamp;constructor(e){super(e.message),this.name="AppError",this.code=e.code,this.category=Ve(e.code),this.userMessage=e.userMessage||Ye(e.code),this.details=e.details,this.cause=e.cause,this.timestamp=(new Date).toISOString(),Error.captureStackTrace&&Error.captureStackTrace(this,_AppError)}toJSON(){const e={code:this.code,category:this.category,message:this.message,userMessage:this.userMessage,timestamp:this.timestamp};return void 0!==this.details&&(e.details=this.details),this.cause&&(this.cause instanceof _AppError?e.cause=this.cause.toJSON():e.cause={message:this.cause.message,stack:this.cause.stack}),e}getErrorChain(){const e=[this];let t=this.cause;for(;t;)e.push(t),t=t instanceof _AppError?t.cause:void 0;return e}getRootCause(){const e=this.getErrorChain();return e[e.length-1]}hasErrorCode(e){return this.getErrorChain().some(t=>t instanceof _AppError&&t.code===e)}hasErrorCategory(e){return this.getErrorChain().some(t=>t instanceof _AppError&&t.category===e)}toLogString(){const e=[`[${this.code}]`,this.message];return this.details&&e.push(`Details: ${JSON.stringify(this.details)}`),this.cause&&e.push(`Caused by: ${this.cause.message}`),e.join(" | ")}};function Je(e){return e instanceof ze}function Qe(e,t,n){const r=n?.model?` (model: ${n.model})`:"",a=n?.provider?` [${n.provider}]`:"",o={[qe.LLM_UNAUTHORIZED]:`LLM API authentication failed${a}${r}`,[qe.LLM_MODEL_NOT_FOUND]:`Model not found or unavailable${r}${a}`,[qe.LLM_RATE_LIMITED]:`LLM API rate limited${a}`,[qe.LLM_TIMEOUT]:`LLM API request timeout${a}${r}`,[qe.LLM_GENERATION_FAILED]:`LLM generation failed${a}${r}`,[qe.LLM_RESPONSE_PARSE_ERROR]:`Failed to parse LLM response${a}`};return new ze({code:e,message:o[e]||`LLM error: ${t.message}`,cause:t,details:n})}function Ze(e,t,n){const r=n?.url?` (${n.url})`:"",a=n?.statusCode?` [${n.statusCode}]`:"",o={[qe.NETWORK_TIMEOUT]:`Network request timeout${r}`,[qe.NETWORK_CONNECTION_FAILED]:`Failed to connect${r}`,[qe.NETWORK_HTTP_ERROR]:`HTTP error${a}${r}`,[qe.NETWORK_DNS_FAILED]:`DNS resolution failed${r}`};return new ze({code:e,message:o[e]||`Network error: ${t.message}`,cause:t,details:n})}function Xe(e,t,n){const r=n.isMarkdownFormat?" (detected markdown format)":"",a=n.schemaName?` for schema "${n.schemaName}"`:"";return new ze({code:e,message:`Failed to parse structured output${a}${r}`,cause:t,details:{rawText:n.rawText,isMarkdownFormat:n.isMarkdownFormat,parseErrorMessage:n.parseErrorMessage,model:n.model,provider:n.provider,schemaName:n.schemaName,usage:n.usage}})}import{NoObjectGeneratedError as et}from"ai";function tt(e){if(!e||"object"!=typeof e)return null;const t=e;if(!("AI_APICallError"===t.name||"APICallError"===t.name||"string"==typeof t.url&&"number"==typeof t.statusCode))return null;const n="number"==typeof t.statusCode?t.statusCode:void 0,r="string"==typeof t.responseBody?t.responseBody:void 0,a="string"==typeof t.url?t.url:void 0,o=t.message;let i,s;if(a&&(a.includes("openai.com")?i="openai":a.includes("anthropic.com")?i="anthropic":a.includes("dashscope.aliyuncs.com")?i="qwen":a.includes("openrouter.ai")?i="openrouter":a.includes("deepseek.com")?i="deepseek":a.includes("moonshot.cn")?i="moonshotai":a.includes("googleapis.com")&&(i="google")),r){const e=r.match(/model[`'":\s]+([^`'"}\s,]+)/i);e&&(s=e[1])}return{isAuthError:401===n||403===n,isModelNotFound:r?.includes("model")&&r?.includes("not exist")||r?.includes("not authorized to access this model")||!1,isRateLimited:429===n,isTimeout:408===n||504===n||o?.toLowerCase().includes("timeout")||!1,statusCode:n,provider:i,model:s,originalMessage:o,responseBody:r}}function nt(e){return[/^```/m,/^#{1,6}\s/m,/^\s*[-*+]\s/m,/^\s*\d+\.\s/m,/\[.+\]\(.+\)/,/^\s*>/m].some(t=>t.test(e))}function rt(e){try{if(void 0===et||"function"!=typeof et.isInstance)return null;if(!et.isInstance(e))return null}catch{return null}const t=e.text,n=!!t&&nt(t),r=e.response?{id:e.response.id,timestamp:e.response.timestamp,modelId:e.response.modelId}:void 0,a=e.usage??void 0,o={isNoObjectGeneratedError:!0,isMarkdownFormat:n};return void 0!==t&&(o.rawText=t),e.cause instanceof Error&&(o.cause=e.cause),void 0!==r&&(o.response=r),void 0!==a&&(o.usage=a),o}function at(e,t=qe.SYSTEM_UNKNOWN,n){if(Je(e))return n&&n!==e.userMessage?new ze({code:e.code,message:e.message,userMessage:n,cause:e.cause,details:e.details}):e;const r=ot(e),a=tt(e);if(a){const e={model:a.model,provider:a.provider,statusCode:a.statusCode,responseBody:a.responseBody};return a.isModelNotFound?Qe(qe.LLM_MODEL_NOT_FOUND,r,e):a.isAuthError?Qe(qe.LLM_UNAUTHORIZED,r,e):a.isRateLimited?Qe(qe.LLM_RATE_LIMITED,r,e):a.isTimeout?Qe(qe.LLM_TIMEOUT,r,e):Qe(qe.LLM_GENERATION_FAILED,r,e)}const o=rt(e);if(o)return Xe(qe.LLM_RESPONSE_PARSE_ERROR,r,{rawText:o.rawText,isMarkdownFormat:o.isMarkdownFormat,parseErrorMessage:o.cause?.message,usage:o.usage});const i=r.message.toLowerCase();return i.includes("timeout")||i.includes("econnrefused")||i.includes("network")||i.includes("fetch failed")?i.includes("timeout")?Ze(qe.NETWORK_TIMEOUT,r):Ze(qe.NETWORK_CONNECTION_FAILED,r):new ze({code:t,message:r.message,cause:r})}function ot(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);if("object"==typeof e&&null!==e){const t=e.message||e.error||JSON.stringify(e);return new Error(String(t))}return new Error(String(e))}function it(e,t){console.error(`[${t.code}] ${e}:`,t.toLogString()),t.cause&&console.error("Original error:",t.cause)}function st(e){return"object"==typeof e&&null!==e}function lt(e,t){t&&st(e.details)&&(e.details.schemaName=t)}function ut(e){const t=e.type;return"string"==typeof t?[t]:Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function ct(e,t){let n=null;for(const r of e){const e=t.get(r);if(void 0!==e&&e.size>0){n??=new Set;for(const t of e)n.add(t)}}return n}function dt(e,t){if(Array.isArray(e))return e.map(e=>dt(e,t));if(null===e||"object"!=typeof e)return e;const n=e,r=ct(ut(n),t),a={};for(const[e,o]of Object.entries(n))null!==r&&r.has(e)||(a[e]=dt(o,t));return a}function mt(e){const t=e.unsupportedKeywordsByType;if(void 0===t)return new Map;const n=new Map;for(const[e,r]of Object.entries(t)){if(0===r.length)continue;const t=n.get(e);if(void 0!==t)for(const e of r)t.add(e);else n.set(e,new Set(r))}const r=n.get("number");if(void 0!==r&&r.size>0){const e=n.get("integer");if(void 0!==e)for(const t of r)e.add(t);else n.set("integer",new Set(r))}return n}function pt(e,t={}){const n=mt(t);if(0===n.size)return e;const r=je(e);return Fe(async()=>dt(await r.jsonSchema,n),{validate:e=>({success:!0,value:e})})}async function gt(e){const{model:t,schema:n,outputSchema:r,schemaName:a,system:o,prompt:i,transformOutput:s,onError:l}=e;try{const e=await We({model:t,...void 0!==o?{system:o}:{},prompt:i,output:Be.object({schema:r??n,...void 0!==a?{name:a}:{}})});if(void 0===r&&void 0===s)return{success:!0,data:e.output,usage:e.usage};const u=void 0!==s?await s(e.output):e.output,c=await n.safeParseAsync(u);if(!c.success){const t=at(c.error,qe.LLM_RESPONSE_PARSE_ERROR);return lt(t,a),it(`Structured output validation (${a||"unknown"})`,t),l&&l(t,e.text),{success:!1,error:t,rawText:e.text}}return{success:!0,data:c.data,usage:e.usage}}catch(e){const t=at(e,qe.LLM_RESPONSE_PARSE_ERROR);let n;return Pe.isInstance(e)&&(n=e.text),lt(t,a),it(`Structured output generation (${a||"unknown"})`,t),l&&l(t,n),{success:!1,error:t,rawText:n}}}var ft=3e4,yt=2e3;async function bt(e){const{model:t,system:n,prompt:r,timeoutMs:a=ft,maxOutputTokens:o=yt,context:i="generateText",onError:s}=e,l=$e.now();try{const e=new AbortController,s=Ce(()=>e.abort(),a),u=await We({model:t,...void 0!==n?{system:n}:{},prompt:r,maxOutputTokens:o,abortSignal:e.signal});Ue(s);const c=Math.round($e.now()-l),d=u.usage,m=void 0!==d?.inputTokens&&void 0!==d?.outputTokens?d.inputTokens+d.outputTokens:void 0,p={inputTokens:d?.inputTokens,outputTokens:d?.outputTokens,totalTokens:m};return fe(`[${i}] 生成成功 | 耗时: ${c}ms | Tokens: ${m??"N/A"} (input: ${p.inputTokens??"?"}, output: ${p.outputTokens??"?"})`),{success:!0,text:u.text,usage:p,latencyMs:c}}catch(e){const t=Math.round($e.now()-l),n=e instanceof Error&&("AbortError"===e.name||e.message.includes("aborted")),r=at(e,n?qe.LLM_TIMEOUT:qe.LLM_GENERATION_FAILED);return st(r.details)&&(r.details.context=i,r.details.latencyMs=t),it(`${i} (${t}ms)`,r),s&&s(r),{success:!1,error:r}}}import{z as ht}from"zod";var St=ht.object({name:ht.string(),baseURL:ht.string(),description:ht.string()}),Nt=ht.record(ht.string(),St),It=ht.object({chatModel:ht.string().optional(),classifyModel:ht.string().optional(),replyModel:ht.string().optional(),providerConfigs:Nt.optional()}),Tt=ht.object({city:ht.string().optional(),defaultBrand:ht.string(),availableBrands:ht.array(ht.string()),storeCount:ht.number()}),At=ht.object({modelConfig:It,candidateMessage:ht.string(),conversationHistory:ht.array(ht.string()).default([]),brandData:Tt.optional(),channelType:O.optional()});function _t(e){const t=O.safeParse(e);return t.success?t.data:"public"}function Et(e="public"){const t=_t(e),n=R.options.filter(e=>w[e].applicableChannels.includes(t));return n.length>0?n:[...R.options]}function Rt(e){return xe.object({stage:xe.enum(e),subGoals:C.shape.subGoals,needs:C.shape.needs,primaryNeed:C.shape.primaryNeed,riskFlags:C.shape.riskFlags,confidence:C.shape.confidence,extractedInfo:C.shape.extractedInfo,reasoningText:C.shape.reasoningText})}var Ot={subGoals:2,needs:8,riskFlags:6,mentionedDistricts:10};function Lt(e){return e.startsWith("anthropic/")}function vt(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function wt(e){if(!vt(e))return e;const t={...e};if(Array.isArray(t.subGoals)&&(t.subGoals=t.subGoals.slice(0,Ot.subGoals)),Array.isArray(t.needs)&&(t.needs=t.needs.slice(0,Ot.needs)),Array.isArray(t.riskFlags)&&(t.riskFlags=t.riskFlags.slice(0,Ot.riskFlags)),vt(t.extractedInfo)){const e={...t.extractedInfo};Array.isArray(e.mentionedDistricts)&&(e.mentionedDistricts=e.mentionedDistricts.slice(0,Ot.mentionedDistricts)),t.extractedInfo=e}return t}var kt=[{need:"salary",patterns:[/薪资|工资|时薪|底薪|提成|奖金|补贴|多少钱|收入/i]},{need:"schedule",patterns:[/排班|班次|几点|上班|下班|工时|周末|节假日|做几天/i]},{need:"policy",patterns:[/五险一金|社保|保险|合同|考勤|迟到|补班|试用期/i]},{need:"availability",patterns:[/还有名额|空位|可用时段|什么时候能上|明天能面/i]},{need:"location",patterns:[/在哪|位置|地址|附近|地铁|门店|哪个区|多远/i]},{need:"stores",patterns:[/门店|哪家店|哪些店|有店吗/i]},{need:"requirements",patterns:[/要求|条件|年龄|经验|学历|健康证|身高|体重/i]},{need:"interview",patterns:[/面试|到店|约时间|约面/i]},{need:"wechat",patterns:[/微信|vx|私聊|联系方式|加你/i]}],Mt=["salary","schedule","location","stores","policy","requirements","availability","interview","wechat","none"];function Dt(e){const t=new Set;for(const n of kt)n.patterns.some(t=>t.test(e))&&t.add(n.need);return 0===t.size?t.add("none"):t.delete("none"),t}function xt(e,t){return Dt(`${t.slice(-4).join(" ")} ${e}`)}function Ct(e){return Dt(e)}function Ut(e,t,n,r=1){const a=new Set(t);a.size>1&&a.has("none")&&a.delete("none");const o=Ct(n);o.delete("none");const i=[];"none"!==e&&a.has(e)&&i.push(e);for(const t of Mt){if(i.length>=r)break;"none"!==t&&t!==e&&(o.has(t)&&a.has(t)&&i.push(t))}return i.length>0?i:"none"===e?["none"]:a.has(e)?[e]:["none"]}function $t(e,t,n){const r=new Set(t);if(r.size>1&&r.has("none")&&r.delete("none"),e&&r.has(e))return e;const a=Ct(n);a.delete("none");for(const e of Mt)if(a.has(e)&&r.has(e))return e;for(const e of Mt)if(r.has(e))return e;return"none"}function jt(e,t,n){const r=new Set([...e.needs,...Array.from(t)]);return r.size>1&&r.has("none")&&r.delete("none"),{...e,subGoals:e.subGoals.slice(0,2),needs:Array.from(r),primaryNeed:$t(e.primaryNeed,r,n),confidence:Number.isFinite(e.confidence)?Math.max(0,Math.min(1,e.confidence)):.5}}function Wt(e,t,n,r="public",a,o){const i=["你是招聘对话回合规划器,不直接回复候选人。","你只输出结构化规划结果,用于后续回复生成。","规划目标:确定阶段目标(stage)、子目标(subGoals)、事实需求(needs)、主回答轴(primaryNeed)、风险标记(riskFlags)。"].join("\n"),s=_t(r);return{system:i,prompt:["[阶段枚举与定义]",...Et(s).map(e=>{const t=w[e];return`- ${e}: ${a?.stageGoals[e]?.description||t.description} (转入条件: ${t.transitionSignal})`}),"","[needs枚举]","private"===s?"- stores, location, salary, schedule, policy, availability, requirements, interview, none":"- stores, location, salary, schedule, policy, availability, requirements, interview, wechat, none","","[riskFlags枚举]","- insurance_promise_risk, age_sensitive, confrontation_emotion, urgency_high, qualification_mismatch","","[规则]","- 优先判断本轮主阶段(stage);subGoals 最多 2 项,只保留最关键的。","- 候选人追问事实时,必须打开对应 needs。","- primaryNeed 必须从 needs 中选择一个最主的 need;如果没有明确事实轴则填 none。","- 不确定时 confidence 降低,不要臆断。","- 根据转入条件判断阶段转化,不要停留在不匹配的阶段。",...o&&o.length>0?[`- 候选人资料中已有:${o.join("、")}。不要生成追问这些字段的 subGoal。`]:[],"","[品牌数据]",JSON.stringify(n||{}),"","[历史对话]",t.slice(-8).join("\n")||"无","","[候选人消息]",e].join("\n")}}async function Ft(e,t){const{providerConfigs:n=Ne,modelConfig:r,conversationHistory:a=[],brandData:o,channelType:i,replyPolicy:s,knownCandidateFields:l}=t,u=Oe(n),c=r?.classifyModel||Ie.classifyModel,d=_t(i),m=Rt(Et(d)),p=Lt(c),g=Wt(e,a,o,d,s,l),f=await gt({model:u.languageModel(c),schema:m,...p?{outputSchema:pt(m,{unsupportedKeywordsByType:{array:["maxItems","minItems"],number:["maximum","minimum","exclusiveMaximum","exclusiveMinimum"]}}),transformOutput:wt}:{},schemaName:"TurnPlanningOutput",system:g.system,prompt:g.prompt}),y=xt(e,a),b=$t(void 0,y,e);return f.success?jt(f.data,y,e):{stage:"trust_building",subGoals:["保持对话并澄清需求"],needs:Array.from(y),primaryNeed:b,riskFlags:[],confidence:.35,extractedInfo:{mentionedBrand:null,city:o?.city||null,mentionedLocations:null,mentionedDistricts:null,specificAge:null,hasUrgency:null,preferredSchedule:null},reasoningText:"规划模型失败,使用规则降级策略"}}import{z as Pt}from"zod";var Bt=Pt.object({id:Pt.number(),name:Pt.string(),aliases:Pt.array(Pt.string()),projectIdList:Pt.array(Pt.number())}),Gt=Pt.object({code:Pt.number(),message:Pt.string().optional(),data:Pt.object({result:Pt.array(Bt),total:Pt.number()})}),qt=3e5,Ht=null;function Kt(){return null!==Ht&&Date.now()-Ht.timestamp<qt}var Vt=3e4;function Yt(){const e=process.env.DULIDAY_BRAND_LIST_URL;return"string"==typeof e&&e.trim().length>0?e:void 0}async function zt(e){const t=e||process.env.DULIDAY_TOKEN,n=Yt();if(!t)throw new ze({code:qe.CONFIG_MISSING_FIELD,message:"DULIDAY_TOKEN 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday Token 配置"});if(!n)throw new ze({code:qe.CONFIG_MISSING_FIELD,message:"DULIDAY_BRAND_LIST_URL 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday 品牌接口配置"});const r=new AbortController,a=setTimeout(()=>r.abort(),Vt);try{const e=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":t},body:JSON.stringify({pageNum:1,pageSize:1e3}),signal:r.signal});if(!e.ok)throw new ze({code:qe.NETWORK_HTTP_ERROR,message:`Duliday 品牌列表 API 返回 HTTP ${e.status}: ${e.statusText}`,userMessage:`品牌别名数据加载失败:服务端返回 ${e.status}`});const a=await e.json(),o=Gt.safeParse(a);if(!o.success)throw new ze({code:qe.VALIDATION_SCHEMA_ERROR,message:`Duliday 品牌列表响应格式校验失败: ${o.error.message}`,userMessage:"品牌别名数据加载失败:响应格式异常"});return o.data.data.result}catch(e){if(e instanceof ze)throw e;if(e instanceof Error&&"AbortError"===e.name)throw new ze({code:qe.NETWORK_TIMEOUT,message:`Duliday 品牌列表 API 请求超时 (${Vt}ms)`,userMessage:"品牌别名数据加载超时,请稍后重试",cause:e});throw new ze({code:qe.SYSTEM_DEPENDENCY_FAILED,message:`获取品牌别名数据失败: ${e instanceof Error?e.message:String(e)}`,userMessage:"品牌别名数据加载失败,请检查网络连接",...e instanceof Error?{cause:e}:{}})}finally{clearTimeout(a)}}function Jt(e,t,n){const r={},a=new Map,o=e=>e.trim(),i=(e,t)=>{const n=o(e).toLowerCase().replace(/[\s._-]+/g,"");if(!n)return;const r=a.get(n);(!r||t.length>r.length)&&a.set(n,t)},s=[...e].sort((e,t)=>t.name.length-e.name.length);for(const e of s){const t=o(e.name);if(!t)continue;const a=Array.from(new Set([t,...e.aliases.map(o).filter(Boolean)].filter(e=>e!==t)));a.unshift(t),r[t]=a;for(const e of a)i(e,t);if(n&&e.projectIdList.length>0)for(const o of e.projectIdList){const e=n[String(o)];if(!e||e===t)continue;if(!e.includes(t))continue;const s=e.replace(t,"");if(!s)continue;const l=[e];for(const e of a)e!==t&&l.push(`${s}${e}`);if(r[e]){const t=new Set(r[e]);for(const n of l)t.has(n)||r[e].push(n)}else r[e]=l;for(const t of l)i(t,e)}}for(const e of t)r[e]||(r[e]=[e],i(e,e));return{dictionary:r,aliasMap:a}}async function Qt(e){if(Kt())return;const t=await zt(e),n=new Set,{dictionary:r,aliasMap:a}=Jt(t,n);Ht={aliasMap:a,dictionary:r,timestamp:Date.now()}}async function Zt(e){return await Qt(e),Ht.aliasMap}function Xt(e){const{base:t,range:n,memo:r}=e;let a="";return t<10&&r?a=`${t}元(${r.replace(/\n/g," ").trim()})`:(a=`${t}元/时`,n&&n!==`${t}-${t}`&&(a+=`,范围${n}元`),r&&r.length<50&&(a+=`(${r.replace(/\n/g," ").trim()})`)),e.scenarioSummary&&(a+=`(${e.scenarioSummary})`),a}function en(e,t,n){if(!e)return null;const r=e=>e.toLowerCase().replace(/[\s._-]+/g,"");if(n){const a=n.get(r(e))||n.get(e.toLowerCase());if(a&&t.includes(a))return a}const a=e.toLowerCase(),o=r(e),i=t.find(e=>e.toLowerCase()===a);if(i)return i;const s=t.find(e=>r(e)===o);if(s)return s;const l=t.filter(e=>{const t=e.toLowerCase();if(t.includes(a)||a.includes(t))return!0;const n=r(e);return n.includes(o)||o.includes(n)});if(l.length>0)return l.sort((e,t)=>t.length-e.length)[0]??null;if(a.includes("山姆")||a.includes("sam")){const e=t.find(e=>{const t=e.toLowerCase();return t.includes("山姆")||t.includes("sam")});if(e)return e}return null}function tn(e){const{uiSelectedBrand:t,configDefaultBrand:n,conversationBrand:r,availableBrands:a,strategy:o="smart",aliasMap:i}=e,s=(e,t)=>{if(e)return en(e,a,i)??void 0};switch(o){case"user-selected":{const e=s(t);if(e)return{resolvedBrand:e,matchType:e===t?"exact":"fuzzy",source:"ui",reason:"用户选择策略",originalInput:t};const r=s(n);return r?{resolvedBrand:r,matchType:r===n?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:n}:{resolvedBrand:a[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}case"conversation-extracted":{const e=s(r);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"对话提取",originalInput:r};const o=s(t);if(o)return{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"ui",reason:"UI选择",originalInput:t};const i=s(n);return i?{resolvedBrand:i,matchType:i===n?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:n}:{resolvedBrand:a[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}default:{const e=s(r),o=s(t);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"智能策略: 对话提取",originalInput:r};if(o)return{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"ui",reason:"智能策略: UI选择",originalInput:t};const i=s(n);return i?{resolvedBrand:i,matchType:i===n?"exact":"fuzzy",source:"config",reason:"智能策略: 配置默认",originalInput:n}:{resolvedBrand:a[0]??"",matchType:"fallback",source:"default",reason:"智能策略: 系统默认"}}}}function nn(e,t){const{mentionedLocations:n,mentionedDistricts:r}=t;return e.map(e=>{let t=0,a=0,o=0,i=0;if(n&&n.length>0){const r=n.find(t=>e.name.includes(t.location)||e.location.includes(t.location)||e.subarea.includes(t.location));r&&(t=40*r.confidence)}if(r&&r.length>0){const t=r.find(t=>e.district.includes(t.district)||e.subarea.includes(t.district));t&&(a=30*t.confidence)}const s=new Set(e.positions.map(e=>e.name));o=Math.min(5*s.size,20);const l=e.positions.filter(e=>e.availableSlots?.some(e=>e.isAvailable));return i=Math.min(2*l.length,10),{store:e,score:t+a+o+i,breakdown:{locationMatch:t,districtMatch:a,positionDiversity:o,availability:i}}}).sort((e,t)=>t.score-e.score).map(e=>({store:e.store,distance:void 0}))}function rn(e){if(!e)return"灵活排班";return{fixed:"固定排班",flexible:"灵活排班",rotating:"轮班制",on_call:"随叫随到"}[e]||"灵活排班"}function an(e){return D[e].length>0}function on(e,t){return e.has(t)}function sn(e,t,n){return"minimal"===t||(1===e||"trust_building"===n||"private_channel"===n)}async function ln(e,t,n,r,a,o,i,s,l=1,u="minimal",c=[t.primaryNeed]){const d=t.extractedInfo,m=t.primaryNeed,p=c.length>0?Array.from(new Set(c.filter(e=>"none"!==e))):"none"===m?[]:[m],g=new Set(p.flatMap(e=>D[e])),f=sn(l,u,t.stage),y=!f&&an(m);let b,h;try{b=await Zt()}catch(e){const t="object"==typeof e&&null!==e&&"userMessage"in e&&"string"==typeof e.userMessage?e.userMessage:e instanceof Error?e.message:String(e);h=t,fe(`[buildContextInfoByNeeds] 品牌别名服务不可用,回退 fuzzy 解析: ${t}`)}const S=tn({uiSelectedBrand:n,configDefaultBrand:Me(e),conversationBrand:r||void 0,availableBrands:ve(e),strategy:a||"smart",aliasMap:b}),N=S.resolvedBrand;fe(`[品牌解析] 工具传参: ${r??"(未指定)"} → 结果: ${N} (${S.matchType}, ${S.source})`);const I=we(e,N),T=I?.stores??[];let A=T;if(A.length>0){const e=d.mentionedLocations||[];if(e.length>0){const t=e[0]?.location?.trim();if(t){const e=A.filter(e=>e.name.includes(t)||e.location.includes(t)||e.district.includes(t)||e.subarea.includes(t));e.length>0&&(A=e)}}const t=d.mentionedDistricts||[];if(t.length>0){const e=A.filter(e=>t.some(t=>e.district.includes(t.district)||e.subarea.includes(t.district)));e.length>0&&(A=e)}if(A.length===T.length&&o?.jobAddress&&on(g,"location")){const e=A.filter(e=>e.name.includes(o.jobAddress||"")||e.location.includes(o.jobAddress||"")||e.district.includes(o.jobAddress||"")||e.subarea.includes(o.jobAddress||""));e.length>0&&(A=e)}}let _=[];A.length>0&&(_=nn(A,d));const E=y?Math.min(1,_.length):0,R=y?"focused":"minimal";let O=`阶段目标:${t.stage}\n默认推荐品牌:${N}\n`;if(h&&(O+=`系统状态:品牌别名服务暂不可用,已回退为规则匹配(${h})。\n`),i){const e=i.stageGoals[t.stage],n=s||i.defaultIndustryVoiceId,r=i.industryVoices[n];O+=`策略目标:${e.primaryGoal}\n`,O+=`推进方式:${e.ctaStrategy}\n`,O+=`主回答轴:${m}\n`,r&&(O+=`行业指纹:${r.name} | 风格:${r.styleKeywords.join("、")}\n`),O+=`红线:${i.hardConstraints.rules.map(e=>e.rule).join(";")}\n`}return y?0===E?O+="暂无可用的门店事实信息,请使用泛化回答,避免任何具体承诺。\n":(O+="匹配到的门店信息:\n",_.slice(0,E).forEach(({store:e})=>{const t=on(g,"location"),n=Array.from(g).some(e=>"location"!==e);O+=t?`• ${e.name}(${e.district}${e.subarea}):${e.location}\n`:`• ${e.name}\n`,n&&e.positions.slice(0,3).forEach(e=>{if(O+=` 职位:${e.name}\n`,on(g,"salary")&&(O+=` 薪资:${Xt(e.salary)}\n`),on(g,"schedule")&&(O+=` 排班:${rn(e.scheduleType)}\n`,O+=` 时间:${e.timeSlots.slice(0,3).join("、")}\n`,(e.minHoursPerWeek||e.maxHoursPerWeek)&&(O+=` 每周工时:${e.minHoursPerWeek||0}-${e.maxHoursPerWeek||"不限"}小时\n`)),on(g,"policy")&&(O+=` 考勤:最多迟到${e.attendancePolicy.lateToleranceMinutes}分钟\n`,e.attendanceRequirement?.description&&(O+=` 出勤要求:${e.attendanceRequirement.description}\n`)),on(g,"availability")){const t=e.availableSlots?.filter(e=>e.isAvailable).slice(0,3)||[];t.length>0&&(O+=` 可用时段:${t.map(e=>e.slot).join("、")}\n`)}if(on(g,"requirements"))if(e.hiringRequirements){const t=e.hiringRequirements,n=[];null==t.minAge&&null==t.maxAge||n.push(`年龄${t.minAge??"不限"}-${t.maxAge??"不限"}岁`),t.genderRequirement&&"0"!==t.genderRequirement&&n.push(`性别:${t.genderRequirement}`),t.education&&"1"!==t.education&&n.push(`学历:${t.education}`),n.length>0&&(O+=` 要求:${n.join("、")}\n`)}else e.requirements?.length&&(O+=` 要求:${e.requirements.filter(e=>"无"!==e).join("、")}\n`)})})):O+=f?"当前处于首轮或浅层沟通,优先泛化回答,不主动展开具体门店、数字或筛选条件。\n":"本轮以推进沟通为主,无需展开岗位细节,请保持回答聚焦且克制。\n",{contextInfo:O,resolvedBrand:N,debugInfo:{relevantStores:_.length>0?_:A.map(e=>({store:e,distance:void 0})),storeCount:E,detailLevel:R,primaryNeed:m,turnPlan:t,aliasLookupError:h}}}var un=["too_many_questions","audit_tone","premature_numeric_disclosure","off_axis_fact_disclosure","reply_overpacked"],cn=/[^。!?!?]*[??]/g,dn={salary:{mention:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i,/时薪|薪资|工资|底薪|收入/i],concrete:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i]},schedule:{mention:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/班次|轮班|白班|晚班|工时|排班/i],concrete:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/轮班|白班|晚班|早班|中班|夜班/i]},location:{mention:[/地址|地铁|附近|位于|门店|在.+(?:路|街|广场|商场|大厦)/i],concrete:[/地址|位于|地铁\S*站|在.+(?:路|街|广场|商场|大厦)/i]},policy:{mention:[/考勤|试用期|社保|五险一金|迟到|补班/i],concrete:[/考勤|试用期|社保|五险一金|迟到|补班/i]},requirements:{mention:[/年龄|学历|经验|健康证|要求/i],concrete:[/年龄|学历|经验|健康证/i]},availability:{mention:[/名额|空位|可用时段|可安排/i],concrete:[/名额|空位|可用时段/i]}};function mn(e){return e?.outputGuards??B}function pn(e){const t=e.match(cn)??[],n=new Set(t.map(e=>e.replace(/[??]/g,"").trim()).filter(Boolean)),r=e.split(/[。!?!?]/).map(e=>e.trim()).filter(Boolean).filter(e=>/[吗呢么]$/.test(e)&&!n.has(e)).length;return t.length+r}function gn(e){return e.split(/[。!?!?]/).map(e=>e.trim()).filter(Boolean).length}function fn(e){return/(?:^|\s)(?:\d+\.\s|- |•)/m.test(e)}function yn(e,t="mention"){return Object.entries(dn).filter(([,n])=>n[t].some(t=>t.test(e))).map(([e])=>e)}function bn(e,t){return t.some(t=>t&&e.includes(t))}function hn(e){return/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i.test(e)||/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/.test(e)||/年龄\s*\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(e)||/\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(e)||/地址在|门店在|位于|在.+(?:路|街|广场|商场|大厦|地铁)/i.test(e)}function Sn(e,t,n){let r=0;return gn(e)>=4&&(r+=1),fn(e)&&(r+=1),t>=3&&(r+=1),n.length>=2&&(r+=1),r>=2}function Nn(e){return yn(e,"concrete")}function In(e){const t=[];return/薪资:/.test(e)&&t.push("salary"),/排班:|时间:|每周工时:/.test(e)&&t.push("schedule"),/匹配到的门店信息:[\s\S]*• .*:/.test(e)&&t.push("location"),/考勤:|出勤要求:/.test(e)&&t.push("policy"),/要求:/.test(e)&&t.push("requirements"),/可用时段:/.test(e)&&t.push("availability"),t}function Tn(e,t){const n=new Set(e.flatMap(e=>D[e]));return 0===n.size?t.length>0:t.some(e=>!n.has(e))}function An(e){const{text:t,turnIndex:n,mode:r,policy:a}=e,o=mn(a),i=pn(t),s=o.maxQuestionsByMode[r],l=Nn(t),u=e.allowedNeeds?.length?e.allowedNeeds:[e.primaryNeed],c=[];return i>s&&c.push("too_many_questions"),bn(t,o.blockedAuditPhrases)&&c.push("audit_tone"),o.blockFirstTurnSpecificFacts&&1===n&&hn(t)&&c.push("premature_numeric_disclosure"),Tn(u,l)&&c.push("off_axis_fact_disclosure"),Sn(t,i,l)&&c.push("reply_overpacked"),{violations:c,questionCount:i,factFamilies:l}}import _n from"ora";function En(){let e=null;return{update(t){e?e.text=t:e=_n({text:t,stream:process.stderr}).start()},succeed(t){e?(e.succeed(t),e=null):console.error(`✓ ${t}`)},fail(t){e?(e.fail(t),e=null):console.error(`✗ ${t}`)}}}import{z as Rn}from"zod";var On=6e4,Ln=200,vn=2e4,wn=null,kn=null;function Mn(){const e=process.env.DULIDAY_JOB_LIST_URL;return"string"==typeof e&&e.trim().length>0?e:void 0}function Dn(){const e=process.env.DULIDAY_TOKEN;return"string"==typeof e&&e.trim().length>0?e:void 0}function xn(e){return(e??"").trim()}function Cn(e){const t=xn(e);if(!t)return[];const n=new Set([t]);return t.endsWith("市")?n.add(t.slice(0,-1)):n.add(`${t}市`),Array.from(n).filter(Boolean)}function Un(e,t){const n=xn(e);if(!n)return[];const r=new Set([n]),a=Cn(t);for(const e of a)if(n.startsWith(e)){const t=n.slice(e.length).trim();t&&r.add(t)}return Array.from(r).filter(Boolean)}var $n=Rn.object({data:Rn.object({result:Rn.array(Rn.unknown()).optional(),list:Rn.array(Rn.unknown()).optional(),total:Rn.number().optional()}).nullable().optional(),result:Rn.array(Rn.unknown()).optional()});function jn(e){const t=$n.safeParse(e);if(!t.success)return{items:[],total:0};const n=t.data,r=n.data?.result??n.data?.list??(Array.isArray(n.result)?n.result:[]),a=n.data?.total??(Array.isArray(r)?r.length:0);return{items:Array.isArray(r)?r:[],total:"number"==typeof a?a:0}}var Wn={includeBasicInfo:!0,includeJobSalary:!0,includeWelfare:!0,includeHiringRequirement:!0,includeWorkTime:!0},Fn={includeBasicInfo:!0,includeHiringRequirement:!0};async function Pn(e,t,n,r,a){const o="test"!==process.env.NODE_ENV,i=Un(a?.brandAlias,a?.cityName),s=Cn(a?.cityName),l=a?.include??Fn,u=JSON.stringify({token:e,brandCandidates:i,cityCandidates:s,pageNum:n,includeOpts:l}),c=Date.now();if(o&&wn&&wn.cacheKey===u&&c-wn.fetchedAt<On)return wn.payload;if(o&&kn?.cacheKey===u)return kn.promise;const d=(async()=>{const a=new AbortController,o=setTimeout(()=>a.abort(),vn),c={};i.length>0&&(c.brandAliasList=i),s.length>0&&(c.cityNameList=s);const d={pageNum:n,pageSize:r,queryParam:c,options:l};try{const n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":e},body:JSON.stringify(d),signal:a.signal});if(!n.ok)throw new Error(`Duliday job list fetch failed: ${n.status}`);const r=await n.json();return wn={cacheKey:u,payload:r,fetchedAt:Date.now()},r}finally{clearTimeout(o),kn=null}})();return o&&(kn={cacheKey:u,promise:d}),d}async function Bn(e,t,n){const r=[];let a=1,o=0;for(;;){const i=jn(await Pn(e,t,a,Ln,n));if(1===a&&(o=i.total),r.push(...i.items),i.items.length<Ln||r.length>=o)break;a+=1}return{items:r,total:o}}var Gn=["pass","fail","unknown"],qn={enabled:!1,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!1,redirectPriority:"low"},Hn=200;function Kn(e){return(e??"").trim().toLowerCase()}function Vn(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function Yn(e){if("string"==typeof e)return e;if(Array.isArray(e)){const t=e.find(e=>"string"==typeof e);return"string"==typeof t?t:""}return""}function zn(e,t){for(const n of t)if(n in e){const t=Yn(e[n]);if(t)return t}return""}function Jn(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const t=Number(e);return Number.isFinite(t)?t:null}return null}function Qn(e,t){const n=t??qn,r="pass"===e?n.passStrategy:"fail"===e?n.failStrategy:n.unknownStrategy;return{...n,status:e,strategy:r}}async function Zn({age:e,brandAlias:t,cityName:n,regionName:r,strategy:a}){const o=Dn(),i=Mn(),s={minAgeObserved:null,maxAgeObserved:null,matchedCount:0,total:0};if(!o)return{status:"unknown",summary:s,appliedStrategy:Qn("unknown",a)};if(!i)return{status:"unknown",summary:s,appliedStrategy:Qn("unknown",a)};try{const l=await Pn(o,i,1,Hn,{brandAlias:t??null,cityName:n??null}),{items:u,total:c}=jn(l);s.total=c;const d=c>u.length&&u.length>=Hn,m=Un(t,n).map(e=>Kn(e)).filter(Boolean),p=Cn(n).map(e=>Kn(e)).filter(Boolean),g=Kn(r);let f=!1,y=!1;for(const t of u){if(!Vn(t))continue;const n=t,r=Vn(n.basicInfo)?n.basicInfo:null,a=r&&Vn(r.storeInfo)?r.storeInfo:null,o=Kn(zn(n,["brandAlias","brandName","brand","organizationName"])||(r?zn(r,["brandAlias","brandName","brand"]):"")),i=Kn(zn(n,["cityName","storeCityName","jobCityName"])||(a?zn(a,["storeCityName","cityName"]):"")),l=Kn(zn(n,["regionName","storeRegionName","districtName"])||(a?zn(a,["storeRegionName","regionName","districtName"]):"")),u=Kn(zn(n,["storeAddress","jobAddress","address","storeExactAddress"])||(a?zn(a,["storeAddress","storeExactAddress","address"]):""));if(m.length>0&&!m.some(e=>o.includes(e)||e.includes(o)))continue;if(p.length>0&&!p.some(e=>i.includes(e)))continue;if(g&&!l.includes(g)&&!u.includes(g))continue;s.matchedCount+=1;const c=Vn(n.hiringRequirement)?n.hiringRequirement:void 0,d=Vn(c?.basicPersonalRequirements)?c.basicPersonalRequirements:void 0,b=Jn(d?.minAge??n.minAge),h=Jn(d?.maxAge??n.maxAge);if(null!==b&&(f=!0,s.minAgeObserved=null===s.minAgeObserved?b:Math.min(s.minAgeObserved,b)),null!==h&&(f=!0,s.maxAgeObserved=null===s.maxAgeObserved?h:Math.max(s.maxAgeObserved,h)),"number"==typeof e){null!==b&&e<b||null!==h&&e>h||(y=!0)}}if("number"!=typeof e||0===s.matchedCount||!f)return{status:"unknown",summary:s,appliedStrategy:Qn("unknown",a)};if(d)return{status:"unknown",summary:s,appliedStrategy:Qn("unknown",a)};const b=y?"pass":"fail";return{status:b,summary:s,appliedStrategy:Qn(b,a)}}catch{return{status:"unknown",summary:s,appliedStrategy:Qn("unknown",a)}}}function Xn(e){if(!e)return;const t=e.match(/(\d+)/);return t?t[1]:e}function er(e,t){if("number"==typeof t?.age)return t.age;const n=Xn(t?.age);if(n){const e=Number(n);if(Number.isFinite(e))return e}return"number"==typeof e.extractedInfo.specificAge?e.extractedInfo.specificAge:void 0}function tr(e,t){const n=e.extractedInfo.mentionedDistricts?.[0]?.district;if(n)return n;const r=e.extractedInfo.mentionedLocations?.[0]?.location;return r||(t?.jobAddress?t.jobAddress:void 0)}var nr=[{key:"age",label:"年龄"},{key:"gender",label:"性别"},{key:"education",label:"学历"},{key:"experience",label:"工作经验"},{key:"expectedSalary",label:"期望薪资"},{key:"expectedLocation",label:"期望位置"},{key:"jobAddress",label:"工作地址"},{key:"healthCertificate",label:"健康证"}];function rr(e){if(!e)return{factsText:"",knownFieldNames:[]};const t=[];for(const{key:n,label:r}of nr){const a=e[n];"string"==typeof a&&a.trim()?t.push({label:r,value:a.trim()}):"boolean"==typeof a&&t.push({label:r,value:a?"是":"否"})}return 0===t.length?{factsText:"",knownFieldNames:[]}:{factsText:t.map(e=>`${e.label}:${e.value}`).join("\n"),knownFieldNames:t.map(e=>e.label)}}function ar(e){if(null===e.minAgeObserved&&null===e.maxAgeObserved)return null;return`${e.minAgeObserved??"?"}-${e.maxAgeObserved??"?"}`}function or(e,t,n){const r=t?.qualificationPolicy?.age;if(!e||!r||!r.enabled)return[];if("unknown"===e.status&&!n.riskFlags.includes("age_sensitive"))return[];const a=["[QualificationPolicy:Age]"],o=ar(e.summary);return a.push(`- gateStatus: ${e.status}`),a.push(`- expressionStrategy: ${e.appliedStrategy.strategy}`),a.push("- redirect: "+(r.allowRedirect?`allowed priority=${r.redirectPriority}`:"not allowed")),a.push("- revealRange: "+(r.revealRange?"allowed":"not allowed")),r.revealRange&&o&&a.push(`- rangeObserved: ${o}`),"pass"===e.status?a.push(`- writingConstraint: ${e.appliedStrategy.strategy};匹配通过后推进下一步,避免强调年龄筛选`):"fail"===e.status?(a.push(`- writingConstraint: ${e.appliedStrategy.strategy};礼貌说明不匹配,避免承诺或争辩`),r.allowRedirect&&a.push("- writingConstraint: 可提示其他岗位或门店选项")):a.push("- writingConstraint: 如确需涉及年龄或资格,用合规、轻量的方式核实,不要审查式逐条盘问"),a}function ir(e){return"minimal"===e?["4. 当前为浅层沟通,优先泛化回答,不主动抛具体数字、时间、地址或筛选条件。","5. 若候选人追问具体事实,承接需求并引导进一步沟通,不编造细节。"]:["4. 围绕 primaryNeed 回答,上下文中有的事实可以正常引用,不要刻意回避。","5. 不主动展开其他事实轴;若候选人同时问两个点,只在上下文支持时简要带上次要问题。"]}function sr(e,t,n,r,a,o,i,s,l,u,c,d){if(!e)return{system:"你是招聘助手。遵循事实,不夸大承诺,回复简洁自然。",prompt:`候选人消息:${a}\n\n上下文:\n${r}\n\n请直接回复候选人。`};const m=e.stageGoals[t.stage],p=e.industryVoices[u||e.defaultIndustryVoiceId],g=e.outputGuards.maxQuestionsByMode[s];return{system:["你是政策驱动的招聘助手。",`当前阶段:${t.stage}`,`当前轮次:${i}`,`当前披露模式:${s}`,`主回答轴:${t.primaryNeed}`,`阶段目标:${m.primaryGoal}`,`阶段成功标准:${m.successCriteria.join(";")}`,`推进策略:${m.ctaStrategy}`,m.disallowedActions?.length?`阶段禁止:${m.disallowedActions.join(";")}`:"",`人格设定:语气=${e.persona.tone},亲和度=${e.persona.warmth},长度=${e.persona.length},称呼=${e.persona.addressStyle},提问风格=${e.persona.questionStyle}`,`共情策略:${e.persona.empathyStrategy}`,p?`行业指纹:${p.name};背景=${p.industryBackground};行业词=${p.jargon.join("、")};避免=${p.tabooPhrases.join("、")}`:"",`红线规则:${e.hardConstraints.rules.map(e=>e.rule).join(";")}`,`FactGate模式:${e.factGate.mode};缺事实回退=${e.factGate.fallbackBehavior}`,`禁止审查措辞:${e.outputGuards.blockedAuditPhrases.join("、")}`,...l.knownFieldNames.length>0?[`候选人资料已确认:${l.knownFieldNames.join("、")}。这些信息不得重复追问;如需引用,自然带过即可,不要像念资料一样复述。`]:[],...or(d,e,t),c?`如涉及换微信,优先引导平台交换,必要时可提供默认微信号:${c}`:"如涉及换微信,优先引导平台交换,不编造联系方式。","必须口语化、简洁,不输出解释。"].filter(Boolean).join("\n"),prompt:["[回合规划]",`stage=${t.stage}`,`subGoals=${t.subGoals.join("、")||"无"}`,`contextNeeds=${n.join("、")||"none"}`,`primaryNeed=${t.primaryNeed}`,`riskFlags=${t.riskFlags.join("、")||"无"}`,`confidence=${t.confidence.toFixed(2)}`,"","[对话历史]",o.slice(-6).join("\n")||"无","","[业务上下文]",r,"",...l.factsText?["[候选人已知信息]",l.factsText,""]:[],"[候选人消息]",a,"","[输出要求]","1. 直接给候选人的单条回复,不得输出多段解释或元信息。",`2. 最多追问 ${g} 个关键问题。`,"3. 禁止使用“是否满足”“是否符合”“基本入职要求”等审查措辞。",...ir(s)].join("\n")}}function lr(e,t,n){const r=Nn(e);if(0===r.length)return!1;const a=new Set(In(t)),o=new Set(n.flatMap(e=>D[e]));return r.some(e=>!o.has(e)||!a.has(e))}function ur(e){return"private_channel"===e||"interview_scheduling"===e}function cr(e,t){return Number.isInteger(t)&&void 0!==t&&t>=1?t:0===e.length?1:2}function dr(e,t){return 1===e||"trust_building"===t||"private_channel"===t?"minimal":"focused"}function mr(e,t){const n=["- 只修正命中的违规点,没有命中的部分不要过度改写。"];return e.includes("too_many_questions")&&n.push(`- 删除多余追问,只保留最关键的 ${t} 个问题。`),e.includes("audit_tone")&&n.push("- 保留原意,但把审查式措辞改成自然口语,不要像筛选候选人。"),e.includes("premature_numeric_disclosure")&&n.push("- 把具体数字、时间和地址细节改成泛化表达,例如“细节我帮你确认”或“以门店安排为准”。"),e.includes("off_axis_fact_disclosure")&&n.push("- 删除不属于主回答轴的具体事实;如果要提到次要问题,只能做不带细节的承接。"),e.includes("reply_overpacked")&&n.push("- 压缩成最多两句,不要列表、不要枚举、不要一口气展开太多信息。"),n}async function pr(e,t,n){const r=["请重写下面这条招聘回复。","要求:","- 不新增任何具体数字、地址、福利承诺。","- 仅保留泛化表达,强调可进一步沟通确认细节。","- 口语化、单行、简洁。","","[原回复]",e,"","[可用上下文]",n].join("\n"),a=await bt({model:t,prompt:r,context:"SmartReplyFactGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return a.success?{text:a.text,usage:a.usage,latencyMs:a.latencyMs}:{text:e}}async function gr(e,t,n,r){const{turnIndex:a,effectiveDisclosureMode:o,primaryNeed:i,allowedNeeds:s,violations:l,policy:u}=r,c=u?.outputGuards.maxQuestionsByMode[o]??1,d=u?.outputGuards.blockedAuditPhrases.join("、")??"",m=mr(l,c),p=(s??[]).filter(e=>e!==i&&"none"!==e),g=["请重写下面这条招聘回复。","要求:",`- 当前轮次=${a},披露模式=${o},主回答轴=${i}。`,p.length>0?`- 允许顺带覆盖的次要轴:${p.join("、")}。`:"",`- 当前违规点:${l.join("、")}。`,...m,"- 只保留单条口语化回复,不输出解释。",`- 问题数最多 ${c} 个。`,"- 围绕主回答轴回答,不主动展开其他事实轴。","- 首轮时不要主动报具体数字、时间、地址或筛选条件。",d?`- 禁止使用这些措辞:${d}。`:"","","[原回复]",e,"","[可用上下文]",n].filter(Boolean).join("\n"),f=await bt({model:t,prompt:g,context:"SmartReplyReplyGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return f.success?{text:f.text,usage:f.usage,latencyMs:f.latencyMs}:{text:e}}async function fr(e){const t=En();ge(!0);try{return await yr(e,t)}catch(e){throw t.fail("回复生成失败"),e}finally{ge(!1)}}async function yr(e,t){const{modelConfig:n,preferredBrand:r,toolBrand:a,brandPriorityStrategy:o,conversationHistory:i=[],candidateMessage:s,configData:l,replyPolicy:u,candidateInfo:c,defaultWechatId:d,industryVoiceId:m,channelType:p,turnIndex:g}=e,f=n?.providerConfigs||Ne,y=De(l),b={...y?{city:y}:{},defaultBrand:Me(l),availableBrands:ve(l),storeCount:Le(l).length},h=rr(c);t.update("分析对话意图...");const S=await Ft(s,{modelConfig:n||{},conversationHistory:i,brandData:b,providerConfigs:f,...void 0!==p?{channelType:p}:{},...void 0!==u?{replyPolicy:u}:{},...h.knownFieldNames.length>0?{knownCandidateFields:h.knownFieldNames}:{}}),N=cr(i,g),I=dr(N,S.stage),T="focused"===I?Ut(S.primaryNeed,S.needs,s,2):[S.primaryNeed],A=a||S.extractedInfo.mentionedBrand||void 0;t.update("构建业务上下文...");const{contextInfo:_,debugInfo:E,resolvedBrand:R}=await ln(l,S,r,A,o,c,u,m,N,I,T);t.update("校验候选人资格...");const O=er(S,c),L=tr(S,c),v=S.extractedInfo.city??De(l,R),w=await Zn({...void 0!==O?{age:O}:{},brandAlias:R,..."string"==typeof v?{cityName:v}:{},...void 0!==L?{regionName:L}:{},...void 0!==u?.qualificationPolicy?.age?{strategy:u.qualificationPolicy.age}:{}}),k=Oe(f),M=n?.replyModel||Ie.replyModel,D=k.languageModel(M),x=sr(u,S,T,_,s,i,N,I,h,m,d,w);t.update("生成回复...");const C=await bt({model:D,system:x.system,prompt:x.prompt,context:"SmartReply",timeoutMs:3e4,maxOutputTokens:2e3});if(!C.success)return t.fail("回复生成失败"),it("SmartReply 生成失败",C.error),{turnPlan:S,suggestedReply:"",confidence:0,shouldExchangeWechat:ur(S.stage),factGateRewritten:!1,replyGateRewritten:!1,gateViolations:[],contextInfo:_,debugInfo:{...E,resolvedBrand:R,turnIndex:N,effectiveDisclosureMode:I,primaryNeed:S.primaryNeed,replyGateRewritten:!1,gateViolations:[],gateStatus:w.status,appliedStrategy:w.appliedStrategy,ageRangeSummary:w.summary},usage:void 0,error:C.error};let U=C.text,$=C.usage,j=C.latencyMs,W=!1,F=!1,P=[];if(t.update("检查回复质量..."),"strict"===u?.factGate.mode){if(lr(U,_,T)){W=!0;const e=await pr(U,D,_);U=e.text,e.usage&&($=e.usage),void 0!==e.latencyMs&&(j=(j??0)+e.latencyMs)}}if(P=An({text:U,turnIndex:N,mode:I,primaryNeed:S.primaryNeed,allowedNeeds:T,policy:u}).violations,P.length>0){t.update("优化回复..."),F=!0;const e=await gr(U,D,_,{turnIndex:N,effectiveDisclosureMode:I,primaryNeed:S.primaryNeed,allowedNeeds:T,violations:P,policy:u});U=e.text,e.usage&&($=e.usage),void 0!==e.latencyMs&&(j=(j??0)+e.latencyMs),P=An({text:U,turnIndex:N,mode:I,primaryNeed:S.primaryNeed,allowedNeeds:T,policy:u}).violations}const B=j??0,G=$?.totalTokens??0;return t.succeed(`回复已生成 | ${S.stage} | ${B}ms | ${G} tokens${F?" | 已优化":""}`),{turnPlan:S,suggestedReply:U,confidence:Math.max(0,Math.min(1,S.confidence)),shouldExchangeWechat:ur(S.stage),factGateRewritten:W,replyGateRewritten:F,gateViolations:P,contextInfo:`${_}\n当前品牌:${R}`,debugInfo:{...E,resolvedBrand:R,turnIndex:N,effectiveDisclosureMode:I,primaryNeed:S.primaryNeed,replyGateRewritten:F,gateViolations:P,gateStatus:w.status,appliedStrategy:w.appliedStrategy,ageRangeSummary:w.summary},usage:$,latencyMs:j}}var br=n.enum(un),hr=n.enum(Gn),Sr=t({name:"generate_reply",description:"根据候选人消息、对话历史和品牌数据生成智能招聘回复。内部流程:回合规划 → primaryNeed 驱动上下文构建 → 年龄资格校验 → 策略化回复生成 → FactGate/ReplyGate 校验。",input:n.object({candidateMessage:n.string().describe("候选人发送的消息"),conversationHistory:n.array(n.string()).optional().describe("对话历史(最近几轮)"),candidateInfo:i.optional().describe("候选人基本信息"),preferredBrand:n.string().optional().describe("偏好品牌"),channelType:O.optional().describe("渠道类型: public(BOSS直聘) 或 private(微信)"),defaultWechatId:n.string().optional().describe("默认微信号"),industryVoiceId:n.string().optional().describe("行业语调ID"),turnIndex:n.number().int().min(1).optional().describe("当前会话回复轮次"),modelConfig:It.optional().describe("模型配置覆盖")}),output:n.object({suggestedReply:n.string(),confidence:n.number(),stage:R,latencyMs:n.number().optional(),shouldExchangeWechat:n.boolean().optional(),error:n.string().optional(),diagnostics:n.object({subGoals:n.array(n.string()),needs:n.array(k),primaryNeed:k,riskFlags:n.array(M),reasoningText:n.string(),extractedInfo:n.object({mentionedBrand:n.string().nullable(),city:n.string().nullable(),specificAge:n.number().nullable(),hasUrgency:n.boolean().nullable(),preferredSchedule:n.string().nullable()}),ageGate:n.object({enabled:n.boolean(),status:hr,strategy:n.string()}),resolvedBrand:n.string(),storeCount:n.number(),detailLevel:L,turnIndex:n.number(),effectiveDisclosureMode:L,replyGateRewritten:n.boolean(),gateViolations:n.array(br),factGateRewritten:n.boolean()}).optional()}),execute:async(e,t)=>{let n;t.logger.info(`Processing message: ${e.candidateMessage.slice(0,50)}...`);try{n=re()}catch{return{suggestedReply:"",confidence:0,stage:"trust_building",error:"品牌数据未配置,请先调用 sync_brand_data 写入数据"}}const r=oe(),a=await fr({candidateMessage:e.candidateMessage,conversationHistory:e.conversationHistory,candidateInfo:e.candidateInfo,preferredBrand:e.preferredBrand,channelType:e.channelType,defaultWechatId:e.defaultWechatId,industryVoiceId:e.industryVoiceId,turnIndex:e.turnIndex,modelConfig:e.modelConfig,configData:n,replyPolicy:r});t.logger.info(`Reply generated. Stage: ${a.turnPlan.stage}, Confidence: ${a.confidence}`);const o=a.debugInfo;return{suggestedReply:a.suggestedReply,confidence:a.confidence,stage:a.turnPlan.stage,latencyMs:a.latencyMs,shouldExchangeWechat:a.shouldExchangeWechat,error:a.error?.userMessage,diagnostics:o?{subGoals:a.turnPlan.subGoals,needs:a.turnPlan.needs,primaryNeed:a.turnPlan.primaryNeed,riskFlags:a.turnPlan.riskFlags,reasoningText:a.turnPlan.reasoningText,extractedInfo:{mentionedBrand:a.turnPlan.extractedInfo.mentionedBrand??null,city:a.turnPlan.extractedInfo.city??null,specificAge:a.turnPlan.extractedInfo.specificAge??null,hasUrgency:a.turnPlan.extractedInfo.hasUrgency??null,preferredSchedule:a.turnPlan.extractedInfo.preferredSchedule??null},ageGate:{enabled:o.appliedStrategy.enabled,status:o.gateStatus,strategy:o.appliedStrategy.strategy},resolvedBrand:o.resolvedBrand,storeCount:o.storeCount,detailLevel:o.detailLevel,turnIndex:o.turnIndex,effectiveDisclosureMode:o.effectiveDisclosureMode,replyGateRewritten:a.replyGateRewritten,gateViolations:a.gateViolations,factGateRewritten:a.factGateRewritten}:void 0}}});import{defineTool as Nr}from"@roll-agent/sdk";import{z as Ir}from"zod";import{z as Tr}from"zod";var Ar=Tr.object({content:Tr.string(),image:Tr.string().nullable().optional()}).passthrough(),_r=Tr.object({storeId:Tr.number().optional(),storeName:Tr.string(),storeCityName:Tr.string().optional(),storeRegionName:Tr.string().optional(),storeAddress:Tr.string().optional(),longitude:Tr.number().optional(),latitude:Tr.number().optional()}).passthrough(),Er=Tr.object({jobId:Tr.number(),jobName:Tr.string(),jobContent:Tr.string().nullable().optional(),brandId:Tr.number().optional(),brandName:Tr.string().optional(),projectId:Tr.number().optional(),projectName:Tr.string().optional(),storeInfo:_r.optional()}).passthrough(),Rr=Tr.object({salaryType:Tr.string().nullable().optional(),salaryPeriod:Tr.string().nullable().optional(),hasStairSalary:Tr.string().nullable().optional(),basicSalary:Tr.object({basicSalary:Tr.number().nullable().optional(),basicSalaryUnit:Tr.string().nullable().optional()}).passthrough().nullable().optional(),stairSalaries:Tr.array(Tr.object({fullWorkTime:Tr.number().nullable().optional(),fullWorkTimeUnit:Tr.string().nullable().optional(),salary:Tr.number().nullable().optional(),salaryUnit:Tr.string().nullable().optional()}).passthrough()).nullable().optional(),comprehensiveSalary:Tr.object({minComprehensiveSalary:Tr.number().nullable().optional(),maxComprehensiveSalary:Tr.number().nullable().optional(),comprehensiveSalaryUnit:Tr.string().nullable().optional()}).passthrough().nullable().optional(),holidaySalary:Tr.object({holidaySalaryType:Tr.string().nullable().optional(),holidaySalaryMultiple:Tr.number().nullable().optional(),holidayFixedSalary:Tr.number().nullable().optional(),holidayFixedSalaryUnit:Tr.string().nullable().optional()}).passthrough().nullable().optional()}).passthrough(),Or=Tr.object({salary:Tr.number().optional(),salaryUnitStr:Tr.string().optional(),salaryScenarioList:Tr.array(Rr).nullable().optional()}).passthrough(),Lr=Tr.object({haveInsurance:Tr.string(),accommodation:Tr.string(),catering:Tr.string().optional(),otherWelfare:Tr.array(Tr.string()).nullable().optional(),accommodationAllowance:Tr.number().nullable().optional(),accommodationAllowanceUnit:Tr.string().nullable().optional(),cateringSalary:Tr.number().nullable().optional(),cateringSalaryUnit:Tr.string().nullable().optional(),trafficAllowanceSalary:Tr.number().nullable().optional(),trafficAllowanceSalaryUnit:Tr.string().nullable().optional(),memo:Tr.string().nullable().optional(),promotionWelfare:Tr.string().nullable().optional(),moreWelfares:Tr.array(Ar).nullable().optional()}).passthrough(),vr=Tr.object({minAge:Tr.number().nullable().optional(),maxAge:Tr.number().nullable().optional(),genderRequirement:Tr.string().nullable().optional()}).passthrough(),wr=Tr.object({education:Tr.string().nullable().optional(),healthCertificate:Tr.string().nullable().optional()}).passthrough(),kr=Tr.object({cooperationMode:Tr.number().optional(),requirementNum:Tr.number().optional(),thresholdNum:Tr.number().optional(),signUpNum:Tr.number().nullable().optional(),basicPersonalRequirements:vr.nullable().optional(),certificate:wr.nullable().optional()}).passthrough(),Mr=Tr.object({employmentForm:Tr.string(),minWorkMonths:Tr.number().nullable().optional(),maxWorkTakingTime:Tr.number().nullable().optional(),restTimeDesc:Tr.string().nullable().optional(),workTimeRemark:Tr.string().nullable().optional(),employmentDescription:Tr.string().nullable().optional(),weekWorkTime:Tr.object({weekWorkTimeRequirement:Tr.string().nullable().optional(),perWeekWorkDays:Tr.number().nullable().optional(),perWeekRestDays:Tr.number().nullable().optional(),perWeekNeedWorkDays:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),workSingleDouble:Tr.string().nullable().optional(),customnWorkTimeList:Tr.array(Tr.object({customMinWorkDays:Tr.number().nullable().optional(),customMaxWorkDays:Tr.number().nullable().optional(),customWorkWeekdays:Tr.array(Tr.union([Tr.string(),Tr.number()])).nullable().optional()}).passthrough()).nullable().optional()}).passthrough().nullable().optional(),monthWorkTime:Tr.object({perMonthMinWorkTime:Tr.number().nullable().optional(),perMonthMinWorkTimeUnit:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),monthWorkTimeRequirement:Tr.string().nullable().optional(),perMonthMaxRestTime:Tr.number().nullable().optional(),perMonthMaxRestTimeUnit:Tr.number().nullable().optional()}).passthrough().nullable().optional(),dayWorkTime:Tr.object({perDayMinWorkHours:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),dayWorkTimeRequirement:Tr.string().nullable().optional()}).passthrough().nullable().optional(),dailyShiftSchedule:Tr.object({arrangementType:Tr.string().nullable().optional(),fixedScheduleList:Tr.array(Tr.object({fixedShiftStartTime:Tr.union([Tr.string(),Tr.number()]).optional(),fixedShiftEndTime:Tr.union([Tr.string(),Tr.number()]).optional(),startTime:Tr.number().optional(),endTime:Tr.number().optional()}).passthrough()).nullable().optional(),combinedArrangement:Tr.array(Tr.object({CombinedArrangementWeekdays:Tr.union([Tr.string(),Tr.array(Tr.number())]).optional(),CombinedArrangementStartTime:Tr.number().optional(),CombinedArrangementEndTime:Tr.number().optional(),startTime:Tr.number().optional(),endTime:Tr.number().optional(),weekdays:Tr.array(Tr.number()).optional()}).passthrough()).nullable().optional(),fixedTime:Tr.object({goToWorkStartTime:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),goToWorkEndTime:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),goOffWorkStartTime:Tr.union([Tr.string(),Tr.number()]).nullable().optional(),goOffWorkEndTime:Tr.union([Tr.string(),Tr.number()]).nullable().optional()}).passthrough().nullable().optional()}).passthrough().nullable().optional(),temporaryEmployment:Tr.object({temporaryEmploymentStartTime:Tr.string().nullable().optional(),temporaryEmploymentEndTime:Tr.string().nullable().optional()}).passthrough().nullable().optional()}).passthrough(),Dr=Tr.object({basicInfo:Er,jobSalary:Or,welfare:Lr,hiringRequirement:kr,workTime:Mr}).passthrough();function xr(e){const t=Dr.safeParse(e);if(!t.success)return null;const n=t.data,r=n.basicInfo,a=r.storeInfo,o=n.jobSalary,i=n.hiringRequirement,s=o.salary??o.salaryScenarioList?.find(e=>"正式"===e.salaryType)?.basicSalary?.basicSalary??o.salaryScenarioList?.[0]?.basicSalary?.basicSalary??0,l=o.salaryUnitStr??o.salaryScenarioList?.find(e=>"正式"===e.salaryType)?.basicSalary?.basicSalaryUnit??o.salaryScenarioList?.[0]?.basicSalary?.basicSalaryUnit??"元/小时";let u=a?.storeId;if(null==u){const e=`${a?.storeName??""}|${a?.storeAddress??""}`;u=Array.from(e).reduce((e,t)=>31*e+t.charCodeAt(0)>>>0,7)}return{jobId:r.jobId,jobName:r.jobName,jobContent:r.jobContent??null,brandId:void 0!==r.brandId?String(r.brandId):void 0!==r.projectId?String(r.projectId):void 0,brandName:r.brandName,projectId:r.projectId,projectName:r.projectName,storeId:u,storeName:a?.storeName??"未知门店",storeCityName:a?.storeCityName??"",storeRegionName:a?.storeRegionName,storeAddress:a?.storeAddress??"",longitude:a?.longitude,latitude:a?.latitude,salary:s,salaryUnitStr:l,salaryScenarioList:o.salaryScenarioList??null,welfare:n.welfare,cooperationMode:i.cooperationMode??0,requirementNum:i.requirementNum??0,thresholdNum:i.thresholdNum??0,signUpNum:i.signUpNum??null,basicPersonalRequirements:i.basicPersonalRequirements?{minAge:i.basicPersonalRequirements.minAge??null,maxAge:i.basicPersonalRequirements.maxAge??null,genderRequirement:i.basicPersonalRequirements.genderRequirement??null}:null,certificate:i.certificate?{education:i.certificate.education??null,healthCertificate:i.certificate.healthCertificate??null}:null,workTime:n.workTime}}function Cr(e,t,n){const r=new Map;let a;for(let o=0;o<e.length;o++){const i=xr(e[o]);if(!i)continue;const s=i.brandName??t??"未知品牌",l=i.brandId??`name:${s}`;void 0!==a||void 0!==t&&s!==t||(a=l);let u=r.get(l);u||(u={id:l,name:s,stores:[]},r.set(l,u));const c=`store_${i.storeId}`;let d=u.stores.find(e=>e.id===c);d||(d=Ur(i,l,n),u.stores.push(d));const m=$r(i);d.positions.push(m)}return{meta:{...a??r.values().next().value?.id?{defaultBrandId:a??r.values().next().value?.id}:{},syncedAt:(new Date).toISOString(),source:"duliday"},brands:Array.from(r.values())}}function Ur(e,t,n){return{id:`store_${e.storeId}`,brandId:t,name:e.storeName,city:e.storeCityName||n,location:e.storeAddress,district:ta(e.storeAddress,e.storeRegionName),subarea:na(e.storeName),coordinates:"number"==typeof e.latitude&&"number"==typeof e.longitude?{lat:e.latitude,lng:e.longitude}:{lat:0,lng:0},positions:[]}}function $r(e){const t=jr(e.workTime);let n=[];return t.combinedArrangementTimes?.length?n=ea(t.combinedArrangementTimes):t.fixedArrangementTimes?.length&&(n=ea(t.fixedArrangementTimes.map(e=>({...e,weekdays:[]})))),{id:`pos_${e.jobId}`,name:ra(e.jobName),brandId:e.brandId,brandName:e.brandName,projectId:void 0!==e.projectId?String(e.projectId):void 0,projectName:e.projectName,timeSlots:n,salary:{...Wr(e.salary,e.welfare),scenarioSummary:Fr(e.salaryScenarioList),settlementCycle:Pr(e.salaryScenarioList)},workHours:String(t.perDayMinWorkHours??8),benefits:Br(e.welfare),requirements:Gr(e),urgent:e.requirementNum>3,scheduleType:2===e.cooperationMode?"flexible":"fixed",attendancePolicy:Kr(e.cooperationMode),availableSlots:Vr(e,t),schedulingFlexibility:Yr(e,t),minHoursPerWeek:Jr(t),maxHoursPerWeek:Qr(t),attendanceRequirement:Zr(t),hiringRequirements:Hr(e),description:e.jobContent||void 0}}function jr(e){const t=e.weekWorkTime,n=e.dayWorkTime,r=e.dailyShiftSchedule,a=Number(e.employmentForm)||({"长期用工":1,"临时用工":2,"短期用工":2}[String(e.employmentForm)]??1),o=Number(r?.arrangementType)||({"固定排班制":1,"组合排班制":3}[String(r?.arrangementType)]??0),i=null!=n?.perDayMinWorkHours?Number(n.perDayMinWorkHours):null,s=null!==i&&Number.isFinite(i)?i:null,l=t?.customnWorkTimeList?.map(e=>({weekdays:Array.isArray(e.customWorkWeekdays)?e.customWorkWeekdays.map(e=>Number(e)).filter(e=>Number.isFinite(e)):[],minWorkDays:e.customMinWorkDays??null,maxWorkDays:e.customMaxWorkDays??null}))??null,u=r?.fixedScheduleList?.map(e=>({startTime:e.startTime??Xr(e.fixedShiftStartTime),endTime:e.endTime??Xr(e.fixedShiftEndTime)}))??null,c=r?.combinedArrangement?.map(e=>{const t=(e.weekdays??("string"==typeof e.CombinedArrangementWeekdays?[Number(e.CombinedArrangementWeekdays)]:Array.isArray(e.CombinedArrangementWeekdays)?e.CombinedArrangementWeekdays:[])).map(e=>"number"==typeof e?e:Number(e)).filter(e=>Number.isFinite(e));return{startTime:e.startTime??e.CombinedArrangementStartTime??0,endTime:e.endTime??e.CombinedArrangementEndTime??0,weekdays:t}})??null;return{employmentForm:a,perDayMinWorkHours:s,perWeekWorkDays:t?.perWeekWorkDays??null,perWeekNeedWorkDays:null!=t?.perWeekNeedWorkDays?Number(t.perWeekNeedWorkDays):null,perWeekRestDays:t?.perWeekRestDays??null,arrangementType:o,maxWorkTakingTime:e.maxWorkTakingTime??0,workTimeRemark:e.workTimeRemark??null,fixedArrangementTimes:u,combinedArrangementTimes:c,customWorkTimes:l}}function Wr(e,t){const n=t.memo||"",r=n.match(/(\d+元?-\d+元?)/),a=r?r[1]:void 0,o=n.match(/(奖金[\d~\-~元]+)/);return{base:e,range:a,bonus:o?o[1]:void 0,memo:n}}function Fr(e){if(!e||0===e.length)return;const t=[];for(const n of e){if("培训期"===n.salaryType)continue;if(n.stairSalaries?.length){const e=n.stairSalaries.filter(e=>null!=e.salary).map(e=>{const t=e.salaryUnit??"元/时";return`满${e.fullWorkTime??"?"}${e.fullWorkTimeUnit??"小时"}后${e.salary}${t}`}).join(",");e&&t.push(e)}const e=n.comprehensiveSalary;null!=e?.minComprehensiveSalary&&null!=e?.maxComprehensiveSalary&&t.push(`综合${e.minComprehensiveSalary}-${e.maxComprehensiveSalary}${e.comprehensiveSalaryUnit??"元/月"}`);const r=n.holidaySalary;r&&(r.holidaySalaryMultiple?t.push(`节假日${r.holidaySalaryMultiple}倍`):null!=r.holidayFixedSalary&&t.push(`节假日${r.holidayFixedSalary}${r.holidayFixedSalaryUnit??"元/时"}`))}return t.length>0?t.join(";"):void 0}function Pr(e){if(!e||0===e.length)return;const t=e.find(e=>"正式"===e.salaryType)??e[0];return{"日结算":"日结","周结算":"周结","月结算":"月结","完结算":"完结","半月结算":"半月结"}[t?.salaryPeriod??""]??void 0}function Br(e){const t=[];if(e.haveInsurance&&"无"!==e.haveInsurance&&"0"!==e.haveInsurance&&t.push("五险一金"),e.accommodation&&"无"!==e.accommodation&&"0"!==e.accommodation&&t.push("住宿"),e.catering&&"无"!==e.catering&&"0"!==e.catering&&t.push("餐饮"),e.moreWelfares&&Array.isArray(e.moreWelfares))for(const n of e.moreWelfares){const e=n.content,r=["保险","年假","补贴","福利","股票","学历提升"];for(const n of r)if(e.includes(n)&&!t.some(e=>e.includes(n))){const r=e.match(new RegExp(`\\d*[天个月年]*${n}[^,。]*`));t.push(r?r[0]:n)}}if(e.memo){const n=["年假","补贴","商保","股票","学历提升"];for(const r of n)e.memo.includes(r)&&!t.some(e=>e.includes(r))&&t.push(r)}return 0===t.length&&t.push("按国家规定"),{items:t,promotion:e.promotionWelfare||void 0}}function Gr(e){const t=[],n=e.basicPersonalRequirements,r=e.certificate;if(null!=n?.minAge||null!=n?.maxAge){const e=n?.minAge??"不限",r=n?.maxAge??"不限";t.push(`年龄${e}-${r}岁`)}if(n?.genderRequirement&&"0"!==n.genderRequirement){if(!/男性.*女性|女性.*男性|不限/.test(n.genderRequirement)){const e={"男性":"限男性","女性":"限女性"};t.push(e[n.genderRequirement]??`性别要求:${n.genderRequirement}`)}}if(r?.education&&"不限"!==r.education){const e={"本科":"本科及以上","专科":"专科及以上","高中":"高中及以上","初中":"初中及以上"};t.push(e[r.education]??`学历${r.education}`)}if(r?.healthCertificate){const e={"食品健康证":"需食品健康证","零售健康证":"需零售健康证"};t.push(e[r.healthCertificate]??"需健康证")}return 0===t.length?qr(e.jobName):t}function qr(e){const t=["工作认真负责","团队合作精神"];return e.includes("服务员")?[...t,"有服务行业经验优先","沟通能力强"]:e.includes("经理")?[...t,"有管理经验","责任心强"]:[...t,"有相关工作经验者优先"]}function Hr(e){const t=e.basicPersonalRequirements,n=e.certificate;if(t||n)return{minAge:t?.minAge??null,maxAge:t?.maxAge??null,genderRequirement:t?.genderRequirement??null,education:n?.education??null,healthCertificate:n?.healthCertificate??null}}function Kr(e){const t=3===e;return{punctualityRequired:t,lateToleranceMinutes:t?5:15,attendanceTracking:t?"strict":"flexible",makeupShiftsAllowed:!t}}function Vr(e,t){const n=[];let r=[];t.combinedArrangementTimes?.length?r=ea(t.combinedArrangementTimes):t.fixedArrangementTimes?.length&&(r=ea(t.fixedArrangementTimes.map(e=>({...e,weekdays:[]}))));for(const t of r)n.push({slot:t,maxCapacity:e.requirementNum,currentBooked:e.signUpNum||0,isAvailable:(e.signUpNum||0)<e.requirementNum,priority:e.requirementNum>3?"high":"medium"});return n}function Yr(e,t){const n=2===e.cooperationMode;return{canSwapShifts:3===t.arrangementType||n,advanceNoticeHours:t.maxWorkTakingTime/60,partTimeAllowed:n,weekendRequired:zr(t),holidayRequired:!1}}function zr(e){return!!e.combinedArrangementTimes&&e.combinedArrangementTimes.some(e=>e.weekdays.includes(0)||e.weekdays.includes(6))}function Jr(e){const t=e.perDayMinWorkHours??8;let n=null;if(null!=e.perWeekWorkDays&&(n=e.perWeekWorkDays),null===n&&e.customWorkTimes?.length){const t=e.customWorkTimes.map(e=>e.minWorkDays).filter(e=>null!=e);t.length>0&&(n=Math.min(...t))}return null===n&&null!=e.perWeekNeedWorkDays&&(n=e.perWeekNeedWorkDays),null===n&&(n=5),t*n}function Qr(e){return 7*(e.perDayMinWorkHours??8)}function Zr(e){let t=[];if(e.combinedArrangementTimes?.length){const n=new Set;for(const t of e.combinedArrangementTimes)for(const e of t.weekdays)null!=e&&Number.isFinite(e)&&n.add(e);t=Array.from(n).sort()}else if(e.customWorkTimes?.length){const n=new Set;for(const t of e.customWorkTimes)for(const e of t.weekdays)null!=e&&Number.isFinite(e)&&n.add(e);t=Array.from(n).sort()}let n=null;if(null!=e.perWeekWorkDays&&(n=e.perWeekWorkDays),null===n&&e.customWorkTimes?.length){const t=e.customWorkTimes.map(e=>e.minWorkDays).filter(e=>null!=e);t.length>0&&(n=Math.min(...t))}return null===n&&null!=e.perWeekNeedWorkDays&&(n=e.perWeekNeedWorkDays),null===n&&(n=5),{minimumDays:n,requiredDays:aa(t),description:e.workTimeRemark||""}}function Xr(e){if("number"==typeof e)return e;if("string"!=typeof e||!e)return 0;const t=e.match(/^(\d{1,2}):(\d{2})$/);return t?3600*Number(t[1])+60*Number(t[2]):Number(e)||0}function ea(e){return e.map(e=>{const t=Math.floor(e.startTime/3600),n=Math.floor(e.startTime%3600/60),r=Math.floor(e.endTime/3600),a=Math.floor(e.endTime%3600/60);return`${t.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}~${r.toString().padStart(2,"0")}:${a.toString().padStart(2,"0")}`})}function ta(e,t){if(t)return t;return e.split("-")[1]||"未知区域"}function na(e){const t=e.match(/(.+?)(附近|周边|旁边|店)/);return t?.[1]??e}function ra(e){const t=e.split("-");return t[t.length-2]||"服务员"}function aa(e){return e.filter(e=>Number.isFinite(e)).map(e=>0===e?7:e)}var oa=Nr({name:"sync_brand_data",description:"从 Duliday API 拉取并同步品牌配置数据(门店、岗位、薪资等)到本地。可选传入品牌别名和城市名称作为过滤条件。",input:Ir.object({brandAlias:Ir.string().optional().describe("品牌别名(可选,配合 cityName 使用可按品牌过滤)"),cityName:Ir.string().describe('城市名称(必填,Duliday API 要求至少提供城市作为筛选条件,如 "上海市")')}),output:Ir.object({success:Ir.boolean(),brandsCount:Ir.number(),storesCount:Ir.number(),positionsCount:Ir.number(),updatedAt:Ir.string(),error:Ir.string().optional()}),execute:async(e,t)=>{const n=Dn(),r=Mn();if(!n||!r)return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:`缺少环境变量: ${[!n&&"DULIDAY_TOKEN",!r&&"DULIDAY_JOB_LIST_URL"].filter(Boolean).join(", ")}`};if(!e.cityName)return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:'cityName 为必填项,Duliday API 要求至少提供城市作为筛选条件(如 "上海市")'};try{t.logger.info("Fetching all job list pages from Duliday API...");const{items:a}=await Bn(n,r,{brandAlias:e.brandAlias??null,cityName:e.cityName??null,include:Wn});let o=sa(a);!o&&e.brandAlias&&(o=await la(e.brandAlias,n)),!o&&e.brandAlias&&(o=e.brandAlias),t.logger.info(`Resolved default brand: ${o??"自动推断"}, converting ${a.length} positions...`);const i=Cr(a,o,e.cityName);ae(i),t.logger.info(`Brand config saved: ${i.brands.length} brands, ${i.brands.reduce((e,t)=>e+t.stores.length,0)} stores`);const s=i.brands.reduce((e,t)=>e+t.stores.length,0),l=i.brands.reduce((e,t)=>e+t.stores.reduce((e,t)=>e+t.positions.length,0),0);return{success:!0,brandsCount:i.brands.length,storesCount:s,positionsCount:l,updatedAt:(new Date).toISOString()}}catch(e){return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:e instanceof Error?e.message:String(e)}}}});function ia(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function sa(e){for(const t of e){if(!ia(t))continue;const e=ia(t.basicInfo)?t.basicInfo:null;if(e&&"string"==typeof e.brandName&&e.brandName)return e.brandName}}async function la(e,t){try{const n=await Zt(t),r=e.trim().toLowerCase().replace(/[\s._-]+/g,"");return n.get(r)}catch{return}}var ua=e({name:"smart-reply-agent",tools:[Sr,oa]});ua.listen().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
@@ -1,5 +1,6 @@
1
1
  import type { TurnPlan, ReplyNeed, ReplyPolicyConfig } from "../types/reply-policy.ts";
2
2
  import type { ProviderConfigs, ClassificationOptions } from "../types/classification.ts";
3
+ export declare function normalizeGeneratedTurnPlanOutput(value: unknown): unknown;
3
4
  export declare const PRIMARY_NEED_PRIORITY: readonly ["salary", "schedule", "location", "stores", "policy", "requirements", "availability", "interview", "wechat", "none"];
4
5
  export declare function detectRuleNeeds(message: string, history: string[]): Set<ReplyNeed>;
5
6
  export declare function selectContextNeeds(primaryNeed: ReplyNeed, availableNeedsInput: Iterable<ReplyNeed>, message: string, maxNeeds?: number): ReplyNeed[];
package/dist/pipeline.js CHANGED
@@ -1 +1 @@
1
- export{generateSmartReply}from"./pipeline/smart-reply.js";
1
+ import{createAnthropic as e}from"@ai-sdk/anthropic";import{createAlibaba as t}from"@ai-sdk/alibaba";import{createProviderRegistry as n}from"ai";import{createOpenAICompatible as r}from"@ai-sdk/openai-compatible";import{createDeepSeek as s}from"@ai-sdk/deepseek";import{createGoogleGenerativeAI as o}from"@ai-sdk/google";import{createOpenAI as a}from"@ai-sdk/openai";var i=!1;function c(e){i=e}function u(...e){i||console.error(...e)}var l=process.env.SMART_REPLY_PROXY_BASE_URL,d="https://apic1.ohmycdn.com/v1",m=l||d,p={anthropic:process.env.ANTHROPIC_BASE_URL||m,openai:process.env.OPENAI_BASE_URL||m,ohmygpt:process.env.OHMYGPT_BASE_URL||m,moonshotai:process.env.MOONSHOT_BASE_URL||"https://api.moonshot.cn/v1",deepseek:process.env.DEEPSEEK_BASE_URL||"https://api.deepseek.com",qwen:process.env.QWEN_BASE_URL||"https://dashscope.aliyuncs.com/compatible-mode/v1",google:process.env.GOOGLE_BASE_URL||"https://generativelanguage.googleapis.com/v1beta"},f={anthropic:{name:"Anthropic",baseURL:p.anthropic,description:"Anthropic Claude"},openai:{name:"OpenAI",baseURL:p.openai,description:"OpenAI GPT"},ohmygpt:{name:"OhMyGPT",baseURL:p.ohmygpt,description:"OhMyGPT"},moonshotai:{name:"MoonshotAI",baseURL:p.moonshotai,description:"MoonshotAI"},deepseek:{name:"DeepSeek",baseURL:p.deepseek,description:"DeepSeek"},qwen:{name:"Qwen",baseURL:p.qwen,description:"Qwen"},google:{name:"Google",baseURL:p.google,description:"Google Gemini"}},g={chatModel:"anthropic/claude-haiku-4-5",classifyModel:process.env.SMART_REPLY_CLASSIFY_MODEL||"openai/gpt-5-mini",replyModel:process.env.SMART_REPLY_REPLY_MODEL||"openai/gpt-5.4"};function y(){return process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||""}function E(e){const t=a({apiKey:e.apiKey||"",...void 0!==e.baseURL?{baseURL:e.baseURL}:{}});return new Proxy(t,{get(e,n){if("languageModel"===n)return e=>t.chat(e);if("chat"===n||"completion"===n)return t[n];if("embeddingModel"===n||"imageModel"===n){return t[n]||void 0}return t[n]}})}function _(a){const i=y();return n({anthropic:e({apiKey:i,baseURL:a.anthropic?.baseURL||p.anthropic}),openai:E({apiKey:i,baseURL:a.openai?.baseURL||p.openai}),ohmygpt:r({name:"ohmygpt",baseURL:a.ohmygpt?.baseURL||p.ohmygpt,apiKey:i}),moonshotai:r({name:"moonshotai",baseURL:a.moonshotai?.baseURL||p.moonshotai,apiKey:process.env.MOONSHOT_API_KEY||""}),deepseek:s({baseURL:a.deepseek?.baseURL||p.deepseek,apiKey:process.env.DEEPSEEK_API_KEY||""}),google:o({apiKey:process.env.GEMINI_API_KEY||"",baseURL:a.google?.baseURL||p.google}),qwen:t({apiKey:process.env.DASHSCOPE_API_KEY||process.env.ALIBABA_API_KEY||"",baseURL:a.qwen?.baseURL||p.qwen})},{separator:"/"})}var N=null,h=null;function I(e){const t=JSON.stringify(e);return N&&h===t||(N=_(e),h=t,u("[DYNAMIC REGISTRY] 创建新的动态registry,配置哈希:",t.substring(0,16)+"...")),N}function A(e){return e.brands.flatMap(e=>e.stores)}function S(e){return e.brands.map(e=>e.name)}function T(e,t){if(t)return e.brands.find(e=>e.name===t)}function O(e){if(e.meta.defaultBrandId){const t=e.brands.find(t=>t.id===e.meta.defaultBrandId);if(t)return t}return e.brands[0]}function L(e){return O(e)?.name??""}function b(e,t){const n=t?T(e,t)?.stores??[]:A(e);return n.find(e=>"string"==typeof e.city&&e.city.trim().length>0)?.city}import{z as R}from"zod";var M=R.enum(["trust_building","private_channel","qualify_candidate","job_consultation","interview_scheduling","onboard_followup"]),v=R.enum(["public","private"]),w=R.enum(["minimal","focused"]),D=R.enum(["salary","schedule","location","policy","requirements","availability"]),U={trust_building:{description:"初次接触,建立信任并了解求职意向",transitionSignal:"候选人表达明确兴趣或开始询问具体岗位信息",applicableChannels:["public","private"]},private_channel:{description:"引导用户从公域平台(如BOSS直聘/鱼泡)转入微信私聊",transitionSignal:"候选人有继续深入了解的意愿,适合引导到私域",applicableChannels:["public"]},qualify_candidate:{description:"轻量确认候选人的关键匹配信息,避免审查式盘问",transitionSignal:"候选人表达求职意向后,需要核实基本资格",applicableChannels:["public","private"]},job_consultation:{description:"回答岗位相关问题(薪资、排班、地点等)并提升兴趣",transitionSignal:"候选人主动询问岗位细节",applicableChannels:["public","private"]},interview_scheduling:{description:"推动面试预约,确认时间和到店安排",transitionSignal:"候选人核心问题已解答,准备推进面试",applicableChannels:["public","private"]},onboard_followup:{description:"促进到岗并保持回访",transitionSignal:"候选人确认上岗安排",applicableChannels:["public","private"]}},$=R.enum(["stores","location","salary","schedule","policy","availability","requirements","interview","wechat","none"]),k=R.enum(["insurance_promise_risk","age_sensitive","confrontation_emotion","urgency_high","qualification_mismatch"]),C={stores:["location"],location:["location"],salary:["salary"],schedule:["schedule"],policy:["policy"],availability:["availability"],requirements:["requirements"],interview:[],wechat:[],none:[]},x=R.object({mentionedBrand:R.string().nullable(),city:R.string().nullable(),mentionedLocations:R.array(R.object({location:R.string(),confidence:R.number().min(0).max(1)})).nullable(),mentionedDistricts:R.array(R.object({district:R.string(),confidence:R.number().min(0).max(1)})).max(10).nullable(),specificAge:R.number().nullable(),hasUrgency:R.boolean().nullable(),preferredSchedule:R.string().nullable()}),F=R.object({stage:M,subGoals:R.array(R.string()).max(2),needs:R.array($).max(8),primaryNeed:$,riskFlags:R.array(k).max(6),confidence:R.number().min(0).max(1),extractedInfo:x,reasoningText:R.string()}),P=R.object({description:R.string().optional(),primaryGoal:R.string(),successCriteria:R.array(R.string()),ctaStrategy:R.preprocess(e=>Array.isArray(e)?e.join("\n"):e,R.string()),disallowedActions:R.array(R.string()).optional()}),B=R.object({tone:R.string(),warmth:R.string(),humor:R.string(),length:R.enum(["short","medium","long"]),questionStyle:R.string(),empathyStrategy:R.string(),addressStyle:R.string(),professionalIdentity:R.string(),companyBackground:R.string()}),G=R.object({name:R.string(),industryBackground:R.string(),jargon:R.array(R.string()),styleKeywords:R.array(R.string()),tabooPhrases:R.array(R.string()),guidance:R.array(R.string())}),j=R.object({id:R.string(),rule:R.string(),severity:R.enum(["high","medium","low"])}),K=R.object({rules:R.array(j)}),H=R.object({mode:R.enum(["strict","balanced","open"]),verifiableClaimTypes:R.array(R.string()),fallbackBehavior:R.enum(["generic_answer","ask_followup","handoff"]),forbiddenWhenMissingFacts:R.array(R.string())}),V={maxQuestionsByMode:{minimal:1,focused:2},blockedAuditPhrases:["是否满足","是否符合","基本入职要求","先确认资格","年龄是否符合"],blockFirstTurnSpecificFacts:!0},W=R.object({maxQuestionsByMode:R.object({minimal:R.number().int().min(0),focused:R.number().int().min(0)}),blockedAuditPhrases:R.array(R.string()),blockFirstTurnSpecificFacts:R.boolean()}),Y=R.object({enabled:R.boolean().default(!0),revealRange:R.boolean().default(!1),failStrategy:R.string().default("礼貌说明不匹配,避免承诺"),unknownStrategy:R.string().default("先核实年龄或资格条件"),passStrategy:R.string().default("确认匹配后推进下一步"),allowRedirect:R.boolean().default(!0),redirectPriority:R.enum(["low","medium","high"]).default("medium")}),q=R.object({age:Y.default({enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"})}).default({age:{enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"}}),z=R.object({trust_building:P,private_channel:P.optional(),qualify_candidate:P,job_consultation:P,interview_scheduling:P,onboard_followup:P}).transform(e=>({...e,private_channel:e.private_channel??e.trust_building})),Q=R.object({stageGoals:z,persona:B,industryVoices:R.record(R.string(),G),defaultIndustryVoiceId:R.string(),hardConstraints:K,factGate:H,qualificationPolicy:q,outputGuards:W.default(V)});import{z as J}from"zod";import{setTimeout as Z,clearTimeout as X}from"node:timers";import{performance as ee}from"node:perf_hooks";import{asSchema as te,generateText as ne,jsonSchema as re,NoObjectGeneratedError as se,Output as oe}from"ai";import"zod";var ae={CONFIG:"CONFIG",AUTH:"AUTH",NETWORK:"NETWORK",LLM:"LLM",VALIDATION:"VALIDATION",BUSINESS:"BUSINESS",SYSTEM:"SYSTEM"},ie={LLM_UNAUTHORIZED:"LLM_UNAUTHORIZED",LLM_MODEL_NOT_FOUND:"LLM_MODEL_NOT_FOUND",LLM_RATE_LIMITED:"LLM_RATE_LIMITED",LLM_TIMEOUT:"LLM_TIMEOUT",LLM_GENERATION_FAILED:"LLM_GENERATION_FAILED",LLM_RESPONSE_PARSE_ERROR:"LLM_RESPONSE_PARSE_ERROR",CONFIG_NOT_FOUND:"CONFIG_NOT_FOUND",CONFIG_INVALID:"CONFIG_INVALID",CONFIG_MISSING_FIELD:"CONFIG_MISSING_FIELD",CONFIG_LOAD_FAILED:"CONFIG_LOAD_FAILED",NETWORK_TIMEOUT:"NETWORK_TIMEOUT",NETWORK_CONNECTION_FAILED:"NETWORK_CONNECTION_FAILED",NETWORK_HTTP_ERROR:"NETWORK_HTTP_ERROR",NETWORK_DNS_FAILED:"NETWORK_DNS_FAILED",AUTH_UNAUTHORIZED:"AUTH_UNAUTHORIZED",AUTH_FORBIDDEN:"AUTH_FORBIDDEN",AUTH_TOKEN_EXPIRED:"AUTH_TOKEN_EXPIRED",AUTH_TOKEN_INVALID:"AUTH_TOKEN_INVALID",VALIDATION_INVALID_INPUT:"VALIDATION_INVALID_INPUT",VALIDATION_MISSING_REQUIRED:"VALIDATION_MISSING_REQUIRED",VALIDATION_FORMAT_ERROR:"VALIDATION_FORMAT_ERROR",VALIDATION_SCHEMA_ERROR:"VALIDATION_SCHEMA_ERROR",BUSINESS_RULE_VIOLATION:"BUSINESS_RULE_VIOLATION",BUSINESS_RESOURCE_NOT_FOUND:"BUSINESS_RESOURCE_NOT_FOUND",BUSINESS_RESOURCE_EXISTS:"BUSINESS_RESOURCE_EXISTS",BUSINESS_OPERATION_NOT_ALLOWED:"BUSINESS_OPERATION_NOT_ALLOWED",SYSTEM_INTERNAL:"SYSTEM_INTERNAL",SYSTEM_DEPENDENCY_FAILED:"SYSTEM_DEPENDENCY_FAILED",SYSTEM_RESOURCE_UNAVAILABLE:"SYSTEM_RESOURCE_UNAVAILABLE",SYSTEM_UNKNOWN:"SYSTEM_UNKNOWN"},ce={[ie.LLM_UNAUTHORIZED]:ae.LLM,[ie.LLM_MODEL_NOT_FOUND]:ae.LLM,[ie.LLM_RATE_LIMITED]:ae.LLM,[ie.LLM_TIMEOUT]:ae.LLM,[ie.LLM_GENERATION_FAILED]:ae.LLM,[ie.LLM_RESPONSE_PARSE_ERROR]:ae.LLM,[ie.CONFIG_NOT_FOUND]:ae.CONFIG,[ie.CONFIG_INVALID]:ae.CONFIG,[ie.CONFIG_MISSING_FIELD]:ae.CONFIG,[ie.CONFIG_LOAD_FAILED]:ae.CONFIG,[ie.NETWORK_TIMEOUT]:ae.NETWORK,[ie.NETWORK_CONNECTION_FAILED]:ae.NETWORK,[ie.NETWORK_HTTP_ERROR]:ae.NETWORK,[ie.NETWORK_DNS_FAILED]:ae.NETWORK,[ie.AUTH_UNAUTHORIZED]:ae.AUTH,[ie.AUTH_FORBIDDEN]:ae.AUTH,[ie.AUTH_TOKEN_EXPIRED]:ae.AUTH,[ie.AUTH_TOKEN_INVALID]:ae.AUTH,[ie.VALIDATION_INVALID_INPUT]:ae.VALIDATION,[ie.VALIDATION_MISSING_REQUIRED]:ae.VALIDATION,[ie.VALIDATION_FORMAT_ERROR]:ae.VALIDATION,[ie.VALIDATION_SCHEMA_ERROR]:ae.VALIDATION,[ie.BUSINESS_RULE_VIOLATION]:ae.BUSINESS,[ie.BUSINESS_RESOURCE_NOT_FOUND]:ae.BUSINESS,[ie.BUSINESS_RESOURCE_EXISTS]:ae.BUSINESS,[ie.BUSINESS_OPERATION_NOT_ALLOWED]:ae.BUSINESS,[ie.SYSTEM_INTERNAL]:ae.SYSTEM,[ie.SYSTEM_DEPENDENCY_FAILED]:ae.SYSTEM,[ie.SYSTEM_RESOURCE_UNAVAILABLE]:ae.SYSTEM,[ie.SYSTEM_UNKNOWN]:ae.SYSTEM},ue={[ie.LLM_UNAUTHORIZED]:"AI 服务认证失败,请检查配置",[ie.LLM_MODEL_NOT_FOUND]:"所选模型暂时不可用,请尝试其他模型",[ie.LLM_RATE_LIMITED]:"请求过于频繁,请稍后重试",[ie.LLM_TIMEOUT]:"AI 响应超时,请稍后重试",[ie.LLM_GENERATION_FAILED]:"内容生成失败,请稍后重试",[ie.LLM_RESPONSE_PARSE_ERROR]:"AI 响应格式异常,请重试",[ie.CONFIG_NOT_FOUND]:"配置数据未找到,请先进行初始化",[ie.CONFIG_INVALID]:"配置格式无效,请检查配置",[ie.CONFIG_MISSING_FIELD]:"配置缺少必需字段",[ie.CONFIG_LOAD_FAILED]:"配置加载失败,请重试",[ie.NETWORK_TIMEOUT]:"网络请求超时,请检查网络连接",[ie.NETWORK_CONNECTION_FAILED]:"网络连接失败,请检查网络",[ie.NETWORK_HTTP_ERROR]:"服务器返回错误,请稍后重试",[ie.NETWORK_DNS_FAILED]:"域名解析失败,请检查网络",[ie.AUTH_UNAUTHORIZED]:"请先登录",[ie.AUTH_FORBIDDEN]:"您没有权限执行此操作",[ie.AUTH_TOKEN_EXPIRED]:"登录已过期,请重新登录",[ie.AUTH_TOKEN_INVALID]:"认证信息无效,请重新登录",[ie.VALIDATION_INVALID_INPUT]:"输入参数无效",[ie.VALIDATION_MISSING_REQUIRED]:"缺少必需参数",[ie.VALIDATION_FORMAT_ERROR]:"数据格式错误",[ie.VALIDATION_SCHEMA_ERROR]:"数据验证失败",[ie.BUSINESS_RULE_VIOLATION]:"操作违反业务规则",[ie.BUSINESS_RESOURCE_NOT_FOUND]:"请求的资源不存在",[ie.BUSINESS_RESOURCE_EXISTS]:"资源已存在",[ie.BUSINESS_OPERATION_NOT_ALLOWED]:"当前操作不被允许",[ie.SYSTEM_INTERNAL]:"系统内部错误,请稍后重试",[ie.SYSTEM_DEPENDENCY_FAILED]:"依赖服务异常,请稍后重试",[ie.SYSTEM_RESOURCE_UNAVAILABLE]:"系统资源不可用,请稍后重试",[ie.SYSTEM_UNKNOWN]:"发生未知错误,请稍后重试"};function le(e){return ce[e]}function de(e){return ue[e]}var me=class _AppError extends Error{code;category;userMessage;details;cause;timestamp;constructor(e){super(e.message),this.name="AppError",this.code=e.code,this.category=le(e.code),this.userMessage=e.userMessage||de(e.code),this.details=e.details,this.cause=e.cause,this.timestamp=(new Date).toISOString(),Error.captureStackTrace&&Error.captureStackTrace(this,_AppError)}toJSON(){const e={code:this.code,category:this.category,message:this.message,userMessage:this.userMessage,timestamp:this.timestamp};return void 0!==this.details&&(e.details=this.details),this.cause&&(this.cause instanceof _AppError?e.cause=this.cause.toJSON():e.cause={message:this.cause.message,stack:this.cause.stack}),e}getErrorChain(){const e=[this];let t=this.cause;for(;t;)e.push(t),t=t instanceof _AppError?t.cause:void 0;return e}getRootCause(){const e=this.getErrorChain();return e[e.length-1]}hasErrorCode(e){return this.getErrorChain().some(t=>t instanceof _AppError&&t.code===e)}hasErrorCategory(e){return this.getErrorChain().some(t=>t instanceof _AppError&&t.category===e)}toLogString(){const e=[`[${this.code}]`,this.message];return this.details&&e.push(`Details: ${JSON.stringify(this.details)}`),this.cause&&e.push(`Caused by: ${this.cause.message}`),e.join(" | ")}};function pe(e){return e instanceof me}function fe(e,t,n){const r=n?.model?` (model: ${n.model})`:"",s=n?.provider?` [${n.provider}]`:"",o={[ie.LLM_UNAUTHORIZED]:`LLM API authentication failed${s}${r}`,[ie.LLM_MODEL_NOT_FOUND]:`Model not found or unavailable${r}${s}`,[ie.LLM_RATE_LIMITED]:`LLM API rate limited${s}`,[ie.LLM_TIMEOUT]:`LLM API request timeout${s}${r}`,[ie.LLM_GENERATION_FAILED]:`LLM generation failed${s}${r}`,[ie.LLM_RESPONSE_PARSE_ERROR]:`Failed to parse LLM response${s}`};return new me({code:e,message:o[e]||`LLM error: ${t.message}`,cause:t,details:n})}function ge(e,t,n){const r=n?.url?` (${n.url})`:"",s=n?.statusCode?` [${n.statusCode}]`:"",o={[ie.NETWORK_TIMEOUT]:`Network request timeout${r}`,[ie.NETWORK_CONNECTION_FAILED]:`Failed to connect${r}`,[ie.NETWORK_HTTP_ERROR]:`HTTP error${s}${r}`,[ie.NETWORK_DNS_FAILED]:`DNS resolution failed${r}`};return new me({code:e,message:o[e]||`Network error: ${t.message}`,cause:t,details:n})}function ye(e,t,n){const r=n.isMarkdownFormat?" (detected markdown format)":"",s=n.schemaName?` for schema "${n.schemaName}"`:"";return new me({code:e,message:`Failed to parse structured output${s}${r}`,cause:t,details:{rawText:n.rawText,isMarkdownFormat:n.isMarkdownFormat,parseErrorMessage:n.parseErrorMessage,model:n.model,provider:n.provider,schemaName:n.schemaName,usage:n.usage}})}import{NoObjectGeneratedError as Ee}from"ai";function _e(e){if(!e||"object"!=typeof e)return null;const t=e;if(!("AI_APICallError"===t.name||"APICallError"===t.name||"string"==typeof t.url&&"number"==typeof t.statusCode))return null;const n="number"==typeof t.statusCode?t.statusCode:void 0,r="string"==typeof t.responseBody?t.responseBody:void 0,s="string"==typeof t.url?t.url:void 0,o=t.message;let a,i;if(s&&(s.includes("openai.com")?a="openai":s.includes("anthropic.com")?a="anthropic":s.includes("dashscope.aliyuncs.com")?a="qwen":s.includes("openrouter.ai")?a="openrouter":s.includes("deepseek.com")?a="deepseek":s.includes("moonshot.cn")?a="moonshotai":s.includes("googleapis.com")&&(a="google")),r){const e=r.match(/model[`'":\s]+([^`'"}\s,]+)/i);e&&(i=e[1])}return{isAuthError:401===n||403===n,isModelNotFound:r?.includes("model")&&r?.includes("not exist")||r?.includes("not authorized to access this model")||!1,isRateLimited:429===n,isTimeout:408===n||504===n||o?.toLowerCase().includes("timeout")||!1,statusCode:n,provider:a,model:i,originalMessage:o,responseBody:r}}function Ne(e){return[/^```/m,/^#{1,6}\s/m,/^\s*[-*+]\s/m,/^\s*\d+\.\s/m,/\[.+\]\(.+\)/,/^\s*>/m].some(t=>t.test(e))}function he(e){try{if(void 0===Ee||"function"!=typeof Ee.isInstance)return null;if(!Ee.isInstance(e))return null}catch{return null}const t=e.text,n=!!t&&Ne(t),r=e.response?{id:e.response.id,timestamp:e.response.timestamp,modelId:e.response.modelId}:void 0,s=e.usage??void 0,o={isNoObjectGeneratedError:!0,isMarkdownFormat:n};return void 0!==t&&(o.rawText=t),e.cause instanceof Error&&(o.cause=e.cause),void 0!==r&&(o.response=r),void 0!==s&&(o.usage=s),o}function Ie(e,t=ie.SYSTEM_UNKNOWN,n){if(pe(e))return n&&n!==e.userMessage?new me({code:e.code,message:e.message,userMessage:n,cause:e.cause,details:e.details}):e;const r=Ae(e),s=_e(e);if(s){const e={model:s.model,provider:s.provider,statusCode:s.statusCode,responseBody:s.responseBody};return s.isModelNotFound?fe(ie.LLM_MODEL_NOT_FOUND,r,e):s.isAuthError?fe(ie.LLM_UNAUTHORIZED,r,e):s.isRateLimited?fe(ie.LLM_RATE_LIMITED,r,e):s.isTimeout?fe(ie.LLM_TIMEOUT,r,e):fe(ie.LLM_GENERATION_FAILED,r,e)}const o=he(e);if(o)return ye(ie.LLM_RESPONSE_PARSE_ERROR,r,{rawText:o.rawText,isMarkdownFormat:o.isMarkdownFormat,parseErrorMessage:o.cause?.message,usage:o.usage});const a=r.message.toLowerCase();return a.includes("timeout")||a.includes("econnrefused")||a.includes("network")||a.includes("fetch failed")?a.includes("timeout")?ge(ie.NETWORK_TIMEOUT,r):ge(ie.NETWORK_CONNECTION_FAILED,r):new me({code:t,message:r.message,cause:r})}function Ae(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);if("object"==typeof e&&null!==e){const t=e.message||e.error||JSON.stringify(e);return new Error(String(t))}return new Error(String(e))}function Se(e,t){console.error(`[${t.code}] ${e}:`,t.toLogString()),t.cause&&console.error("Original error:",t.cause)}function Te(e){return"object"==typeof e&&null!==e}function Oe(e,t){t&&Te(e.details)&&(e.details.schemaName=t)}function Le(e){const t=e.type;return"string"==typeof t?[t]:Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function be(e,t){let n=null;for(const r of e){const e=t.get(r);if(void 0!==e&&e.size>0){n??=new Set;for(const t of e)n.add(t)}}return n}function Re(e,t){if(Array.isArray(e))return e.map(e=>Re(e,t));if(null===e||"object"!=typeof e)return e;const n=e,r=be(Le(n),t),s={};for(const[e,o]of Object.entries(n))null!==r&&r.has(e)||(s[e]=Re(o,t));return s}function Me(e){const t=e.unsupportedKeywordsByType;if(void 0===t)return new Map;const n=new Map;for(const[e,r]of Object.entries(t)){if(0===r.length)continue;const t=n.get(e);if(void 0!==t)for(const e of r)t.add(e);else n.set(e,new Set(r))}const r=n.get("number");if(void 0!==r&&r.size>0){const e=n.get("integer");if(void 0!==e)for(const t of r)e.add(t);else n.set("integer",new Set(r))}return n}function ve(e,t={}){const n=Me(t);if(0===n.size)return e;const r=te(e);return re(async()=>Re(await r.jsonSchema,n),{validate:e=>({success:!0,value:e})})}async function we(e){const{model:t,schema:n,outputSchema:r,schemaName:s,system:o,prompt:a,transformOutput:i,onError:c}=e;try{const e=await ne({model:t,...void 0!==o?{system:o}:{},prompt:a,output:oe.object({schema:r??n,...void 0!==s?{name:s}:{}})});if(void 0===r&&void 0===i)return{success:!0,data:e.output,usage:e.usage};const u=void 0!==i?await i(e.output):e.output,l=await n.safeParseAsync(u);if(!l.success){const t=Ie(l.error,ie.LLM_RESPONSE_PARSE_ERROR);return Oe(t,s),Se(`Structured output validation (${s||"unknown"})`,t),c&&c(t,e.text),{success:!1,error:t,rawText:e.text}}return{success:!0,data:l.data,usage:e.usage}}catch(e){const t=Ie(e,ie.LLM_RESPONSE_PARSE_ERROR);let n;return se.isInstance(e)&&(n=e.text),Oe(t,s),Se(`Structured output generation (${s||"unknown"})`,t),c&&c(t,n),{success:!1,error:t,rawText:n}}}var De=3e4,Ue=2e3;async function $e(e){const{model:t,system:n,prompt:r,timeoutMs:s=De,maxOutputTokens:o=Ue,context:a="generateText",onError:i}=e,c=ee.now();try{const e=new AbortController,i=Z(()=>e.abort(),s),l=await ne({model:t,...void 0!==n?{system:n}:{},prompt:r,maxOutputTokens:o,abortSignal:e.signal});X(i);const d=Math.round(ee.now()-c),m=l.usage,p=void 0!==m?.inputTokens&&void 0!==m?.outputTokens?m.inputTokens+m.outputTokens:void 0,f={inputTokens:m?.inputTokens,outputTokens:m?.outputTokens,totalTokens:p};return u(`[${a}] 生成成功 | 耗时: ${d}ms | Tokens: ${p??"N/A"} (input: ${f.inputTokens??"?"}, output: ${f.outputTokens??"?"})`),{success:!0,text:l.text,usage:f,latencyMs:d}}catch(e){const t=Math.round(ee.now()-c),n=e instanceof Error&&("AbortError"===e.name||e.message.includes("aborted")),r=Ie(e,n?ie.LLM_TIMEOUT:ie.LLM_GENERATION_FAILED);return Te(r.details)&&(r.details.context=a,r.details.latencyMs=t),Se(`${a} (${t}ms)`,r),i&&i(r),{success:!1,error:r}}}import{z as ke}from"zod";var Ce=ke.object({name:ke.string(),baseURL:ke.string(),description:ke.string()}),xe=ke.record(ke.string(),Ce),Fe=ke.object({chatModel:ke.string().optional(),classifyModel:ke.string().optional(),replyModel:ke.string().optional(),providerConfigs:xe.optional()}),Pe=ke.object({city:ke.string().optional(),defaultBrand:ke.string(),availableBrands:ke.array(ke.string()),storeCount:ke.number()}),Be=ke.object({modelConfig:Fe,candidateMessage:ke.string(),conversationHistory:ke.array(ke.string()).default([]),brandData:Pe.optional(),channelType:v.optional()});function Ge(e){const t=v.safeParse(e);return t.success?t.data:"public"}function je(e="public"){const t=Ge(e),n=M.options.filter(e=>U[e].applicableChannels.includes(t));return n.length>0?n:[...M.options]}function Ke(e){return J.object({stage:J.enum(e),subGoals:F.shape.subGoals,needs:F.shape.needs,primaryNeed:F.shape.primaryNeed,riskFlags:F.shape.riskFlags,confidence:F.shape.confidence,extractedInfo:F.shape.extractedInfo,reasoningText:F.shape.reasoningText})}var He={subGoals:2,needs:8,riskFlags:6,mentionedDistricts:10};function Ve(e){return e.startsWith("anthropic/")}function We(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function Ye(e){if(!We(e))return e;const t={...e};if(Array.isArray(t.subGoals)&&(t.subGoals=t.subGoals.slice(0,He.subGoals)),Array.isArray(t.needs)&&(t.needs=t.needs.slice(0,He.needs)),Array.isArray(t.riskFlags)&&(t.riskFlags=t.riskFlags.slice(0,He.riskFlags)),We(t.extractedInfo)){const e={...t.extractedInfo};Array.isArray(e.mentionedDistricts)&&(e.mentionedDistricts=e.mentionedDistricts.slice(0,He.mentionedDistricts)),t.extractedInfo=e}return t}var qe=[{need:"salary",patterns:[/薪资|工资|时薪|底薪|提成|奖金|补贴|多少钱|收入/i]},{need:"schedule",patterns:[/排班|班次|几点|上班|下班|工时|周末|节假日|做几天/i]},{need:"policy",patterns:[/五险一金|社保|保险|合同|考勤|迟到|补班|试用期/i]},{need:"availability",patterns:[/还有名额|空位|可用时段|什么时候能上|明天能面/i]},{need:"location",patterns:[/在哪|位置|地址|附近|地铁|门店|哪个区|多远/i]},{need:"stores",patterns:[/门店|哪家店|哪些店|有店吗/i]},{need:"requirements",patterns:[/要求|条件|年龄|经验|学历|健康证|身高|体重/i]},{need:"interview",patterns:[/面试|到店|约时间|约面/i]},{need:"wechat",patterns:[/微信|vx|私聊|联系方式|加你/i]}],ze=["salary","schedule","location","stores","policy","requirements","availability","interview","wechat","none"];function Qe(e){const t=new Set;for(const n of qe)n.patterns.some(t=>t.test(e))&&t.add(n.need);return 0===t.size?t.add("none"):t.delete("none"),t}function Je(e,t){return Qe(`${t.slice(-4).join(" ")} ${e}`)}function Ze(e){return Qe(e)}function Xe(e,t,n,r=1){const s=new Set(t);s.size>1&&s.has("none")&&s.delete("none");const o=Ze(n);o.delete("none");const a=[];"none"!==e&&s.has(e)&&a.push(e);for(const t of ze){if(a.length>=r)break;"none"!==t&&t!==e&&(o.has(t)&&s.has(t)&&a.push(t))}return a.length>0?a:"none"===e?["none"]:s.has(e)?[e]:["none"]}function et(e,t,n){const r=new Set(t);if(r.size>1&&r.has("none")&&r.delete("none"),e&&r.has(e))return e;const s=Ze(n);s.delete("none");for(const e of ze)if(s.has(e)&&r.has(e))return e;for(const e of ze)if(r.has(e))return e;return"none"}function tt(e,t,n){const r=new Set([...e.needs,...Array.from(t)]);return r.size>1&&r.has("none")&&r.delete("none"),{...e,subGoals:e.subGoals.slice(0,2),needs:Array.from(r),primaryNeed:et(e.primaryNeed,r,n),confidence:Number.isFinite(e.confidence)?Math.max(0,Math.min(1,e.confidence)):.5}}function nt(e,t,n,r="public",s,o){const a=["你是招聘对话回合规划器,不直接回复候选人。","你只输出结构化规划结果,用于后续回复生成。","规划目标:确定阶段目标(stage)、子目标(subGoals)、事实需求(needs)、主回答轴(primaryNeed)、风险标记(riskFlags)。"].join("\n"),i=Ge(r);return{system:a,prompt:["[阶段枚举与定义]",...je(i).map(e=>{const t=U[e];return`- ${e}: ${s?.stageGoals[e]?.description||t.description} (转入条件: ${t.transitionSignal})`}),"","[needs枚举]","private"===i?"- stores, location, salary, schedule, policy, availability, requirements, interview, none":"- stores, location, salary, schedule, policy, availability, requirements, interview, wechat, none","","[riskFlags枚举]","- insurance_promise_risk, age_sensitive, confrontation_emotion, urgency_high, qualification_mismatch","","[规则]","- 优先判断本轮主阶段(stage);subGoals 最多 2 项,只保留最关键的。","- 候选人追问事实时,必须打开对应 needs。","- primaryNeed 必须从 needs 中选择一个最主的 need;如果没有明确事实轴则填 none。","- 不确定时 confidence 降低,不要臆断。","- 根据转入条件判断阶段转化,不要停留在不匹配的阶段。",...o&&o.length>0?[`- 候选人资料中已有:${o.join("、")}。不要生成追问这些字段的 subGoal。`]:[],"","[品牌数据]",JSON.stringify(n||{}),"","[历史对话]",t.slice(-8).join("\n")||"无","","[候选人消息]",e].join("\n")}}async function rt(e,t){const{providerConfigs:n=f,modelConfig:r,conversationHistory:s=[],brandData:o,channelType:a,replyPolicy:i,knownCandidateFields:c}=t,u=I(n),l=r?.classifyModel||g.classifyModel,d=Ge(a),m=Ke(je(d)),p=Ve(l),y=nt(e,s,o,d,i,c),E=await we({model:u.languageModel(l),schema:m,...p?{outputSchema:ve(m,{unsupportedKeywordsByType:{array:["maxItems","minItems"],number:["maximum","minimum","exclusiveMaximum","exclusiveMinimum"]}}),transformOutput:Ye}:{},schemaName:"TurnPlanningOutput",system:y.system,prompt:y.prompt}),_=Je(e,s),N=et(void 0,_,e);return E.success?tt(E.data,_,e):{stage:"trust_building",subGoals:["保持对话并澄清需求"],needs:Array.from(_),primaryNeed:N,riskFlags:[],confidence:.35,extractedInfo:{mentionedBrand:null,city:o?.city||null,mentionedLocations:null,mentionedDistricts:null,specificAge:null,hasUrgency:null,preferredSchedule:null},reasoningText:"规划模型失败,使用规则降级策略"}}import{z as st}from"zod";var ot=st.object({id:st.number(),name:st.string(),aliases:st.array(st.string()),projectIdList:st.array(st.number())}),at=st.object({code:st.number(),message:st.string().optional(),data:st.object({result:st.array(ot),total:st.number()})}),it=3e5,ct=null;function ut(){return null!==ct&&Date.now()-ct.timestamp<it}var lt=3e4;function dt(){const e=process.env.DULIDAY_BRAND_LIST_URL;return"string"==typeof e&&e.trim().length>0?e:void 0}async function mt(e){const t=e||process.env.DULIDAY_TOKEN,n=dt();if(!t)throw new me({code:ie.CONFIG_MISSING_FIELD,message:"DULIDAY_TOKEN 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday Token 配置"});if(!n)throw new me({code:ie.CONFIG_MISSING_FIELD,message:"DULIDAY_BRAND_LIST_URL 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday 品牌接口配置"});const r=new AbortController,s=setTimeout(()=>r.abort(),lt);try{const e=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":t},body:JSON.stringify({pageNum:1,pageSize:1e3}),signal:r.signal});if(!e.ok)throw new me({code:ie.NETWORK_HTTP_ERROR,message:`Duliday 品牌列表 API 返回 HTTP ${e.status}: ${e.statusText}`,userMessage:`品牌别名数据加载失败:服务端返回 ${e.status}`});const s=await e.json(),o=at.safeParse(s);if(!o.success)throw new me({code:ie.VALIDATION_SCHEMA_ERROR,message:`Duliday 品牌列表响应格式校验失败: ${o.error.message}`,userMessage:"品牌别名数据加载失败:响应格式异常"});return o.data.data.result}catch(e){if(e instanceof me)throw e;if(e instanceof Error&&"AbortError"===e.name)throw new me({code:ie.NETWORK_TIMEOUT,message:`Duliday 品牌列表 API 请求超时 (${lt}ms)`,userMessage:"品牌别名数据加载超时,请稍后重试",cause:e});throw new me({code:ie.SYSTEM_DEPENDENCY_FAILED,message:`获取品牌别名数据失败: ${e instanceof Error?e.message:String(e)}`,userMessage:"品牌别名数据加载失败,请检查网络连接",...e instanceof Error?{cause:e}:{}})}finally{clearTimeout(s)}}function pt(e,t,n){const r={},s=new Map,o=e=>e.trim(),a=(e,t)=>{const n=o(e).toLowerCase().replace(/[\s._-]+/g,"");if(!n)return;const r=s.get(n);(!r||t.length>r.length)&&s.set(n,t)},i=[...e].sort((e,t)=>t.name.length-e.name.length);for(const e of i){const t=o(e.name);if(!t)continue;const s=Array.from(new Set([t,...e.aliases.map(o).filter(Boolean)].filter(e=>e!==t)));s.unshift(t),r[t]=s;for(const e of s)a(e,t);if(n&&e.projectIdList.length>0)for(const o of e.projectIdList){const e=n[String(o)];if(!e||e===t)continue;if(!e.includes(t))continue;const i=e.replace(t,"");if(!i)continue;const c=[e];for(const e of s)e!==t&&c.push(`${i}${e}`);if(r[e]){const t=new Set(r[e]);for(const n of c)t.has(n)||r[e].push(n)}else r[e]=c;for(const t of c)a(t,e)}}for(const e of t)r[e]||(r[e]=[e],a(e,e));return{dictionary:r,aliasMap:s}}async function ft(e){if(ut())return;const t=await mt(e),n=new Set,{dictionary:r,aliasMap:s}=pt(t,n);ct={aliasMap:s,dictionary:r,timestamp:Date.now()}}async function gt(e){return await ft(e),ct.aliasMap}function yt(e){const{base:t,range:n,memo:r}=e;let s="";return t<10&&r?s=`${t}元(${r.replace(/\n/g," ").trim()})`:(s=`${t}元/时`,n&&n!==`${t}-${t}`&&(s+=`,范围${n}元`),r&&r.length<50&&(s+=`(${r.replace(/\n/g," ").trim()})`)),e.scenarioSummary&&(s+=`(${e.scenarioSummary})`),s}function Et(e,t,n){if(!e)return null;const r=e=>e.toLowerCase().replace(/[\s._-]+/g,"");if(n){const s=n.get(r(e))||n.get(e.toLowerCase());if(s&&t.includes(s))return s}const s=e.toLowerCase(),o=r(e),a=t.find(e=>e.toLowerCase()===s);if(a)return a;const i=t.find(e=>r(e)===o);if(i)return i;const c=t.filter(e=>{const t=e.toLowerCase();if(t.includes(s)||s.includes(t))return!0;const n=r(e);return n.includes(o)||o.includes(n)});if(c.length>0)return c.sort((e,t)=>t.length-e.length)[0]??null;if(s.includes("山姆")||s.includes("sam")){const e=t.find(e=>{const t=e.toLowerCase();return t.includes("山姆")||t.includes("sam")});if(e)return e}return null}function _t(e){const{uiSelectedBrand:t,configDefaultBrand:n,conversationBrand:r,availableBrands:s,strategy:o="smart",aliasMap:a}=e,i=(e,t)=>{if(e)return Et(e,s,a)??void 0};switch(o){case"user-selected":{const e=i(t);if(e)return{resolvedBrand:e,matchType:e===t?"exact":"fuzzy",source:"ui",reason:"用户选择策略",originalInput:t};const r=i(n);return r?{resolvedBrand:r,matchType:r===n?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:n}:{resolvedBrand:s[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}case"conversation-extracted":{const e=i(r);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"对话提取",originalInput:r};const o=i(t);if(o)return{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"ui",reason:"UI选择",originalInput:t};const a=i(n);return a?{resolvedBrand:a,matchType:a===n?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:n}:{resolvedBrand:s[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}default:{const e=i(r),o=i(t);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"智能策略: 对话提取",originalInput:r};if(o)return{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"ui",reason:"智能策略: UI选择",originalInput:t};const a=i(n);return a?{resolvedBrand:a,matchType:a===n?"exact":"fuzzy",source:"config",reason:"智能策略: 配置默认",originalInput:n}:{resolvedBrand:s[0]??"",matchType:"fallback",source:"default",reason:"智能策略: 系统默认"}}}}function Nt(e,t){const{mentionedLocations:n,mentionedDistricts:r}=t;return e.map(e=>{let t=0,s=0,o=0,a=0;if(n&&n.length>0){const r=n.find(t=>e.name.includes(t.location)||e.location.includes(t.location)||e.subarea.includes(t.location));r&&(t=40*r.confidence)}if(r&&r.length>0){const t=r.find(t=>e.district.includes(t.district)||e.subarea.includes(t.district));t&&(s=30*t.confidence)}const i=new Set(e.positions.map(e=>e.name));o=Math.min(5*i.size,20);const c=e.positions.filter(e=>e.availableSlots?.some(e=>e.isAvailable));return a=Math.min(2*c.length,10),{store:e,score:t+s+o+a,breakdown:{locationMatch:t,districtMatch:s,positionDiversity:o,availability:a}}}).sort((e,t)=>t.score-e.score).map(e=>({store:e.store,distance:void 0}))}function ht(e){if(!e)return"灵活排班";return{fixed:"固定排班",flexible:"灵活排班",rotating:"轮班制",on_call:"随叫随到"}[e]||"灵活排班"}function It(e){return C[e].length>0}function At(e,t){return e.has(t)}function St(e,t,n){return"minimal"===t||(1===e||"trust_building"===n||"private_channel"===n)}async function Tt(e,t,n,r,s,o,a,i,c=1,l="minimal",d=[t.primaryNeed]){const m=t.extractedInfo,p=t.primaryNeed,f=d.length>0?Array.from(new Set(d.filter(e=>"none"!==e))):"none"===p?[]:[p],g=new Set(f.flatMap(e=>C[e])),y=St(c,l,t.stage),E=!y&&It(p);let _,N;try{_=await gt()}catch(e){const t="object"==typeof e&&null!==e&&"userMessage"in e&&"string"==typeof e.userMessage?e.userMessage:e instanceof Error?e.message:String(e);N=t,u(`[buildContextInfoByNeeds] 品牌别名服务不可用,回退 fuzzy 解析: ${t}`)}const h=_t({uiSelectedBrand:n,configDefaultBrand:L(e),conversationBrand:r||void 0,availableBrands:S(e),strategy:s||"smart",aliasMap:_}),I=h.resolvedBrand;u(`[品牌解析] 工具传参: ${r??"(未指定)"} → 结果: ${I} (${h.matchType}, ${h.source})`);const A=T(e,I),O=A?.stores??[];let b=O;if(b.length>0){const e=m.mentionedLocations||[];if(e.length>0){const t=e[0]?.location?.trim();if(t){const e=b.filter(e=>e.name.includes(t)||e.location.includes(t)||e.district.includes(t)||e.subarea.includes(t));e.length>0&&(b=e)}}const t=m.mentionedDistricts||[];if(t.length>0){const e=b.filter(e=>t.some(t=>e.district.includes(t.district)||e.subarea.includes(t.district)));e.length>0&&(b=e)}if(b.length===O.length&&o?.jobAddress&&At(g,"location")){const e=b.filter(e=>e.name.includes(o.jobAddress||"")||e.location.includes(o.jobAddress||"")||e.district.includes(o.jobAddress||"")||e.subarea.includes(o.jobAddress||""));e.length>0&&(b=e)}}let R=[];b.length>0&&(R=Nt(b,m));const M=E?Math.min(1,R.length):0,v=E?"focused":"minimal";let w=`阶段目标:${t.stage}\n默认推荐品牌:${I}\n`;if(N&&(w+=`系统状态:品牌别名服务暂不可用,已回退为规则匹配(${N})。\n`),a){const e=a.stageGoals[t.stage],n=i||a.defaultIndustryVoiceId,r=a.industryVoices[n];w+=`策略目标:${e.primaryGoal}\n`,w+=`推进方式:${e.ctaStrategy}\n`,w+=`主回答轴:${p}\n`,r&&(w+=`行业指纹:${r.name} | 风格:${r.styleKeywords.join("、")}\n`),w+=`红线:${a.hardConstraints.rules.map(e=>e.rule).join(";")}\n`}return E?0===M?w+="暂无可用的门店事实信息,请使用泛化回答,避免任何具体承诺。\n":(w+="匹配到的门店信息:\n",R.slice(0,M).forEach(({store:e})=>{const t=At(g,"location"),n=Array.from(g).some(e=>"location"!==e);w+=t?`• ${e.name}(${e.district}${e.subarea}):${e.location}\n`:`• ${e.name}\n`,n&&e.positions.slice(0,3).forEach(e=>{if(w+=` 职位:${e.name}\n`,At(g,"salary")&&(w+=` 薪资:${yt(e.salary)}\n`),At(g,"schedule")&&(w+=` 排班:${ht(e.scheduleType)}\n`,w+=` 时间:${e.timeSlots.slice(0,3).join("、")}\n`,(e.minHoursPerWeek||e.maxHoursPerWeek)&&(w+=` 每周工时:${e.minHoursPerWeek||0}-${e.maxHoursPerWeek||"不限"}小时\n`)),At(g,"policy")&&(w+=` 考勤:最多迟到${e.attendancePolicy.lateToleranceMinutes}分钟\n`,e.attendanceRequirement?.description&&(w+=` 出勤要求:${e.attendanceRequirement.description}\n`)),At(g,"availability")){const t=e.availableSlots?.filter(e=>e.isAvailable).slice(0,3)||[];t.length>0&&(w+=` 可用时段:${t.map(e=>e.slot).join("、")}\n`)}if(At(g,"requirements"))if(e.hiringRequirements){const t=e.hiringRequirements,n=[];null==t.minAge&&null==t.maxAge||n.push(`年龄${t.minAge??"不限"}-${t.maxAge??"不限"}岁`),t.genderRequirement&&"0"!==t.genderRequirement&&n.push(`性别:${t.genderRequirement}`),t.education&&"1"!==t.education&&n.push(`学历:${t.education}`),n.length>0&&(w+=` 要求:${n.join("、")}\n`)}else e.requirements?.length&&(w+=` 要求:${e.requirements.filter(e=>"无"!==e).join("、")}\n`)})})):w+=y?"当前处于首轮或浅层沟通,优先泛化回答,不主动展开具体门店、数字或筛选条件。\n":"本轮以推进沟通为主,无需展开岗位细节,请保持回答聚焦且克制。\n",{contextInfo:w,resolvedBrand:I,debugInfo:{relevantStores:R.length>0?R:b.map(e=>({store:e,distance:void 0})),storeCount:M,detailLevel:v,primaryNeed:p,turnPlan:t,aliasLookupError:N}}}var Ot=/[^。!?!?]*[??]/g,Lt={salary:{mention:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i,/时薪|薪资|工资|底薪|收入/i],concrete:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i]},schedule:{mention:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/班次|轮班|白班|晚班|工时|排班/i],concrete:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/轮班|白班|晚班|早班|中班|夜班/i]},location:{mention:[/地址|地铁|附近|位于|门店|在.+(?:路|街|广场|商场|大厦)/i],concrete:[/地址|位于|地铁\S*站|在.+(?:路|街|广场|商场|大厦)/i]},policy:{mention:[/考勤|试用期|社保|五险一金|迟到|补班/i],concrete:[/考勤|试用期|社保|五险一金|迟到|补班/i]},requirements:{mention:[/年龄|学历|经验|健康证|要求/i],concrete:[/年龄|学历|经验|健康证/i]},availability:{mention:[/名额|空位|可用时段|可安排/i],concrete:[/名额|空位|可用时段/i]}};function bt(e){return e?.outputGuards??V}function Rt(e){const t=e.match(Ot)??[],n=new Set(t.map(e=>e.replace(/[??]/g,"").trim()).filter(Boolean)),r=e.split(/[。!?!?]/).map(e=>e.trim()).filter(Boolean).filter(e=>/[吗呢么]$/.test(e)&&!n.has(e)).length;return t.length+r}function Mt(e){return e.split(/[。!?!?]/).map(e=>e.trim()).filter(Boolean).length}function vt(e){return/(?:^|\s)(?:\d+\.\s|- |•)/m.test(e)}function wt(e,t="mention"){return Object.entries(Lt).filter(([,n])=>n[t].some(t=>t.test(e))).map(([e])=>e)}function Dt(e,t){return t.some(t=>t&&e.includes(t))}function Ut(e){return/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i.test(e)||/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/.test(e)||/年龄\s*\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(e)||/\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(e)||/地址在|门店在|位于|在.+(?:路|街|广场|商场|大厦|地铁)/i.test(e)}function $t(e,t,n){let r=0;return Mt(e)>=4&&(r+=1),vt(e)&&(r+=1),t>=3&&(r+=1),n.length>=2&&(r+=1),r>=2}function kt(e){return wt(e,"concrete")}function Ct(e){const t=[];return/薪资:/.test(e)&&t.push("salary"),/排班:|时间:|每周工时:/.test(e)&&t.push("schedule"),/匹配到的门店信息:[\s\S]*• .*:/.test(e)&&t.push("location"),/考勤:|出勤要求:/.test(e)&&t.push("policy"),/要求:/.test(e)&&t.push("requirements"),/可用时段:/.test(e)&&t.push("availability"),t}function xt(e,t){const n=new Set(e.flatMap(e=>C[e]));return 0===n.size?t.length>0:t.some(e=>!n.has(e))}function Ft(e){const{text:t,turnIndex:n,mode:r,policy:s}=e,o=bt(s),a=Rt(t),i=o.maxQuestionsByMode[r],c=kt(t),u=e.allowedNeeds?.length?e.allowedNeeds:[e.primaryNeed],l=[];return a>i&&l.push("too_many_questions"),Dt(t,o.blockedAuditPhrases)&&l.push("audit_tone"),o.blockFirstTurnSpecificFacts&&1===n&&Ut(t)&&l.push("premature_numeric_disclosure"),xt(u,c)&&l.push("off_axis_fact_disclosure"),$t(t,a,c)&&l.push("reply_overpacked"),{violations:l,questionCount:a,factFamilies:c}}import Pt from"ora";function Bt(){let e=null;return{update(t){e?e.text=t:e=Pt({text:t,stream:process.stderr}).start()},succeed(t){e?(e.succeed(t),e=null):console.error(`✓ ${t}`)},fail(t){e?(e.fail(t),e=null):console.error(`✗ ${t}`)}}}import{z as Gt}from"zod";var jt=6e4,Kt=2e4,Ht=null,Vt=null;function Wt(){const e=process.env.DULIDAY_JOB_LIST_URL;return"string"==typeof e&&e.trim().length>0?e:void 0}function Yt(){const e=process.env.DULIDAY_TOKEN;return"string"==typeof e&&e.trim().length>0?e:void 0}function qt(e){return(e??"").trim()}function zt(e){const t=qt(e);if(!t)return[];const n=new Set([t]);return t.endsWith("市")?n.add(t.slice(0,-1)):n.add(`${t}市`),Array.from(n).filter(Boolean)}function Qt(e,t){const n=qt(e);if(!n)return[];const r=new Set([n]),s=zt(t);for(const e of s)if(n.startsWith(e)){const t=n.slice(e.length).trim();t&&r.add(t)}return Array.from(r).filter(Boolean)}var Jt=Gt.object({data:Gt.object({result:Gt.array(Gt.unknown()).optional(),list:Gt.array(Gt.unknown()).optional(),total:Gt.number().optional()}).nullable().optional(),result:Gt.array(Gt.unknown()).optional()});function Zt(e){const t=Jt.safeParse(e);if(!t.success)return{items:[],total:0};const n=t.data,r=n.data?.result??n.data?.list??(Array.isArray(n.result)?n.result:[]),s=n.data?.total??(Array.isArray(r)?r.length:0);return{items:Array.isArray(r)?r:[],total:"number"==typeof s?s:0}}var Xt={includeBasicInfo:!0,includeHiringRequirement:!0};async function en(e,t,n,r,s){const o="test"!==process.env.NODE_ENV,a=Qt(s?.brandAlias,s?.cityName),i=zt(s?.cityName),c=s?.include??Xt,u=JSON.stringify({token:e,brandCandidates:a,cityCandidates:i,pageNum:n,includeOpts:c}),l=Date.now();if(o&&Ht&&Ht.cacheKey===u&&l-Ht.fetchedAt<jt)return Ht.payload;if(o&&Vt?.cacheKey===u)return Vt.promise;const d=(async()=>{const s=new AbortController,o=setTimeout(()=>s.abort(),Kt),l={};a.length>0&&(l.brandAliasList=a),i.length>0&&(l.cityNameList=i);const d={pageNum:n,pageSize:r,queryParam:l,options:c};try{const n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":e},body:JSON.stringify(d),signal:s.signal});if(!n.ok)throw new Error(`Duliday job list fetch failed: ${n.status}`);const r=await n.json();return Ht={cacheKey:u,payload:r,fetchedAt:Date.now()},r}finally{clearTimeout(o),Vt=null}})();return o&&(Vt={cacheKey:u,promise:d}),d}var tn={enabled:!1,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!1,redirectPriority:"low"},nn=200;function rn(e){return(e??"").trim().toLowerCase()}function sn(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function on(e){if("string"==typeof e)return e;if(Array.isArray(e)){const t=e.find(e=>"string"==typeof e);return"string"==typeof t?t:""}return""}function an(e,t){for(const n of t)if(n in e){const t=on(e[n]);if(t)return t}return""}function cn(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const t=Number(e);return Number.isFinite(t)?t:null}return null}function un(e,t){const n=t??tn,r="pass"===e?n.passStrategy:"fail"===e?n.failStrategy:n.unknownStrategy;return{...n,status:e,strategy:r}}async function ln({age:e,brandAlias:t,cityName:n,regionName:r,strategy:s}){const o=Yt(),a=Wt(),i={minAgeObserved:null,maxAgeObserved:null,matchedCount:0,total:0};if(!o)return{status:"unknown",summary:i,appliedStrategy:un("unknown",s)};if(!a)return{status:"unknown",summary:i,appliedStrategy:un("unknown",s)};try{const c=await en(o,a,1,nn,{brandAlias:t??null,cityName:n??null}),{items:u,total:l}=Zt(c);i.total=l;const d=l>u.length&&u.length>=nn,m=Qt(t,n).map(e=>rn(e)).filter(Boolean),p=zt(n).map(e=>rn(e)).filter(Boolean),f=rn(r);let g=!1,y=!1;for(const t of u){if(!sn(t))continue;const n=t,r=sn(n.basicInfo)?n.basicInfo:null,s=r&&sn(r.storeInfo)?r.storeInfo:null,o=rn(an(n,["brandAlias","brandName","brand","organizationName"])||(r?an(r,["brandAlias","brandName","brand"]):"")),a=rn(an(n,["cityName","storeCityName","jobCityName"])||(s?an(s,["storeCityName","cityName"]):"")),c=rn(an(n,["regionName","storeRegionName","districtName"])||(s?an(s,["storeRegionName","regionName","districtName"]):"")),u=rn(an(n,["storeAddress","jobAddress","address","storeExactAddress"])||(s?an(s,["storeAddress","storeExactAddress","address"]):""));if(m.length>0&&!m.some(e=>o.includes(e)||e.includes(o)))continue;if(p.length>0&&!p.some(e=>a.includes(e)))continue;if(f&&!c.includes(f)&&!u.includes(f))continue;i.matchedCount+=1;const l=sn(n.hiringRequirement)?n.hiringRequirement:void 0,d=sn(l?.basicPersonalRequirements)?l.basicPersonalRequirements:void 0,E=cn(d?.minAge??n.minAge),_=cn(d?.maxAge??n.maxAge);if(null!==E&&(g=!0,i.minAgeObserved=null===i.minAgeObserved?E:Math.min(i.minAgeObserved,E)),null!==_&&(g=!0,i.maxAgeObserved=null===i.maxAgeObserved?_:Math.max(i.maxAgeObserved,_)),"number"==typeof e){null!==E&&e<E||null!==_&&e>_||(y=!0)}}if("number"!=typeof e||0===i.matchedCount||!g)return{status:"unknown",summary:i,appliedStrategy:un("unknown",s)};if(d)return{status:"unknown",summary:i,appliedStrategy:un("unknown",s)};const E=y?"pass":"fail";return{status:E,summary:i,appliedStrategy:un(E,s)}}catch{return{status:"unknown",summary:i,appliedStrategy:un("unknown",s)}}}function dn(e){if(!e)return;const t=e.match(/(\d+)/);return t?t[1]:e}function mn(e,t){if("number"==typeof t?.age)return t.age;const n=dn(t?.age);if(n){const e=Number(n);if(Number.isFinite(e))return e}return"number"==typeof e.extractedInfo.specificAge?e.extractedInfo.specificAge:void 0}function pn(e,t){const n=e.extractedInfo.mentionedDistricts?.[0]?.district;if(n)return n;const r=e.extractedInfo.mentionedLocations?.[0]?.location;return r||(t?.jobAddress?t.jobAddress:void 0)}var fn=[{key:"age",label:"年龄"},{key:"gender",label:"性别"},{key:"education",label:"学历"},{key:"experience",label:"工作经验"},{key:"expectedSalary",label:"期望薪资"},{key:"expectedLocation",label:"期望位置"},{key:"jobAddress",label:"工作地址"},{key:"healthCertificate",label:"健康证"}];function gn(e){if(!e)return{factsText:"",knownFieldNames:[]};const t=[];for(const{key:n,label:r}of fn){const s=e[n];"string"==typeof s&&s.trim()?t.push({label:r,value:s.trim()}):"boolean"==typeof s&&t.push({label:r,value:s?"是":"否"})}return 0===t.length?{factsText:"",knownFieldNames:[]}:{factsText:t.map(e=>`${e.label}:${e.value}`).join("\n"),knownFieldNames:t.map(e=>e.label)}}function yn(e){if(null===e.minAgeObserved&&null===e.maxAgeObserved)return null;return`${e.minAgeObserved??"?"}-${e.maxAgeObserved??"?"}`}function En(e,t,n){const r=t?.qualificationPolicy?.age;if(!e||!r||!r.enabled)return[];if("unknown"===e.status&&!n.riskFlags.includes("age_sensitive"))return[];const s=["[QualificationPolicy:Age]"],o=yn(e.summary);return s.push(`- gateStatus: ${e.status}`),s.push(`- expressionStrategy: ${e.appliedStrategy.strategy}`),s.push("- redirect: "+(r.allowRedirect?`allowed priority=${r.redirectPriority}`:"not allowed")),s.push("- revealRange: "+(r.revealRange?"allowed":"not allowed")),r.revealRange&&o&&s.push(`- rangeObserved: ${o}`),"pass"===e.status?s.push(`- writingConstraint: ${e.appliedStrategy.strategy};匹配通过后推进下一步,避免强调年龄筛选`):"fail"===e.status?(s.push(`- writingConstraint: ${e.appliedStrategy.strategy};礼貌说明不匹配,避免承诺或争辩`),r.allowRedirect&&s.push("- writingConstraint: 可提示其他岗位或门店选项")):s.push("- writingConstraint: 如确需涉及年龄或资格,用合规、轻量的方式核实,不要审查式逐条盘问"),s}function _n(e){return"minimal"===e?["4. 当前为浅层沟通,优先泛化回答,不主动抛具体数字、时间、地址或筛选条件。","5. 若候选人追问具体事实,承接需求并引导进一步沟通,不编造细节。"]:["4. 围绕 primaryNeed 回答,上下文中有的事实可以正常引用,不要刻意回避。","5. 不主动展开其他事实轴;若候选人同时问两个点,只在上下文支持时简要带上次要问题。"]}function Nn(e,t,n,r,s,o,a,i,c,u,l,d){if(!e)return{system:"你是招聘助手。遵循事实,不夸大承诺,回复简洁自然。",prompt:`候选人消息:${s}\n\n上下文:\n${r}\n\n请直接回复候选人。`};const m=e.stageGoals[t.stage],p=e.industryVoices[u||e.defaultIndustryVoiceId],f=e.outputGuards.maxQuestionsByMode[i];return{system:["你是政策驱动的招聘助手。",`当前阶段:${t.stage}`,`当前轮次:${a}`,`当前披露模式:${i}`,`主回答轴:${t.primaryNeed}`,`阶段目标:${m.primaryGoal}`,`阶段成功标准:${m.successCriteria.join(";")}`,`推进策略:${m.ctaStrategy}`,m.disallowedActions?.length?`阶段禁止:${m.disallowedActions.join(";")}`:"",`人格设定:语气=${e.persona.tone},亲和度=${e.persona.warmth},长度=${e.persona.length},称呼=${e.persona.addressStyle},提问风格=${e.persona.questionStyle}`,`共情策略:${e.persona.empathyStrategy}`,p?`行业指纹:${p.name};背景=${p.industryBackground};行业词=${p.jargon.join("、")};避免=${p.tabooPhrases.join("、")}`:"",`红线规则:${e.hardConstraints.rules.map(e=>e.rule).join(";")}`,`FactGate模式:${e.factGate.mode};缺事实回退=${e.factGate.fallbackBehavior}`,`禁止审查措辞:${e.outputGuards.blockedAuditPhrases.join("、")}`,...c.knownFieldNames.length>0?[`候选人资料已确认:${c.knownFieldNames.join("、")}。这些信息不得重复追问;如需引用,自然带过即可,不要像念资料一样复述。`]:[],...En(d,e,t),l?`如涉及换微信,优先引导平台交换,必要时可提供默认微信号:${l}`:"如涉及换微信,优先引导平台交换,不编造联系方式。","必须口语化、简洁,不输出解释。"].filter(Boolean).join("\n"),prompt:["[回合规划]",`stage=${t.stage}`,`subGoals=${t.subGoals.join("、")||"无"}`,`contextNeeds=${n.join("、")||"none"}`,`primaryNeed=${t.primaryNeed}`,`riskFlags=${t.riskFlags.join("、")||"无"}`,`confidence=${t.confidence.toFixed(2)}`,"","[对话历史]",o.slice(-6).join("\n")||"无","","[业务上下文]",r,"",...c.factsText?["[候选人已知信息]",c.factsText,""]:[],"[候选人消息]",s,"","[输出要求]","1. 直接给候选人的单条回复,不得输出多段解释或元信息。",`2. 最多追问 ${f} 个关键问题。`,"3. 禁止使用“是否满足”“是否符合”“基本入职要求”等审查措辞。",..._n(i)].join("\n")}}function hn(e,t,n){const r=kt(e);if(0===r.length)return!1;const s=new Set(Ct(t)),o=new Set(n.flatMap(e=>C[e]));return r.some(e=>!o.has(e)||!s.has(e))}function In(e){return"private_channel"===e||"interview_scheduling"===e}function An(e,t){return Number.isInteger(t)&&void 0!==t&&t>=1?t:0===e.length?1:2}function Sn(e,t){return 1===e||"trust_building"===t||"private_channel"===t?"minimal":"focused"}function Tn(e,t){const n=["- 只修正命中的违规点,没有命中的部分不要过度改写。"];return e.includes("too_many_questions")&&n.push(`- 删除多余追问,只保留最关键的 ${t} 个问题。`),e.includes("audit_tone")&&n.push("- 保留原意,但把审查式措辞改成自然口语,不要像筛选候选人。"),e.includes("premature_numeric_disclosure")&&n.push("- 把具体数字、时间和地址细节改成泛化表达,例如“细节我帮你确认”或“以门店安排为准”。"),e.includes("off_axis_fact_disclosure")&&n.push("- 删除不属于主回答轴的具体事实;如果要提到次要问题,只能做不带细节的承接。"),e.includes("reply_overpacked")&&n.push("- 压缩成最多两句,不要列表、不要枚举、不要一口气展开太多信息。"),n}async function On(e,t,n){const r=["请重写下面这条招聘回复。","要求:","- 不新增任何具体数字、地址、福利承诺。","- 仅保留泛化表达,强调可进一步沟通确认细节。","- 口语化、单行、简洁。","","[原回复]",e,"","[可用上下文]",n].join("\n"),s=await $e({model:t,prompt:r,context:"SmartReplyFactGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return s.success?{text:s.text,usage:s.usage,latencyMs:s.latencyMs}:{text:e}}async function Ln(e,t,n,r){const{turnIndex:s,effectiveDisclosureMode:o,primaryNeed:a,allowedNeeds:i,violations:c,policy:u}=r,l=u?.outputGuards.maxQuestionsByMode[o]??1,d=u?.outputGuards.blockedAuditPhrases.join("、")??"",m=Tn(c,l),p=(i??[]).filter(e=>e!==a&&"none"!==e),f=["请重写下面这条招聘回复。","要求:",`- 当前轮次=${s},披露模式=${o},主回答轴=${a}。`,p.length>0?`- 允许顺带覆盖的次要轴:${p.join("、")}。`:"",`- 当前违规点:${c.join("、")}。`,...m,"- 只保留单条口语化回复,不输出解释。",`- 问题数最多 ${l} 个。`,"- 围绕主回答轴回答,不主动展开其他事实轴。","- 首轮时不要主动报具体数字、时间、地址或筛选条件。",d?`- 禁止使用这些措辞:${d}。`:"","","[原回复]",e,"","[可用上下文]",n].filter(Boolean).join("\n"),g=await $e({model:t,prompt:f,context:"SmartReplyReplyGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return g.success?{text:g.text,usage:g.usage,latencyMs:g.latencyMs}:{text:e}}async function bn(e){const t=Bt();c(!0);try{return await Rn(e,t)}catch(e){throw t.fail("回复生成失败"),e}finally{c(!1)}}async function Rn(e,t){const{modelConfig:n,preferredBrand:r,toolBrand:s,brandPriorityStrategy:o,conversationHistory:a=[],candidateMessage:i,configData:c,replyPolicy:u,candidateInfo:l,defaultWechatId:d,industryVoiceId:m,channelType:p,turnIndex:y}=e,E=n?.providerConfigs||f,_=b(c),N={..._?{city:_}:{},defaultBrand:L(c),availableBrands:S(c),storeCount:A(c).length},h=gn(l);t.update("分析对话意图...");const T=await rt(i,{modelConfig:n||{},conversationHistory:a,brandData:N,providerConfigs:E,...void 0!==p?{channelType:p}:{},...void 0!==u?{replyPolicy:u}:{},...h.knownFieldNames.length>0?{knownCandidateFields:h.knownFieldNames}:{}}),O=An(a,y),R=Sn(O,T.stage),M="focused"===R?Xe(T.primaryNeed,T.needs,i,2):[T.primaryNeed],v=s||T.extractedInfo.mentionedBrand||void 0;t.update("构建业务上下文...");const{contextInfo:w,debugInfo:D,resolvedBrand:U}=await Tt(c,T,r,v,o,l,u,m,O,R,M);t.update("校验候选人资格...");const $=mn(T,l),k=pn(T,l),C=T.extractedInfo.city??b(c,U),x=await ln({...void 0!==$?{age:$}:{},brandAlias:U,..."string"==typeof C?{cityName:C}:{},...void 0!==k?{regionName:k}:{},...void 0!==u?.qualificationPolicy?.age?{strategy:u.qualificationPolicy.age}:{}}),F=I(E),P=n?.replyModel||g.replyModel,B=F.languageModel(P),G=Nn(u,T,M,w,i,a,O,R,h,m,d,x);t.update("生成回复...");const j=await $e({model:B,system:G.system,prompt:G.prompt,context:"SmartReply",timeoutMs:3e4,maxOutputTokens:2e3});if(!j.success)return t.fail("回复生成失败"),Se("SmartReply 生成失败",j.error),{turnPlan:T,suggestedReply:"",confidence:0,shouldExchangeWechat:In(T.stage),factGateRewritten:!1,replyGateRewritten:!1,gateViolations:[],contextInfo:w,debugInfo:{...D,resolvedBrand:U,turnIndex:O,effectiveDisclosureMode:R,primaryNeed:T.primaryNeed,replyGateRewritten:!1,gateViolations:[],gateStatus:x.status,appliedStrategy:x.appliedStrategy,ageRangeSummary:x.summary},usage:void 0,error:j.error};let K=j.text,H=j.usage,V=j.latencyMs,W=!1,Y=!1,q=[];if(t.update("检查回复质量..."),"strict"===u?.factGate.mode){if(hn(K,w,M)){W=!0;const e=await On(K,B,w);K=e.text,e.usage&&(H=e.usage),void 0!==e.latencyMs&&(V=(V??0)+e.latencyMs)}}if(q=Ft({text:K,turnIndex:O,mode:R,primaryNeed:T.primaryNeed,allowedNeeds:M,policy:u}).violations,q.length>0){t.update("优化回复..."),Y=!0;const e=await Ln(K,B,w,{turnIndex:O,effectiveDisclosureMode:R,primaryNeed:T.primaryNeed,allowedNeeds:M,violations:q,policy:u});K=e.text,e.usage&&(H=e.usage),void 0!==e.latencyMs&&(V=(V??0)+e.latencyMs),q=Ft({text:K,turnIndex:O,mode:R,primaryNeed:T.primaryNeed,allowedNeeds:M,policy:u}).violations}const z=V??0,Q=H?.totalTokens??0;return t.succeed(`回复已生成 | ${T.stage} | ${z}ms | ${Q} tokens${Y?" | 已优化":""}`),{turnPlan:T,suggestedReply:K,confidence:Math.max(0,Math.min(1,T.confidence)),shouldExchangeWechat:In(T.stage),factGateRewritten:W,replyGateRewritten:Y,gateViolations:q,contextInfo:`${w}\n当前品牌:${U}`,debugInfo:{...D,resolvedBrand:U,turnIndex:O,effectiveDisclosureMode:R,primaryNeed:T.primaryNeed,replyGateRewritten:Y,gateViolations:q,gateStatus:x.status,appliedStrategy:x.appliedStrategy,ageRangeSummary:x.summary},usage:H,latencyMs:V}}export{bn as generateSmartReply};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/smart-reply-agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -55,7 +55,7 @@
55
55
  "ai": "^6.0.108",
56
56
  "ora": "^8.2.0",
57
57
  "zod": "^3.25.76",
58
- "@roll-agent/sdk": "0.1.4"
58
+ "@roll-agent/sdk": "0.1.5"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "^22.0.0"
@@ -63,7 +63,7 @@
63
63
  "scripts": {
64
64
  "dev": "node --experimental-strip-types src/index.ts",
65
65
  "test": "node --experimental-strip-types --test \"src/**/*.test.ts\"",
66
- "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/obfuscate.mjs",
66
+ "build": "rm -rf dist && tsc -p tsconfig.build.json --emitDeclarationOnly && node ../../scripts/bundle.mjs && node ../../scripts/obfuscate.mjs",
67
67
  "typecheck": "tsc --noEmit",
68
68
  "clean": "rm -rf dist"
69
69
  }
@@ -1 +0,0 @@
1
- import{createAnthropic as e}from"@ai-sdk/anthropic";import{createAlibaba as o}from"@ai-sdk/alibaba";import{createProviderRegistry as i}from"ai";import{createOpenAICompatible as a}from"@ai-sdk/openai-compatible";import{createDeepSeek as n}from"@ai-sdk/deepseek";import{createGoogleGenerativeAI as r}from"@ai-sdk/google";import{createOpenAI as t}from"@ai-sdk/openai";import{verboseLog as p}from"../log-control.js";export const MODEL_DICTIONARY={"qwen/qwen-max-latest":{provider:"qwen",name:"Qwen Max Latest",categories:["general"]},"qwen/qwen-plus-latest":{provider:"qwen",name:"Qwen Plus Latest",categories:["general"]},"google/gemini-embedding-2-preview":{provider:"google",name:"Gemini Embedding 2 Preview",categories:["general"]},"google/gemini-3.1-flash-lite-preview":{provider:"google",name:"Gemini 3.1 Flash Lite Preview",categories:["general","chat"]},"google/gemini-3.1-pro-preview":{provider:"google",name:"Gemini 3.1 Pro Preview",categories:["chat","general"]},"anthropic/claude-sonnet-4-6":{provider:"anthropic",name:"Claude Sonnet 4.6",categories:["chat","general"]},"anthropic/claude-opus-4-6":{provider:"anthropic",name:"Claude Opus 4.6",categories:["chat","general"]},"anthropic/claude-haiku-4-5":{provider:"anthropic",name:"Claude Haiku 4.5",categories:["chat","general"]},"openai/gpt-5.1":{provider:"openai",name:"GPT-5.1",categories:["chat","general"]},"openai/gpt-5.2":{provider:"openai",name:"GPT-5.2",categories:["general"]},"openai/gpt-5.4":{provider:"openai",name:"GPT-5.4",categories:["chat","general"]},"openai/gpt-5-mini":{provider:"openai",name:"GPT-5 Mini",categories:["general"]},"ohmygpt/gemini-3.1-flash-lite-preview":{provider:"ohmygpt",name:"Gemini 3.1 Flash Lite Preview (OhMyGPT)",categories:["general"]},"ohmygpt/gemini-3.1-pro-preview":{provider:"ohmygpt",name:"Gemini 3.1 Pro Preview (OhMyGPT)",categories:["general"]},"moonshotai/kimi-k2.5":{provider:"moonshotai",name:"Kimi K2.5",categories:["chat","general"]},"moonshotai/kimi-k2-thinking-turbo":{provider:"moonshotai",name:"Kimi K2 Thinking Turbo",categories:["chat","general"]},"deepseek/deepseek-chat":{provider:"deepseek",name:"DeepSeek Chat",categories:["chat","general"]}};const s=process.env.SMART_REPLY_PROXY_BASE_URL,g="https://apic1.ohmycdn.com/v1",c=s||g,m={anthropic:process.env.ANTHROPIC_BASE_URL||c,openai:process.env.OPENAI_BASE_URL||c,ohmygpt:process.env.OHMYGPT_BASE_URL||c,moonshotai:process.env.MOONSHOT_BASE_URL||"https://api.moonshot.cn/v1",deepseek:process.env.DEEPSEEK_BASE_URL||"https://api.deepseek.com",qwen:process.env.QWEN_BASE_URL||"https://dashscope.aliyuncs.com/compatible-mode/v1",google:process.env.GOOGLE_BASE_URL||"https://generativelanguage.googleapis.com/v1beta"};export const DEFAULT_PROVIDER_CONFIGS={anthropic:{name:"Anthropic",baseURL:m.anthropic,description:"Anthropic Claude"},openai:{name:"OpenAI",baseURL:m.openai,description:"OpenAI GPT"},ohmygpt:{name:"OhMyGPT",baseURL:m.ohmygpt,description:"OhMyGPT"},moonshotai:{name:"MoonshotAI",baseURL:m.moonshotai,description:"MoonshotAI"},deepseek:{name:"DeepSeek",baseURL:m.deepseek,description:"DeepSeek"},qwen:{name:"Qwen",baseURL:m.qwen,description:"Qwen"},google:{name:"Google",baseURL:m.google,description:"Google Gemini"}};export const DEFAULT_MODEL_CONFIG={chatModel:"anthropic/claude-haiku-4-5",classifyModel:process.env.SMART_REPLY_CLASSIFY_MODEL||"openai/gpt-5-mini",replyModel:process.env.SMART_REPLY_REPLY_MODEL||"openai/gpt-5.4"};function h(){return process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||""}function l(e){const o=t({apiKey:e.apiKey||"",...void 0!==e.baseURL?{baseURL:e.baseURL}:{}});return new Proxy(o,{get(e,i){if("languageModel"===i)return e=>o.chat(e);if("chat"===i||"completion"===i)return o[i];if("embeddingModel"===i||"imageModel"===i){return o[i]||void 0}return o[i]}})}function d(t){const p=h();return i({anthropic:e({apiKey:p,baseURL:t.anthropic?.baseURL||m.anthropic}),openai:l({apiKey:p,baseURL:t.openai?.baseURL||m.openai}),ohmygpt:a({name:"ohmygpt",baseURL:t.ohmygpt?.baseURL||m.ohmygpt,apiKey:p}),moonshotai:a({name:"moonshotai",baseURL:t.moonshotai?.baseURL||m.moonshotai,apiKey:process.env.MOONSHOT_API_KEY||""}),deepseek:n({baseURL:t.deepseek?.baseURL||m.deepseek,apiKey:process.env.DEEPSEEK_API_KEY||""}),google:r({apiKey:process.env.GEMINI_API_KEY||"",baseURL:t.google?.baseURL||m.google}),qwen:o({apiKey:process.env.DASHSCOPE_API_KEY||process.env.ALIBABA_API_KEY||"",baseURL:t.qwen?.baseURL||m.qwen})},{separator:"/"})}let v=null,L=null;export function getDynamicRegistry(e){const o=JSON.stringify(e);return v&&L===o||(v=d(e),L=o,p("[DYNAMIC REGISTRY] 创建新的动态registry,配置哈希:",o.substring(0,16)+"...")),v}
@@ -1 +0,0 @@
1
- import{generateText as t,NoObjectGeneratedError as e,Output as o}from"ai";import{z as s}from"zod";import{wrapError as n,logError as r,ErrorCode as a}from"../errors/index.js";import{verboseLog as u}from"../log-control.js";export async function safeGenerateObject(s){const{model:u,schema:c,schemaName:m,system:i,prompt:p,onError:d}=s;try{const e=await t({model:u,...void 0!==i?{system:i}:{},prompt:p,output:o.object({schema:c,...void 0!==m?{name:m}:{}})});return{success:!0,data:e.output,usage:e.usage}}catch(t){const o=n(t,a.LLM_RESPONSE_PARSE_ERROR);let s;return e.isInstance(t)&&(s=t.text),m&&o.details&&"object"==typeof o.details&&(o.details.schemaName=m),r(`Structured output generation (${m||"unknown"})`,o),d&&d(o,s),{success:!1,error:o,rawText:s}}}const c=3e4,m=2e3;export async function safeGenerateText(e){const{model:o,system:s,prompt:i,timeoutMs:p=c,maxOutputTokens:d=m,context:l="generateText",onError:T}=e,k=performance.now();try{const e=new AbortController,n=setTimeout(()=>e.abort(),p),r=await t({model:o,...void 0!==s?{system:s}:{},prompt:i,maxOutputTokens:d,abortSignal:e.signal});clearTimeout(n);const a=Math.round(performance.now()-k),c=r.usage,m=void 0!==c?.inputTokens&&void 0!==c?.outputTokens?c.inputTokens+c.outputTokens:void 0,T={inputTokens:c?.inputTokens,outputTokens:c?.outputTokens,totalTokens:m};return u(`[${l}] 生成成功 | 耗时: ${a}ms | Tokens: ${m??"N/A"} (input: ${T.inputTokens??"?"}, output: ${T.outputTokens??"?"})`),{success:!0,text:r.text,usage:T,latencyMs:a}}catch(t){const e=Math.round(performance.now()-k),o=t instanceof Error&&("AbortError"===t.name||t.message.includes("aborted"))?a.LLM_TIMEOUT:a.LLM_GENERATION_FAILED,s=n(t,o);return s.details&&"object"==typeof s.details&&(s.details.context=l,s.details.latencyMs=e),r(`${l} (${e}ms)`,s),T&&T(s),{success:!1,error:s}}}
@@ -1 +0,0 @@
1
- import{getErrorCategory as r,getErrorUserMessage as e}from"./error-codes.js";export class AppError extends Error{code;category;userMessage;details;cause;timestamp;constructor(s){super(s.message),this.name="AppError",this.code=s.code,this.category=r(s.code),this.userMessage=s.userMessage||e(s.code),this.details=s.details,this.cause=s.cause,this.timestamp=(new Date).toISOString(),Error.captureStackTrace&&Error.captureStackTrace(this,AppError)}toJSON(){const r={code:this.code,category:this.category,message:this.message,userMessage:this.userMessage,timestamp:this.timestamp};return void 0!==this.details&&(r.details=this.details),this.cause&&(this.cause instanceof AppError?r.cause=this.cause.toJSON():r.cause={message:this.cause.message,stack:this.cause.stack}),r}getErrorChain(){const r=[this];let e=this.cause;for(;e;)r.push(e),e=e instanceof AppError?e.cause:void 0;return r}getRootCause(){const r=this.getErrorChain();return r[r.length-1]}hasErrorCode(r){return this.getErrorChain().some(e=>e instanceof AppError&&e.code===r)}hasErrorCategory(r){return this.getErrorChain().some(e=>e instanceof AppError&&e.category===r)}toLogString(){const r=[`[${this.code}]`,this.message];return this.details&&r.push(`Details: ${JSON.stringify(this.details)}`),this.cause&&r.push(`Caused by: ${this.cause.message}`),r.join(" | ")}}export function isAppError(r){return r instanceof AppError}export function isErrorCode(r,e){return isAppError(r)&&r.code===e}export function isErrorCategory(r,e){return isAppError(r)&&r.category===e}export function isLLMError(r){return isErrorCategory(r,"LLM")}export function isConfigError(r){return isErrorCategory(r,"CONFIG")}export function isNetworkError(r){return isErrorCategory(r,"NETWORK")}export function isAuthError(r){return isErrorCategory(r,"AUTH")}
@@ -1 +0,0 @@
1
- export const ErrorCategory={CONFIG:"CONFIG",AUTH:"AUTH",NETWORK:"NETWORK",LLM:"LLM",VALIDATION:"VALIDATION",BUSINESS:"BUSINESS",SYSTEM:"SYSTEM"};export const ErrorCode={LLM_UNAUTHORIZED:"LLM_UNAUTHORIZED",LLM_MODEL_NOT_FOUND:"LLM_MODEL_NOT_FOUND",LLM_RATE_LIMITED:"LLM_RATE_LIMITED",LLM_TIMEOUT:"LLM_TIMEOUT",LLM_GENERATION_FAILED:"LLM_GENERATION_FAILED",LLM_RESPONSE_PARSE_ERROR:"LLM_RESPONSE_PARSE_ERROR",CONFIG_NOT_FOUND:"CONFIG_NOT_FOUND",CONFIG_INVALID:"CONFIG_INVALID",CONFIG_MISSING_FIELD:"CONFIG_MISSING_FIELD",CONFIG_LOAD_FAILED:"CONFIG_LOAD_FAILED",NETWORK_TIMEOUT:"NETWORK_TIMEOUT",NETWORK_CONNECTION_FAILED:"NETWORK_CONNECTION_FAILED",NETWORK_HTTP_ERROR:"NETWORK_HTTP_ERROR",NETWORK_DNS_FAILED:"NETWORK_DNS_FAILED",AUTH_UNAUTHORIZED:"AUTH_UNAUTHORIZED",AUTH_FORBIDDEN:"AUTH_FORBIDDEN",AUTH_TOKEN_EXPIRED:"AUTH_TOKEN_EXPIRED",AUTH_TOKEN_INVALID:"AUTH_TOKEN_INVALID",VALIDATION_INVALID_INPUT:"VALIDATION_INVALID_INPUT",VALIDATION_MISSING_REQUIRED:"VALIDATION_MISSING_REQUIRED",VALIDATION_FORMAT_ERROR:"VALIDATION_FORMAT_ERROR",VALIDATION_SCHEMA_ERROR:"VALIDATION_SCHEMA_ERROR",BUSINESS_RULE_VIOLATION:"BUSINESS_RULE_VIOLATION",BUSINESS_RESOURCE_NOT_FOUND:"BUSINESS_RESOURCE_NOT_FOUND",BUSINESS_RESOURCE_EXISTS:"BUSINESS_RESOURCE_EXISTS",BUSINESS_OPERATION_NOT_ALLOWED:"BUSINESS_OPERATION_NOT_ALLOWED",SYSTEM_INTERNAL:"SYSTEM_INTERNAL",SYSTEM_DEPENDENCY_FAILED:"SYSTEM_DEPENDENCY_FAILED",SYSTEM_RESOURCE_UNAVAILABLE:"SYSTEM_RESOURCE_UNAVAILABLE",SYSTEM_UNKNOWN:"SYSTEM_UNKNOWN"};export const ERROR_CODE_TO_CATEGORY={[ErrorCode.LLM_UNAUTHORIZED]:ErrorCategory.LLM,[ErrorCode.LLM_MODEL_NOT_FOUND]:ErrorCategory.LLM,[ErrorCode.LLM_RATE_LIMITED]:ErrorCategory.LLM,[ErrorCode.LLM_TIMEOUT]:ErrorCategory.LLM,[ErrorCode.LLM_GENERATION_FAILED]:ErrorCategory.LLM,[ErrorCode.LLM_RESPONSE_PARSE_ERROR]:ErrorCategory.LLM,[ErrorCode.CONFIG_NOT_FOUND]:ErrorCategory.CONFIG,[ErrorCode.CONFIG_INVALID]:ErrorCategory.CONFIG,[ErrorCode.CONFIG_MISSING_FIELD]:ErrorCategory.CONFIG,[ErrorCode.CONFIG_LOAD_FAILED]:ErrorCategory.CONFIG,[ErrorCode.NETWORK_TIMEOUT]:ErrorCategory.NETWORK,[ErrorCode.NETWORK_CONNECTION_FAILED]:ErrorCategory.NETWORK,[ErrorCode.NETWORK_HTTP_ERROR]:ErrorCategory.NETWORK,[ErrorCode.NETWORK_DNS_FAILED]:ErrorCategory.NETWORK,[ErrorCode.AUTH_UNAUTHORIZED]:ErrorCategory.AUTH,[ErrorCode.AUTH_FORBIDDEN]:ErrorCategory.AUTH,[ErrorCode.AUTH_TOKEN_EXPIRED]:ErrorCategory.AUTH,[ErrorCode.AUTH_TOKEN_INVALID]:ErrorCategory.AUTH,[ErrorCode.VALIDATION_INVALID_INPUT]:ErrorCategory.VALIDATION,[ErrorCode.VALIDATION_MISSING_REQUIRED]:ErrorCategory.VALIDATION,[ErrorCode.VALIDATION_FORMAT_ERROR]:ErrorCategory.VALIDATION,[ErrorCode.VALIDATION_SCHEMA_ERROR]:ErrorCategory.VALIDATION,[ErrorCode.BUSINESS_RULE_VIOLATION]:ErrorCategory.BUSINESS,[ErrorCode.BUSINESS_RESOURCE_NOT_FOUND]:ErrorCategory.BUSINESS,[ErrorCode.BUSINESS_RESOURCE_EXISTS]:ErrorCategory.BUSINESS,[ErrorCode.BUSINESS_OPERATION_NOT_ALLOWED]:ErrorCategory.BUSINESS,[ErrorCode.SYSTEM_INTERNAL]:ErrorCategory.SYSTEM,[ErrorCode.SYSTEM_DEPENDENCY_FAILED]:ErrorCategory.SYSTEM,[ErrorCode.SYSTEM_RESOURCE_UNAVAILABLE]:ErrorCategory.SYSTEM,[ErrorCode.SYSTEM_UNKNOWN]:ErrorCategory.SYSTEM};export const ERROR_USER_MESSAGES={[ErrorCode.LLM_UNAUTHORIZED]:"AI 服务认证失败,请检查配置",[ErrorCode.LLM_MODEL_NOT_FOUND]:"所选模型暂时不可用,请尝试其他模型",[ErrorCode.LLM_RATE_LIMITED]:"请求过于频繁,请稍后重试",[ErrorCode.LLM_TIMEOUT]:"AI 响应超时,请稍后重试",[ErrorCode.LLM_GENERATION_FAILED]:"内容生成失败,请稍后重试",[ErrorCode.LLM_RESPONSE_PARSE_ERROR]:"AI 响应格式异常,请重试",[ErrorCode.CONFIG_NOT_FOUND]:"配置数据未找到,请先进行初始化",[ErrorCode.CONFIG_INVALID]:"配置格式无效,请检查配置",[ErrorCode.CONFIG_MISSING_FIELD]:"配置缺少必需字段",[ErrorCode.CONFIG_LOAD_FAILED]:"配置加载失败,请重试",[ErrorCode.NETWORK_TIMEOUT]:"网络请求超时,请检查网络连接",[ErrorCode.NETWORK_CONNECTION_FAILED]:"网络连接失败,请检查网络",[ErrorCode.NETWORK_HTTP_ERROR]:"服务器返回错误,请稍后重试",[ErrorCode.NETWORK_DNS_FAILED]:"域名解析失败,请检查网络",[ErrorCode.AUTH_UNAUTHORIZED]:"请先登录",[ErrorCode.AUTH_FORBIDDEN]:"您没有权限执行此操作",[ErrorCode.AUTH_TOKEN_EXPIRED]:"登录已过期,请重新登录",[ErrorCode.AUTH_TOKEN_INVALID]:"认证信息无效,请重新登录",[ErrorCode.VALIDATION_INVALID_INPUT]:"输入参数无效",[ErrorCode.VALIDATION_MISSING_REQUIRED]:"缺少必需参数",[ErrorCode.VALIDATION_FORMAT_ERROR]:"数据格式错误",[ErrorCode.VALIDATION_SCHEMA_ERROR]:"数据验证失败",[ErrorCode.BUSINESS_RULE_VIOLATION]:"操作违反业务规则",[ErrorCode.BUSINESS_RESOURCE_NOT_FOUND]:"请求的资源不存在",[ErrorCode.BUSINESS_RESOURCE_EXISTS]:"资源已存在",[ErrorCode.BUSINESS_OPERATION_NOT_ALLOWED]:"当前操作不被允许",[ErrorCode.SYSTEM_INTERNAL]:"系统内部错误,请稍后重试",[ErrorCode.SYSTEM_DEPENDENCY_FAILED]:"依赖服务异常,请稍后重试",[ErrorCode.SYSTEM_RESOURCE_UNAVAILABLE]:"系统资源不可用,请稍后重试",[ErrorCode.SYSTEM_UNKNOWN]:"发生未知错误,请稍后重试"};export function getErrorCategory(E){return ERROR_CODE_TO_CATEGORY[E]}export function getErrorUserMessage(E){return ERROR_USER_MESSAGES[E]}export function isErrorInCategory(E,r){return ERROR_CODE_TO_CATEGORY[E]===r}
@@ -1 +0,0 @@
1
- import{AppError as e}from"./app-error.js";import{ErrorCode as r}from"./error-codes.js";export function createLLMError(o,t,a){const s=a?.model?` (model: ${a.model})`:"",d=a?.provider?` [${a.provider}]`:"",i={[r.LLM_UNAUTHORIZED]:`LLM API authentication failed${d}${s}`,[r.LLM_MODEL_NOT_FOUND]:`Model not found or unavailable${s}${d}`,[r.LLM_RATE_LIMITED]:`LLM API rate limited${d}`,[r.LLM_TIMEOUT]:`LLM API request timeout${d}${s}`,[r.LLM_GENERATION_FAILED]:`LLM generation failed${d}${s}`,[r.LLM_RESPONSE_PARSE_ERROR]:`Failed to parse LLM response${d}`};return new e({code:o,message:i[o]||`LLM error: ${t.message}`,cause:t,details:a})}export function createConfigError(r,o,t,a){return new e({code:r,message:o,...void 0!==a?{cause:a}:{},...void 0!==t?{details:t}:{}})}export function createNetworkError(o,t,a){const s=a?.url?` (${a.url})`:"",d=a?.statusCode?` [${a.statusCode}]`:"",i={[r.NETWORK_TIMEOUT]:`Network request timeout${s}`,[r.NETWORK_CONNECTION_FAILED]:`Failed to connect${s}`,[r.NETWORK_HTTP_ERROR]:`HTTP error${d}${s}`,[r.NETWORK_DNS_FAILED]:`DNS resolution failed${s}`};return new e({code:o,message:i[o]||`Network error: ${t.message}`,cause:t,details:a})}export function createValidationError(r,o,t){return new e({code:r,message:o,...void 0!==t?{details:t}:{}})}export function createBusinessError(r,o,t,a){return new e({code:r,message:o,...void 0!==t?{userMessage:t}:{},...void 0!==a?{details:a}:{}})}export function createSystemError(r,o,t,a){return new e({code:r,message:o,...void 0!==t?{cause:t}:{},...void 0!==a?{details:a}:{}})}export function createStructuredOutputError(r,o,t){const a=t.isMarkdownFormat?" (detected markdown format)":"",s=t.schemaName?` for schema "${t.schemaName}"`:"";return new e({code:r,message:`Failed to parse structured output${s}${a}`,cause:o,details:{rawText:t.rawText,isMarkdownFormat:t.isMarkdownFormat,parseErrorMessage:t.parseErrorMessage,model:t.model,provider:t.provider,schemaName:t.schemaName,usage:t.usage}})}
@@ -1 +0,0 @@
1
- import{NoObjectGeneratedError as e}from"ai";import{AppError as r,isAppError as o}from"./app-error.js";import{ErrorCode as s}from"./error-codes.js";import{createLLMError as t,createNetworkError as n,createStructuredOutputError as i}from"./error-factory.js";export function parseAISDKError(e){if(!e||"object"!=typeof e)return null;const r=e;if(!("AI_APICallError"===r.name||"APICallError"===r.name||"string"==typeof r.url&&"number"==typeof r.statusCode))return null;const o="number"==typeof r.statusCode?r.statusCode:void 0,s="string"==typeof r.responseBody?r.responseBody:void 0,t="string"==typeof r.url?r.url:void 0,n=r.message;let i,a;if(t&&(t.includes("openai.com")?i="openai":t.includes("anthropic.com")?i="anthropic":t.includes("dashscope.aliyuncs.com")?i="qwen":t.includes("openrouter.ai")?i="openrouter":t.includes("deepseek.com")?i="deepseek":t.includes("moonshot.cn")?i="moonshotai":t.includes("googleapis.com")&&(i="google")),s){const e=s.match(/model[`'":\s]+([^`'"}\s,]+)/i);e&&(a=e[1])}return{isAuthError:401===o||403===o,isModelNotFound:s?.includes("model")&&s?.includes("not exist")||s?.includes("not authorized to access this model")||!1,isRateLimited:429===o,isTimeout:408===o||504===o||n?.toLowerCase().includes("timeout")||!1,statusCode:o,provider:i,model:a,originalMessage:n,responseBody:s}}function a(e){return[/^```/m,/^#{1,6}\s/m,/^\s*[-*+]\s/m,/^\s*\d+\.\s/m,/\[.+\]\(.+\)/,/^\s*>/m].some(r=>r.test(e))}export function parseNoObjectGeneratedError(r){try{if(void 0===e||"function"!=typeof e.isInstance)return null;if(!e.isInstance(r))return null}catch{return null}const o=r.text,s=!!o&&a(o),t=r.response?{id:r.response.id,timestamp:r.response.timestamp,modelId:r.response.modelId}:void 0,n=r.usage??void 0,i={isNoObjectGeneratedError:!0,isMarkdownFormat:s};return void 0!==o&&(i.rawText=o),r.cause instanceof Error&&(i.cause=r.cause),void 0!==t&&(i.response=t),void 0!==n&&(i.usage=n),i}export function wrapError(e,a=s.SYSTEM_UNKNOWN,u){if(o(e))return u&&u!==e.userMessage?new r({code:e.code,message:e.message,userMessage:u,cause:e.cause,details:e.details}):e;const c=toError(e),d=parseAISDKError(e);if(d){const e={model:d.model,provider:d.provider,statusCode:d.statusCode,responseBody:d.responseBody};return d.isModelNotFound?t(s.LLM_MODEL_NOT_FOUND,c,e):d.isAuthError?t(s.LLM_UNAUTHORIZED,c,e):d.isRateLimited?t(s.LLM_RATE_LIMITED,c,e):d.isTimeout?t(s.LLM_TIMEOUT,c,e):t(s.LLM_GENERATION_FAILED,c,e)}const l=parseNoObjectGeneratedError(e);if(l)return i(s.LLM_RESPONSE_PARSE_ERROR,c,{rawText:l.rawText,isMarkdownFormat:l.isMarkdownFormat,parseErrorMessage:l.cause?.message,usage:l.usage});const m=c.message.toLowerCase();return m.includes("timeout")||m.includes("econnrefused")||m.includes("network")||m.includes("fetch failed")?m.includes("timeout")?n(s.NETWORK_TIMEOUT,c):n(s.NETWORK_CONNECTION_FAILED,c):new r({code:a,message:c.message,cause:c})}export function toError(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);if("object"==typeof e&&null!==e){const r=e.message||e.error||JSON.stringify(e);return new Error(String(r))}return new Error(String(e))}export function extractErrorContext(e){const r={errorCode:e.code,category:e.category};return e.cause&&(r.originalError=e.cause.message),e.details&&(r.details=e.details),r}export function getUserMessage(e){return o(e)?e.userMessage:e instanceof Error?"操作失败,请稍后重试":"发生未知错误,请稍后重试"}export function logError(e,r){console.error(`[${r.code}] ${e}:`,r.toLogString()),r.cause&&console.error("Original error:",r.cause)}
@@ -1 +0,0 @@
1
- export{ErrorCode,ErrorCategory,ERROR_CODE_TO_CATEGORY,ERROR_USER_MESSAGES,getErrorCategory,getErrorUserMessage,isErrorInCategory}from"./error-codes.js";export{AppError,isAppError,isErrorCode,isErrorCategory,isLLMError,isConfigError,isNetworkError,isAuthError}from"./app-error.js";export{createLLMError,createConfigError,createNetworkError,createValidationError,createBusinessError,createSystemError,createStructuredOutputError}from"./error-factory.js";export{wrapError,toError,parseAISDKError,parseNoObjectGeneratedError,extractErrorContext,getUserMessage,logError}from"./error-utils.js";
@@ -1 +0,0 @@
1
- let e=!1;export function setSuppressVerboseLogs(o){e=o}export function verboseLog(...o){e||console.error(...o)}
@@ -1 +0,0 @@
1
- import{fetchJobListPage as e,getDulidayToken as n,getDulidayJobListEndpoint as t,extractResults as r,buildCityCandidates as a,buildBrandCandidates as i}from"../services/duliday-api.js";export const AGE_ELIGIBILITY_STATUSES=["pass","fail","unknown"];const s={enabled:!1,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!1,redirectPriority:"low"},o=200;function u(e){return(e??"").trim().toLowerCase()}function l(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function m(e){if("string"==typeof e)return e;if(Array.isArray(e)){const n=e.find(e=>"string"==typeof e);return"string"==typeof n?n:""}return""}function d(e,n){for(const t of n)if(t in e){const n=m(e[t]);if(n)return n}return""}function c(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const n=Number(e);return Number.isFinite(n)?n:null}return null}function f(e,n){const t=n??s,r="pass"===e?t.passStrategy:"fail"===e?t.failStrategy:t.unknownStrategy;return{...t,status:e,strategy:r}}export async function evaluateAgeEligibility({age:s,brandAlias:m,cityName:g,regionName:y,strategy:b}){const p=n(),A=t(),N={minAgeObserved:null,maxAgeObserved:null,matchedCount:0,total:0};if(!p)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};if(!A)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};try{const n=await e(p,A,1,o,{brandAlias:m??null,cityName:g??null}),{items:t,total:w}=r(n);N.total=w;const S=w>t.length&&t.length>=o,k=i(m,g).map(e=>u(e)).filter(Boolean),v=a(g).map(e=>u(e)).filter(Boolean),h=u(y);let x=!1,I=!1;for(const e of t){if(!l(e))continue;const n=e,t=l(n.basicInfo)?n.basicInfo:null,r=t&&l(t.storeInfo)?t.storeInfo:null,a=u(d(n,["brandAlias","brandName","brand","organizationName"])||(t?d(t,["brandAlias","brandName","brand"]):"")),i=u(d(n,["cityName","storeCityName","jobCityName"])||(r?d(r,["storeCityName","cityName"]):"")),o=u(d(n,["regionName","storeRegionName","districtName"])||(r?d(r,["storeRegionName","regionName","districtName"]):"")),m=u(d(n,["storeAddress","jobAddress","address","storeExactAddress"])||(r?d(r,["storeAddress","storeExactAddress","address"]):""));if(k.length>0&&!k.some(e=>a.includes(e)||e.includes(a)))continue;if(v.length>0&&!v.some(e=>i.includes(e)))continue;if(h&&!o.includes(h)&&!m.includes(h))continue;N.matchedCount+=1;const f=l(n.hiringRequirement)?n.hiringRequirement:void 0,g=l(f?.basicPersonalRequirements)?f.basicPersonalRequirements:void 0,y=c(g?.minAge??n.minAge),b=c(g?.maxAge??n.maxAge);if(null!==y&&(x=!0,N.minAgeObserved=null===N.minAgeObserved?y:Math.min(N.minAgeObserved,y)),null!==b&&(x=!0,N.maxAgeObserved=null===N.maxAgeObserved?b:Math.max(N.maxAgeObserved,b)),"number"==typeof s){null!==y&&s<y||null!==b&&s>b||(I=!0)}}if("number"!=typeof s||0===N.matchedCount||!x)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};if(S)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};const O=I?"pass":"fail";return{status:O,summary:N,appliedStrategy:f(O,b)}}catch{return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)}}}
@@ -1 +0,0 @@
1
- const e=[{key:"age",label:"年龄"},{key:"gender",label:"性别"},{key:"education",label:"学历"},{key:"experience",label:"工作经验"},{key:"expectedSalary",label:"期望薪资"},{key:"expectedLocation",label:"期望位置"},{key:"jobAddress",label:"工作地址"},{key:"healthCertificate",label:"健康证"}];export function buildKnownCandidateContext(l){if(!l)return{factsText:"",knownFieldNames:[]};const a=[];for(const{key:t,label:n}of e){const e=l[t];"string"==typeof e&&e.trim()?a.push({label:n,value:e.trim()}):"boolean"==typeof e&&a.push({label:n,value:e?"是":"否"})}return 0===a.length?{factsText:"",knownFieldNames:[]}:{factsText:a.map(e=>`${e.label}:${e.value}`).join("\n"),knownFieldNames:a.map(e=>e.label)}}
@@ -1 +0,0 @@
1
- export function parseAge(e){if(!e)return;const t=e.match(/(\d+)/);return t?t[1]:e}export function resolveCandidateAge(e,t){if("number"==typeof t?.age)return t.age;const n=parseAge(t?.age);if(n){const e=Number(n);if(Number.isFinite(e))return e}return"number"==typeof e.extractedInfo.specificAge?e.extractedInfo.specificAge:void 0}export function resolveRegionName(e,t){const n=e.extractedInfo.mentionedDistricts?.[0]?.district;if(n)return n;const r=e.extractedInfo.mentionedLocations?.[0]?.location;return r||(t?.jobAddress?t.jobAddress:void 0)}
@@ -1 +0,0 @@
1
- import{z as e}from"zod";import{getDynamicRegistry as n,DEFAULT_MODEL_CONFIG as t,DEFAULT_PROVIDER_CONFIGS as s}from"../ai/model-registry.js";import{safeGenerateObject as i}from"../ai/structured-output.js";import{FunnelStageSchema as o,ChannelTypeSchema as r,TurnPlanSchema as a,STAGE_DEFINITIONS as c}from"../types/reply-policy.js";import{BrandDataSchema as l}from"../types/classification.js";function d(e){const n=r.safeParse(e);return n.success?n.data:"public"}function u(e="public"){const n=d(e),t=o.options.filter(e=>c[e].applicableChannels.includes(n));return t.length>0?t:[...o.options]}function p(n){return e.object({stage:e.enum(n),subGoals:a.shape.subGoals,needs:a.shape.needs,primaryNeed:a.shape.primaryNeed,riskFlags:a.shape.riskFlags,confidence:a.shape.confidence,extractedInfo:a.shape.extractedInfo,reasoningText:a.shape.reasoningText})}const f=[{need:"salary",patterns:[/薪资|工资|时薪|底薪|提成|奖金|补贴|多少钱|收入/i]},{need:"schedule",patterns:[/排班|班次|几点|上班|下班|工时|周末|节假日|做几天/i]},{need:"policy",patterns:[/五险一金|社保|保险|合同|考勤|迟到|补班|试用期/i]},{need:"availability",patterns:[/还有名额|空位|可用时段|什么时候能上|明天能面/i]},{need:"location",patterns:[/在哪|位置|地址|附近|地铁|门店|哪个区|多远/i]},{need:"stores",patterns:[/门店|哪家店|哪些店|有店吗/i]},{need:"requirements",patterns:[/要求|条件|年龄|经验|学历|健康证|身高|体重/i]},{need:"interview",patterns:[/面试|到店|约时间|约面/i]},{need:"wechat",patterns:[/微信|vx|私聊|联系方式|加你/i]}];export const PRIMARY_NEED_PRIORITY=["salary","schedule","location","stores","policy","requirements","availability","interview","wechat","none"];function m(e){const n=new Set;for(const t of f)t.patterns.some(n=>n.test(e))&&n.add(t.need);return 0===n.size?n.add("none"):n.delete("none"),n}export function detectRuleNeeds(e,n){return m(`${n.slice(-4).join(" ")} ${e}`)}function y(e){return m(e)}export function selectContextNeeds(e,n,t,s=1){const i=new Set(n);i.size>1&&i.has("none")&&i.delete("none");const o=y(t);o.delete("none");const r=[];"none"!==e&&i.has(e)&&r.push(e);for(const n of PRIMARY_NEED_PRIORITY){if(r.length>=s)break;"none"!==n&&n!==e&&(o.has(n)&&i.has(n)&&r.push(n))}return r.length>0?r:"none"===e?["none"]:i.has(e)?[e]:["none"]}export function selectPrimaryNeed(e,n,t){const s=new Set(n);if(s.size>1&&s.has("none")&&s.delete("none"),e&&s.has(e))return e;const i=y(t);i.delete("none");for(const e of PRIMARY_NEED_PRIORITY)if(i.has(e)&&s.has(e))return e;for(const e of PRIMARY_NEED_PRIORITY)if(s.has(e))return e;return"none"}export function sanitizePlan(e,n,t){const s=new Set([...e.needs,...Array.from(n)]);return s.size>1&&s.has("none")&&s.delete("none"),{...e,subGoals:e.subGoals.slice(0,2),needs:Array.from(s),primaryNeed:selectPrimaryNeed(e.primaryNeed,s,t),confidence:Number.isFinite(e.confidence)?Math.max(0,Math.min(1,e.confidence)):.5}}function h(e,n,t,s="public",i,o){const r=["你是招聘对话回合规划器,不直接回复候选人。","你只输出结构化规划结果,用于后续回复生成。","规划目标:确定阶段目标(stage)、子目标(subGoals)、事实需求(needs)、主回答轴(primaryNeed)、风险标记(riskFlags)。"].join("\n"),a=d(s);return{system:r,prompt:["[阶段枚举与定义]",...u(a).map(e=>{const n=c[e];return`- ${e}: ${i?.stageGoals[e]?.description||n.description} (转入条件: ${n.transitionSignal})`}),"","[needs枚举]","private"===a?"- stores, location, salary, schedule, policy, availability, requirements, interview, none":"- stores, location, salary, schedule, policy, availability, requirements, interview, wechat, none","","[riskFlags枚举]","- insurance_promise_risk, age_sensitive, confrontation_emotion, urgency_high, qualification_mismatch","","[规则]","- 优先判断本轮主阶段(stage);subGoals 最多 2 项,只保留最关键的。","- 候选人追问事实时,必须打开对应 needs。","- primaryNeed 必须从 needs 中选择一个最主的 need;如果没有明确事实轴则填 none。","- 不确定时 confidence 降低,不要臆断。","- 根据转入条件判断阶段转化,不要停留在不匹配的阶段。",...o&&o.length>0?[`- 候选人资料中已有:${o.join("、")}。不要生成追问这些字段的 subGoal。`]:[],"","[品牌数据]",JSON.stringify(t||{}),"","[历史对话]",n.slice(-8).join("\n")||"无","","[候选人消息]",e].join("\n")}}export async function planTurn(e,o){const{providerConfigs:r=s,modelConfig:a,conversationHistory:c=[],brandData:l,channelType:f,replyPolicy:m,knownCandidateFields:y}=o,g=n(r),b=a?.classifyModel||t.classifyModel,N=d(f),R=p(u(N)),P=h(e,c,l,N,m,y),x=await i({model:g.languageModel(b),schema:R,schemaName:"TurnPlanningOutput",system:P.system,prompt:P.prompt}),I=detectRuleNeeds(e,c),_=selectPrimaryNeed(void 0,I,e);return x.success?sanitizePlan(x.data,I,e):{stage:"trust_building",subGoals:["保持对话并澄清需求"],needs:Array.from(I),primaryNeed:_,riskFlags:[],confidence:.35,extractedInfo:{mentionedBrand:null,city:l?.city||null,mentionedLocations:null,mentionedDistricts:null,specificAge:null,hasUrgency:null,preferredSchedule:null},reasoningText:"规划模型失败,使用规则降级策略"}}
@@ -1 +0,0 @@
1
- import{findBrandByName as e,getAvailableBrandNames as n,resolveDefaultBrandName as t}from"../services/brand-config-selectors.js";import{PRIMARY_NEED_FACT_MAP as r}from"../types/reply-policy.js";import{verboseLog as i}from"../log-control.js";import{getSharedBrandAliasMap as s}from"../services/brand-alias.js";function o(e){const{base:n,range:t,memo:r}=e;let i="";return n<10&&r?i=`${n}元(${r.replace(/\n/g," ").trim()})`:(i=`${n}元/时`,t&&t!==`${n}-${n}`&&(i+=`,范围${t}元`),r&&r.length<50&&(i+=`(${r.replace(/\n/g," ").trim()})`)),e.scenarioSummary&&(i+=`(${e.scenarioSummary})`),i}export function fuzzyMatchBrand(e,n,t){if(!e)return null;const r=e=>e.toLowerCase().replace(/[\s._-]+/g,"");if(t){const i=t.get(r(e))||t.get(e.toLowerCase());if(i&&n.includes(i))return i}const i=e.toLowerCase(),s=r(e),o=n.find(e=>e.toLowerCase()===i);if(o)return o;const a=n.find(e=>r(e)===s);if(a)return a;const c=n.filter(e=>{const n=e.toLowerCase();if(n.includes(i)||i.includes(n))return!0;const t=r(e);return t.includes(s)||s.includes(t)});if(c.length>0)return c.sort((e,n)=>n.length-e.length)[0]??null;if(i.includes("山姆")||i.includes("sam")){const e=n.find(e=>{const n=e.toLowerCase();return n.includes("山姆")||n.includes("sam")});if(e)return e}return null}export function resolveBrandConflict(e){const{uiSelectedBrand:n,configDefaultBrand:t,conversationBrand:r,availableBrands:i,strategy:s="smart",aliasMap:o}=e,a=(e,n)=>{if(e)return fuzzyMatchBrand(e,i,o)??void 0};switch(s){case"user-selected":{const e=a(n);if(e)return{resolvedBrand:e,matchType:e===n?"exact":"fuzzy",source:"ui",reason:"用户选择策略",originalInput:n};const r=a(t);return r?{resolvedBrand:r,matchType:r===t?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:t}:{resolvedBrand:i[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}case"conversation-extracted":{const e=a(r);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"对话提取",originalInput:r};const s=a(n);if(s)return{resolvedBrand:s,matchType:s===n?"exact":"fuzzy",source:"ui",reason:"UI选择",originalInput:n};const o=a(t);return o?{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"config",reason:"配置默认",originalInput:t}:{resolvedBrand:i[0]??"",matchType:"fallback",source:"default",reason:"系统默认"}}default:{const e=a(r),s=a(n);if(e)return{resolvedBrand:e,matchType:e===r?"exact":"fuzzy",source:"conversation",reason:"智能策略: 对话提取",originalInput:r};if(s)return{resolvedBrand:s,matchType:s===n?"exact":"fuzzy",source:"ui",reason:"智能策略: UI选择",originalInput:n};const o=a(t);return o?{resolvedBrand:o,matchType:o===t?"exact":"fuzzy",source:"config",reason:"智能策略: 配置默认",originalInput:t}:{resolvedBrand:i[0]??"",matchType:"fallback",source:"default",reason:"智能策略: 系统默认"}}}}function a(e,n){const{mentionedLocations:t,mentionedDistricts:r}=n;return e.map(e=>{let n=0,i=0,s=0,o=0;if(t&&t.length>0){const r=t.find(n=>e.name.includes(n.location)||e.location.includes(n.location)||e.subarea.includes(n.location));r&&(n=40*r.confidence)}if(r&&r.length>0){const n=r.find(n=>e.district.includes(n.district)||e.subarea.includes(n.district));n&&(i=30*n.confidence)}const a=new Set(e.positions.map(e=>e.name));s=Math.min(5*a.size,20);const c=e.positions.filter(e=>e.availableSlots?.some(e=>e.isAvailable));return o=Math.min(2*c.length,10),{store:e,score:n+i+s+o,breakdown:{locationMatch:n,districtMatch:i,positionDiversity:s,availability:o}}}).sort((e,n)=>n.score-e.score).map(e=>({store:e.store,distance:void 0}))}function c(e){if(!e)return"灵活排班";return{fixed:"固定排班",flexible:"灵活排班",rotating:"轮班制",on_call:"随叫随到"}[e]||"灵活排班"}function l(e){return r[e].length>0}function u(e,n){return e.has(n)}function d(e,n,t){return"minimal"===n||(1===e||"trust_building"===t||"private_channel"===t)}export async function buildContextInfoByNeeds(f,m,g,p,h,y,$,v,b=1,B="minimal",z=[m.primaryNeed]){const x=m.extractedInfo,I=m.primaryNeed,j=z.length>0?Array.from(new Set(z.filter(e=>"none"!==e))):"none"===I?[]:[I],M=new Set(j.flatMap(e=>r[e])),T=d(b,B,m.stage),w=!T&&l(I);let A,S;try{A=await s()}catch(e){const n="object"==typeof e&&null!==e&&"userMessage"in e&&"string"==typeof e.userMessage?e.userMessage:e instanceof Error?e.message:String(e);S=n,i(`[buildContextInfoByNeeds] 品牌别名服务不可用,回退 fuzzy 解析: ${n}`)}const C=resolveBrandConflict({uiSelectedBrand:g,configDefaultBrand:t(f),conversationBrand:p||void 0,availableBrands:n(f),strategy:h||"smart",aliasMap:A}),q=C.resolvedBrand;i(`[品牌解析] 工具传参: ${p??"(未指定)"} → 结果: ${q} (${C.matchType}, ${C.source})`);const L=e(f,q),k=L?.stores??[];let R=k;if(R.length>0){const e=x.mentionedLocations||[];if(e.length>0){const n=e[0]?.location?.trim();if(n){const e=R.filter(e=>e.name.includes(n)||e.location.includes(n)||e.district.includes(n)||e.subarea.includes(n));e.length>0&&(R=e)}}const n=x.mentionedDistricts||[];if(n.length>0){const e=R.filter(e=>n.some(n=>e.district.includes(n.district)||e.subarea.includes(n.district)));e.length>0&&(R=e)}if(R.length===k.length&&y?.jobAddress&&u(M,"location")){const e=R.filter(e=>e.name.includes(y.jobAddress||"")||e.location.includes(y.jobAddress||"")||e.district.includes(y.jobAddress||"")||e.subarea.includes(y.jobAddress||""));e.length>0&&(R=e)}}let P=[];R.length>0&&(P=a(R,x));const D=w?Math.min(1,P.length):0,N=w?"focused":"minimal";let E=`阶段目标:${m.stage}\n默认推荐品牌:${q}\n`;if(S&&(E+=`系统状态:品牌别名服务暂不可用,已回退为规则匹配(${S})。\n`),$){const e=$.stageGoals[m.stage],n=v||$.defaultIndustryVoiceId,t=$.industryVoices[n];E+=`策略目标:${e.primaryGoal}\n`,E+=`推进方式:${e.ctaStrategy}\n`,E+=`主回答轴:${I}\n`,t&&(E+=`行业指纹:${t.name} | 风格:${t.styleKeywords.join("、")}\n`),E+=`红线:${$.hardConstraints.rules.map(e=>e.rule).join(";")}\n`}return w?0===D?E+="暂无可用的门店事实信息,请使用泛化回答,避免任何具体承诺。\n":(E+="匹配到的门店信息:\n",P.slice(0,D).forEach(({store:e})=>{const n=u(M,"location"),t=Array.from(M).some(e=>"location"!==e);E+=n?`• ${e.name}(${e.district}${e.subarea}):${e.location}\n`:`• ${e.name}\n`,t&&e.positions.slice(0,3).forEach(e=>{if(E+=` 职位:${e.name}\n`,u(M,"salary")&&(E+=` 薪资:${o(e.salary)}\n`),u(M,"schedule")&&(E+=` 排班:${c(e.scheduleType)}\n`,E+=` 时间:${e.timeSlots.slice(0,3).join("、")}\n`,(e.minHoursPerWeek||e.maxHoursPerWeek)&&(E+=` 每周工时:${e.minHoursPerWeek||0}-${e.maxHoursPerWeek||"不限"}小时\n`)),u(M,"policy")&&(E+=` 考勤:最多迟到${e.attendancePolicy.lateToleranceMinutes}分钟\n`,e.attendanceRequirement?.description&&(E+=` 出勤要求:${e.attendanceRequirement.description}\n`)),u(M,"availability")){const n=e.availableSlots?.filter(e=>e.isAvailable).slice(0,3)||[];n.length>0&&(E+=` 可用时段:${n.map(e=>e.slot).join("、")}\n`)}if(u(M,"requirements"))if(e.hiringRequirements){const n=e.hiringRequirements,t=[];null==n.minAge&&null==n.maxAge||t.push(`年龄${n.minAge??"不限"}-${n.maxAge??"不限"}岁`),n.genderRequirement&&"0"!==n.genderRequirement&&t.push(`性别:${n.genderRequirement}`),n.education&&"1"!==n.education&&t.push(`学历:${n.education}`),t.length>0&&(E+=` 要求:${t.join("、")}\n`)}else e.requirements?.length&&(E+=` 要求:${e.requirements.filter(e=>"无"!==e).join("、")}\n`)})})):E+=T?"当前处于首轮或浅层沟通,优先泛化回答,不主动展开具体门店、数字或筛选条件。\n":"本轮以推进沟通为主,无需展开岗位细节,请保持回答聚焦且克制。\n",{contextInfo:E,resolvedBrand:q,debugInfo:{relevantStores:P.length>0?P:R.map(e=>({store:e,distance:void 0})),storeCount:D,detailLevel:N,primaryNeed:I,turnPlan:m,aliasLookupError:S}}}
@@ -1 +0,0 @@
1
- import e from"ora";export function createPipelineProgress(){let r=null;return{update(t){r?r.text=t:r=e({text:t,stream:process.stderr}).start()},succeed(e){r?(r.succeed(e),r=null):console.error(`✓ ${e}`)},fail(e){r?(r.fail(e),r=null):console.error(`✗ ${e}`)}}}
@@ -1 +0,0 @@
1
- import{DEFAULT_OUTPUT_GUARDS as t,PRIMARY_NEED_FACT_MAP as e}from"../types/reply-policy.js";export const REPLY_GATE_VIOLATION_CODES=["too_many_questions","audit_tone","premature_numeric_disclosure","off_axis_fact_disclosure","reply_overpacked"];const n=/[^。!?!?]*[??]/g,s={salary:{mention:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i,/时薪|薪资|工资|底薪|收入/i],concrete:[/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i]},schedule:{mention:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/班次|轮班|白班|晚班|工时|排班/i],concrete:[/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/,/轮班|白班|晚班|早班|中班|夜班/i]},location:{mention:[/地址|地铁|附近|位于|门店|在.+(?:路|街|广场|商场|大厦)/i],concrete:[/地址|位于|地铁\S*站|在.+(?:路|街|广场|商场|大厦)/i]},policy:{mention:[/考勤|试用期|社保|五险一金|迟到|补班/i],concrete:[/考勤|试用期|社保|五险一金|迟到|补班/i]},requirements:{mention:[/年龄|学历|经验|健康证|要求/i],concrete:[/年龄|学历|经验|健康证/i]},availability:{mention:[/名额|空位|可用时段|可安排/i],concrete:[/名额|空位|可用时段/i]}};function i(e){return e?.outputGuards??t}export function countQuestions(t){const e=t.match(n)??[],s=new Set(e.map(t=>t.replace(/[??]/g,"").trim()).filter(Boolean)),i=t.split(/[。!?!?]/).map(t=>t.trim()).filter(Boolean).filter(t=>/[吗呢么]$/.test(t)&&!s.has(t)).length;return e.length+i}function o(t){return t.split(/[。!?!?]/).map(t=>t.trim()).filter(Boolean).length}function r(t){return/(?:^|\s)(?:\d+\.\s|- |•)/m.test(t)}function c(t,e="mention"){return Object.entries(s).filter(([,n])=>n[e].some(e=>e.test(t))).map(([t])=>t)}function u(t,e){return e.some(e=>e&&t.includes(e))}function a(t){return/\d+(?:\.\d+)?\s*元(?:\/时|\/小时)?/i.test(t)||/\d{1,2}:\d{2}\s*[~-]\s*\d{1,2}:\d{2}/.test(t)||/年龄\s*\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(t)||/\d{1,2}\s*[-~到]\s*\d{1,2}\s*岁/i.test(t)||/地址在|门店在|位于|在.+(?:路|街|广场|商场|大厦|地铁)/i.test(t)}function l(t,e,n){let s=0;return o(t)>=4&&(s+=1),r(t)&&(s+=1),e>=3&&(s+=1),n.length>=2&&(s+=1),s>=2}export function detectConcreteFactFamilies(t){return c(t,"concrete")}export function detectContextFactFamilies(t){const e=[];return/薪资:/.test(t)&&e.push("salary"),/排班:|时间:|每周工时:/.test(t)&&e.push("schedule"),/匹配到的门店信息:[\s\S]*• .*:/.test(t)&&e.push("location"),/考勤:|出勤要求:/.test(t)&&e.push("policy"),/要求:/.test(t)&&e.push("requirements"),/可用时段:/.test(t)&&e.push("availability"),e}function d(t,n){const s=new Set(t.flatMap(t=>e[t]));return 0===s.size?n.length>0:n.some(t=>!s.has(t))}export function validateReply(t){const{text:e,turnIndex:n,mode:s,policy:o}=t,r=i(o),c=countQuestions(e),p=r.maxQuestionsByMode[s],m=detectConcreteFactFamilies(e),f=t.allowedNeeds?.length?t.allowedNeeds:[t.primaryNeed],h=[];return c>p&&h.push("too_many_questions"),u(e,r.blockedAuditPhrases)&&h.push("audit_tone"),r.blockFirstTurnSpecificFacts&&1===n&&a(e)&&h.push("premature_numeric_disclosure"),d(f,m)&&h.push("off_axis_fact_disclosure"),l(e,c,m)&&h.push("reply_overpacked"),{violations:h,questionCount:c,factFamilies:m}}
@@ -1 +0,0 @@
1
- import{getDynamicRegistry as e,DEFAULT_MODEL_CONFIG as t,DEFAULT_PROVIDER_CONFIGS as a}from"../ai/model-registry.js";import{getAllStores as n,getAvailableBrandNames as r,resolveDefaultBrandName as o,resolvePrimaryCity as i}from"../services/brand-config-selectors.js";import{PRIMARY_NEED_FACT_MAP as s}from"../types/reply-policy.js";import{planTurn as l,selectContextNeeds as d}from"./classification.js";import{buildContextInfoByNeeds as u}from"./context-builder.js";import{detectConcreteFactFamilies as c,detectContextFactFamilies as p,validateReply as m}from"./reply-gate.js";import{safeGenerateText as y}from"../ai/structured-output.js";import{logError as g}from"../errors/index.js";import{setSuppressVerboseLogs as f}from"../log-control.js";import{createPipelineProgress as $}from"./pipeline-progress.js";import{evaluateAgeEligibility as x}from"./age-eligibility.js";import{resolveCandidateAge as h,resolveRegionName as v}from"./candidate-utils.js";import{buildKnownCandidateContext as w}from"./candidate-context.js";function j(e){if(null===e.minAgeObserved&&null===e.maxAgeObserved)return null;return`${e.minAgeObserved??"?"}-${e.maxAgeObserved??"?"}`}function N(e,t,a){const n=t?.qualificationPolicy?.age;if(!e||!n||!n.enabled)return[];if("unknown"===e.status&&!a.riskFlags.includes("age_sensitive"))return[];const r=["[QualificationPolicy:Age]"],o=j(e.summary);return r.push(`- gateStatus: ${e.status}`),r.push(`- expressionStrategy: ${e.appliedStrategy.strategy}`),r.push("- redirect: "+(n.allowRedirect?`allowed priority=${n.redirectPriority}`:"not allowed")),r.push("- revealRange: "+(n.revealRange?"allowed":"not allowed")),n.revealRange&&o&&r.push(`- rangeObserved: ${o}`),"pass"===e.status?r.push(`- writingConstraint: ${e.appliedStrategy.strategy};匹配通过后推进下一步,避免强调年龄筛选`):"fail"===e.status?(r.push(`- writingConstraint: ${e.appliedStrategy.strategy};礼貌说明不匹配,避免承诺或争辩`),n.allowRedirect&&r.push("- writingConstraint: 可提示其他岗位或门店选项")):r.push("- writingConstraint: 如确需涉及年龄或资格,用合规、轻量的方式核实,不要审查式逐条盘问"),r}function M(e){return"minimal"===e?["4. 当前为浅层沟通,优先泛化回答,不主动抛具体数字、时间、地址或筛选条件。","5. 若候选人追问具体事实,承接需求并引导进一步沟通,不编造细节。"]:["4. 围绕 primaryNeed 回答,上下文中有的事实可以正常引用,不要刻意回避。","5. 不主动展开其他事实轴;若候选人同时问两个点,只在上下文支持时简要带上次要问题。"]}function S(e,t,a,n,r,o,i,s,l,d,u,c){if(!e)return{system:"你是招聘助手。遵循事实,不夸大承诺,回复简洁自然。",prompt:`候选人消息:${r}\n\n上下文:\n${n}\n\n请直接回复候选人。`};const p=e.stageGoals[t.stage],m=e.industryVoices[d||e.defaultIndustryVoiceId],y=e.outputGuards.maxQuestionsByMode[s];return{system:["你是政策驱动的招聘助手。",`当前阶段:${t.stage}`,`当前轮次:${i}`,`当前披露模式:${s}`,`主回答轴:${t.primaryNeed}`,`阶段目标:${p.primaryGoal}`,`阶段成功标准:${p.successCriteria.join(";")}`,`推进策略:${p.ctaStrategy}`,p.disallowedActions?.length?`阶段禁止:${p.disallowedActions.join(";")}`:"",`人格设定:语气=${e.persona.tone},亲和度=${e.persona.warmth},长度=${e.persona.length},称呼=${e.persona.addressStyle},提问风格=${e.persona.questionStyle}`,`共情策略:${e.persona.empathyStrategy}`,m?`行业指纹:${m.name};背景=${m.industryBackground};行业词=${m.jargon.join("、")};避免=${m.tabooPhrases.join("、")}`:"",`红线规则:${e.hardConstraints.rules.map(e=>e.rule).join(";")}`,`FactGate模式:${e.factGate.mode};缺事实回退=${e.factGate.fallbackBehavior}`,`禁止审查措辞:${e.outputGuards.blockedAuditPhrases.join("、")}`,...l.knownFieldNames.length>0?[`候选人资料已确认:${l.knownFieldNames.join("、")}。这些信息不得重复追问;如需引用,自然带过即可,不要像念资料一样复述。`]:[],...N(c,e,t),u?`如涉及换微信,优先引导平台交换,必要时可提供默认微信号:${u}`:"如涉及换微信,优先引导平台交换,不编造联系方式。","必须口语化、简洁,不输出解释。"].filter(Boolean).join("\n"),prompt:["[回合规划]",`stage=${t.stage}`,`subGoals=${t.subGoals.join("、")||"无"}`,`contextNeeds=${a.join("、")||"none"}`,`primaryNeed=${t.primaryNeed}`,`riskFlags=${t.riskFlags.join("、")||"无"}`,`confidence=${t.confidence.toFixed(2)}`,"","[对话历史]",o.slice(-6).join("\n")||"无","","[业务上下文]",n,"",...l.factsText?["[候选人已知信息]",l.factsText,""]:[],"[候选人消息]",r,"","[输出要求]","1. 直接给候选人的单条回复,不得输出多段解释或元信息。",`2. 最多追问 ${y} 个关键问题。`,"3. 禁止使用“是否满足”“是否符合”“基本入职要求”等审查措辞。",...M(s)].join("\n")}}export function hasUnsupportedFactClaims(e,t,a){const n=c(e);if(0===n.length)return!1;const r=new Set(p(t)),o=new Set(a.flatMap(e=>s[e]));return n.some(e=>!o.has(e)||!r.has(e))}function b(e){return"private_channel"===e||"interview_scheduling"===e}export function resolveTurnIndex(e,t){return Number.isInteger(t)&&void 0!==t&&t>=1?t:0===e.length?1:2}export function resolveEffectiveDisclosureMode(e,t){return 1===e||"trust_building"===t||"private_channel"===t?"minimal":"focused"}function I(e,t){const a=["- 只修正命中的违规点,没有命中的部分不要过度改写。"];return e.includes("too_many_questions")&&a.push(`- 删除多余追问,只保留最关键的 ${t} 个问题。`),e.includes("audit_tone")&&a.push("- 保留原意,但把审查式措辞改成自然口语,不要像筛选候选人。"),e.includes("premature_numeric_disclosure")&&a.push("- 把具体数字、时间和地址细节改成泛化表达,例如“细节我帮你确认”或“以门店安排为准”。"),e.includes("off_axis_fact_disclosure")&&a.push("- 删除不属于主回答轴的具体事实;如果要提到次要问题,只能做不带细节的承接。"),e.includes("reply_overpacked")&&a.push("- 压缩成最多两句,不要列表、不要枚举、不要一口气展开太多信息。"),a}async function R(e,t,a){const n=["请重写下面这条招聘回复。","要求:","- 不新增任何具体数字、地址、福利承诺。","- 仅保留泛化表达,强调可进一步沟通确认细节。","- 口语化、单行、简洁。","","[原回复]",e,"","[可用上下文]",a].join("\n"),r=await y({model:t,prompt:n,context:"SmartReplyFactGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return r.success?{text:r.text,usage:r.usage,latencyMs:r.latencyMs}:{text:e}}async function G(e,t,a,n){const{turnIndex:r,effectiveDisclosureMode:o,primaryNeed:i,allowedNeeds:s,violations:l,policy:d}=n,u=d?.outputGuards.maxQuestionsByMode[o]??1,c=d?.outputGuards.blockedAuditPhrases.join("、")??"",p=I(l,u),m=(s??[]).filter(e=>e!==i&&"none"!==e),g=["请重写下面这条招聘回复。","要求:",`- 当前轮次=${r},披露模式=${o},主回答轴=${i}。`,m.length>0?`- 允许顺带覆盖的次要轴:${m.join("、")}。`:"",`- 当前违规点:${l.join("、")}。`,...p,"- 只保留单条口语化回复,不输出解释。",`- 问题数最多 ${u} 个。`,"- 围绕主回答轴回答,不主动展开其他事实轴。","- 首轮时不要主动报具体数字、时间、地址或筛选条件。",c?`- 禁止使用这些措辞:${c}。`:"","","[原回复]",e,"","[可用上下文]",a].filter(Boolean).join("\n"),f=await y({model:t,prompt:g,context:"SmartReplyReplyGateRewrite",timeoutMs:2e4,maxOutputTokens:500});return f.success?{text:f.text,usage:f.usage,latencyMs:f.latencyMs}:{text:e}}export async function generateSmartReply(e){const t=$();f(!0);try{return await k(e,t)}catch(e){throw t.fail("回复生成失败"),e}finally{f(!1)}}async function k(s,c){const{modelConfig:p,preferredBrand:f,toolBrand:$,brandPriorityStrategy:j,conversationHistory:N=[],candidateMessage:M,configData:I,replyPolicy:k,candidateInfo:B,defaultWechatId:C,industryVoiceId:_,channelType:F,turnIndex:P}=s,A=p?.providerConfigs||a,T=i(I),D={...T?{city:T}:{},defaultBrand:o(I),availableBrands:r(I),storeCount:n(I).length},O=w(B);c.update("分析对话意图...");const V=await l(M,{modelConfig:p||{},conversationHistory:N,brandData:D,providerConfigs:A,...void 0!==F?{channelType:F}:{},...void 0!==k?{replyPolicy:k}:{},...O.knownFieldNames.length>0?{knownCandidateFields:O.knownFieldNames}:{}}),q=resolveTurnIndex(N,P),E=resolveEffectiveDisclosureMode(q,V.stage),Q="focused"===E?d(V.primaryNeed,V.needs,M,2):[V.primaryNeed],W=$||V.extractedInfo.mentionedBrand||void 0;c.update("构建业务上下文...");const{contextInfo:H,debugInfo:U,resolvedBrand:z}=await u(I,V,f,W,j,B,k,_,q,E,Q);c.update("校验候选人资格...");const J=h(V,B),K=v(V,B),L=V.extractedInfo.city??i(I,z),X=await x({...void 0!==J?{age:J}:{},brandAlias:z,..."string"==typeof L?{cityName:L}:{},...void 0!==K?{regionName:K}:{},...void 0!==k?.qualificationPolicy?.age?{strategy:k.qualificationPolicy.age}:{}}),Y=e(A),Z=p?.replyModel||t.replyModel,ee=Y.languageModel(Z),te=S(k,V,Q,H,M,N,q,E,O,_,C,X);c.update("生成回复...");const ae=await y({model:ee,system:te.system,prompt:te.prompt,context:"SmartReply",timeoutMs:3e4,maxOutputTokens:2e3});if(!ae.success)return c.fail("回复生成失败"),g("SmartReply 生成失败",ae.error),{turnPlan:V,suggestedReply:"",confidence:0,shouldExchangeWechat:b(V.stage),factGateRewritten:!1,replyGateRewritten:!1,gateViolations:[],contextInfo:H,debugInfo:{...U,resolvedBrand:z,turnIndex:q,effectiveDisclosureMode:E,primaryNeed:V.primaryNeed,replyGateRewritten:!1,gateViolations:[],gateStatus:X.status,appliedStrategy:X.appliedStrategy,ageRangeSummary:X.summary},usage:void 0,error:ae.error};let ne=ae.text,re=ae.usage,oe=ae.latencyMs,ie=!1,se=!1,le=[];if(c.update("检查回复质量..."),"strict"===k?.factGate.mode){if(hasUnsupportedFactClaims(ne,H,Q)){ie=!0;const e=await R(ne,ee,H);ne=e.text,e.usage&&(re=e.usage),void 0!==e.latencyMs&&(oe=(oe??0)+e.latencyMs)}}if(le=m({text:ne,turnIndex:q,mode:E,primaryNeed:V.primaryNeed,allowedNeeds:Q,policy:k}).violations,le.length>0){c.update("优化回复..."),se=!0;const e=await G(ne,ee,H,{turnIndex:q,effectiveDisclosureMode:E,primaryNeed:V.primaryNeed,allowedNeeds:Q,violations:le,policy:k});ne=e.text,e.usage&&(re=e.usage),void 0!==e.latencyMs&&(oe=(oe??0)+e.latencyMs),le=m({text:ne,turnIndex:q,mode:E,primaryNeed:V.primaryNeed,allowedNeeds:Q,policy:k}).violations}const de=oe??0,ue=re?.totalTokens??0;return c.succeed(`回复已生成 | ${V.stage} | ${de}ms | ${ue} tokens${se?" | 已优化":""}`),{turnPlan:V,suggestedReply:ne,confidence:Math.max(0,Math.min(1,V.confidence)),shouldExchangeWechat:b(V.stage),factGateRewritten:ie,replyGateRewritten:se,gateViolations:le,contextInfo:`${H}\n当前品牌:${z}`,debugInfo:{...U,resolvedBrand:z,turnIndex:q,effectiveDisclosureMode:E,primaryNeed:V.primaryNeed,replyGateRewritten:se,gateViolations:le,gateStatus:X.status,appliedStrategy:X.appliedStrategy,ageRangeSummary:X.summary},usage:re,latencyMs:oe}}
@@ -1 +0,0 @@
1
- import{z as e}from"zod";import{AppError as t}from"../errors/app-error.js";import{ErrorCode as o}from"../errors/error-codes.js";const s=e.object({id:e.number(),name:e.string(),aliases:e.array(e.string()),projectIdList:e.array(e.number())}),n=e.object({code:e.number(),message:e.string().optional(),data:e.object({result:e.array(s),total:e.number()})}),r=3e5;let a=null;function i(){return null!==a&&Date.now()-a.timestamp<r}const c=3e4;function u(){const e=process.env.DULIDAY_BRAND_LIST_URL;return"string"==typeof e&&e.trim().length>0?e:void 0}async function f(e){const s=e||process.env.DULIDAY_TOKEN,r=u();if(!s)throw new t({code:o.CONFIG_MISSING_FIELD,message:"DULIDAY_TOKEN 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday Token 配置"});if(!r)throw new t({code:o.CONFIG_MISSING_FIELD,message:"DULIDAY_BRAND_LIST_URL 未配置,无法获取品牌别名数据",userMessage:"品牌别名数据加载失败:缺少 Duliday 品牌接口配置"});const a=new AbortController,i=setTimeout(()=>a.abort(),c);try{const e=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":s},body:JSON.stringify({pageNum:1,pageSize:1e3}),signal:a.signal});if(!e.ok)throw new t({code:o.NETWORK_HTTP_ERROR,message:`Duliday 品牌列表 API 返回 HTTP ${e.status}: ${e.statusText}`,userMessage:`品牌别名数据加载失败:服务端返回 ${e.status}`});const i=await e.json(),c=n.safeParse(i);if(!c.success)throw new t({code:o.VALIDATION_SCHEMA_ERROR,message:`Duliday 品牌列表响应格式校验失败: ${c.error.message}`,userMessage:"品牌别名数据加载失败:响应格式异常"});return c.data.data.result}catch(e){if(e instanceof t)throw e;if(e instanceof Error&&"AbortError"===e.name)throw new t({code:o.NETWORK_TIMEOUT,message:"Duliday 品牌列表 API 请求超时 (30000ms)",userMessage:"品牌别名数据加载超时,请稍后重试",cause:e});throw new t({code:o.SYSTEM_DEPENDENCY_FAILED,message:`获取品牌别名数据失败: ${e instanceof Error?e.message:String(e)}`,userMessage:"品牌别名数据加载失败,请检查网络连接",...e instanceof Error?{cause:e}:{}})}finally{clearTimeout(i)}}function l(e,t,o){const s={},n=new Map,r=e=>e.trim(),a=(e,t)=>{const o=r(e).toLowerCase().replace(/[\s._-]+/g,"");if(!o)return;const s=n.get(o);(!s||t.length>s.length)&&n.set(o,t)},i=[...e].sort((e,t)=>t.name.length-e.name.length);for(const e of i){const t=r(e.name);if(!t)continue;const n=Array.from(new Set([t,...e.aliases.map(r).filter(Boolean)].filter(e=>e!==t)));n.unshift(t),s[t]=n;for(const e of n)a(e,t);if(o&&e.projectIdList.length>0)for(const r of e.projectIdList){const e=o[String(r)];if(!e||e===t)continue;if(!e.includes(t))continue;const i=e.replace(t,"");if(!i)continue;const c=[e];for(const e of n)e!==t&&c.push(`${i}${e}`);if(s[e]){const t=new Set(s[e]);for(const o of c)t.has(o)||s[e].push(o)}else s[e]=c;for(const t of c)a(t,e)}}for(const e of t)s[e]||(s[e]=[e],a(e,e));return{dictionary:s,aliasMap:n}}async function m(e){if(i())return;const t=await f(e),o=new Set,{dictionary:s,aliasMap:n}=l(t,o);a={aliasMap:n,dictionary:s,timestamp:Date.now()}}export async function getSharedBrandDictionary(e){return await m(e),a.dictionary}export async function getSharedBrandAliasMap(e){return await m(e),a.aliasMap}
@@ -1 +0,0 @@
1
- export function getAllStores(e){return e.brands.flatMap(e=>e.stores)}export function getAvailableBrandNames(e){return e.brands.map(e=>e.name)}export function findBrandByName(e,r){if(r)return e.brands.find(e=>e.name===r)}export function resolveDefaultBrand(e){if(e.meta.defaultBrandId){const r=e.brands.find(r=>r.id===e.meta.defaultBrandId);if(r)return r}return e.brands[0]}export function resolveDefaultBrandName(e){return resolveDefaultBrand(e)?.name??""}export function resolvePrimaryCity(e,r){const n=r?findBrandByName(e,r)?.stores??[]:getAllStores(e);return n.find(e=>"string"==typeof e.city&&e.city.trim().length>0)?.city}
@@ -1 +0,0 @@
1
- import{readFileSync as t,writeFileSync as o}from"node:fs";import{join as n}from"node:path";import{ZhipinDataSchema as a}from"../types/zhipin.js";import{ReplyPolicyConfigSchema as e,DEFAULT_REPLY_POLICY as r}from"../types/reply-policy.js";const d=n(import.meta.dirname,"../../data"),i=n(d,"brand-config.json"),l=n(d,"reply-policy.json"),p=3e5;let f=null,u=null;function s(t){return Date.now()-t<p}export function loadBrandConfig(){if(f&&s(f.loadedAt))return f.data;const o=t(i,"utf-8"),n=a.parse(JSON.parse(o));return f={data:n,loadedAt:Date.now()},n}export function saveBrandConfig(t){o(i,JSON.stringify(t,null,2),"utf-8"),f={data:t,loadedAt:Date.now()}}export function loadReplyPolicy(){if(u&&s(u.loadedAt))return u.data;try{const o=t(l,"utf-8"),n=e.parse(JSON.parse(o));return u={data:n,loadedAt:Date.now()},n}catch{return r}}export function saveReplyPolicy(t){o(l,JSON.stringify(t,null,2),"utf-8"),u={data:t,loadedAt:Date.now()}}
@@ -1 +0,0 @@
1
- import{z as t}from"zod";const e=6e4,n=200,a=2e4;let r=null,i=null;export function getDulidayJobListEndpoint(){const t=process.env.DULIDAY_JOB_LIST_URL;return"string"==typeof t&&t.trim().length>0?t:void 0}export function getDulidayToken(){const t=process.env.DULIDAY_TOKEN;return"string"==typeof t&&t.trim().length>0?t:void 0}export function normalizeName(t){return(t??"").trim()}export function buildCityCandidates(t){const e=normalizeName(t);if(!e)return[];const n=new Set([e]);return e.endsWith("市")?n.add(e.slice(0,-1)):n.add(`${e}市`),Array.from(n).filter(Boolean)}export function buildBrandCandidates(t,e){const n=normalizeName(t);if(!n)return[];const a=new Set([n]),r=buildCityCandidates(e);for(const t of r)if(n.startsWith(t)){const e=n.slice(t.length).trim();e&&a.add(e)}return Array.from(a).filter(Boolean)}const o=t.object({data:t.object({result:t.array(t.unknown()).optional(),list:t.array(t.unknown()).optional(),total:t.number().optional()}).nullable().optional(),result:t.array(t.unknown()).optional()});export function extractResults(t){const e=o.safeParse(t);if(!e.success)return{items:[],total:0};const n=e.data,a=n.data?.result??n.data?.list??(Array.isArray(n.result)?n.result:[]),r=n.data?.total??(Array.isArray(a)?a.length:0);return{items:Array.isArray(a)?a:[],total:"number"==typeof r?r:0}}export const FULL_INCLUDE_OPTIONS={includeBasicInfo:!0,includeJobSalary:!0,includeWelfare:!0,includeHiringRequirement:!0,includeWorkTime:!0};const s={includeBasicInfo:!0,includeHiringRequirement:!0};export async function fetchJobListPage(t,n,o,l,c){const u="test"!==process.env.NODE_ENV,d=buildBrandCandidates(c?.brandAlias,c?.cityName),f=buildCityCandidates(c?.cityName),y=c?.include??s,m=JSON.stringify({token:t,brandCandidates:d,cityCandidates:f,pageNum:o,includeOpts:y}),p=Date.now();if(u&&r&&r.cacheKey===m&&p-r.fetchedAt<e)return r.payload;if(u&&i?.cacheKey===m)return i.promise;const h=(async()=>{const e=new AbortController,s=setTimeout(()=>e.abort(),a),c={};d.length>0&&(c.brandAliasList=d),f.length>0&&(c.cityNameList=f);const u={pageNum:o,pageSize:l,queryParam:c,options:y};try{const a=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","Duliday-Token":t},body:JSON.stringify(u),signal:e.signal});if(!a.ok)throw new Error(`Duliday job list fetch failed: ${a.status}`);const i=await a.json();return r={cacheKey:m,payload:i,fetchedAt:Date.now()},i}finally{clearTimeout(s),i=null}})();return u&&(i={cacheKey:m,promise:h}),h}export async function fetchAllJobListPages(t,e,a){const r=[];let i=1,o=0;for(;;){const s=extractResults(await fetchJobListPage(t,e,i,n,a));if(1===i&&(o=s.total),r.push(...s.items),s.items.length<n||r.length>=o)break;i+=1}return{items:r,total:o}}
@@ -1 +0,0 @@
1
- import{DulidayNewPositionSchema as e}from"../types/duliday-api.js";function r(r){const n=e.safeParse(r);if(!n.success)return null;const t=n.data,i=t.basicInfo,a=i.storeInfo,o=t.jobSalary,s=t.hiringRequirement,m=o.salary??o.salaryScenarioList?.find(e=>"正式"===e.salaryType)?.basicSalary?.basicSalary??o.salaryScenarioList?.[0]?.basicSalary?.basicSalary??0,l=o.salaryUnitStr??o.salaryScenarioList?.find(e=>"正式"===e.salaryType)?.basicSalary?.basicSalaryUnit??o.salaryScenarioList?.[0]?.basicSalary?.basicSalaryUnit??"元/小时";let u=a?.storeId;if(null==u){const e=`${a?.storeName??""}|${a?.storeAddress??""}`;u=Array.from(e).reduce((e,r)=>31*e+r.charCodeAt(0)>>>0,7)}return{jobId:i.jobId,jobName:i.jobName,jobContent:i.jobContent??null,brandId:void 0!==i.brandId?String(i.brandId):void 0!==i.projectId?String(i.projectId):void 0,brandName:i.brandName,projectId:i.projectId,projectName:i.projectName,storeId:u,storeName:a?.storeName??"未知门店",storeCityName:a?.storeCityName??"",storeRegionName:a?.storeRegionName,storeAddress:a?.storeAddress??"",longitude:a?.longitude,latitude:a?.latitude,salary:m,salaryUnitStr:l,salaryScenarioList:o.salaryScenarioList??null,welfare:t.welfare,cooperationMode:s.cooperationMode??0,requirementNum:s.requirementNum??0,thresholdNum:s.thresholdNum??0,signUpNum:s.signUpNum??null,basicPersonalRequirements:s.basicPersonalRequirements?{minAge:s.basicPersonalRequirements.minAge??null,maxAge:s.basicPersonalRequirements.maxAge??null,genderRequirement:s.basicPersonalRequirements.genderRequirement??null}:null,certificate:s.certificate?{education:s.certificate.education??null,healthCertificate:s.certificate.healthCertificate??null}:null,workTime:t.workTime}}export function convertPositionsToZhipinData(e,i,a){const o=new Map;let s;for(let m=0;m<e.length;m++){const l=r(e[m]);if(!l)continue;const u=l.brandName??i??"未知品牌",c=l.brandId??`name:${u}`;void 0!==s||void 0!==i&&u!==i||(s=c);let d=o.get(c);d||(d={id:c,name:u,stores:[]},o.set(c,d));const y=`store_${l.storeId}`;let f=d.stores.find(e=>e.id===y);f||(f=n(l,c,a),d.stores.push(f));const p=t(l);f.positions.push(p)}return{meta:{...s??o.values().next().value?.id?{defaultBrandId:s??o.values().next().value?.id}:{},syncedAt:(new Date).toISOString(),source:"duliday"},brands:Array.from(o.values())}}function n(e,r,n){return{id:`store_${e.storeId}`,brandId:r,name:e.storeName,city:e.storeCityName||n,location:e.storeAddress,district:p(e.storeAddress,e.storeRegionName),subarea:g(e.storeName),coordinates:"number"==typeof e.latitude&&"number"==typeof e.longitude?{lat:e.latitude,lng:e.longitude}:{lat:0,lng:0},positions:[]}}function t(e){const r=i(e.workTime);let n=[];return r.combinedArrangementTimes?.length?n=f(r.combinedArrangementTimes):r.fixedArrangementTimes?.length&&(n=f(r.fixedArrangementTimes.map(e=>({...e,weekdays:[]})))),{id:`pos_${e.jobId}`,name:h(e.jobName),brandId:e.brandId,brandName:e.brandName,projectId:void 0!==e.projectId?String(e.projectId):void 0,projectName:e.projectName,timeSlots:n,salary:{...parseSalaryDetails(e.salary,e.welfare),scenarioSummary:buildScenarioSummary(e.salaryScenarioList),settlementCycle:extractSettlementCycle(e.salaryScenarioList)},workHours:String(r.perDayMinWorkHours??8),benefits:parseBenefits(e.welfare),requirements:generateRequirements(e),urgent:e.requirementNum>3,scheduleType:2===e.cooperationMode?"flexible":"fixed",attendancePolicy:s(e.cooperationMode),availableSlots:m(e,r),schedulingFlexibility:l(e,r),minHoursPerWeek:c(r),maxHoursPerWeek:d(r),attendanceRequirement:y(r),hiringRequirements:o(e),description:e.jobContent||void 0}}function i(e){const r=e.weekWorkTime,n=e.dayWorkTime,t=e.dailyShiftSchedule,i=Number(e.employmentForm)||({"长期用工":1,"临时用工":2,"短期用工":2}[String(e.employmentForm)]??1),a=Number(t?.arrangementType)||({"固定排班制":1,"组合排班制":3}[String(t?.arrangementType)]??0),o=null!=n?.perDayMinWorkHours?Number(n.perDayMinWorkHours):null,s=null!==o&&Number.isFinite(o)?o:null,m=r?.customnWorkTimeList?.map(e=>({weekdays:Array.isArray(e.customWorkWeekdays)?e.customWorkWeekdays.map(e=>Number(e)).filter(e=>Number.isFinite(e)):[],minWorkDays:e.customMinWorkDays??null,maxWorkDays:e.customMaxWorkDays??null}))??null,l=t?.fixedScheduleList?.map(e=>({startTime:e.startTime??parseTimeStringToSeconds(e.fixedShiftStartTime),endTime:e.endTime??parseTimeStringToSeconds(e.fixedShiftEndTime)}))??null,u=t?.combinedArrangement?.map(e=>{const r=(e.weekdays??("string"==typeof e.CombinedArrangementWeekdays?[Number(e.CombinedArrangementWeekdays)]:Array.isArray(e.CombinedArrangementWeekdays)?e.CombinedArrangementWeekdays:[])).map(e=>"number"==typeof e?e:Number(e)).filter(e=>Number.isFinite(e));return{startTime:e.startTime??e.CombinedArrangementStartTime??0,endTime:e.endTime??e.CombinedArrangementEndTime??0,weekdays:r}})??null;return{employmentForm:i,perDayMinWorkHours:s,perWeekWorkDays:r?.perWeekWorkDays??null,perWeekNeedWorkDays:null!=r?.perWeekNeedWorkDays?Number(r.perWeekNeedWorkDays):null,perWeekRestDays:r?.perWeekRestDays??null,arrangementType:a,maxWorkTakingTime:e.maxWorkTakingTime??0,workTimeRemark:e.workTimeRemark??null,fixedArrangementTimes:l,combinedArrangementTimes:u,customWorkTimes:m}}export function parseSalaryDetails(e,r){const n=r.memo||"",t=n.match(/(\d+元?-\d+元?)/),i=t?t[1]:void 0,a=n.match(/(奖金[\d~\-~元]+)/);return{base:e,range:i,bonus:a?a[1]:void 0,memo:n}}export function buildScenarioSummary(e){if(!e||0===e.length)return;const r=[];for(const n of e){if("培训期"===n.salaryType)continue;if(n.stairSalaries?.length){const e=n.stairSalaries.filter(e=>null!=e.salary).map(e=>{const r=e.salaryUnit??"元/时";return`满${e.fullWorkTime??"?"}${e.fullWorkTimeUnit??"小时"}后${e.salary}${r}`}).join(",");e&&r.push(e)}const e=n.comprehensiveSalary;null!=e?.minComprehensiveSalary&&null!=e?.maxComprehensiveSalary&&r.push(`综合${e.minComprehensiveSalary}-${e.maxComprehensiveSalary}${e.comprehensiveSalaryUnit??"元/月"}`);const t=n.holidaySalary;t&&(t.holidaySalaryMultiple?r.push(`节假日${t.holidaySalaryMultiple}倍`):null!=t.holidayFixedSalary&&r.push(`节假日${t.holidayFixedSalary}${t.holidayFixedSalaryUnit??"元/时"}`))}return r.length>0?r.join(";"):void 0}export function extractSettlementCycle(e){if(!e||0===e.length)return;const r=e.find(e=>"正式"===e.salaryType)??e[0];return{"日结算":"日结","周结算":"周结","月结算":"月结","完结算":"完结","半月结算":"半月结"}[r?.salaryPeriod??""]??void 0}export function parseBenefits(e){const r=[];if(e.haveInsurance&&"无"!==e.haveInsurance&&"0"!==e.haveInsurance&&r.push("五险一金"),e.accommodation&&"无"!==e.accommodation&&"0"!==e.accommodation&&r.push("住宿"),e.catering&&"无"!==e.catering&&"0"!==e.catering&&r.push("餐饮"),e.moreWelfares&&Array.isArray(e.moreWelfares))for(const n of e.moreWelfares){const e=n.content,t=["保险","年假","补贴","福利","股票","学历提升"];for(const n of t)if(e.includes(n)&&!r.some(e=>e.includes(n))){const t=e.match(new RegExp(`\\d*[天个月年]*${n}[^,。]*`));r.push(t?t[0]:n)}}if(e.memo){const n=["年假","补贴","商保","股票","学历提升"];for(const t of n)e.memo.includes(t)&&!r.some(e=>e.includes(t))&&r.push(t)}return 0===r.length&&r.push("按国家规定"),{items:r,promotion:e.promotionWelfare||void 0}}export function generateRequirements(e){const r=[],n=e.basicPersonalRequirements,t=e.certificate;if(null!=n?.minAge||null!=n?.maxAge){const e=n?.minAge??"不限",t=n?.maxAge??"不限";r.push(`年龄${e}-${t}岁`)}if(n?.genderRequirement&&"0"!==n.genderRequirement){if(!/男性.*女性|女性.*男性|不限/.test(n.genderRequirement)){const e={"男性":"限男性","女性":"限女性"};r.push(e[n.genderRequirement]??`性别要求:${n.genderRequirement}`)}}if(t?.education&&"不限"!==t.education){const e={"本科":"本科及以上","专科":"专科及以上","高中":"高中及以上","初中":"初中及以上"};r.push(e[t.education]??`学历${t.education}`)}if(t?.healthCertificate){const e={"食品健康证":"需食品健康证","零售健康证":"需零售健康证"};r.push(e[t.healthCertificate]??"需健康证")}return 0===r.length?a(e.jobName):r}function a(e){const r=["工作认真负责","团队合作精神"];return e.includes("服务员")?[...r,"有服务行业经验优先","沟通能力强"]:e.includes("经理")?[...r,"有管理经验","责任心强"]:[...r,"有相关工作经验者优先"]}function o(e){const r=e.basicPersonalRequirements,n=e.certificate;if(r||n)return{minAge:r?.minAge??null,maxAge:r?.maxAge??null,genderRequirement:r?.genderRequirement??null,education:n?.education??null,healthCertificate:n?.healthCertificate??null}}function s(e){const r=3===e;return{punctualityRequired:r,lateToleranceMinutes:r?5:15,attendanceTracking:r?"strict":"flexible",makeupShiftsAllowed:!r}}function m(e,r){const n=[];let t=[];r.combinedArrangementTimes?.length?t=f(r.combinedArrangementTimes):r.fixedArrangementTimes?.length&&(t=f(r.fixedArrangementTimes.map(e=>({...e,weekdays:[]}))));for(const r of t)n.push({slot:r,maxCapacity:e.requirementNum,currentBooked:e.signUpNum||0,isAvailable:(e.signUpNum||0)<e.requirementNum,priority:e.requirementNum>3?"high":"medium"});return n}function l(e,r){const n=2===e.cooperationMode;return{canSwapShifts:3===r.arrangementType||n,advanceNoticeHours:r.maxWorkTakingTime/60,partTimeAllowed:n,weekendRequired:u(r),holidayRequired:!1}}function u(e){return!!e.combinedArrangementTimes&&e.combinedArrangementTimes.some(e=>e.weekdays.includes(0)||e.weekdays.includes(6))}function c(e){const r=e.perDayMinWorkHours??8;let n=null;if(null!=e.perWeekWorkDays&&(n=e.perWeekWorkDays),null===n&&e.customWorkTimes?.length){const r=e.customWorkTimes.map(e=>e.minWorkDays).filter(e=>null!=e);r.length>0&&(n=Math.min(...r))}return null===n&&null!=e.perWeekNeedWorkDays&&(n=e.perWeekNeedWorkDays),null===n&&(n=5),r*n}function d(e){return 7*(e.perDayMinWorkHours??8)}function y(e){let r=[];if(e.combinedArrangementTimes?.length){const n=new Set;for(const r of e.combinedArrangementTimes)for(const e of r.weekdays)null!=e&&Number.isFinite(e)&&n.add(e);r=Array.from(n).sort()}else if(e.customWorkTimes?.length){const n=new Set;for(const r of e.customWorkTimes)for(const e of r.weekdays)null!=e&&Number.isFinite(e)&&n.add(e);r=Array.from(n).sort()}let n=null;if(null!=e.perWeekWorkDays&&(n=e.perWeekWorkDays),null===n&&e.customWorkTimes?.length){const r=e.customWorkTimes.map(e=>e.minWorkDays).filter(e=>null!=e);r.length>0&&(n=Math.min(...r))}return null===n&&null!=e.perWeekNeedWorkDays&&(n=e.perWeekNeedWorkDays),null===n&&(n=5),{minimumDays:n,requiredDays:k(r),description:e.workTimeRemark||""}}export function parseTimeStringToSeconds(e){if("number"==typeof e)return e;if("string"!=typeof e||!e)return 0;const r=e.match(/^(\d{1,2}):(\d{2})$/);return r?3600*Number(r[1])+60*Number(r[2]):Number(e)||0}function f(e){return e.map(e=>{const r=Math.floor(e.startTime/3600),n=Math.floor(e.startTime%3600/60),t=Math.floor(e.endTime/3600),i=Math.floor(e.endTime%3600/60);return`${r.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}~${t.toString().padStart(2,"0")}:${i.toString().padStart(2,"0")}`})}function p(e,r){if(r)return r;return e.split("-")[1]||"未知区域"}function g(e){const r=e.match(/(.+?)(附近|周边|旁边|店)/);return r?.[1]??e}function h(e){const r=e.split("-");return r[r.length-2]||"服务员"}function k(e){return e.filter(e=>Number.isFinite(e)).map(e=>0===e?7:e)}
@@ -1 +0,0 @@
1
- import{defineTool as e}from"@roll-agent/sdk";import{z as t}from"zod";import{CandidateInfoSchema as n}from"../types/zhipin.js";import{loadBrandConfig as r,loadReplyPolicy as a}from"../services/config-loader.js";import{generateSmartReply as i}from"../pipeline/smart-reply.js";import{AGE_ELIGIBILITY_STATUSES as o}from"../pipeline/age-eligibility.js";import{ChannelTypeSchema as s,EffectiveDisclosureModeSchema as l,FunnelStageSchema as d,ReplyNeedSchema as c,RiskFlagSchema as g}from"../types/reply-policy.js";import{REPLY_GATE_VIOLATION_CODES as u}from"../pipeline/reply-gate.js";import{ModelConfigSchema as p}from"../types/classification.js";const y=t.enum(u),f=t.enum(o);export const generateReply=e({name:"generate_reply",description:"根据候选人消息、对话历史和品牌数据生成智能招聘回复。内部流程:回合规划 → primaryNeed 驱动上下文构建 → 年龄资格校验 → 策略化回复生成 → FactGate/ReplyGate 校验。",input:t.object({candidateMessage:t.string().describe("候选人发送的消息"),conversationHistory:t.array(t.string()).optional().describe("对话历史(最近几轮)"),candidateInfo:n.optional().describe("候选人基本信息"),preferredBrand:t.string().optional().describe("偏好品牌"),channelType:s.optional().describe("渠道类型: public(BOSS直聘) 或 private(微信)"),defaultWechatId:t.string().optional().describe("默认微信号"),industryVoiceId:t.string().optional().describe("行业语调ID"),turnIndex:t.number().int().min(1).optional().describe("当前会话回复轮次"),modelConfig:p.optional().describe("模型配置覆盖")}),output:t.object({suggestedReply:t.string(),confidence:t.number(),stage:d,latencyMs:t.number().optional(),shouldExchangeWechat:t.boolean().optional(),error:t.string().optional(),diagnostics:t.object({subGoals:t.array(t.string()),needs:t.array(c),primaryNeed:c,riskFlags:t.array(g),reasoningText:t.string(),extractedInfo:t.object({mentionedBrand:t.string().nullable(),city:t.string().nullable(),specificAge:t.number().nullable(),hasUrgency:t.boolean().nullable(),preferredSchedule:t.string().nullable()}),ageGate:t.object({enabled:t.boolean(),status:f,strategy:t.string()}),resolvedBrand:t.string(),storeCount:t.number(),detailLevel:l,turnIndex:t.number(),effectiveDisclosureMode:l,replyGateRewritten:t.boolean(),gateViolations:t.array(y),factGateRewritten:t.boolean()}).optional()}),execute:async(e,t)=>{let n;t.logger.info(`Processing message: ${e.candidateMessage.slice(0,50)}...`);try{n=r()}catch{return{suggestedReply:"",confidence:0,stage:"trust_building",error:"品牌数据未配置,请先调用 sync_brand_data 写入数据"}}const o=a(),s=await i({candidateMessage:e.candidateMessage,conversationHistory:e.conversationHistory,candidateInfo:e.candidateInfo,preferredBrand:e.preferredBrand,channelType:e.channelType,defaultWechatId:e.defaultWechatId,industryVoiceId:e.industryVoiceId,turnIndex:e.turnIndex,modelConfig:e.modelConfig,configData:n,replyPolicy:o});t.logger.info(`Reply generated. Stage: ${s.turnPlan.stage}, Confidence: ${s.confidence}`);const l=s.debugInfo;return{suggestedReply:s.suggestedReply,confidence:s.confidence,stage:s.turnPlan.stage,latencyMs:s.latencyMs,shouldExchangeWechat:s.shouldExchangeWechat,error:s.error?.userMessage,diagnostics:l?{subGoals:s.turnPlan.subGoals,needs:s.turnPlan.needs,primaryNeed:s.turnPlan.primaryNeed,riskFlags:s.turnPlan.riskFlags,reasoningText:s.turnPlan.reasoningText,extractedInfo:{mentionedBrand:s.turnPlan.extractedInfo.mentionedBrand??null,city:s.turnPlan.extractedInfo.city??null,specificAge:s.turnPlan.extractedInfo.specificAge??null,hasUrgency:s.turnPlan.extractedInfo.hasUrgency??null,preferredSchedule:s.turnPlan.extractedInfo.preferredSchedule??null},ageGate:{enabled:l.appliedStrategy.enabled,status:l.gateStatus,strategy:l.appliedStrategy.strategy},resolvedBrand:l.resolvedBrand,storeCount:l.storeCount,detailLevel:l.detailLevel,turnIndex:l.turnIndex,effectiveDisclosureMode:l.effectiveDisclosureMode,replyGateRewritten:s.replyGateRewritten,gateViolations:s.gateViolations,factGateRewritten:s.factGateRewritten}:void 0}}});
@@ -1 +0,0 @@
1
- import{defineTool as t}from"@roll-agent/sdk";import{z as n}from"zod";import{getDulidayToken as r,getDulidayJobListEndpoint as e,fetchAllJobListPages as s,FULL_INCLUDE_OPTIONS as o}from"../services/duliday-api.js";import{getSharedBrandAliasMap as a}from"../services/brand-alias.js";import{convertPositionsToZhipinData as i}from"../services/duliday-mapper.js";import{saveBrandConfig as c}from"../services/config-loader.js";export const syncBrandData=t({name:"sync_brand_data",description:"从 Duliday API 拉取并同步品牌配置数据(门店、岗位、薪资等)到本地。可选传入品牌别名和城市名称作为过滤条件。",input:n.object({brandAlias:n.string().optional().describe("品牌别名(可选,配合 cityName 使用可按品牌过滤)"),cityName:n.string().describe('城市名称(必填,Duliday API 要求至少提供城市作为筛选条件,如 "上海市")')}),output:n.object({success:n.boolean(),brandsCount:n.number(),storesCount:n.number(),positionsCount:n.number(),updatedAt:n.string(),error:n.string().optional()}),execute:async(t,n)=>{const a=r(),u=e();if(!a||!u)return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:`缺少环境变量: ${[!a&&"DULIDAY_TOKEN",!u&&"DULIDAY_JOB_LIST_URL"].filter(Boolean).join(", ")}`};if(!t.cityName)return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:'cityName 为必填项,Duliday API 要求至少提供城市作为筛选条件(如 "上海市")'};try{n.logger.info("Fetching all job list pages from Duliday API...");const{items:r}=await s(a,u,{brandAlias:t.brandAlias??null,cityName:t.cityName??null,include:o});let e=d(r);!e&&t.brandAlias&&(e=await l(t.brandAlias,a)),!e&&t.brandAlias&&(e=t.brandAlias),n.logger.info(`Resolved default brand: ${e??"自动推断"}, converting ${r.length} positions...`);const b=i(r,e,t.cityName);c(b),n.logger.info(`Brand config saved: ${b.brands.length} brands, ${b.brands.reduce((t,n)=>t+n.stores.length,0)} stores`);const g=b.brands.reduce((t,n)=>t+n.stores.length,0),m=b.brands.reduce((t,n)=>t+n.stores.reduce((t,n)=>t+n.positions.length,0),0);return{success:!0,brandsCount:b.brands.length,storesCount:g,positionsCount:m,updatedAt:(new Date).toISOString()}}catch(t){return{success:!1,brandsCount:0,storesCount:0,positionsCount:0,updatedAt:(new Date).toISOString(),error:t instanceof Error?t.message:String(t)}}}});function u(t){return null!==t&&"object"==typeof t&&!Array.isArray(t)}function d(t){for(const n of t){if(!u(n))continue;const t=u(n.basicInfo)?n.basicInfo:null;if(t&&"string"==typeof t.brandName&&t.brandName)return t.brandName}}async function l(t,n){try{const r=await a(n),e=t.trim().toLowerCase().replace(/[\s._-]+/g,"");return r.get(e)}catch{return}}
@@ -1 +0,0 @@
1
- import{z as t}from"zod";import{BrandPriorityStrategySchema as a}from"./config.js";export const BrandResolutionInputSchema=t.object({uiSelectedBrand:t.string().optional(),configDefaultBrand:t.string().optional(),conversationBrand:t.string().optional(),availableBrands:t.array(t.string()),strategy:a});export const BrandMatchTypeSchema=t.enum(["exact","fuzzy","fallback"]);export const BrandSourceSchema=t.enum(["ui","conversation","config","default"]);export const BrandResolutionOutputSchema=t.object({resolvedBrand:t.string(),matchType:BrandMatchTypeSchema,source:BrandSourceSchema,reason:t.string(),originalInput:t.string().optional()});export const FuzzyMatchResultSchema=t.object({brand:t.string(),isExact:t.boolean(),originalInput:t.string()});export const BRAND_RESOLUTION_PRIORITY={"user-selected":["uiSelectedBrand","configDefaultBrand","firstAvailable"],"conversation-extracted":["conversationBrand","uiSelectedBrand","configDefaultBrand","firstAvailable"],smart:["conversationBrand","uiSelectedBrand","configDefaultBrand","firstAvailable"]};export function isBrandPriorityStrategy(t){return a.safeParse(t).success}
@@ -1 +0,0 @@
1
- import{z as o}from"zod";import{ChannelTypeSchema as t}from"./reply-policy.js";export const ProviderConfigSchema=o.object({name:o.string(),baseURL:o.string(),description:o.string()});export const ProviderConfigsSchema=o.record(o.string(),ProviderConfigSchema);export const ModelConfigSchema=o.object({chatModel:o.string().optional(),classifyModel:o.string().optional(),replyModel:o.string().optional(),providerConfigs:ProviderConfigsSchema.optional()});export const BrandDataSchema=o.object({city:o.string().optional(),defaultBrand:o.string(),availableBrands:o.array(o.string()),storeCount:o.number()});export const ClassificationOptionsSchema=o.object({modelConfig:ModelConfigSchema,candidateMessage:o.string(),conversationHistory:o.array(o.string()).default([]),brandData:BrandDataSchema.optional(),channelType:t.optional()});
@@ -1 +0,0 @@
1
- import{z as e}from"zod";export const BrandPriorityStrategySchema=e.enum(["user-selected","conversation-extracted","smart"]);
@@ -1 +0,0 @@
1
- import{z as l}from"zod";export const DulidayMoreWelfareItemSchema=l.object({content:l.string(),image:l.string().nullable().optional()}).passthrough();export const DulidayStoreInfoSchema=l.object({storeId:l.number().optional(),storeName:l.string(),storeCityName:l.string().optional(),storeRegionName:l.string().optional(),storeAddress:l.string().optional(),longitude:l.number().optional(),latitude:l.number().optional()}).passthrough();export const DulidayBasicInfoSchema=l.object({jobId:l.number(),jobName:l.string(),jobContent:l.string().nullable().optional(),brandId:l.number().optional(),brandName:l.string().optional(),projectId:l.number().optional(),projectName:l.string().optional(),storeInfo:DulidayStoreInfoSchema.optional()}).passthrough();export const DulidaySalaryScenarioSchema=l.object({salaryType:l.string().nullable().optional(),salaryPeriod:l.string().nullable().optional(),hasStairSalary:l.string().nullable().optional(),basicSalary:l.object({basicSalary:l.number().nullable().optional(),basicSalaryUnit:l.string().nullable().optional()}).passthrough().nullable().optional(),stairSalaries:l.array(l.object({fullWorkTime:l.number().nullable().optional(),fullWorkTimeUnit:l.string().nullable().optional(),salary:l.number().nullable().optional(),salaryUnit:l.string().nullable().optional()}).passthrough()).nullable().optional(),comprehensiveSalary:l.object({minComprehensiveSalary:l.number().nullable().optional(),maxComprehensiveSalary:l.number().nullable().optional(),comprehensiveSalaryUnit:l.string().nullable().optional()}).passthrough().nullable().optional(),holidaySalary:l.object({holidaySalaryType:l.string().nullable().optional(),holidaySalaryMultiple:l.number().nullable().optional(),holidayFixedSalary:l.number().nullable().optional(),holidayFixedSalaryUnit:l.string().nullable().optional()}).passthrough().nullable().optional()}).passthrough();export const DulidayJobSalarySchema=l.object({salary:l.number().optional(),salaryUnitStr:l.string().optional(),salaryScenarioList:l.array(DulidaySalaryScenarioSchema).nullable().optional()}).passthrough();export const DulidayNewWelfareSchema=l.object({haveInsurance:l.string(),accommodation:l.string(),catering:l.string().optional(),otherWelfare:l.array(l.string()).nullable().optional(),accommodationAllowance:l.number().nullable().optional(),accommodationAllowanceUnit:l.string().nullable().optional(),cateringSalary:l.number().nullable().optional(),cateringSalaryUnit:l.string().nullable().optional(),trafficAllowanceSalary:l.number().nullable().optional(),trafficAllowanceSalaryUnit:l.string().nullable().optional(),memo:l.string().nullable().optional(),promotionWelfare:l.string().nullable().optional(),moreWelfares:l.array(DulidayMoreWelfareItemSchema).nullable().optional()}).passthrough();export const DulidayBasicPersonalRequirementsSchema=l.object({minAge:l.number().nullable().optional(),maxAge:l.number().nullable().optional(),genderRequirement:l.string().nullable().optional()}).passthrough();export const DulidayCertificateSchema=l.object({education:l.string().nullable().optional(),healthCertificate:l.string().nullable().optional()}).passthrough();export const DulidayHiringRequirementSchema=l.object({cooperationMode:l.number().optional(),requirementNum:l.number().optional(),thresholdNum:l.number().optional(),signUpNum:l.number().nullable().optional(),basicPersonalRequirements:DulidayBasicPersonalRequirementsSchema.nullable().optional(),certificate:DulidayCertificateSchema.nullable().optional()}).passthrough();export const DulidayNewWorkTimeSchema=l.object({employmentForm:l.string(),minWorkMonths:l.number().nullable().optional(),maxWorkTakingTime:l.number().nullable().optional(),restTimeDesc:l.string().nullable().optional(),workTimeRemark:l.string().nullable().optional(),employmentDescription:l.string().nullable().optional(),weekWorkTime:l.object({weekWorkTimeRequirement:l.string().nullable().optional(),perWeekWorkDays:l.number().nullable().optional(),perWeekRestDays:l.number().nullable().optional(),perWeekNeedWorkDays:l.union([l.string(),l.number()]).nullable().optional(),workSingleDouble:l.string().nullable().optional(),customnWorkTimeList:l.array(l.object({customMinWorkDays:l.number().nullable().optional(),customMaxWorkDays:l.number().nullable().optional(),customWorkWeekdays:l.array(l.union([l.string(),l.number()])).nullable().optional()}).passthrough()).nullable().optional()}).passthrough().nullable().optional(),monthWorkTime:l.object({perMonthMinWorkTime:l.number().nullable().optional(),perMonthMinWorkTimeUnit:l.union([l.string(),l.number()]).nullable().optional(),monthWorkTimeRequirement:l.string().nullable().optional(),perMonthMaxRestTime:l.number().nullable().optional(),perMonthMaxRestTimeUnit:l.number().nullable().optional()}).passthrough().nullable().optional(),dayWorkTime:l.object({perDayMinWorkHours:l.union([l.string(),l.number()]).nullable().optional(),dayWorkTimeRequirement:l.string().nullable().optional()}).passthrough().nullable().optional(),dailyShiftSchedule:l.object({arrangementType:l.string().nullable().optional(),fixedScheduleList:l.array(l.object({fixedShiftStartTime:l.union([l.string(),l.number()]).optional(),fixedShiftEndTime:l.union([l.string(),l.number()]).optional(),startTime:l.number().optional(),endTime:l.number().optional()}).passthrough()).nullable().optional(),combinedArrangement:l.array(l.object({CombinedArrangementWeekdays:l.union([l.string(),l.array(l.number())]).optional(),CombinedArrangementStartTime:l.number().optional(),CombinedArrangementEndTime:l.number().optional(),startTime:l.number().optional(),endTime:l.number().optional(),weekdays:l.array(l.number()).optional()}).passthrough()).nullable().optional(),fixedTime:l.object({goToWorkStartTime:l.union([l.string(),l.number()]).nullable().optional(),goToWorkEndTime:l.union([l.string(),l.number()]).nullable().optional(),goOffWorkStartTime:l.union([l.string(),l.number()]).nullable().optional(),goOffWorkEndTime:l.union([l.string(),l.number()]).nullable().optional()}).passthrough().nullable().optional()}).passthrough().nullable().optional(),temporaryEmployment:l.object({temporaryEmploymentStartTime:l.string().nullable().optional(),temporaryEmploymentEndTime:l.string().nullable().optional()}).passthrough().nullable().optional()}).passthrough();export const DulidayNewPositionSchema=l.object({basicInfo:DulidayBasicInfoSchema,jobSalary:DulidayJobSalarySchema,welfare:DulidayNewWelfareSchema,hiringRequirement:DulidayHiringRequirementSchema,workTime:DulidayNewWorkTimeSchema}).passthrough();
@@ -1 +0,0 @@
1
- import{z as n}from"zod";export const CoordinatesSchema=n.object({lat:n.number(),lng:n.number()});export const CHINA_BOUNDS={minLat:3.86,maxLat:53.55,minLng:73.66,maxLng:135.05};
@@ -1 +0,0 @@
1
- import{z as e}from"zod";export const FunnelStageSchema=e.enum(["trust_building","private_channel","qualify_candidate","job_consultation","interview_scheduling","onboard_followup"]);export const ChannelTypeSchema=e.enum(["public","private"]);export const EffectiveDisclosureModeSchema=e.enum(["minimal","focused"]);export const ReplyFactFamilySchema=e.enum(["salary","schedule","location","policy","requirements","availability"]);export const STAGE_DEFINITIONS={trust_building:{description:"初次接触,建立信任并了解求职意向",transitionSignal:"候选人表达明确兴趣或开始询问具体岗位信息",applicableChannels:["public","private"]},private_channel:{description:"引导用户从公域平台(如BOSS直聘/鱼泡)转入微信私聊",transitionSignal:"候选人有继续深入了解的意愿,适合引导到私域",applicableChannels:["public"]},qualify_candidate:{description:"轻量确认候选人的关键匹配信息,避免审查式盘问",transitionSignal:"候选人表达求职意向后,需要核实基本资格",applicableChannels:["public","private"]},job_consultation:{description:"回答岗位相关问题(薪资、排班、地点等)并提升兴趣",transitionSignal:"候选人主动询问岗位细节",applicableChannels:["public","private"]},interview_scheduling:{description:"推动面试预约,确认时间和到店安排",transitionSignal:"候选人核心问题已解答,准备推进面试",applicableChannels:["public","private"]},onboard_followup:{description:"促进到岗并保持回访",transitionSignal:"候选人确认上岗安排",applicableChannels:["public","private"]}};export const ReplyNeedSchema=e.enum(["stores","location","salary","schedule","policy","availability","requirements","interview","wechat","none"]);export const RiskFlagSchema=e.enum(["insurance_promise_risk","age_sensitive","confrontation_emotion","urgency_high","qualification_mismatch"]);export const PRIMARY_NEED_FACT_MAP={stores:["location"],location:["location"],salary:["salary"],schedule:["schedule"],policy:["policy"],availability:["availability"],requirements:["requirements"],interview:[],wechat:[],none:[]};export const TurnExtractedInfoSchema=e.object({mentionedBrand:e.string().nullable(),city:e.string().nullable(),mentionedLocations:e.array(e.object({location:e.string(),confidence:e.number().min(0).max(1)})).nullable(),mentionedDistricts:e.array(e.object({district:e.string(),confidence:e.number().min(0).max(1)})).max(10).nullable(),specificAge:e.number().nullable(),hasUrgency:e.boolean().nullable(),preferredSchedule:e.string().nullable()});export const TurnPlanSchema=e.object({stage:FunnelStageSchema,subGoals:e.array(e.string()).max(2),needs:e.array(ReplyNeedSchema).max(8),primaryNeed:ReplyNeedSchema,riskFlags:e.array(RiskFlagSchema).max(6),confidence:e.number().min(0).max(1),extractedInfo:TurnExtractedInfoSchema,reasoningText:e.string()});export const StageGoalPolicySchema=e.object({description:e.string().optional(),primaryGoal:e.string(),successCriteria:e.array(e.string()),ctaStrategy:e.preprocess(e=>Array.isArray(e)?e.join("\n"):e,e.string()),disallowedActions:e.array(e.string()).optional()});export const PersonaPolicySchema=e.object({tone:e.string(),warmth:e.string(),humor:e.string(),length:e.enum(["short","medium","long"]),questionStyle:e.string(),empathyStrategy:e.string(),addressStyle:e.string(),professionalIdentity:e.string(),companyBackground:e.string()});export const IndustryVoicePolicySchema=e.object({name:e.string(),industryBackground:e.string(),jargon:e.array(e.string()),styleKeywords:e.array(e.string()),tabooPhrases:e.array(e.string()),guidance:e.array(e.string())});export const HardConstraintRuleSchema=e.object({id:e.string(),rule:e.string(),severity:e.enum(["high","medium","low"])});export const HardConstraintsPolicySchema=e.object({rules:e.array(HardConstraintRuleSchema)});export const FactGatePolicySchema=e.object({mode:e.enum(["strict","balanced","open"]),verifiableClaimTypes:e.array(e.string()),fallbackBehavior:e.enum(["generic_answer","ask_followup","handoff"]),forbiddenWhenMissingFacts:e.array(e.string())});export const DEFAULT_OUTPUT_GUARDS={maxQuestionsByMode:{minimal:1,focused:2},blockedAuditPhrases:["是否满足","是否符合","基本入职要求","先确认资格","年龄是否符合"],blockFirstTurnSpecificFacts:!0};export const OutputGuardsPolicySchema=e.object({maxQuestionsByMode:e.object({minimal:e.number().int().min(0),focused:e.number().int().min(0)}),blockedAuditPhrases:e.array(e.string()),blockFirstTurnSpecificFacts:e.boolean()});export const AgeQualificationPolicySchema=e.object({enabled:e.boolean().default(!0),revealRange:e.boolean().default(!1),failStrategy:e.string().default("礼貌说明不匹配,避免承诺"),unknownStrategy:e.string().default("先核实年龄或资格条件"),passStrategy:e.string().default("确认匹配后推进下一步"),allowRedirect:e.boolean().default(!0),redirectPriority:e.enum(["low","medium","high"]).default("medium")});export const QualificationPolicySchema=e.object({age:AgeQualificationPolicySchema.default({enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"})}).default({age:{enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"}});export const StageGoalsSchema=e.object({trust_building:StageGoalPolicySchema,private_channel:StageGoalPolicySchema.optional(),qualify_candidate:StageGoalPolicySchema,job_consultation:StageGoalPolicySchema,interview_scheduling:StageGoalPolicySchema,onboard_followup:StageGoalPolicySchema}).transform(e=>({...e,private_channel:e.private_channel??e.trust_building}));export const ReplyPolicyConfigSchema=e.object({stageGoals:StageGoalsSchema,persona:PersonaPolicySchema,industryVoices:e.record(e.string(),IndustryVoicePolicySchema),defaultIndustryVoiceId:e.string(),hardConstraints:HardConstraintsPolicySchema,factGate:FactGatePolicySchema,qualificationPolicy:QualificationPolicySchema,outputGuards:OutputGuardsPolicySchema.default(DEFAULT_OUTPUT_GUARDS)});export const DEFAULT_REPLY_POLICY={stageGoals:{trust_building:{description:"初次接触,建立信任并了解求职意向",primaryGoal:"建立信任并了解求职意向",successCriteria:["候选人愿意继续沟通"],ctaStrategy:"用轻量提问引导需求细化",disallowedActions:["过早承诺具体待遇"]},private_channel:{description:"引导用户从公域平台(如BOSS直聘/鱼泡)转入微信私聊",primaryGoal:"推动进入私域沟通",successCriteria:["候选人愿意交换联系方式"],ctaStrategy:"说明后续沟通效率与资料同步价值",disallowedActions:["强迫式要微信"]},qualify_candidate:{description:"轻量确认候选人的关键匹配信息,避免审查式盘问",primaryGoal:"确认一个关键匹配信息并保持继续沟通意愿",successCriteria:["明确一个关键匹配信息","候选人愿意继续沟通"],ctaStrategy:"先回应关切,再顺带确认一个最关键条件",disallowedActions:["连续盘问多个资格条件","直接否定候选人"]},job_consultation:{description:"回答岗位相关问题(薪资、排班、地点等)并提升兴趣",primaryGoal:"回答岗位问题并提升兴趣",successCriteria:["候选人对岗位保持兴趣"],ctaStrategy:"先答核心问题,再给下一步建议",disallowedActions:["编造数字或政策"]},interview_scheduling:{description:"推动面试预约,确认时间和到店安排",primaryGoal:"推动面试预约",successCriteria:["候选人给出可面试时间"],ctaStrategy:"给出明确时间选项并确认",disallowedActions:["不确认候选人可到店性"]},onboard_followup:{description:"促进到岗并保持回访",primaryGoal:"促进到岗并保持回访",successCriteria:["候选人确认上岗安排"],ctaStrategy:"明确下一步动作与提醒",disallowedActions:["承诺不确定资源"]}},persona:{tone:"口语化",warmth:"高",humor:"低",length:"short",questionStyle:"单轮一个关键问题",empathyStrategy:"先认可关切再给建议",addressStyle:"使用你",professionalIdentity:"资深招聘专员",companyBackground:"连锁餐饮招聘"},industryVoices:{default:{name:"餐饮连锁招聘",industryBackground:"门店密集、排班灵活、强调稳定出勤",jargon:["排班","到岗","门店","班次"],styleKeywords:["直接","清晰","可信"],tabooPhrases:["包过","绝对","随便都行"],guidance:["先解决顾虑,再推动下一步"]}},defaultIndustryVoiceId:"default",hardConstraints:{rules:[{id:"no-fabrication",rule:"不得编造门店、薪资、排班、福利等事实信息",severity:"high"},{id:"no-insurance-promise",rule:"兼职场景不得承诺五险一金",severity:"high"},{id:"age-sensitive",rule:"年龄敏感问题使用合规话术,不暴露内部筛选线",severity:"high"}]},factGate:{mode:"strict",verifiableClaimTypes:["salary","location","schedule","policy","availability"],fallbackBehavior:"generic_answer",forbiddenWhenMissingFacts:["具体数字","具体门店承诺","明确福利承诺"]},qualificationPolicy:{age:{enabled:!0,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!0,redirectPriority:"medium"}},outputGuards:DEFAULT_OUTPUT_GUARDS};
@@ -1 +0,0 @@
1
- import{z as e}from"zod";import{CoordinatesSchema as t}from"./geocoding.js";export const CandidateInfoSchema=e.object({name:e.string().optional(),position:e.string().optional(),expectedPosition:e.string().optional(),communicationPosition:e.string().optional(),age:e.string().optional(),gender:e.string().optional(),experience:e.string().optional(),education:e.string().optional(),expectedSalary:e.string().optional(),expectedLocation:e.string().optional(),jobAddress:e.string().optional(),height:e.string().optional(),weight:e.string().optional(),healthCertificate:e.boolean().optional(),activeTime:e.string().optional(),info:e.array(e.string()).optional(),fullText:e.string().optional()});export const SalaryDetailsSchema=e.object({base:e.number(),range:e.string().optional(),bonus:e.string().optional(),memo:e.string(),scenarioSummary:e.string().optional(),settlementCycle:e.string().optional()});export const BenefitsSchema=e.object({items:e.array(e.string()),promotion:e.string().optional()});export const AttendanceRequirementSchema=e.object({requiredDays:e.array(e.number().min(1).max(7)).optional(),minimumDays:e.number().min(0).optional(),description:e.string()});export const ScheduleTypeSchema=e.enum(["fixed","flexible","rotating","on_call"]);export const AttendancePolicySchema=e.object({punctualityRequired:e.boolean(),lateToleranceMinutes:e.number().min(0),attendanceTracking:e.enum(["strict","flexible","none"]),makeupShiftsAllowed:e.boolean()});export const TimeSlotAvailabilitySchema=e.object({slot:e.string(),maxCapacity:e.number().min(0),currentBooked:e.number().min(0),isAvailable:e.boolean(),priority:e.enum(["high","medium","low"])});export const SchedulingFlexibilitySchema=e.object({canSwapShifts:e.boolean(),advanceNoticeHours:e.number().min(0),partTimeAllowed:e.boolean(),weekendRequired:e.boolean(),holidayRequired:e.boolean()});export const HiringRequirementsSchema=e.object({minAge:e.number().nullable().optional(),maxAge:e.number().nullable().optional(),genderRequirement:e.string().nullable().optional(),education:e.string().nullable().optional(),healthCertificate:e.string().nullable().optional()});export const PositionSchema=e.object({id:e.string(),name:e.string(),brandId:e.string().optional(),brandName:e.string().optional(),projectId:e.string().optional(),projectName:e.string().optional(),timeSlots:e.array(e.string()),salary:SalaryDetailsSchema,workHours:e.string(),benefits:BenefitsSchema,requirements:e.array(e.string()),urgent:e.boolean(),scheduleType:ScheduleTypeSchema,attendancePolicy:AttendancePolicySchema,availableSlots:e.array(TimeSlotAvailabilitySchema),schedulingFlexibility:SchedulingFlexibilitySchema,minHoursPerWeek:e.number().min(0).optional(),maxHoursPerWeek:e.number().min(0).optional(),attendanceRequirement:AttendanceRequirementSchema.optional(),hiringRequirements:HiringRequirementsSchema.optional(),description:e.string().optional()});export const StoreSchema=e.object({id:e.string(),brandId:e.string(),name:e.string(),city:e.string().optional(),location:e.string(),district:e.string(),subarea:e.string(),coordinates:t,positions:e.array(PositionSchema)});export const BrandDatasetMetaSchema=e.object({defaultBrandId:e.string().optional(),syncedAt:e.string().optional(),source:e.string().optional()});export const BrandSchema=e.object({id:e.string(),name:e.string(),aliases:e.array(e.string()).optional(),stores:e.array(StoreSchema)});export const ZhipinDataSchema=e.object({meta:BrandDatasetMetaSchema,brands:e.array(BrandSchema)});