CouchFilter:用一句提示詞搜尋 19,426 個國考職位
I Stopped Opening the Spreadsheet: Asking 19,426 Job Posts One Lazy Sentence
作者打造 CouchFilter,讓用戶用一句中文查詢從 19,426 個國考職位中取得排序清單。工具以本機運行的開放權重模型 Gemma 3 1B 將查詢轉成結構化篩選條件,再由 Python 確定性地篩選及排序,不會將職位表傳送至雲端 API。
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
DEV Hacktoberfest Week 3 — "Slow and Steady". Built this week, from scratch, with an open-weight model (Gemma 3 1B, running locally).
What I Built
The most boring week of my October
Every year, China's national civil-service exam (国考) publishes its position table: 19,426 rows of departments, quotas, majors, degree requirements, locations, and footnotes. To find posts I could actually apply for, I spent a week scrolling, Excel-filtering, and re-checking constraints — degree, major, English certificates, party membership, grassroots experience, "remote western region" postings.
With my AI assistant's help I got it down to 23 genuinely eligible posts. And then I noticed something: every remaining question I had was a one-liner.
英语专业,不要四六级,招3人以上,太原的岗
English major, no CET-4/6 requirement, 3+ openings, in Taiyuan招4个人的税务局岗
Tax bureau posts hiring 4 people提供宿舍的岗
Posts that provide a dormitory
Each of those used to mean: open the spreadsheet, remember which column holds what, apply three filters, squint. So I built the laziest possible interface: type one sentence, get a ranked shortlist.
Demo
Meet CouchFilter 🛋️
$ python couchfilter_cli.py "英语专业,不要四六级,招3人以上,太原的岗"
问:英语专业,不要四六级,招3人以上,太原的岗
(Gemma 本地解析)
命中 1 个岗(按招考人数排序):
1. [300110021004] 国家税务总局清徐县税务局|一级行政执法员(四)|招 3 人
地点:山西省太原市清徐县|备注:男性,在本单位最低服务年限为5年
Or open the tiny web UI and ask from the couch:
python serve.py → http://127.0.0.1:8000 — one input box, one button, ranked cards.
Code
Repo: https://github.com/986036734/couchfilter
How I Built It
How it works (and where the model earns its keep)
The pipeline is deliberately split in two:
Fuzzy part → Gemma 3 1B (open weights), running locally via Ollama.
It turns one Chinese sentence into structured filters — a JSON object with location, minimum headcount, major, excluded terms, dormitory requirement, degree. Nothing leaves the machine; the position table never touches a cloud API.Exact part → plain Python.
Filters are applied deterministically over the spreadsheet, so every result is reproducible and every rejection comes with a reason ("只招 2 人(要 ≥3)", "备注未写提供宿舍").
Why the split? A 1B model is great at understanding "招3人以上" means min_headcount: 3, but I don't want it deciding my career shortlist from vibes. The model translates; the code filters. If Ollama is down, a rule-based parser takes over and says so honestly.
Ranking is laziness-first: more openings first (better odds per application), then everything else.
Total new code: ~400 lines. Dependencies: openpyxl, requests, and a local model. The web UI is Python stdlib only.
Try it on your own table:
pip install -r requirements.txt
ollama pull gemma3:1b
python couchfilter_cli.py "你的问题" --table your-positions.xlsx
Any 国考-style table works if the column layout matches (the C_* constants in couchfilter.py are the whole schema).
Why Does Open Innovation Matter?
CouchFilter is built around an open-weight model, Gemma 3 1B, running locally via Ollama. Because the model runs locally, nothing leaves the machine and the position table never touches a cloud API. The model does the fuzzy language part, and deterministic code does the filtering, so every result is reproducible and every rejection comes with a reason. If Ollama is down, a rule-based parser takes over and says so honestly.
The Lazy Payoff
The "slow and steady" philosophy
The theme this week is rewarding laziness, and I took it literally. The laziest query wins: no column names to memorize, no filter dialogs, no "wait, which sheet was it". The tool even tells you what it excluded and why, so you can loosen one constraint instead of starting over.
Prize Categories
- Best Use of Gemma
Built 2026-10-24 for DEV Hacktoberfest Week 3. The 23-post shortlist is public recruitment data; the week of manual screening that produced it is a story for another post.
來源:DEV Community · dev.to