beacon-mfg-mcp 1.9.0 → 1.10.0
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/RELEASE.md +150 -124
- package/package.json +1 -1
- package/server.py +115 -4
package/RELEASE.md
CHANGED
|
@@ -1,124 +1,150 @@
|
|
|
1
|
-
# Beacon-MFG 只读 MCP 服务 · v1.
|
|
2
|
-
|
|
3
|
-
面向 Agent 的供应商名录检索服务:**只读**,不参与交易、不写入任何数据、不持有密钥。
|
|
4
|
-
|
|
5
|
-
## 本次更新(v1.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
-
|
|
1
|
+
# Beacon-MFG 只读 MCP 服务 · v1.10.0
|
|
2
|
+
|
|
3
|
+
面向 Agent 的供应商名录检索服务:**只读**,不参与交易、不写入任何数据、不持有密钥。
|
|
4
|
+
|
|
5
|
+
## 本次更新(v1.10.0)
|
|
6
|
+
|
|
7
|
+
**一句话:Agent 终于能「把人带到正确的入口」——新增 `vendor_onboarding`,
|
|
8
|
+
用户说「我是某某公司的、想入驻」时,Agent 不再手足无措 —— 但**仍然不代填**。
|
|
9
|
+
|
|
10
|
+
- **新增工具 `vendor_onboarding`**(第 10 个工具):返回一个可直接点开的控制台
|
|
11
|
+
链接(`/console/?id=CN-xxx` 深链)+ 分步说明 + 明确的边界清单。
|
|
12
|
+
`intent` 三档:`claim`(认领已收录企业)/ `register`(库里没有、新建档)/
|
|
13
|
+
`status`(查审核进度)。
|
|
14
|
+
传了 `supplier_id` 会自动带出公司名,Agent 可以说「帮您找到 XX 公司,从这里认领」,
|
|
15
|
+
而不是甩一个 ID 让人自己去搜。
|
|
16
|
+
|
|
17
|
+
- **为什么只给链接、不代填(这是设计,不是没做完)**:
|
|
18
|
+
认领要过短信验证码与法人手机核验。如果让用户把验证码念给 Agent,等于把
|
|
19
|
+
「证明我是这家企业」的凭证交给第三方,还会落进 LLM 上下文与日志。
|
|
20
|
+
工具返回值里直接写了 `boundary` / `what_not_to_do`,约束 Agent 的说法:
|
|
21
|
+
不代办、不索取验证码、不承诺审核结果、不把未认领企业说成「已经属于你」。
|
|
22
|
+
|
|
23
|
+
- **顺带说明平台侧的这次改动**(影响 Agent 该怎么讲):
|
|
24
|
+
认领不再「填个公开的信用代码就算你的」。`/claim/confirm` 现在只提交申请
|
|
25
|
+
(`status=pending`、`can_add_skill=false`),人工复核通过才真正认领。
|
|
26
|
+
所以 Agent **不要**在提交后说「认领成功了」,只能说「申请已提交,等审核」。
|
|
27
|
+
|
|
28
|
+
- 兼容性:仅新增工具与文案,原有 9 个工具的参数、返回结构**无变化**;
|
|
29
|
+
老客户端不受影响。
|
|
30
|
+
|
|
31
|
+
## 本次更新(v1.9.0,已发布)
|
|
32
|
+
|
|
33
|
+
**一句话:坐标检索接入 MCP——「青创港附近的便利店」这类问法,先解析坐标再传
|
|
34
|
+
`lat/lng`,结果按距离排;给 `radius_km` 就只留圈内。地标名解析归调用方
|
|
35
|
+
(MCP 不持地图 API),距离口径归 server。**
|
|
36
|
+
|
|
37
|
+
- **新参数 `lat` / `lng` / `radius_km`**(`search_vendors`):数据来自指纹层
|
|
38
|
+
98.5% 坐标覆盖(2026-10-22 坐标下沉改造的产出),无需重建索引。
|
|
39
|
+
- 只传 `lat/lng`:**距离为第一排序键**(等距才按召回纯度),每条结果带
|
|
40
|
+
`distance_km`(球面直线距离,非路程);无坐标记录 `distance_km=null` 排末位。
|
|
41
|
+
- 加 `radius_km`(上限 500):圈外与无坐标记录剔除——无坐标 = 无法证明在圈内,
|
|
42
|
+
与项目「宁可留空不编造」同源;`total_matched` 变为圈内数,被剔除数见
|
|
43
|
+
`geo_filtered_out`;圈内按召回纯度分档、档内距离升序。
|
|
44
|
+
- 连锁品牌行报**最近门店**距离(`_collapse_brands` 透传),不是代表店。
|
|
45
|
+
- **参数校验全部显式报错**(不静默忽略):只传 lat/lng 之一、经纬度顺序写反
|
|
46
|
+
(超出中国范围 lat 3~54 / lng 73~136)、`radius_km<=0` 或超 500、非数字——
|
|
47
|
+
坐标查询的失败模式必须是显式的,否则 agent 会把「按全城排的结果」当成
|
|
48
|
+
「按距离排的结果」交付。
|
|
49
|
+
- **0 条时的 hint 换了说法**:圈内 0 条不再建议「改查 gb= 重试」(换什么码圈内
|
|
50
|
+
都还是没有),改为明示「检索命中 N 条但都超出半径/无坐标」,并要求 agent
|
|
51
|
+
**不要对用户说「这一带没有」**——圈内无收录 ≠ 这一带真的没有。
|
|
52
|
+
- 返回体新增 `geo` 回显(lat/lng/radius_km/records_with_coords),agent 可核对
|
|
53
|
+
服务端确实按所给坐标处理;`order_note` 动态追加坐标口径说明。
|
|
54
|
+
|
|
55
|
+
## 本次更新(v1.8.0,已发布)
|
|
56
|
+
|
|
57
|
+
**一句话:口语词不再对不上字——「律所」「饭店」「保洁」直接命中,完全没听过的词
|
|
58
|
+
也会收到货架初筛候选,语义归一这一步正式交给 LLM,但候选来源归 server。**
|
|
59
|
+
|
|
60
|
+
- **L1 高频俗称表**(+151 条):律所→7231 律师及相关法律服务、饭店→6210 正餐服务、
|
|
61
|
+
奶茶→6231/6232、保洁公司→8010/8219、开锁→8290……覆盖餐饮、居民服务、房产、医疗、
|
|
62
|
+
金融、法律、制造、软件、运输、零售、文娱、专业服务十二个方向。每条码都经
|
|
63
|
+
`gb-index.json` 校验 `records>0`——货架上没有的类不上表。
|
|
64
|
+
- **L2 类名机械简称**(+937 条):由 `scripts/gen_class_abbr.py` 从 GB/T 4754 全量类名
|
|
65
|
+
切短生成(法律服务、正餐、快递、网吧、歌舞厅……),作为 alias 机制第三层
|
|
66
|
+
(data > curated > abbr,setdefault 保序,冲突自动让位前两层)。
|
|
67
|
+
- **L3 零命中货架兜底**(`_shelf_recall`):查询词与名录写法完全对不上字时,返回体
|
|
68
|
+
`suggested_gb` 直接带上按 bigram 重合度初筛的货架候选(全灭档启用类名首字单字回溯),
|
|
69
|
+
附 `records` 与门类路径;agent 零额外轮次改传 `gb=`。字面零关联时给的是货架 records
|
|
70
|
+
最高榜,why 字段明码标注「仅供语义参考,不对味就如实告诉用户没收录」。
|
|
71
|
+
- **性能修复(线上已部署)**:Pages 索引落后于数据导致每次查询回退全量扫 460 个分片
|
|
72
|
+
(实测 153.5 秒),已重跑部署修复;索引新鲜时查询回到 0.2~1 秒。
|
|
73
|
+
- **部署洁净度**:`data/` 下一级 `_` 开头的内部产物(剪枝备份等,313 MiB)不再打进
|
|
74
|
+
Pages 发布面。
|
|
75
|
+
|
|
76
|
+
## 本次更新(v1.7.0,已发布)
|
|
77
|
+
|
|
78
|
+
**一句话:从「工厂名录」升级为「全部国标行业门类的主体名录」——服务业、
|
|
79
|
+
流通业主体现在是一等公民,不再被当成「非制造商」反向排除。**
|
|
80
|
+
|
|
81
|
+
- **新增 `etype` 参数**(`生产` / `流通` / `服务`),替换已下线的 `manufacturer_only`。
|
|
82
|
+
按国标门类推导:生产 = A/B/C/D/E(含采矿、电力、建筑),流通 = F/G,服务 = H~S。
|
|
83
|
+
旧的布尔开关在全行业口径下会把采矿/电力/建筑这些生产型主体也排掉,所以直接下线,
|
|
84
|
+
不做兼容层;传非法值返回 `bad_etype` 而不是静默忽略。
|
|
85
|
+
- **修复 `etype` 只筛计数不筛结果**:别名召回与能力召回两条路径绕过了统一过滤点,
|
|
86
|
+
表现为「`total_matched` 跟着 etype 变、`results` 却原样返回」。
|
|
87
|
+
- **能力命中提到首档排序**:查询词是能力别名时(「医院」「银行」「药店」「物流」),
|
|
88
|
+
主体本身干这个的排在「名字里恰好有这个词」的前面。此前搜「医院」前 10 条全是
|
|
89
|
+
「XX酒店(市人民医院店)」。
|
|
90
|
+
- **修复一词多码被单域抢占**:`物流` 原来只映射到零售域的 `rdm_freight`,物流服务域的
|
|
91
|
+
`log_freight` 永不命中;改为一词多码后按域并集召回。
|
|
92
|
+
- **`_name_fit` 增加连片命中分档**:关键词在品牌名里连续出现(「贵州启谋法律顾问部」)
|
|
93
|
+
得 2 分,散落命中得 1 分,缓解同名门店压过本体的老问题。
|
|
94
|
+
- 数据侧同步覆盖到 B/D/G/J/K/L/N/Q 八个原本空白门类(采矿、电力热力、住宿餐饮之外
|
|
95
|
+
的交通运输、信息服务、金融、租赁商务、科研技术、水利环境、居民服务等)。
|
|
96
|
+
|
|
97
|
+
## 本次更新(v1.6.0,已发布)
|
|
98
|
+
|
|
99
|
+
- **新增 `account` 工具**(`status` / `request` / `confirm` / `logout`):匿名档查额度、
|
|
100
|
+
绑手机号升 `verified`、解绑回匿名。此前没有任何升级入口,verified 档等于空档。
|
|
101
|
+
- **新增 CLI**:`npx beacon-mfg-mcp login | status | logout`。MCP 客户端里能用工具,
|
|
102
|
+
终端里也能自助——两条路并存,覆盖「还没接 agent 就先用起来」的场景。
|
|
103
|
+
- **修复**:所有身份请求现在显式带 `User-Agent`。此前 urllib 默认 UA 会被 Cloudflare
|
|
104
|
+
以 `error code 1010` 拒绝,且失败是**静默降级**的,看起来像「网络不好」。
|
|
105
|
+
- **修复**:`identity.py` 补进 `package.json` 的 `files`。此前 npm 包里根本没有这个文件,
|
|
106
|
+
装完即降级为「无身份」——服务端开强制鉴权后所有 npx 用户会集体 401。
|
|
107
|
+
- 服务端同步:匿名 200 次 / verified 2000 次(滚动 24h),L2 能力卡另有 300 条/日上限。
|
|
108
|
+
|
|
109
|
+
## 九个 tool(8 只读 + 1 身份自助)
|
|
110
|
+
|
|
111
|
+
| tool | 用途 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `search_vendors` | 按关键词 / 城市 / 国标码检索 |
|
|
114
|
+
| `get_vendor` | 按 id 取完整档案 |
|
|
115
|
+
| `get_capability_card` | 按 id 取能力卡 |
|
|
116
|
+
| `suggest_filters` | 把口语需求映射成候选国标小类码 |
|
|
117
|
+
| `list_industries` | 浏览有数据的国标小类货架 |
|
|
118
|
+
| `start_sourcing` / `answer_sourcing` / `refine_sourcing` | 多轮采购寻源 |
|
|
119
|
+
| `account` | 查额度 / 绑手机号升 verified / 解绑(`action`) |
|
|
120
|
+
|
|
121
|
+
## 安装
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm i -g beacon-mfg-mcp
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Windows 客户端配置需经 `cmd /c`:
|
|
128
|
+
|
|
129
|
+
```jsonc
|
|
130
|
+
{ "mcpServers": { "beacon-mfg": { "command": "cmd", "args": ["/c", "beacon-mfg-mcp"] } } }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
macOS / Linux:
|
|
134
|
+
|
|
135
|
+
```jsonc
|
|
136
|
+
{ "mcpServers": { "beacon-mfg": { "command": "beacon-mfg-mcp", "args": [] } } }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
也可直接用本 Release 的 tarball 解压运行:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python server.py # stdio
|
|
143
|
+
python http_server.py # Streamable HTTP,端点 /mcp
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
数据默认取自公开数据源,无需密钥、无需克隆仓库。
|
|
147
|
+
|
|
148
|
+
## 本次更新
|
|
149
|
+
|
|
150
|
+
- 稳定性与接入体验改进。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beacon-mfg-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"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).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
package/server.py
CHANGED
|
@@ -289,9 +289,9 @@ def _pkg_version() -> str:
|
|
|
289
289
|
try:
|
|
290
290
|
p = os.path.join(os.path.dirname(os.path.abspath(__file__)), "package.json")
|
|
291
291
|
with open(p, "r", encoding="utf-8") as f:
|
|
292
|
-
return json.load(f).get("version", "1.
|
|
292
|
+
return json.load(f).get("version", "1.10.0")
|
|
293
293
|
except Exception:
|
|
294
|
-
return "1.
|
|
294
|
+
return "1.10.0"
|
|
295
295
|
|
|
296
296
|
|
|
297
297
|
# HTTP 基址列表(含兜底镜像)。默认部署 Cloudflare Pages 不可达时,自动回退到
|
|
@@ -3073,7 +3073,8 @@ def search_vendors(query: str = "", city: str = "", gb: str = "",
|
|
|
3073
3073
|
"order_note": ("results 按「关键词在企业名里成片出现」优先排列;同一品牌的连锁门店"
|
|
3074
3074
|
"已折成一行(`branches` = 门店数,`branch_ids` 前 20 个)。"
|
|
3075
3075
|
"score 是**能力画像分**(有已发布能力卡才有分,无卡为 0),"
|
|
3076
|
-
"**不是相关度**,不要拿它反推排序。")
|
|
3076
|
+
"**不是相关度**,不要拿它反推排序。")
|
|
3077
|
+
+ (_geo_note(rad) if lat_v is not None else ""),
|
|
3077
3078
|
# 坐标回显:让 agent 能核对「服务端确实按我给的坐标排了」。
|
|
3078
3079
|
**({"geo": {"lat": lat_v, "lng": lng_v, "radius_km": rad,
|
|
3079
3080
|
"records_with_coords": geo_ncoord}} if lat_v is not None else {}),
|
|
@@ -3278,7 +3279,7 @@ def search_vendors(query: str = "", city: str = "", gb: str = "",
|
|
|
3278
3279
|
"厂名里成片出现」优先(所以「商务酒店」会把 龙翔商务酒店 排在 "
|
|
3279
3280
|
"某酒店(中央商务区店) 前面);score 是**能力画像分**"
|
|
3280
3281
|
"(有已发布能力卡才有分,无卡为 0),**不是相关度**,不要拿它反推排序。")
|
|
3281
|
-
+ _geo_note(rad),
|
|
3282
|
+
+ (_geo_note(rad) if lat_v is not None else ""),
|
|
3282
3283
|
# 坐标回显 + 圈外剔除数(仅坐标查询时出现)。
|
|
3283
3284
|
**({"geo": {"lat": lat_v, "lng": lng_v, "radius_km": rad,
|
|
3284
3285
|
"records_with_coords": geo_ncoord}} if lat_v is not None else {}),
|
|
@@ -3698,9 +3699,109 @@ TOOLS = [
|
|
|
3698
3699
|
},
|
|
3699
3700
|
},
|
|
3700
3701
|
},
|
|
3702
|
+
{
|
|
3703
|
+
"name": "vendor_onboarding",
|
|
3704
|
+
"description": "给供应商开通「入驻 / 认领」入口:返回一个**可直接点开**的控制台链接与步骤。"
|
|
3705
|
+
"当用户说「我是某某公司的」「我想入驻 / 上架产品 / 让客户搜到我」"
|
|
3706
|
+
"「我要认领我的企业」「怎么注册成供应商」「我的审核到哪一步了」时调用。"
|
|
3707
|
+
"⚠ 本工具**只给入口,绝不代办**:短信验证码与法人手机核验必须由用户"
|
|
3708
|
+
"在自己的浏览器里输入 —— 让他把验证码念给你,等于把「证明他是这家企业」"
|
|
3709
|
+
"的凭证交给第三方,还会进入对话上下文与日志。"
|
|
3710
|
+
"⚠ 不要替用户编造企业信息,不要承诺审核结果或时效,"
|
|
3711
|
+
"也不要把未认领的企业说成「已经属于你」。",
|
|
3712
|
+
"inputSchema": {
|
|
3713
|
+
"type": "object",
|
|
3714
|
+
"properties": {
|
|
3715
|
+
"supplier_id": {"type": "string",
|
|
3716
|
+
"description": "可选。已在检索结果里拿到的企业 ID(如 CN-MFG-0020317),用于生成直达深链,省得用户再搜一遍公司名"},
|
|
3717
|
+
"company": {"type": "string", "description": "可选。企业名称,用于把步骤说得更具体"},
|
|
3718
|
+
"intent": {"type": "string", "enum": ["claim", "register", "status"],
|
|
3719
|
+
"description": "claim=认领库里已收录的企业(默认);register=库里没有、要新建档;status=查自己申请的审核进度"},
|
|
3720
|
+
},
|
|
3721
|
+
},
|
|
3722
|
+
},
|
|
3701
3723
|
]
|
|
3702
3724
|
|
|
3703
3725
|
|
|
3726
|
+
CONSOLE_BASE = "https://beacon-mfg.pages.dev/console/"
|
|
3727
|
+
|
|
3728
|
+
|
|
3729
|
+
def vendor_onboarding(supplier_id: str = "", company: str = "",
|
|
3730
|
+
intent: str = "claim") -> Dict[str, Any]:
|
|
3731
|
+
"""给供应商一个**能自己走完**的入驻入口。
|
|
3732
|
+
|
|
3733
|
+
为什么 MCP 只做到「给链接」为止
|
|
3734
|
+
------------------------------
|
|
3735
|
+
Agent 拿得到 HTML 也没用(它不会填表单)。但更要紧的是**凭证不能进对话**:
|
|
3736
|
+
认领要过短信验证码与法人手机核验,让用户把验证码念给 Agent,等于把
|
|
3737
|
+
「证明我是这家企业」的东西交给第三方,还顺带落进 LLM 上下文与日志。
|
|
3738
|
+
所以这里只返回链接 + 步骤 + 明确的边界,由用户在浏览器里自己完成。
|
|
3739
|
+
|
|
3740
|
+
同源红线:即便将来 Agent 能代填字段,也必须企业自己确认过才算「企业自述」——
|
|
3741
|
+
否则就是拿平台推断冒充企业说的话。
|
|
3742
|
+
"""
|
|
3743
|
+
from urllib.parse import quote as _q
|
|
3744
|
+
|
|
3745
|
+
sid = (supplier_id or "").strip()
|
|
3746
|
+
name = (company or "").strip()
|
|
3747
|
+
if sid and not name:
|
|
3748
|
+
# 顺带把公司名查出来,好让 Agent 说「帮您找到 XX 公司」而不是干巴巴一个 ID
|
|
3749
|
+
try:
|
|
3750
|
+
v = get_vendor(sid)
|
|
3751
|
+
# get_vendor 的外层是 {"vendor": {...}},公司名在 vendor 顶层或 identity 里
|
|
3752
|
+
vv = (v.get("vendor") if isinstance(v, dict) and v.get("vendor") else v) or {}
|
|
3753
|
+
name = str(
|
|
3754
|
+
vv.get("company")
|
|
3755
|
+
or (vv.get("identity") or {}).get("company")
|
|
3756
|
+
or ""
|
|
3757
|
+
).strip()
|
|
3758
|
+
except Exception:
|
|
3759
|
+
pass
|
|
3760
|
+
|
|
3761
|
+
url = CONSOLE_BASE + ("?id=" + _q(sid, safe="") if sid else "")
|
|
3762
|
+
intent = (intent or "claim").strip().lower()
|
|
3763
|
+
|
|
3764
|
+
if intent == "register":
|
|
3765
|
+
steps = [
|
|
3766
|
+
"打开链接,用手机号收验证码登录(同一个号在 App 与网页是同一账号,草稿也共享)",
|
|
3767
|
+
"在「找到我的企业」里搜公司名;搜不到就走「新建档」",
|
|
3768
|
+
"填主体 / 联系 / 产能 / 质量 / 服务与产品条目(单价可不填,对外显示「面议」)",
|
|
3769
|
+
"提交后进入人工审核;不通过会带**逐字段理由**退回,改完可重新提交",
|
|
3770
|
+
]
|
|
3771
|
+
elif intent == "status":
|
|
3772
|
+
steps = [
|
|
3773
|
+
"打开链接,用提交时那个手机号登录",
|
|
3774
|
+
"页面会列出「我的申请」:待审核 / 已通过 / 已驳回(驳回带理由)",
|
|
3775
|
+
]
|
|
3776
|
+
else:
|
|
3777
|
+
steps = [
|
|
3778
|
+
"打开链接,用手机号收验证码登录",
|
|
3779
|
+
"搜到自己的企业并选中(已在库里的话,公开信息会自动带成底稿,不用重填)",
|
|
3780
|
+
"第一步:填 18 位统一社会信用代码(按 GB 32100-2015 校验位当场验)",
|
|
3781
|
+
"第二步:填工商登记的法人手机号并收码",
|
|
3782
|
+
"提交认领申请 → **人工复核通过后**才算认领成功",
|
|
3783
|
+
]
|
|
3784
|
+
|
|
3785
|
+
return {
|
|
3786
|
+
"url": url,
|
|
3787
|
+
"company": name or None,
|
|
3788
|
+
"supplier_id": sid or None,
|
|
3789
|
+
"intent": intent,
|
|
3790
|
+
"steps": steps,
|
|
3791
|
+
"boundary": "验证码、法人手机号这类凭证只能由用户在本页面自己输入。"
|
|
3792
|
+
"**不要让用户把它们告诉你**,也不要替他填写。",
|
|
3793
|
+
"what_not_to_do": [
|
|
3794
|
+
"不要代替用户完成认领或注册",
|
|
3795
|
+
"不要索取、转述或回显任何验证码",
|
|
3796
|
+
"不要承诺审核结果或时效",
|
|
3797
|
+
"不要把未认领的企业说成「已经属于该用户」",
|
|
3798
|
+
],
|
|
3799
|
+
"why_not_here": "USCC 是企业公开信息,单凭它认领等于把企业送人。"
|
|
3800
|
+
"所以我们不用「填个代码就算认领」的做法,而是提交后人工复核。",
|
|
3801
|
+
"next": "把 url 原样给用户,让他在浏览器里走完;完成后可以回来让你查他企业的能力卡。",
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
|
|
3704
3805
|
def account(action: str = "status", phone: str = "", code: str = "") -> Dict[str, Any]:
|
|
3705
3806
|
"""身份与配额自助。**任何异常都转成可读错误**,不让对话里冒出堆栈。"""
|
|
3706
3807
|
try:
|
|
@@ -3840,6 +3941,13 @@ def _dispatch_inner(name: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
3840
3941
|
offset=args.get("offset", 0),
|
|
3841
3942
|
group_branches=bool(args.get("group_branches", True)),
|
|
3842
3943
|
etype=args.get("etype", ""),
|
|
3944
|
+
# 2026-10-24 fix:v1.9.0 的坐标参数一度漏转发(schema 里有、
|
|
3945
|
+
# search_vendors 签名里也有,但 dispatch 没传 → 三个参数恒为 None,
|
|
3946
|
+
# 表现为 radius_km 静默失效、结果无 distance_km / geo、
|
|
3947
|
+
# 且「只传 lat」不报 bad_geo)。坐标必须转发。
|
|
3948
|
+
lat=args.get("lat"),
|
|
3949
|
+
lng=args.get("lng"),
|
|
3950
|
+
radius_km=args.get("radius_km"),
|
|
3843
3951
|
)
|
|
3844
3952
|
if name == "get_vendor":
|
|
3845
3953
|
return get_vendor(args.get("id", ""), args.get("gb", ""))
|
|
@@ -3860,6 +3968,9 @@ def _dispatch_inner(name: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
3860
3968
|
return refine_sourcing(args.get("session_id", ""), args.get("action", ""), args.get("value"))
|
|
3861
3969
|
if name == "account":
|
|
3862
3970
|
return account(args.get("action", "status"), args.get("phone", ""), args.get("code", ""))
|
|
3971
|
+
if name == "vendor_onboarding":
|
|
3972
|
+
return vendor_onboarding(args.get("supplier_id", ""), args.get("company", ""),
|
|
3973
|
+
args.get("intent", "claim"))
|
|
3863
3974
|
except Exception as e: # 任何异常都包成文本,避免协议崩
|
|
3864
3975
|
return {"error": f"{name} 执行异常: {e}"}
|
|
3865
3976
|
return {"error": f"未知 tool: {name}"}
|