# 公司信息多标签分类接口(F062) `POST /api/company/classify`,请求和响应均为 `application/json`,返回普通JSON,不使用SSE。 直接将 `/api/chat` 的 `result` 事件中 `data.company_info` 对象作为整个请求体,不增加外层 `company_info`,不传 `thread_id`。 ```javascript const response = await fetch('/api/company/classify', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(chatResult.data.company_info) }); const classification = await response.json(); ``` 请求沿用 `company / profile / honors` 全量原始结构,见 [公司信息契约](api-chat.md)。下面仅为合成片段,不限制工商原始字段: ```json { "company": {"KeyNo": "synthetic-key", "Name": "合成公司"}, "profile": { "data": {"KeyNo": "synthetic-key", "Name": "合成公司", "OperName": "合成法人", "主营": "农产品生产及加工"}, "source": {"provider": "synthetic", "fetched_at": "2026-09-18T15:00:00+08:00"} }, "honors": {"records": [], "sources": [], "complete": true, "error": null} } ``` 返回主要字段示意(`details`、`as_of`、`classification_version` 省略): ```json { "company_name": "合成公司", "credit_code": null, "legal_representative": "合成法人", "categories": { "基本信息": [], "资质荣誉": [], "产业信息": ["第一产业", "第二产业", "现代农业"], "经营活动": [], "行业信息": ["涉农(广义)", "农产品生产", "农产品加工"], "许可认证": [] }, "status": "completed", "errors": {}, "input_warnings": [] } ``` 示意标签不是所有输入的固定输出,实际依据LLM判断。每个角度总是数组,可以多选,信息不足直接为 `[]`,不返回“未知”或“其他”等表外兜底标签。未选中不等于判定“不符合”。 公司名称、统一社会信用代码、法人(法定代表人)分别取 `profile.data.Name/CreditCode/OperName`,缺失才回退 `company` 同名字段,再缺失返回 `null`,不交由LLM生成。`KeyNo/Name/CreditCode` 或来源主体键明确冲突时拒绝,HTTP422;不会合并两家公司。此接口信任调用方提供的资料,不重新查询或认证资料真实性。 允许请求 `{}`(chat未取得公司资料),身份均为null、六角度均为[]、不调用LLM,`input_warnings=["company_info_empty"]`。允许 `honors=null`,提示 `honors_unavailable`。荣誉 `complete=false` 时仍使用已取得记录,提示 `honors_incomplete`,不把查询失败或缺少记录作为否定事实。 `details`按相同六角度返回命中对象数组:`{tag, reason, citations:[{path, quote}]}`。path为请求原始对象的JSON Pointer,quote必须在该字段连续逐字出现;保留reason中的年份和有效期限制。来源可用该路径回溯原请求的profile.source或对应honors.records项的source。程序检查引用真实存在,不能证明LLM语义判断一定正确。 `as_of`为服务器上海日期,供成立年限、上月税务、许可有效期判断;来源获取时间保留在模型输入。`classification_version`覆盖六套提示、公共规则及标签列表的哈希,便于后续评测比较。没有缓存、落库或跨请求画像复用。 | 状态/错误 | 行为 | | --- | --- | | HTTP200 / completed | 六个角度分析完成,允许全部为空数组 | | HTTP200 / partial | 部分角度失败,已成功角度保留;失败角度仍为空数组,必须结合errors判断 | | HTTP502 / failed | 全部角度调用或输出校验失败,不能当成没有标签 | | errors中的model_call_failed | 对应角度LLM调用失败;不输出异常原文 | | errors中的invalid_model_result | 对应角度两次结构/标签/引用验证仍未通过 | | HTTP415 / application_json_required | Content-Type错误 | | HTTP422 / invalid_request | 请求结构、类型、嵌套深度或公司身份冲突 | | HTTP413 / request_too_large | 请求体超过65536字节,不截断资料 | | HTTP408 / request_timeout | 请求体读取超过15秒 | | HTTP429 / server_busy | 分类工作池已满,Retry-After为2秒 | | HTTP504 / classification_timeout | HTTP等待超时,后台尚未退出时继续占用名额 | | HTTP503 / service_unavailable | 分类服务不可用 | | HTTP502 / classification_failed | 接口内部失败,返回安全错误码 | 六个角度当前依次独立调用LLM,每个角度仅接收对应白名单,其他角度结果不作为事实输入。每角度结构错误最多修正一次,服务错误不自动重试。非空请求正常6次调用,最多12次;空对象0次。按六角度拆分便于以后独立评测、选择模型或有界并行,目前未宣称并行提速或生产吞吐。 分类工作池容量沿用 `API_MAX_CONCURRENT_REQUESTS` 对应的 `settings.max_concurrent`,独立于问答工作池;底层LLM共享现有 `settings.llm_concurrent` 信号量,不扩大同时调用模型上限。HTTP等待沿用 `settings.stream_timeout_seconds`。停止服务时等待分类工作结束再关闭共享运行时。请求正文、模型结果及异常原文不写日志,响应含X-Request-ID。 标签来自 `docs/法人标签表.xlsx`:166行、165个唯一分类标签,基本信息22、资质荣誉83、产业信息26、经营活动11、行业信息10、许可认证13。“产业信息 / 是否落户在综合保税区”重复一次,稳定去重,其余原名保留。运行时使用 `src/company_classification/taxonomy.json` 快照,无新增Excel依赖;测试核对源文件SHA256及全部行,更新xlsx后须同步快照并重新验证。 提示词位于 `src/company_classification/prompts/`,common.md为共享证据与输出规则,另有basic、honors、industry_sector、operations、industry、licenses六份独立判断规则。成立年限区间采用左闭右开,3/6月内和1年内允许重叠;规模、税务、认证等不能仅凭注册资本、注册地址、经营范围推断。 验证: ```powershell .\.venv\Scripts\python.exe -m unittest discover -s src/company_classification/tests -v .\.venv\Scripts\python.exe -m src.company_classification.smoke_check ``` 第二条会调用已配置的真实模型,仅发送脚本内合成资料,不查询真实公司、不写DMS。启动仍为 `python -m src.step4_api`,需重启加载新接口;整个服务启动仍依赖原有Redis、Embedding等。生产鉴权、真实公司分类质量、大量并发和延迟尚未验收。