# policy-agent-go **Repository Path**: ggforever/policy-agent-go ## Basic Information - **Project Name**: policy-agent-go - **Description**: 面向企业政策资格评估场景的智能体服务。给定一份政策文件与一份企业档案,系统会: 将政策拆解为 AND/OR 逻辑树形式的资格条件(门槛 / 加成 / 裁量三类); 对每个条件调用 LLM ReAct Agent,通过 MCP 工具(企查查 QCC 企业数据 + Web 搜索)逐条核验; 按逻辑树聚合出企业是否符合该政策的结论,并生成可追溯的人读 HTML 报告。 - **Primary Language**: Go - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-24 - **Last Updated**: 2026-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一企一策智能体 (policy-agent-go) 面向**企业政策资格评估**场景的智能体服务。给定一份政策文件与一份企业档案,系统会: 1. 将政策拆解为 AND/OR 逻辑树形式的资格条件(门槛 / 加成 / 裁量三类); 2. 对每个条件调用 LLM ReAct Agent,通过 MCP 工具(企查查 QCC 企业数据 + Web 搜索)逐条核验; 3. 按逻辑树聚合出企业**是否符合**该政策的结论,并生成可追溯的人读 HTML 报告。 项目基于 [CloudWeGo/eino](https://github.com/cloudwego/eino) 框架,采用**依赖注入(`samber/do/v2`)+ 面向接口**的架构开发,Go 版本 1.25.5。 --- ## 核心能力 - **政策条件拆分(Split)**:一次 LLM 调用把政策正文拆成 `LogicNode` 逻辑树,系统提示词强制执行三分类——门槛(进入 AND/OR 判定的条件)、加成(进入 `Condition.Bonus`,只影响奖励额度、**不**否决资格)、裁量(丢弃)。 - **确定性预检查(Precheck)**:纯 Go、不调 LLM,先行拦截——政策是否过期、适用区域是否包含企业所在行政区、企业登记状态是否在业/存续。任一不满足即 `EarlyReject` 短路。 - **分层核验(Tiered Verify,T0~T3)**:结合企业画像与画像提取事实,逐叶条件走最便宜的置信分层,逐级降级到 T3(完整 ReAct),核验并发度默认 8。 - **MCP 工具栈(`internal/agent/tool`)**:`Tolerate`(单次 MCP 失败降级为 `{"ok":false}`,不拖垮整个 ReAct 会话)、`Timeout`(单次调用上限 3s)、`Instrument`(eino 回调埋点)、`GuardWebTools`(约束 web 搜索仅用 bing、拒绝抓搜索引擎结果页)、`Cache`(Redis 工具响应缓存,最内层包装,按工具 TTL 区分搜索与稳定数据)。 - **企业画像(Profile)**:QCC 数据收割 + 缺口 Web 搜索 + 单次 LLM 抽取 + 向量化(BGE-M3),带置信度与 TTL 刷新。 - **检索召回(Retrieval)**:预检查后按向量余弦排序召回 Top-K 候选政策,再逐条深度核验并落库决策。 - **异步任务(Task)**:拆分 / 画像构建 / 推荐均为异步,`tasks` 台账 + 轮询 `GET /task/:id`,同 `(type, policy_id)` 防重复执行(部分唯一索引)。 - **HTML 报告**:推荐任务成功后通过内嵌 Go 模板渲染「企业政策适配性评估报告」。 - **可观测性**:进程级 eino 回调按流水线阶段(`profile` / `split` / `verify.`)写入独立 agent 日志。 --- ## 架构总览 ``` 政策文件 ──┐ ┌──► Precheck(确定性,无 LLM) 企业档案 ──┘ │ ▼ ┌───────────────────────┐ │ Split LLM 拆分逻辑树 │ AND/OR + 门槛/加成/裁量 └───────────┬───────────┘ ▼ ┌───────────────────────┐ │ Verify 逐叶 ReAct │ MCP: QCC + Web-search(Tolerate/Timeout/Guard/Cache) │ errgroup并发(默认8)│ └───────────┬───────────┘ ▼ ┌───────────────────────┐ │ Aggregate 逻辑树求值 │ LogicNode.Eval(satisfied) └───────────┬───────────┘ ▼ Decision(符合/不符合 + 证据 + 报告) ``` **入口分离**(`cmd/` 下每个目录是一个独立可执行): | 命令 | 用途 | |-----------------------|------------------------------------------------------------------------------------| | `cmd/server` | HTTP API 服务(Gin,走 DI 容器),监听 `:8080` | | `cmd/policy-agent` | CLI:给定企业与政策 JSON,端到端跑完整评估,stdout 输出单个 JSON 决策文档 | | `cmd/splitter` | CLI:只跑条件拆分,输出逻辑树 JSON | | `cmd/profile-builder` | CLI:只跑企业画像构建(不落库),人工检查抽取质量 | | `cmd/verify-node` | CLI:对指定拆分节点重跑核验,排查搜索约束 / 超时降级 | | `cmd/recall-check` | CLI:跑真实召回链路(Precheck + Rank),验证排序效果 | | `cmd/report-gen` | CLI:为指定 decision/task 渲染交互式 HTML 报告 | | `cmd/embedbackfill` | CLI:为缺失向量的叶子条件批量补向量(BGE-M3) | | `cmd/modelgen` | 反向工程 PostgreSQL 表结构,再生成 GORM model/query 代码(**生成代码,禁止手改**) | CLI 工具直接构造依赖、绕过 DI 容器;stdout 输出单个 JSON 文档,人类可读的渲染走 stderr(便于重定向为基线文件)。 --- ## 目录结构 ``` cmd/ 各入口(server / 各 CLI harness / modelgen) internal/ app/ DI 组合根:do.New 装配 + taskwire(task.Manager 最佳努力接线) agent/ LLM chat model(openai 兼容)、重试、eino 工具栈 tool/ Tolerate/Timeout/Instrument/GuardWebTools/Cache 装饰器 mcp/ QCC + WebSearch MCP 服务(streamable HTTP,mark3labs/mcp-go) cache/ Redis 工具响应缓存(mcp:cache::) codes/ 业务状态码(business code ↔ HTTP status,含 repo 哨兵错误) config/ Viper 读取 config.yaml(cwd) embedding/ Ollama BGE-M3 向量化 evaluation/ 评估流水线:precheck / splitter / verifier / tiered / evaluator http/ Gin:router 组装 + 各 handler(自注册路由) observability/ 进程级 eino 回调 → agent 日志 policytask/ task.Manager 各业务任务的处理器(split/profile/recommend) profile/ 企业画像构建 report/ HTML 报告组装 + Go 模板 retrieval/ 召回排序(向量余弦 + 预检查过滤) storage/ GORM 仓储 + 生成的 model/query(*.gen.go) task/ task.Manager(异步任务状态机 + 进度) migrations/ 手写 DDL(schema 的唯一真相源,modelgen 据此生成) data/ 示例 JSON(policy/company/decision)+ 本地数据卷 docs/reference/ HTTP API 参考文档 compose.yaml PostgreSQL(pgvector)/Redis/web-search 一键起 ``` --- ## 快速开始 ### 前置依赖 - Go 1.25+ - Docker(起本地 PostgreSQL / Redis / web-search) - 企查查 MCP token 与可用的 LLM(openai 兼容)/ embedding(ollama)配置 ### 1. 配置 ```bash cp config.example.yaml config.yaml # 填入 qcc token、LLM apikey、DB 密码等 ``` 添加 `.env` 提供 compose 所需的环境变量: ```bash POSTGRES_DB=policy_agent POSTGRES_USER=policy_agent POSTGRES_PASSWORD=change-me REDIS_USER=policy_agent REDIS_PASSWORD=change-me ``` ### 2. 启动基础设施 ```bash docker compose up -d . ``` - `postgres` 使用 `pgvector/pgvector:pg16` 镜像(支持 `CREATE EXTENSION vector`),并把 `migrations/*.sql` 作为 `docker-entrypoint-initdb.d` 初始化脚本。 - `redis` 启用命名 ACL 用户(默认用户被禁用),应用需同时用用户名+密码认证以启用 MCP 工具缓存;缓存为最佳努力,Redis 不可达时自动降级为无缓存。 - `web-search` 是 MCP 搜索服务(国内镜像),监听 `:3000`。 ### 3. 初始化 / 同步存储代码 手写 DDL 在 `migrations/`,模型代码由 `modelgen` 反向工程生成: ```bash go run ./cmd/modelgen # 需要 postgres 已启动且 schema 已应用 ``` > 存储结构变更流程:改 `migrations/` → 跑 `modelgen` → build → vet → test。**不要手改** `internal/storage/model|query/*.gen.go`。 ### 4. 启动服务 ```bash go run ./cmd/server # 读取 ./config.yaml,监听 :8080 ``` ### 5. CLI 示例 ```bash # 完整评估:企业与政策各给一个 JSON 文件,stdout 得到决策 JSON go run ./cmd/policy-agent -company data/company.example.json -policy data/policy.example.json -concurrency 8 # 只拆逻辑树 go run ./cmd/splitter -policy data/policy.example.json ``` ### 6. 常用校验命令 ```bash go build ./... go vet ./... golangci-lint run ./... # 由 .golangci.yaml 配置 gofmt -w . go test ./... go test -race -cover ./... ``` --- ## HTTP API 服务根路径 `/api/v1`,所有端点返回统一信封: ```json { "code": 2000, "msg": "ok", "data": { ... } } ``` 核心端点: | 方法 | 路径 | 说明 | |------|--------------------------------|----------------------------------------------------| | POST | `/api/v1/policy/submit` | 提交政策(标题唯一,服务端生成 Snowflake id) | | POST | `/api/v1/policy/split` | 异步拆分政策为逻辑树(202 + task_id) | | GET | `/api/v1/policy/:id/splits` | 列出某政策全部拆分版本(含重构的树) | | POST | `/api/v1/enterprise/submit` | 提交企业(名称 / 统一社会信用代码唯一) | | POST | `/api/v1/enterprise/profile` | 异步构建 / 刷新企业画像(202 + task_id) | | POST | `/api/v1/recommend` | 「企业找政策」异步推荐 + 深度核验(202 + task_id) | | GET | `/api/v1/task/:id` | 轮询异步任务状态 / 进度 / 结果 | | GET | `/api/v1/report/task/:task_id` | 渲染成功 recommend 任务的人读 HTML 评估报告 | 常用业务码:`2000 OK`、`1001 参数错误`、`1002 资源已存在`、`1003 资源不存在`、`1004 任务进行中(409)`、`2022 异步任务已受理(202)`、`5000 内部错误`。 完整字段级说明与示例见 [docs/reference/http-api.md](docs/reference/http-api.md)。 --- ## 评估流水线(`internal/evaluation/`) `Evaluator.Evaluate` 分四步: 1. **Precheck** —— 确定性、无 LLM:政策过期、中国行政区包含、企业登记状态(在业/存续/在营…,拒绝 注销/吊销/停业…)。失败即 `EarlyReject`。 2. **Split** —— 一次 LLM 调用把 `policy.Content` 变 `LogicNode` 树。`parseLogicTree` 容忍散文/markdown 围栏,兼容遗留扁平数组(包一层 AND 根)。 3. **Verify** —— 每个叶子跑一个 `react.Agent`(5 分钟超时),调 MCP 工具;`errgroup` 并发(默认 8)。加成项按叶最佳努力核验,失败不回滚、不翻转父节点 `Satisfied`。单叶核验失败**降级为不满足**而非报错——errgroup 首错即取消全局,一个幻觉工具名不能拖垮整个决策。 4. **Aggregate** —— `LogicNode.Eval(satisfied)` 按预序把逐叶结果映射回树,求出整体 Eligible / Ineligible。 企业找政策推荐链路(`/recommend`)在拆解之上还含:检索侧确定性预检查 → 画像构建/刷新(过期则刷新)→ Top-K 向量召回 → 分层核验 T0~T3 → 每候选落库决策。 --- ## 开发约定 - **TDD 为默认开发模式**(Red → Green → Refactor):行为变更先写失败测试。测试文件放被测包的 `tests/` 子目录,表驱动 + `httptest` + 轻量 `fake*`/`stub*` 双替身(不用 mock 框架)。Bug 修复 = 先补回归测试。 - **DI 装配**:`samber/do/v2` 扁平装配,每个业务包导出自己的 `Package`(`do.Package` / `do.Lazy`),`app.NewContainer` 只是 `do.New(...)` 列表。`context.Context` 以参数传递,**不**注册为容器服务。 - **程序面向接口**:消费者依赖贴近自身的小接口(repo 方法集 1~7 个),不嵌大接口;GORM / `*gorm.DB` / `do.Injector` 只准出现在 `internal/storage` 与 `internal/app`。仓储接口定义在 `internal/storage/repo`,测试替身因此很轻。 - **生成代码**:`internal/storage/model|query/*.gen.go`、`cmd/modelgen` 输出永不手改。 - **注释用英文**;中文仅保留给模型 / 用户能看到的领域字符串(prompt、门槛/加成/裁量标签、业务错误消息)。改动带中文注释的既有代码时保留原注释,新增注释一律英文。 - **不提交** `config.yaml` / 凭据 / DSN / token,安全占位放在 `config.example.yaml`。 - API 行为变化时同步更新 [docs/reference/http-api.md](docs/reference/http-api.md)。 --- ## 技术栈 - 框架:CloudWeGo [eino](https://github.com/cloudwego/eino)(agent / ReAct / 工具调用)、Gin - 数据:PostgreSQL + pgvector(向量检索)、Redis(MCP 工具缓存)、GORM + gorm/gen - 工具协议:MCP(mark3labs/mcp-go,streamable HTTP)—— 企查查 QCC + 自建 web-search - 注入:samber/do/v2;配置:Viper;日志:logrus(按天滚动)+ 独立 agent 日志 - 编码:Snowflake(十进制字符串 PK);嵌入:Ollama BGE-M3