generative-ai 合同合规多智能体流水线深度解析:ADK RemoteA2aAgent 与 Go A2A 确定性策略引擎的跨语言编排
2026/9/13 2:54:47 网站建设 项目流程

generative-ai 合同合规多智能体流水线深度解析:ADK RemoteA2aAgent 与 Go A2A 确定性策略引擎的跨语言编排

【免费下载链接】generative-aiSample code and notebooks for Generative AI on Google Cloud, with Gemini Enterprise Agent Platform项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai

本文以agents/adk/contract-compliance-pipeline/ARCHITECTURE.md为主线,拆解 generative-ai 仓库中合同合规(Contract Compliance)演示系统的可执行架构:浏览器驾驶舱如何通过 Python FastAPI 完成确定性字段抽取,再经 Google ADK 的RemoteA2aAgent通过 A2A JSON-RPCSendMessage把契约字段交给 Go 侧的确定性策略引擎完成审计裁决。读完本文,你将完整掌握该流水线的运行时拓扑、A2A 报文的请求/响应结构、五种策略检查规则的实现位置,以及三层信任边界的 fail-closed(失败即降级人工审核)设计。

一、架构核心思想:LLM 处理歧义,确定性代码执行硬策略

ARCHITECTURE.md 开宗明义地描述了当前仓库中“可执行”的架构形态——一个本地跨语言系统,由四个部分构成:

  • 由 Python 在/live-compliance/路径下提供的浏览器驾驶舱(Browser Cockpit);
  • 运行在127.0.0.1:8000的 Python FastAPI 服务;
  • 运行在:8888的 Go A2A 合规服务;
  • 通过 Go Agent Card 与 A2A JSON-RPCSendMessage完成的 ADKRemoteA2aAgent交接(handoff)。

其中最关键的一句设计声明是:Go 服务在设计上是确定性的。它不是 LLM 智能体,而是执行需要可重复审计行为的策略阈值。这与同目录 README.md 中的核心观点一致:“LLMs are useful for ambiguity. Deterministic agents should enforce hard policy.”(LLM 适合处理歧义,确定性智能体应该执行硬策略)。如果政策规定供应商合同金额不得超过$500,000、期限不得超过 5 年、必须包含退出条款,那么这些检查就应该是可审计、可重复的——这正是 Go 侧checker.go的职责。

从仓库结构看,该演示被刻意拆成两个独立语言栈:Python 侧负责接入、抽取、会话状态与产物生成;Go 侧暴露 A2A Agent Card 并通过 JSON-RPC 执行同步、无 I/O 等待的策略校验。这种分工让演示在不依赖 Gemini API key 的前提下保持完全可复现(抽取夹具与策略检查均为确定性代码)。

二、运行时流程拓扑

ARCHITECTURE.md 用一张 mermaid 流程图定义了从浏览器到 Go 裁决再回到 UI 的完整数据流:

注意浏览器节点发出的载荷包含三类信息:样例合同文本 + 活动策略值 + 模拟器状态(sample text + policy + simulator state)。这意味着 UI 侧的策略覆盖值会真实地进入 A2A 数据部分并影响 Go 侧裁决,而不是装饰性的展示项。

三、实时请求路径的十步拆解

ARCHITECTURE.md 将一次完整的合规审计请求归纳为 10 个步骤,每一步都能在仓库源码中找到对应实现:

  1. 浏览器从sample-contracts/中选择内置合同夹具(例如 standard-vendor-agreement.pdf)。
  2. 浏览器通过/api/compliance/sample-contracts/{filename}获取夹具文本。
  3. 浏览器将文本、活动策略值、模拟器设置 POST 到/api/compliance/upload
  4. Python 在 tools.py 中用确定性解析抽取合同字段。
  5. Python 分类风险等级并构建 A2A 数据载荷。
  6. Python 在 fast_api_app.py 中创建一个聚焦的RemoteA2aAgent
  7. ADK 从GO_AGENT_CARD_URL解析 Go Agent Card。
  8. ADK 向 Go 服务发送 A2A JSON-RPCSendMessage
  9. Go 解码 data part、执行确定性策略检查,返回已完成的 A2A Task。
  10. Python 保存案件状态、生成 HTML 产物,并返回 UI 可见的载荷。

