company-classification.md 7.7 KB

公司信息多标签分类接口(F062)

POST /api/company/classify,请求和响应均为 application/json,返回普通JSON,不使用SSE。 直接将 /api/chat 的 result 事件中 data.company_info 对象作为整个请求体,不增加外层 company_info,不传 thread_id。

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 全量原始结构,见 公司信息契约。下面仅为合成片段,不限制工商原始字段:

{
  "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 省略):

{
  "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秒
HTTP429 / memory_busy 共享内存接纳预算或系统余量不足,Retry-After为2秒
HTTP504 / classification_timeout HTTP等待超时,后台尚未退出时继续占用名额
HTTP503 / service_unavailable 分类服务不可用
HTTP503 / memory_status_unavailable 无法读取内存状态,Retry-After为2秒
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(2026-09-21由用户提供的“法人标签表UI对照.xlsx”原样替换):只取 Sheet1 的 TAG_CATEGORY 和 最终使用TAG 列,共164行、164个唯一分类标签,基本信息22、资质荣誉82、产业信息26、经营活动11、行业信息10、许可认证13。对照表 中的 TAG_NAME、UI展示、说明仅供查阅,不作为输出白名单;最终表已去除“服务业十强”和重复的综合保税区记录。

categories 中的字符串与 details[].tag 均逐字使用“最终使用TAG”,前端以“分类角度+最终标签”匹配UI。例如明确肯定的原始事实对应“上市企业”“存在严重违法失信”“质量管理体系认证”,不再返回“是否上市”等旧名称。只有存在依据才命中;明确否定、已解除和缺少资料均不输出该标签,空数组不表示已核验无风险。接口结构保持,旧标签结果不会自动回填,后端重启后重新请求分类;classification_version 随新白名单及提示更新,前端如有缓存应据此更新。

运行时使用 src/company_classification/taxonomy.json 快照;Excel只在更新和测试时由已有openpyxl读取,不新增依赖。更新工作簿后,在项目根目录运行 python -m src.company_classification.build_taxonomy,再运行下列验证。生成器按列名读取且不回退旧TAG_NAME,测试核对SHA256和全量最终标签;六角度提示按类别名称绑定,不依赖工作表类别出现顺序。

提示词位于 src/company_classification/prompts/,common.md为共享证据与输出规则,另有basic、honors、industry_sector、operations、industry、licenses六份独立判断规则。成立年限区间采用左闭右开,3/6月内和1年内允许重叠;规模、税务、认证等不能仅凭注册资本、注册地址、经营范围推断。

验证:

.\.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等。生产鉴权、真实公司分类质量、大量并发和延迟尚未验收。