跳转到内容

岗位信息提取规则

处理流水线(pipeline 步骤)从 JD 文字里读出几个事实。词表和阈值都在 pipeline.yaml(随产品发布的文件:把 config/pipeline.yaml 复制到 data/config/pipeline.yaml 再改你的副本),改了之后用 pnpm reprocess 让已入库的岗位按新规则重读(采集到的原文不动)。原则不变:JD 没写的就是 unknown,不推测。

来源给了值就用来源的;来源没给(unknown)时才读文字,规则在 workplace 段:

  • head:标题、地点文字、合同提示,各自单独匹配,比较宽松(“(Remote)”、“Berlin (Onsite)”、“Hybrid – Munich”)。只出现一种方式就用它。
  • text:描述正文,严格(“hybrid work model”、“3 days in the office”、“Home Office möglich”、“100% remote”、#LI-Hybrid)。福利列表里的 “Remote Working Furniture”、“home office stipend”、“100% remote work option during pregnancy” 不算。
  • negation_patterns:同一句里命中前面有否定词(“not available as fully remote”、“we do not offer remote-only roles”)就不算。
  • 同一处出现两种以上方式(“remote 或 hybrid”)保持 unknown,不选边。
  • 带标签或括号的写法也算:Location: Berlin - On-site、Deutschlandweit (Hybrid)、“im Büro oder remote”、“Bürotage”、“Mobilarbeit”、“Office-First-Kultur”。

办公方式会影响硬过滤:remote 的岗位按“欧盟范围远程”判断地点,所以 JD 明说远程但地点在范围外(Manila、Remote - India)会被过滤,写 “Germany” 的远程岗位会通过。

这是德国市场的本地语言步骤:规则来自市场包(config/packs/de/language.yaml);市场包没有这门语言的规则时跳过这一步(见 market-packs.zh.md)。

值 含义 扣分
B1…C2 JD 要求这个等级(“fließend”、“verhandlungssicher”、“gute Deutschkenntnisse”、“German C1”) 按 ai.yaml: scoring.language_penalty
preferred 德语只是加分项(“German is a plus”、“Preferred: Fluent in German”、“Preferred Qualifications” 标题下、“von Vorteil”) 不扣
none JD 明说不需要德语(“No German required”) 不扣
unknown 没提到等级 不扣
  • 加分词(german_optional_patterns)只看离 “German” 不超过 german_optional_max_distance 个字符的,而不是整个分句:LinkedIn 的描述没有标点时,一个“分句”可能是整段话。
  • “加分项”标题(german_preferred_section_patterns,例如 Preferred Qualifications / Nice to have)之后的德语是 preferred,除非遇到别的标题(german_required_section_patterns)或同一句里有 “required / erforderlich”(german_required_patterns)。
  • “German, Spanish, French or Italian are a plus” 这种语言列表:逗号后面全是语言名时,结尾的加分词仍算在 German 头上(german_list_languages)。
  • “C1-Deutschkenntnisse sind von Vorteil, B2-Niveau ist ausreichend”:加分项之后同一句里“等级 + ausreichend / genügt / sufficient / enough”(german_sufficient_patterns)说明这个等级才是最低要求,读作该等级(这个例子读 B2,扣分)。
  • “German citizen”、“German defence company” 这类不是在说语言的词组先被抹掉(german_ignore_patterns)。
  • AI 的 german_level_required 和规则值的交叉检查:不一致时 JD 里明写 CEFR 等级的那个胜出,且只数“要求”(“German C1 is a plus” 里的 C1 不算明写);规则读到 preferred / none 时规则胜出。已打分的岗位在每次打分前按新的规则值重新计算扣分(不调 AI)。

公司名在 company.agency_names,或匹配 agency.company_name_patterns,或标题 / 正文匹配 agency.text_patterns(“on behalf of one of our customers”、“exclusively retained”、“I’m hiring … to join a … company”、“Im Auftrag unseres Kunden”、“recruitment agency”、“Personalberatung”)。雇主声明不要中介的句子(“we do not accept unsolicited CVs from recruitment agencies”,agency.exclude_context_patterns)不算。标记在合并时只增不减,所以全量 pnpm reprocess 会先清掉岗位上的办公方式、德语等级和中介标记再按原文重建。

contract 段:学生 / 实习只看标题和合同提示;劳务派遣、自由职业、定期(fixed_term)、长期(permanent)。顺序:student > temp_agency > freelance > fixed_term > permanent,所以 “Vollzeit, befristet” 是定期。劳务派遣只认 Zeitarbeit / Arbeitnehmerüberlassung 本身:“Personaldienstleister” 只说明发布者是中介,不再算劳务派遣;正文写明是中介代招、直接受雇于客户(contract.direct_hire:“Vertragsart: Personalvermittlung”、“Direktvermittlung”、“im Rahmen der Personalvermittlung”、“direkte Festanstellung bei unserem Kunden”)时,即使中介的公司简介里提到 Zeitarbeit 也不算劳务派遣。这类岗位会保留,靠 agency 打上“中介”标签(agency.text_patterns 里有 Personalvermittlung、Personaldienstleister)。另外补了德英写法:unbefristet、Festanstellung、open-ended contract、Dauerstelle、Mutterschaftsvertretung,以及带标签的 Employment Type: Permanent、Art der Anstellung: Festanstellung。Vollzeit / full-time 说的是工时不是合同期限,不算长期,合同保持 unknown。福利里的 “full-time employees”、“equal employment opportunity” 不算。大多数 JD 根本不写合同类型,所以仍是 unknown。

company.agency_names 补了 Haystack、Rocken、Instaffo、Optimus Search;agency.text_patterns 补了 “hiring on behalf of …”、“Im Kundenauftrag”。

同一岗位在不同来源里公司名写法不同(“SIV.AG” / “SIV.AG - Service für Informationsverarbeitung AG”、“Allianz in Deutschland” / “Allianz Kunde und Markt GmbH”)时,原来只在同一个公司名下找重复,所以漏合并。现在公司名相关(一个是另一个的词前缀,或首词相同)、标题相同、SimHash 距离 ≤ dedupe.cross_company_simhash_max_distance(默认 14,比同公司的 simhash_max_distance 宽,因为 StepStone 和 LinkedIn 的同一份 JD 样板文字不同)才合并。已经入库的重复岗位不会被追溯合并,只对之后新进来的岗位生效。

岗位详情页的来源区每行显示该来源自己写的地点(job_sources.location_label;pnpm reprocess 会补上已有来源的地点)。一个岗位由同一公司 ATS 的多份 posting 合并而成时(Databricks 各办公室),靠它区分每行。

  • trailing_ui_patterns:删掉采集留在末尾的界面文字(“Show more Show less”)。
  • restore:整段没有换行的长描述(Bright Data 的 job_summary)按已知小标题、行内列表符号和句子恢复段落与列表,只加换行,不改任何文字。Bright Data 的 LinkedIn 数据集现在同时请求 job_description_formatted(HTML),有的话优先用它,段落和列表原样保留;老的采集记录没有这个字段,走上面的整理。
终端窗口
pnpm reprocess # 全部通道
pnpm reprocess --channel linkedin-brightdata

(在项目目录运行。)全量运行先清掉岗位上的办公方式、德语等级、中介标记(--channel 时不清,只补充),再把已解析的原文放回待处理,依次跑 pipeline、薪资(用当前解析重读所有岗位的原文并修正已存的薪资,见 signals.zh.md;--channel 时也是全部岗位)、硬过滤、规则信号、薪资信号同步、加分、已打分岗位的德语扣分;不采集、不调 AI。有岗位延迟处理或规则扫描失败时退出码为 1,参数错误为 2。描述变了的岗位:规则信号会重新扫描,AI 信号复核和译文在之后的处理轮里按需重做(会用到 AI 额度)。

本页同步自产品库 Joevonlong/job-radar@2947f47 的 docs/。Job Radar 与任何招聘网站均无关联。代码采用 AGPL-3.0-only 许可。