feat: 快递查询与企业查询工具

This commit is contained in:
wolter 2025-12-15 15:48:57 +08:00
parent 9c3f0300ad
commit fc90dc5b38
7 changed files with 437 additions and 1 deletions

View File

@ -75,6 +75,16 @@ tools:
enabled: true
base_url: "https://restapi.amap.com/v3/weather/weatherInfo"
api_key: "12afbde5ab78cb7e575ff76bd0bdef2b"
cozeExpress:
enabled: true
base_url: "https://api.coze.cn"
api_key: "7582477438102552616"
api_secret: "pat_eEN0BdLNDughEtABjJJRYTW71olvDU0qUbfQUeaPc2NnYWO8HeyNoui5aR9z0sSZ"
cozeCompany:
enabled: true
base_url: "https://api.coze.cn"
api_key: "7583905168607100978"
api_secret: "pat_eEN0BdLNDughEtABjJJRYTW71olvDU0qUbfQUeaPc2NnYWO8HeyNoui5aR9z0sSZ"

4
go.mod
View File

@ -51,6 +51,7 @@ require (
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/cloudwego/eino-ext/libs/acl/openai v0.1.2 // indirect
github.com/coze-dev/coze-go v0.0.0-20251029161603-312b7fd62d20 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@ -59,8 +60,9 @@ require (
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/goph/emperror v0.17.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect

6
go.sum
View File

@ -139,6 +139,8 @@ github.com/cloudwego/eino-ext/libs/acl/openai v0.1.2/go.mod h1:S4OkvglPY9hsm9tXe
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coze-dev/coze-go v0.0.0-20251029161603-312b7fd62d20 h1:m6P88V9lLrxZsE7uj9otq7l7nqDuCSAJ86KhzRlWf0M=
github.com/coze-dev/coze-go v0.0.0-20251029161603-312b7fd62d20/go.mod h1:wdT5CFt/sFsWz9hna2Z7DWzUra9spx0SoX1PUZyoSB0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -200,6 +202,8 @@ github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPArei
github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w=
github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -269,6 +273,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=

View File

@ -105,6 +105,10 @@ type ToolsConfig struct {
ZltxOrderAfterSaleReseller ToolConfig `mapstructure:"zltxOrderAfterSaleReseller"`
// 下游批充订单售后
ZltxOrderAfterSaleResellerBatch ToolConfig `mapstructure:"zltxOrderAfterSaleResellerBatch"`
// Coze 快递查询工具
CozeExpress ToolConfig `mapstructure:"cozeExpress"`
// Coze 公司查询工具
CozeCompany ToolConfig `mapstructure:"cozeCompany"`
}
// ToolConfig 单个工具配置

View File

@ -92,6 +92,16 @@ func NewManager(config *config.Config, llm *utils_ollama.Client) *Manager {
weatherTool := public.NewWeatherTool(config.Tools.Weather)
m.tools[weatherTool.Name()] = weatherTool
}
// 注册 Coze 快递查询工具
if config.Tools.CozeExpress.Enabled {
cozeTool := public.NewCozeExpress(config.Tools.CozeExpress, m.llm)
m.tools[cozeTool.Name()] = cozeTool
}
// 注册 Coze 公司查询工具
if config.Tools.CozeCompany.Enabled {
cozeTool := public.NewCozeCompany(config.Tools.CozeCompany, m.llm)
m.tools[cozeTool.Name()] = cozeTool
}
// 普通对话
chat := public.NewNormalChatTool(m.llm, config)

View File

@ -0,0 +1,264 @@
package public
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg/utils_ollama"
"context"
"encoding/json"
"fmt"
"github.com/ollama/ollama/api"
"net/http"
"time"
"github.com/coze-dev/coze-go"
)
type CozeCompany struct {
cozeApi coze.CozeAPI
config config.ToolConfig
llm *utils_ollama.Client
}
// NewCoze 创建 Coze 实例
func NewCozeCompany(config config.ToolConfig, llm *utils_ollama.Client) *CozeCompany {
return &CozeCompany{
cozeApi: newCozeApi(config),
config: config,
llm: llm,
}
}
// newCozeClient 创建 Coze 客户端
func newCozeApi(config config.ToolConfig) coze.CozeAPI {
authCli := coze.NewTokenAuth(config.APISecret)
cozeApi := coze.NewCozeAPI(authCli, coze.WithBaseURL(config.BaseURL), coze.WithHttpClient(&http.Client{
Timeout: time.Second * 120,
}))
return cozeApi
}
// Name 返回工具名称
func (c *CozeCompany) Name() string {
return "coze_company"
}
// Description 返回工具描述
func (c *CozeCompany) Description() string {
return "查询企业信息"
}
// Definition 返回工具定义
func (c *CozeCompany) Definition() entitys.ToolDefinition {
return entitys.ToolDefinition{
Type: "function",
Function: entitys.FunctionDef{
Name: c.Name(),
Description: c.Description(),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"company_name": map[string]interface{}{
"type": "string",
"description": "企业名称",
},
},
"required": []string{"company_name"},
},
},
}
}
// Execute 执行查询
func (c *CozeCompany) Execute(ctx context.Context, requireData *entitys.RequireData) error {
var req map[string]interface{}
if err := json.Unmarshal([]byte(requireData.Match.Parameters), &req); err != nil {
return fmt.Errorf("invalid express request: %w", err)
}
if req["company_name"] == "" {
return fmt.Errorf("company_name is required")
}
// 调用 Coze 工作流
rsp, err := c.callWorkflow(ctx, req)
if err != nil {
return fmt.Errorf("failed to get real weather: %w", err)
}
companyInfo := CompanyInfo{}
err = json.Unmarshal([]byte(rsp.Data), &companyInfo)
if err != nil {
return fmt.Errorf("failed to unmarshal company info: %w", err)
}
// 调用 LLM 模型
err = c.llm.ChatStream(ctx, requireData.Ch, []api.Message{
{
Role: "system",
Content: `# Role: 企业信息分析与经营诊断专家
请基于以下12项指定数据字段无需补充未提供的信息完成目标企业的全维度分析总结要求每部分结论必须100%锚定对应数据拒绝主观推测突出风险可见性关键信息关联性
输入数据清单需逐一对应分析
行政处罚公司是否有行政处罚含处罚事由处罚机关处罚日期处罚文号
清算信息公司的清算信息含清算原因清算组构成清算进展状态
变更记录公司的变更记录含变更事项注册资本/股东/法定代表人/经营范围变更时间变更前后内容
主要成员公司名搜索公司的主要成员含姓名职位任职时间核心履历关键词
企业详情公司名称搜索公司详细信息含成立时间注册资本经营范围行业分类注册地址
经营异常公司是否有经营异常含列入原因列入日期移出状态
破产重组公司破产重组的信息含申请法院受理时间重组方案核心内容
严重违法是否有严重违法信息含违法事由认定机关公示期限
司法信息司法信息含案件类型原被告身份判决结果/进展
被执行信息公司的被执行信息含执行法院执行标的未履行金额失信状态
企业手机号公司名称查询企业手机号需标注是否为公开备案号
股东信息公司对应的股东含股东名称出资额出资比例股东类型自然人/企业/机构
分析总结框架严格对应数据
1. 企业基础画像锚定公司名称搜索公司详细信息
核心属性成立时间注册资本实缴/认缴行业分类批发和零售业软件和信息技术服务业主营业务从经营范围中提炼1-2个核心赛道专注于智能仓储设备研发与销售
注册地址是否与主要经营地一致若公司名称搜索公司详细信息有披露
2. 股权结构与股东特征锚定公司对应的股东
股权集中度前三大股东出资占比之和第一大股东持股60%为绝对控股股东
股东类型分布自然人股东企业股东机构股东的占比70%为企业股东含1家行业头部企业
关键股东亮点若有知名企业/机构股东需明确标注股东含XX产业投资基金具备产业链资源协同潜力
3. 经营稳定性与合规风险锚定公司是否有行政处罚/公司是否有经营异常/是否有严重违法信息/公司的清算信息/公司破产重组的信息
行政处罚风险是否存在行政处罚
若有列清处罚事由虚假宣传税务逾期申报处罚机关处罚日期并判断是否属于高频违规如1年内2次同类处罚高风险
若无标注无公开行政处罚记录
经营异常风险是否存在经营异常
若有说明列入原因通过登记的住所无法联系未公示年度报告是否已移出并评估对业务的影响地址失联可能导致客户信任下降
若无标注无经营异常记录
严重违法红线是否存在严重违法
若有明确违法事由拒不履行生效法律文书欺诈消费者认定机关公示期限并标注触及监管红线需重点核查整改情况
若无标注无严重违法记录
极端状态预警是否存在清算或破产重组
若有清算说明清算原因股东会决议解散营业期限届满清算进展清算组已完成资产清查
若有破产重组说明申请法院受理时间重组方案核心内容拟引入战略投资者注资5000万
若无标注无清算或破产重组记录
4. 司法与执行风险锚定司法信息/公司的被执行信息
涉诉情况司法案件的核心特征80%为买卖合同纠纷20%为劳动争议仲裁作为被告的案件占比75%判决胜诉率约60%
被执行压力是否存在被执行信息
若有列清执行法院执行标的金额未履行金额是否纳入失信被执行人名单并计算未履行金额占注册资本的比例未履行800万占注册资本的20%
若无标注无公开被执行记录
5. 管理团队与联系信息锚定公司名搜索公司的主要成员/公司名称查询企业手机号
核心团队稳定性主要成员的任职时间分布CEO任职4年CFO任职2年核心团队平均任职3年是否有频繁变动1年内2位高管离职需提示管理稳定性风险
关键岗位资质核心成员如法定代表人CEOCFO的过往履历亮点CEO曾任职XX上市公司主导过亿元级项目落地
联系信息可信度企业手机号是否为公开备案与工商登记预留电话一致可信度高为非备案号需提示联系信息真实性存疑
6. 综合结论与行动建议整合所有数据
整体风险评级基于数据密度给出低风险/中风险/高风险定性示例无行政处罚无被执行仅1条普通合同纠纷低风险有失信被执行+严重违法高风险
Top3核心风险严重违法被执行破产重组行政处罚经营异常司法纠纷排序列出最需关注的3个问题1. 未履行金额占注册资本20%存在债务违约风险2. 1年内2次地址异常经营稳定性弱3. 自然人股东占比过高决策易受个人因素影响
actionable 建议针对每个核心风险给出可执行的核查/应对动作核查未履行金额对应的案件进展评估企业偿债能力要求企业提供近1年的地址证明确认经营场所稳定性穿透核查自然人股东的资产状况降低决策风险
输出规则强制遵守
数据溯源每句结论必须标注对应数据字段根据公司是否有行政处罚企业2023年因税务逾期申报被区税务局处罚
量化优先拒绝模糊表述如不说很多案件要说近1年涉及5起买卖合同纠纷
风险分级标注风险等级越多越严重严重违法被执行经营异常
语言风格专业简洁避免冗余适合风控/投资/合作前的快速决策阅读
示例输出片段
3. 经营稳定性与合规风险
行政处罚根据公司是否有行政处罚企业2022年11月因发布虚假广告被区市场监管局处以3万元罚款文号X市监罚字2022456无后续同类处罚风险等级
经营异常根据公司是否有经营异常企业2023年6月因通过登记的住所无法联系被列入异常名录2023年12月已移出风险等级
严重违法根据是否有严重违法信息无公开严重违法记录风险等级无
极端状态根据公司的清算信息/公司破产重组的信息无清算或破产重组记录风险等级无
6. 综合结论与行动建议
整体评级低风险仅1次轻微行政处罚无重大合规瑕疵
Top3核心风险1. 根据司法信息近1年作为被告的合同纠纷占比75%需警惕应收账款回收风险2. 根据公司名搜索公司的主要成员1年内2位销售总监离职管理稳定性弱3. 根据公司名称查询企业手机号企业手机号为非备案号联系信息真实性存疑
建议核查合同纠纷案件的原告身份及回款情况评估坏账概率要求企业提供离职人员的交接说明确认业务连续性索要企业备案的联系方式验证沟通有效性
`,
},
{
Role: "assistant",
Content: fmt.Sprintf(`请分析企业:%s
公司是否有行政处罚%v
公司的清算信息%v
公司的变更记录%v
公司名搜索公司的主要成员%v
公司名称搜索公司详细信息%v
公司是否有经营异常%v
公司破产重组的信息%v
是否有严重违法信息%v
司法信息%v
公司的被执行信息%v
公司名称查询企业手机号%v
公司对应的股东%v`, req["company_name"], companyInfo.Xzcf, companyInfo.Clears, companyInfo.Changes, companyInfo.Employees, companyInfo.Searchdata, companyInfo.Operations, companyInfo.BankruptcyPublicList, companyInfo.Illegals, companyInfo.JudicialList, companyInfo.Executes, companyInfo.Phone, companyInfo.Partners),
},
{
Role: "user",
Content: requireData.Req.Text,
},
},
c.Name(), "")
if err != nil {
return fmt.Errorf("failed to get express info: %w", err)
}
//entitys.ResText(requireData.Ch, "", rsp.Data)
return nil
}
// CallWorkflow 调用 Coze 工作流
// 参数:
// - ctx: 上下文,用于控制超时和取消
// - workflowId: 工作流 ID
// - params: 工作流参数
// 返回:
// - interface{}: 工作流执行结果
// - error: 错误信息
func (c *CozeCompany) callWorkflow(ctx context.Context, params map[string]interface{}) (*coze.RunWorkflowsResp, error) {
// 准备工作流请求参数
workflowReq := &coze.RunWorkflowsReq{
WorkflowID: c.config.APIKey,
Parameters: params,
}
// 调用工作流
resp, err := c.cozeApi.Workflows.Runs.Create(ctx, workflowReq)
if err != nil {
return nil, fmt.Errorf("工作流调用失败: %w", err)
}
// 处理工作流响应
if resp == nil {
return nil, fmt.Errorf("工作流响应为空")
}
// 返回工作流执行结果
return resp, nil
}
type CompanyInfo struct {
BankruptcyPublicList interface{} `json:"bankruptcy_public_list"` // 破产公示列表
Changes interface{} `json:"changes"` // 变更记录
Clears interface{} `json:"clears"` // 清算记录
Employees interface{} `json:"employees"` // 员工列表
Executes interface{} `json:"executes"` // 执行记录
Illegals interface{} `json:"illegals"` // 违法记录
JudicialList interface{} `json:"judicial_list"` // 司法记录
Operations interface{} `json:"operations"` // 经营记录
Partners interface{} `json:"partners"` // 合伙人列表
Phone string `json:"phone"` // 联系电话
// 搜索数据
Searchdata struct {
Authority interface{} `json:"authority"`
BusinessScope interface{} `json:"business_scope"`
Capital interface{} `json:"capital"`
CompanyAddress interface{} `json:"company_address"`
CompanyName string `json:"company_name"`
CompanyStatus interface{} `json:"company_status"`
CompanyType interface{} `json:"company_type"`
CreditNo interface{} `json:"credit_no"`
EstablishDate interface{} `json:"establish_date"`
Industry interface{} `json:"industry"`
LegalPerson interface{} `json:"legal_person"`
Province interface{} `json:"province"`
} `json:"searchdata"`
Xzcf interface{} `json:"xzcf"` // 行政处罚
}