3.1 Python 侧的确定性抽取

步骤 4 的实现是extract_contract_details_from_text(filename, text)(tools.py)。它并非调用 LLM,而是用正则表达式提取结构化字段:

  • 金额:r"\$[\d,]+(?:\.\d{2})?"
  • 日期:英文月份 + 日 + 年(如June 1, 2026),转换为 ISO 格式2026-06-01
  • 当事方:匹配between ... ("Contractor") and ... ("Client")结构;
  • 期限:r"(\d+)\s+years?"
  • 责任条款段落:截取LIMITATION OF LIABILITY:INSURANCE:之间的文本用于判定“无限责任”措辞。

对三份内置样例合同,模块还维护了SAMPLE_CONTRACT_DETAILS映射(tools.py#L127-L164),保证在文本缺失时也能回退到与样例一致的结构化字段——这是演示可复现性的另一道保险。

3.2 ADK RemoteA2aAgent 交接的实现细节

步骤 6~8 集中在invoke_go_compliance_service(...)(fast_api_app.py#L165-L242)中,几个值得注意的实现点:

  • Agent Card URL 可配置GO_AGENT_CARD_URL环境变量默认指向http://localhost:8888/.well-known/agent.json(fast_api_app.py#L77-L80);_go_jsonrpc_url()会把/.well-known/agent.json后缀剥离,得到 JSON-RPC 根 URL。
  • 请求拦截器注入任务元数据RemoteA2aAgent通过A2aRemoteAgentConfig挂载RequestInterceptor(before_request=add_task_metadata),在每次请求前把task_id写入request_metadata,保证 Go 侧能按案件 ID 归档任务。
  • DataPart 的 GenAI 编码桥接_a2a_data_part_as_genai_part()先把a2a.types.DataPart序列化为 JSON,再包裹进 ADK 的A2A_DATA_PART_START_TAG/A2A_DATA_PART_END_TAG标签并封装为genai_types.Part的 inline blob——因为RemoteA2aAgent接受的是 GenAI 格式的Content,ADK 内部会将其转回 A2A DataPart 发出。
  • 事件流回收原始报文runner.run_async(...)的每个事件里,ADK 会把真实的 A2A 请求/响应放在event.custom_metadataa2a:requesta2a:response键中。演示的“Agent Exchange”面板展示的就是这两份原始报文,而非伪造的日志。

3.3 Go 侧的 JSON-RPC 分发

Go 服务端只有一个 JSON-RPC 入口HandleJSONRPC(task_handler.go#L131-L181),按 method 名分发:

Method处理函数说明
message/send/SendMessagehandleMessageSend当前 A2A 报文形态(Live 演示走这里)
tasks/sendhandleTasksSend旧版形态,保留给老客户端
tasks/get/GetTaskhandleTasksGet按任务 ID 从内存 map 查询历史任务

这与 ARCHITECTURE.md “Go 服务处理当前SendMessage外加遗留tasks/sendtasks/get” 的描述完全对应。validateMessage(task_handler.go#L214-L254)的执行顺序是:extractContractPayload(找 data part)→unpackContractPayload(拆策略覆盖与合同字段)→decodeContractDetails(绑定到ContractDetails结构体)→ 同步调用compliance.CheckCompliance→ 构造已完成 Task → 写入内存任务表供tasks/get查询。源码注释明确写道:“Run compliance checks synchronously — pure computation, no I/O wait needed.”

tasks/get的存储是进程内map[string]*Task(task_handler.go#L105-L109),重启即失——从源码结构看这是一个演示级的轻量实现,而非持久化任务系统。

四、A2A 报文形态(Payload Shape)

4.1 请求信封

Python 在build_go_message_payload(...)(fast_api_app.py#L102-L119)中构建 UI 可见的请求信封,其完整形状如下(来自 ARCHITECTURE.md):

{ "jsonrpc": "2.0", "id": "case-{case_id}", "method": "SendMessage", "params": { "metadata": { "task_id": "{case_id}" }, "message": { "messageId": "case-{case_id}-request", "taskId": "{case_id}", "role": "ROLE_USER", "parts": [ { "data": { "schema_version": "contract-compliance.a2a.v1", "case_id": "{case_id}", "contract": { "contract_value": 250000.0, "contractor_name": "ACME CLOUD SOLUTIONS", "insurance_coverage": 2000000.0, "liability_limit": "$1,000,000.00", "term_length_years": 2, "auto_renewal": false, "has_termination_clause": true }, "policy": { "max_contract_value": 500000.0, "required_insurance_minimum": 1000000.0, "max_term_years": 5, "required_termination_clause": true, "prohibited_clauses": ["unlimited liability", "auto-renewal > 3yr"] } }, "mediaType": "application/json" } ] } } }

其中schema_version: "contract-compliance.a2a.v1"是双方约定的数据部分版本标识;contract字段与 Go 侧ContractDetails结构体(checker.go#L21-L32)的 JSON tag 一一对应;policy字段可选——当且仅当 UI 提交了策略覆盖值时才会出现。

Go 侧对policy覆盖的处理在unpackContractPayload(task_handler.go#L271-L298):若载荷中存在policy键,则decodePolicy将其反序列化为compliance.Policy整体替换进程默认策略;否则回落到启动时由InitPolicies加载的策略。此外它还会按contractcontract_details→ 平铺直传的顺序兼容三种历史载荷布局。这解释了为什么 ARCHITECTURE.md 强调 Python “只调用已配置的 Go Agent Card URL”,而策略灵活性完全通过报文内的 data part 传递。

4.2 响应:已完成 A2A Task 中的裁决

Go 返回一个已完成(completed)的 A2A Task,其状态消息携带 data part:

{ "passed": false, "violations": [ "Contract value $850000.00 exceeds company framework limit of $500000.00" ], "verdict_timestamp": "2026-06-03T00:00:00Z" }

completedTask(task_handler.go#L337-L368)源码可以看到响应 Task 的精确形态:role: "ROLE_AGENT"state: "TASK_STATE_COMPLETED",裁决数据被封装成mediaType: "application/json"的 data part 放进status.message.parts。Python 侧的extract_verdict_from_go_response(fast_api_app.py#L122-L135)正是沿着result.status.message.parts逐 part 查找含data键的部分来还原裁决;找不到则抛出RuntimeError("Go compliance service returned no verdict data"),触发后文的 fail-closed 路径。

五、确定性策略引擎:checker.go 的六项审计

ARCHITECTURE.md 把策略检查定位在checker.go。源码中CheckCompliance(checker.go#L56-L107)按固定顺序执行以下检查,任一失败即追加到violations列表,最终passed = len(violations) == 0

  1. 合同金额审计contract_value > max_contract_value→ “Contract value $%.2f exceeds company framework limit of $%.2f”;
  2. 禁止条款(无限责任):当策略声明unlimited liability时,若liability_limit文本(小写化后)包含unlimitedwaived→ 违规;
  3. 禁止条款(长周期自动续约):当策略声明auto-renewal > 3yrauto_renewal == true && term_length_years > 3→ 违规;
  4. 保险下限insurance_coverage < required_insurance_minimum→ 违规;
  5. 期限上限term_length_years > max_term_years→ 违规;
  6. 退出条款required_termination_clause == truehas_termination_clause == false→ 违规。

verdict_timestamp使用time.Now().UTC().Format(time.RFC3339)生成(checker.go#L105),保证裁决带可追溯的时间戳。

默认策略阈值定义在 default_policy.json:

{ "max_contract_value": 500000.0, "prohibited_clauses": ["unlimited liability", "auto-renewal > 3yr"], "required_insurance_minimum": 1000000.0, "max_term_years": 5, "required_termination_clause": true }

Go 服务通过命令行参数加载该文件,见 main.go#L18-L32:-port默认8888-policy默认internal/policies/default_policy.json。若策略文件加载失败,InitPolicies(task_handler.go#L112-L126)会打印警告并回退到一套硬编码默认值,与上述 JSON 完全一致。路由注册顺序也有讲究:mux先注册/.well-known/agent.json再注册/,确保 Agent Card 端点优先于 JSON-RPC 根路由;绑定地址由HOST环境变量控制,默认0.0.0.0以适配容器部署。

Agent Card 本身由 card.go 的GetCard()构造:名称Security Compliance Validator、版本1.0.0、唯一接口为JSONRPC绑定(协议版本1.0),声明contract_compliance_check技能(tags: compliance / contract / validation),输入输出模式均为application/json。其 URL 可通过AGENT_URL环境变量覆盖,默认http://localhost:8888

六、状态机与三态可见结果

ARCHITECTURE.md 将实时驾驶舱映射出的三种可见结果定义如下,必须原样继承:

OutcomeTrigger
APPROVEDGo 返回passed: true
REVIEW_READYGo 返回passed: false且带策略违规。
MANUAL_REVIEWGo 不可用,或模拟器模式为Crashed (503)

文档同时指出:state_schema.py 中更丰富的枚举仍保留着完整 ADK 参考路径所用的中间状态,但驾驶舱在健康路径下一次 API 调用即完成。源码印证了这一点——ComplianceStep枚举(state_schema.py#L18-L36)定义了 7 个状态:INGESTED → EXTRACTED → COMPLIANCE_PENDING → COMPLIANCE_COMPLETE → APPROVED / REVIEW_READY,外加超时/失败分支的MANUAL_REVIEW。而upload_contract_file在一次请求内同步走完抽取、交接、裁决并直接落到APPROVEDREVIEW_READY(fast_api_app.py#L500-L504),中间态只存在于agent.pySequentialAgent参考实现中。

6.1 Fail-closed:交接失败如何降级

“Go 不可用”这一触发条件在源码中对应两层机制:

  1. 真实故障invoke_go_compliance_service抛出的ConnectionErrorTimeoutErrorurllib.error.URLErrorRuntimeError被统一捕获,案件被置为ComplianceStep.MANUAL_REVIEW,并写入带 fail-safe 语义的裁决(“SYSTEM TIMEOUT... Document routed for legal manager manual verification”),trace 中追加resilience_fallback_gatespan(fast_api_app.py#L527-L552);
  2. 模拟器注入:当表单字段simulated_server_state == "crashed"时,代码主动raise ConnectionError("Simulated Go compliance service 503 failure")(fast_api_app.py#L478-L480),走与真实故障完全相同的路径——这就是 ARCHITECTURE.md 中“模拟器模式为Crashed (503)触发MANUAL_REVIEW”的底层实现。

也就是说该演示的降级策略是fail-closed:远端合规裁决不可得时,合同绝不自动放行,而是路由给人工审核。这一设计在合规场景下比 fail-open 更合理。

七、信任边界(Trust Boundaries)

ARCHITECTURE.md 按三个主体划定了信任边界,以下内容完整保留:

浏览器(Browser):

  • 选择内置样例合同;
  • 发送策略覆盖值;
  • 从不直接调用 Go 服务。

Python 服务:

  • 强制文件扩展名与 5MB 上传限制;
  • 拒绝二进制 PDF 上传;内置的.pdf文件其实是文本夹具;
  • 以 basename 与根边界检查解析样例与产物路径;
  • 只调用已配置的 Go Agent Card URL;
  • Go 交接失败时 fail-closed 到MANUAL_REVIEW

Go 服务:

  • /.well-known/agent.json提供 Agent Card;
  • 接受 JSON-RPC POST 请求;
  • 处理当前SendMessage及遗留tasks/sendtasks/get
  • 应用来自default_policy.json或请求策略覆盖的确定性策略规则。

这些边界在源码中的对应证据:upload_contract_file白名单校验扩展名(.pdf/.txt/.md)、将文件名改写为 UUID 防注入、abspath前缀做路径穿越检查、5MB 尺寸上限,并对以%PDF魔数开头的真正二进制 PDF 直接返回 400(fast_api_app.py#L383-L411);_secure_resolve_path(tools.py#L36-L55)则对..、反斜杠与绝对路径做 fail-close 拦截。Go 侧只接受 POST(task_handler.go#L134-L137),且策略来源仅限于启动参数文件与请求载荷内嵌的policy字段两个入口。

八、完整 ADK 参考路径与 Live 路径的分工

ARCHITECTURE.md 的 Key Files 表把python-extraction-agent/app/agent.py标注为“更完整的 ADKSequentialAgent参考”。其模块 docstring 明确了层级结构(agent.py#L15-L30):

SequentialAgent (coordinator) ├── Agent: extractor_agent — 解析合同,抽取关键法务字段(Gemini 模型) ├── RemoteA2aAgent: compliance_agent — 经 A2A 把字段发给 Go 合规服务 └── Agent: report_agent — 生成最终审计报告

该参考路径中extractor_agent使用 Gemini 模型(而非正则)做抽取,需要 Google Cloud 凭证;而 Live 驾驶舱路径刻意选择“确定性抽取 + 一次聚焦RemoteA2aAgent调用”,在稳定可复现的前提下真实演练当前 A2ASendMessage协议。理解两条路径的分工,是避免误读本模块的关键:浏览器演示跑的是fast_api_app.pyagent.py仅作架构参考

九、本地运行与验证方式

结合 README.md 的快速开始说明,验证上述架构的行为如下(路径均相对于agents/adk/contract-compliance-pipeline/):

# 终端 1:启动 Go A2A 合规智能体 cd go-compliance-agent go run cmd/server/main.go # 终端 2:启动 Python FastAPI 驾驶舱 cd python-extraction-agent uv sync uv run uvicorn app.fast_api_app:app --host 127.0.0.1 --port 8000

打开http://127.0.0.1:8000/live-compliance/。该路径不需要 Gemini API key。也可用 Docker Compose 一键拉起(docker-compose.yml):

docker-compose up --build

验证点建议按架构文档逐项核对:

  1. 手动冒烟:curl http://127.0.0.1:8888/.well-known/agent.json应返回Security Compliance Validator的 Agent Card JSON;
  2. Agent Exchange 面板出现SendMessage报文,且 A2A 载荷包含jsonrpc: "2.0"
  3. Go 裁决在 UI 呈现,生成合规证书产物可渲染(产物端点/api/compliance/cases/{case_id}/artifacts/{artifact_id}由 live_compliance.py 的artifact_response提供,产物写入local_artifacts/compliance/{case_id}且经过路径边界检查);
  4. 将模拟器切换到Crashed (503)后再运行,案件结果应为MANUAL_REVIEW

单元测试覆盖:Python 侧uv run pytest tests/unit -v,Go 侧go test -v ./...(包含 checker_test.go 与 task_handler_test.go)。

十、关键文件索引

下表完整继承 ARCHITECTURE.md 的 Key Files 清单(路径已转换为仓库根相对路径):

文件角色
python-extraction-agent/app/static/live-compliance/index.html浏览器驾驶舱。
python-extraction-agent/app/fast_api_app.pyAPI 路由、ADK 交接、案件响应。
python-extraction-agent/app/tools.py确定性抽取与风险分类。
python-extraction-agent/app/live_compliance.py案件状态、事件流、产物生成。
python-extraction-agent/app/agent.py更完整的 ADKSequentialAgent参考。
python-extraction-agent/app/state_schema.py案件状态机枚举。
go-compliance-agent/internal/agentcard/card.goAgent Card。
go-compliance-agent/internal/handler/task_handler.goA2A JSON-RPC 处理器。
go-compliance-agent/internal/compliance/checker.go确定性策略检查器。
go-compliance-agent/internal/policies/default_policy.json默认策略阈值。
go-compliance-agent/cmd/server/main.goGo 服务入口与路由注册。

十一、小结

这套合同合规流水线给出的工程范式可归纳为三点:其一,按可审计性划分智能体职责——抽取环节允许不确定性(演示中甚至用正则替代 LLM 换取可复现性),策略裁决环节则交由确定性代码;其二,用 A2A 协议做跨语言粘合剂——Agent Card 负责发现,JSON-RPCSendMessage负责调用,结构化 DataPart(schema_version+contract+ 可选policy)负责跨语言契约,策略覆盖通过载荷内嵌而非配置通道传递;其三,以 fail-closed 作为合规系统的底线语义——任何交接失败都收敛到MANUAL_REVIEW,绝不静默放行。若你需要在自己的 ADK 项目中编排非 LLM 的远端智能体,这套 “Agent Card 发现 + DataPart 载荷 + 请求拦截器注入任务元数据 + 内存会话 Runner” 的组合,是一个可以直接对照 fast_api_app.py 逐步复现的最小参考实现。

【免费下载链接】generative-aiSample code and notebooks for Generative AI on Google Cloud, with Gemini Enterprise Agent Platform项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询