beacon-mfg-mcp 1.12.2 → 1.12.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.
- package/README.md +13 -0
- package/package.json +1 -1
- package/server.py +56 -19
package/README.md
CHANGED
|
@@ -60,6 +60,19 @@ linked_entity 温州市亿鸿装饰工程有限公司 ← 营业执照
|
|
|
60
60
|
登记机关),只有政府公开数据来源的主体才有,且**只读不改**;`linked_entity` 是**场所与主体的
|
|
61
61
|
关联边**,可撤销、可人工修正。
|
|
62
62
|
|
|
63
|
+
## 工艺证据等级(search_vendors 的 `process_evidence`)
|
|
64
|
+
|
|
65
|
+
工艺码有**三种来源**,混为一谈会把「这一类大概都做」当成「这家确实做」:
|
|
66
|
+
|
|
67
|
+
| 值 | 含义 | 该不该当事实 |
|
|
68
|
+
|----|------|--------------|
|
|
69
|
+
| `null` | 能力卡 / 企业自述登记的事实工艺(或类默认但只有 1~3 个码) | 可当事实,仍建议 `get_capability_card` 复核 |
|
|
70
|
+
| `inferred:A1` ~ `inferred:B0` | 零采集推断,字母是证据分档(A1 三路一致 … B0 单路有信号) | 推断不是核实,严苛场景要追问 |
|
|
71
|
+
| `class_default` | 该记录挂了 ≥4 个「国标小类默认工艺码」,是同类企业的众数,**不是这家企业的工艺事实** | ❌ 必须降权,不要据此下单 |
|
|
72
|
+
|
|
73
|
+
`class_default` 覆盖约 1.3 万条。工艺码一个没删(按工艺检索的召回不受影响),
|
|
74
|
+
只是把证据等级如实标出来 —— 让 Agent 少做一轮二次筛选。
|
|
75
|
+
|
|
63
76
|
## 身份与额度(不需要注册)
|
|
64
77
|
|
|
65
78
|
首次运行会在 `~/.beacon-mfg/` 生成一个匿名身份(机器本地 UUID → 单向哈希),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beacon-mfg-mcp",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.3",
|
|
4
4
|
"mcpName": "io.github.eiry16/beacon-mfg-mcp",
|
|
5
5
|
"description": "Read-only MCP server exposing the BeaconMFG灯塔 directory of business entities across ALL GB/T 4754 industry divisions (manufacturing, services, retail, logistics, catering, IT, and more) to any MCP-compatible agent. No signup and no API key: an anonymous identity is generated on first run, phone binding is optional (only needed for L2 capability cards).",
|
|
6
6
|
"keywords": [
|
package/server.py
CHANGED
|
@@ -341,7 +341,7 @@ def _recall_for_sourcing(text: str, top_k: int = 200, pack_id: str | None = None
|
|
|
341
341
|
# 都对齐到这个值,`scripts/check_version.py` 在发布前校验它们没有漂移。
|
|
342
342
|
# 读不到 package.json 时的兜底值 —— 改版本时**必须**和 package.json 一起改,
|
|
343
343
|
# 否则 check_version.py 会报错(这就是它的用途)。
|
|
344
|
-
VERSION_FALLBACK = "1.12.
|
|
344
|
+
VERSION_FALLBACK = "1.12.3"
|
|
345
345
|
|
|
346
346
|
|
|
347
347
|
def _pkg_version() -> str:
|
|
@@ -3275,12 +3275,20 @@ def _rec_summary(rec: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
3275
3275
|
"score": rec.get("sc"),
|
|
3276
3276
|
"has_phone": bool(rec.get("tel")),
|
|
3277
3277
|
"process": rec.get("proc", []),
|
|
3278
|
-
# 工艺来源(2026-11-06
|
|
3279
|
-
#
|
|
3280
|
-
#
|
|
3281
|
-
#
|
|
3282
|
-
#
|
|
3283
|
-
|
|
3278
|
+
# 工艺来源(2026-11-06,2026-11-13 补第三档):
|
|
3279
|
+
# None = 能力卡 / 企业自述 / 类默认但工艺码少于 4 个
|
|
3280
|
+
# "inferred:A1" = 零采集推断,字母是分档(A1 三路一致 99.3% / A2 97.4% /
|
|
3281
|
+
# A3 94.3% / B0 单路 96.0%,见 docs/PROCESS_INFERENCE.md)
|
|
3282
|
+
# "class_default" = 该记录挂了 ≥4 个「国标小类默认工艺码」,不是这家企业
|
|
3283
|
+
# 的事实工艺,只是同类众数。全库 12,988 条属此类(其中
|
|
3284
|
+
# 12,970 条 pv=derived)。必须让 Agent 看得见:否则它会把
|
|
3285
|
+
# 「这一类大概都做」当成「这家确实做」,严苛场景直接下单。
|
|
3286
|
+
# 红线:工艺码一个不动(召回不变),只加证据标签 —— 不编造也不减召回。
|
|
3287
|
+
# 12,988 条里只有 838 条有推断候选,其余 12,150 条没有任何强证据可换。
|
|
3288
|
+
"process_evidence": (
|
|
3289
|
+
"inferred:" + rec["pinf"] if rec.get("pinf")
|
|
3290
|
+
else ("class_default" if (rec.get("pv") == "derived"
|
|
3291
|
+
and len(rec.get("proc") or []) >= 4) else None)),
|
|
3284
3292
|
# 业务能力码(2026-11-06):非制造业门类的「能干什么」,如 fmt_bar 酒吧 /
|
|
3285
3293
|
# edu_vocational 职业技能培训 / con_steel 钢结构。此前它只参与匹配不返回,
|
|
3286
3294
|
# 于是「搜酒吧」的结果里看不出哪家真是酒吧。现在一并返回,agent 可据此
|
|
@@ -4486,11 +4494,27 @@ def search_vendors(query: str = "", city: str = "", gb: str = "",
|
|
|
4486
4494
|
# `alias_scanned` 仍按**记录数**计,与改前口径一致。
|
|
4487
4495
|
if _via_server:
|
|
4488
4496
|
def _alias_recs():
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4497
|
+
codes = list(alias_by_code)[:6]
|
|
4498
|
+
if not codes:
|
|
4499
|
+
return
|
|
4500
|
+
# 2026-11-13 多码列举:一次请求把全部国标码交给 worker。
|
|
4501
|
+
# 旧 worker 不认 gbs:它只按首码 gb 应答、且不回 gbs_supported,
|
|
4502
|
+
# 于是这里退回逐码补发其余码 —— 绝不出现「取首个码漏掉另外两类」。
|
|
4503
|
+
_r = _server_search(q="", city=city, gb=codes, et=et_want,
|
|
4504
|
+
limit=SERVER_SEARCH_WINDOW)
|
|
4505
|
+
done = set()
|
|
4506
|
+
if _r:
|
|
4507
|
+
yield from _r["records"]
|
|
4508
|
+
if _r.get("gbs_supported") and len(codes) > 1:
|
|
4509
|
+
return
|
|
4510
|
+
done.add(codes[0])
|
|
4511
|
+
for _code in codes:
|
|
4512
|
+
if _code in done:
|
|
4513
|
+
continue
|
|
4514
|
+
_r2 = _server_search(q="", city=city, gb=_code, et=et_want,
|
|
4515
|
+
limit=SERVER_SEARCH_WINDOW)
|
|
4516
|
+
if _r2:
|
|
4517
|
+
yield from _r2["records"]
|
|
4494
4518
|
_alias_iter = _alias_recs()
|
|
4495
4519
|
else:
|
|
4496
4520
|
_alias_iter = _records_from_paths(alias_paths or [])
|
|
@@ -4832,7 +4856,7 @@ _SEARCH_PROBE_UNTIL = [0.0]
|
|
|
4832
4856
|
SERVER_SEARCH_WINDOW = 200
|
|
4833
4857
|
|
|
4834
4858
|
|
|
4835
|
-
def _server_search(q: str = "", city: str = "", gb:
|
|
4859
|
+
def _server_search(q: str = "", city: str = "", gb: Any = "", et: int = 0,
|
|
4836
4860
|
limit: int = SERVER_SEARCH_WINDOW,
|
|
4837
4861
|
cap: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
|
4838
4862
|
"""POST /v1/search —— 服务端检索(「按条来拉」的检索面,2026-11-03)。
|
|
@@ -4849,8 +4873,13 @@ def _server_search(q: str = "", city: str = "", gb: str = "", et: int = 0,
|
|
|
4849
4873
|
return None
|
|
4850
4874
|
if time.time() < _SEARCH_PROBE_UNTIL[0]:
|
|
4851
4875
|
return None
|
|
4852
|
-
|
|
4853
|
-
|
|
4876
|
+
# 2026-11-13:gb 可为多个国标码(别名「注塑」→2929/3525/3399)。
|
|
4877
|
+
# 一次请求全交给 worker,别再逐码各发一次 —— 额度与 RTT 都按请求数翻。
|
|
4878
|
+
if isinstance(gb, (list, tuple, set)):
|
|
4879
|
+
gb_list = [str(x).strip() for x in gb if str(x).strip()][:6]
|
|
4880
|
+
else:
|
|
4881
|
+
gb_list = [str(gb).strip()] if str(gb or "").strip() else []
|
|
4882
|
+
if not (str(q or "").strip() or str(city or "").strip() or gb_list or cap):
|
|
4854
4883
|
return None
|
|
4855
4884
|
try:
|
|
4856
4885
|
import identity as _idn
|
|
@@ -4860,8 +4889,11 @@ def _server_search(q: str = "", city: str = "", gb: str = "", et: int = 0,
|
|
|
4860
4889
|
payload: Dict[str, Any] = {"q": str(q or ""), "limit": int(limit), "offset": 0}
|
|
4861
4890
|
if str(city or "").strip():
|
|
4862
4891
|
payload["city"] = str(city).strip()
|
|
4863
|
-
if
|
|
4864
|
-
|
|
4892
|
+
if gb_list:
|
|
4893
|
+
# 旧 worker 只认单码 gb:首码照传,保证线上还没部署新 worker 时
|
|
4894
|
+
# 至少这一个码仍生效;新 worker 见到 gbs 数组会改用多码列举。
|
|
4895
|
+
payload["gb"] = gb_list[0]
|
|
4896
|
+
payload["gbs"] = gb_list
|
|
4865
4897
|
if et:
|
|
4866
4898
|
payload["et"] = int(et)
|
|
4867
4899
|
if cap:
|
|
@@ -4893,7 +4925,8 @@ def _server_search(q: str = "", city: str = "", gb: str = "", et: int = 0,
|
|
|
4893
4925
|
if isinstance(hits, list):
|
|
4894
4926
|
return {"total": int(o.get("total_matched") or 0),
|
|
4895
4927
|
"cap_matched": int(o.get("cap_matched") or 0),
|
|
4896
|
-
"records": hits
|
|
4928
|
+
"records": hits,
|
|
4929
|
+
"gbs_supported": bool(o.get("gbs_supported"))}
|
|
4897
4930
|
continue
|
|
4898
4931
|
except _ue.HTTPError as e:
|
|
4899
4932
|
st[0] = e.code
|
|
@@ -4904,7 +4937,8 @@ def _server_search(q: str = "", city: str = "", gb: str = "", et: int = 0,
|
|
|
4904
4937
|
try:
|
|
4905
4938
|
_o = json.loads(e.read().decode("utf-8"))
|
|
4906
4939
|
if _o.get("error") == "bad_gb":
|
|
4907
|
-
return {"total": 0, "records": []
|
|
4940
|
+
return {"total": 0, "records": [],
|
|
4941
|
+
"gbs_supported": False}
|
|
4908
4942
|
except Exception:
|
|
4909
4943
|
pass
|
|
4910
4944
|
continue
|
|
@@ -5478,6 +5512,9 @@ TOOLS = [
|
|
|
5478
5512
|
"加 radius_km 只留圈内(地标名需调用方先解析成坐标)。"
|
|
5479
5513
|
"返回精简档案(id/名称/城市/区县/国标码/认证等级/"
|
|
5480
5514
|
"工艺/材料/认证/是否含电话/连锁门店数/距离)。"
|
|
5515
|
+
"每条带 process_evidence:null=有事实工艺;inferred:A1~B0=零采集"
|
|
5516
|
+
"推断(分档非核实);class_default=挂的是国标小类默认工艺码(≥4个),"
|
|
5517
|
+
"不是这家企业的事实工艺,严苛场景请降权或追问核实。"
|
|
5481
5518
|
"⚠ 关键词是**字面子串**匹配:用户的口语词若与名录写法不同(如「机加工」"
|
|
5482
5519
|
"vs 归档用的「机械零部件加工」、「面馆」vs「苏式面馆」),命中会偏少。"
|
|
5483
5520
|
"此时不要直接报「没有」——看返回体里的 hint / suggested_gb,"
|