View File

@ -0,0 +1,140 @@
package public
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg"
"ai_scheduler/internal/pkg/utils_ollama"
"context"
"encoding/json"
"fmt"
"github.com/ollama/ollama/api"
"github.com/coze-dev/coze-go"
)
type CozeExpress struct {
cozeApi coze.CozeAPI
config config.ToolConfig
llm *utils_ollama.Client
}
// NewCozeExpress 创建 CozeExpress 实例
func NewCozeExpress(config config.ToolConfig, llm *utils_ollama.Client) *CozeExpress {
return &CozeExpress{
cozeApi: newCozeApi(config),
config: config,
llm: llm,
}
}
// newCozeExpressClient 创建 CozeExpress 客户端
func newCozeExpressApi(config config.ToolConfig) coze.CozeAPI {
authCli := coze.NewTokenAuth(config.APISecret)
cozeApi := coze.NewCozeAPI(authCli, coze.WithBaseURL(config.BaseURL))
return cozeApi
}
// Name 返回工具名称
func (c *CozeExpress) Name() string {
return "coze_express"
}
// Description 返回工具描述
func (c *CozeExpress) Description() string {
return "查询快递物流信息"
}
// Definition 返回工具定义
func (c *CozeExpress) Definition() entitys.ToolDefinition {
return entitys.ToolDefinition{
Type: "function",
Function: entitys.FunctionDef{
Name: c.Name(),
Description: c.Description(),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"express_id": map[string]interface{}{
"type": "string",
"description": "快递单号",
},
},
"required": []string{"express_id"},
},
},
}
}
// Execute 执行查询
func (c *CozeExpress) Execute(ctx context.Context, requireData *entitys.RequireData) error {
var req map[string]interface{}
if err := json.Unmarshal([]byte(requireData.Match.Parameters), &req); err != nil {
return fmt.Errorf("invalid express request: %w", err)
}
if req["express_id"] == "" {
return fmt.Errorf("express_id is required")
}
// 调用 Coze 工作流查询快递物流信息
rsp, err := c.callWorkflow(ctx, req)
if err != nil {
return fmt.Errorf("failed to get real weather: %w", err)
}
err = c.llm.ChatStream(ctx, requireData.Ch, []api.Message{
{
Role: "system",
Content: "你是一个快递查询助手。用户可能会提供快递单号,你需要分析快递单号,根据快递单号查询物流信息并反馈给我",
},
{
Role: "assistant",
Content: fmt.Sprintf("聊天记录:%s", pkg.JsonStringIgonErr(requireData.Histories)),
},
{
Role: "assistant",
Content: fmt.Sprintf("需要分析的快递单号:%s", rsp.Data),
},
{
Role: "user",
Content: requireData.Req.Text,
},
}, c.Name(), "")
if err != nil {
return fmt.Errorf("failed to get express info: %w", err)
}
//entitys.ResText(requireData.Ch, "", rsp.Data)
return nil
}
// CallWorkflow 调用 Coze 工作流
// 参数:
// - ctx: 上下文,用于控制超时和取消
// - workflowId: 工作流 ID
// - params: 工作流参数
// 返回:
// - interface{}: 工作流执行结果
// - error: 错误信息
func (c *CozeExpress) callWorkflow(ctx context.Context, params map[string]interface{}) (*coze.RunWorkflowsResp, error) {
// 准备工作流请求参数
workflowReq := &coze.RunWorkflowsReq{
WorkflowID: c.config.APIKey,
Parameters: params,
}
// 调用工作流
resp, err := c.cozeApi.Workflows.Runs.Create(ctx, workflowReq)
if err != nil {
return nil, fmt.Errorf("工作流调用失败: %w", err)
}
// 处理工作流响应
if resp == nil {
return nil, fmt.Errorf("工作流响应为空")
}
// 返回工作流执行结果
return resp, nil
}