各位专家、同仁,下午好!
今天,我们齐聚一堂,共同探讨一个前瞻性且极具挑战的课题:如何构建一个能够处理复杂决策、融合人工智能集体智慧与人类最终裁决的“最高法院”图架构。随着人工智能技术的飞速发展,其应用场景日益深入社会的各个层面,从金融风控到医疗诊断,从智能推荐到自动驾驶。然而,在面对高风险、高复杂性、需要高度透明和可解释性的决策时,纯粹的AI系统往往难以满足社会对公正、负责和可信赖的要求。
这就是我们今天主题的由来——一个旨在解决这一核心矛盾的“三位一体”架构:多代理辩论、共识投票、人类终审。我们将以图数据库为核心,构建一个能够清晰记录决策过程、追溯推理路径、并最终交由人类进行裁决的智能决策平台。这不是一个简单的系统,它融合了分布式智能、群体决策理论和人机协作的精髓。
核心挑战与设计理念
在深入架构细节之前,我们首先要明确构建这样一个“最高法院”系统所面临的关键挑战以及我们应对这些挑战的设计理念。
1. 核心挑战
- 管理多代理视角的异构性: 不同的AI代理可能拥有不同的知识库、推理模型甚至预设的立场。如何有效地协调这些异构代理,使其在同一案件背景下进行有建设性的辩论,而非简单的信息堆砌或立场固化,是一个巨大的挑战。
- 确保结构化与连贯的辩论流程: 自由开放的辩论固然重要,但在决策场景中,我们需要辩论有明确的目标、结构和进展。如何设计机制,引导代理在辩论中逐步深化对问题的理解,并有效地形成支持或反对某一观点的论据链,避免陷入循环论证或离题万里。
- 量化与达成共识的复杂性: “共识”并非总是简单的多数票。如何在复杂的论证网络中,评估每个论点的权重、代理对论点的接受程度,以及最终达成“高质量”共识的标准,需要精密的算法设计。有时,即使票数接近,其背后的推理强度也可能大相径庭。
- 无缝集成人类判断: 人类终审不应仅仅是简单的“是”或“否”的按钮。它需要为人类提供一个直观、全面且可交互的界面,使其能够迅速理解案件的来龙去脉、代理的辩论过程、共识形成的依据,并在必要时深入探究细节。同时,人类的决策及其理由也应被系统完整地记录下来,以供审计和未来的学习。
- 保持可审计性与透明度: 这是一个高风险的决策系统,任何一个决策都必须能够被完整地追溯。从案件的初始输入,到每个代理的发言、投票,再到最终的裁决,每一步都必须有据可查、有理可依,以满足合规性、可解释性和信任的需求。
- 可扩展性与性能: 随着处理的案件数量增加,代理数量的增多,以及辩论复杂度的提升,系统必须能够有效地扩展,并保持高性能的响应。图数据库在处理复杂关系查询方面具有天然优势,但其具体实现和优化仍需深思熟虑。
2. 设计理念
针对上述挑战,我们提出以下核心设计理念:
- 模块化与解耦: 将系统的各个功能模块(如代理管理、辩论引擎、投票机制、人机交互接口)进行解耦,使其能够独立开发、测试和部署。这不仅提升了开发效率,也增强了系统的灵活性和可维护性。
- 以图为核心的数据模型: 图数据库以其天然的关联性表达能力,能够完美地捕捉案件、代理、论点、证据、投票、决策之间的复杂关系。这不仅便于存储和查询,更是实现高透明度与可追溯性的基石。
- 透明度与可解释性优先: 系统的每一个环节,从代理的推理过程到共识的形成路径,都力求透明化。通过图结构,我们可以直观地可视化整个决策过程,为人类终审提供全面的上下文。
- 韧性与容错: 代理可能出现推理错误、信息缺失甚至“故障”。系统需要具备处理这些情况的能力,例如通过多代理交叉验证、引入“中立”观察者代理、或在无法达成共识时及时上报人类。
- 人类中心化的人机协作: 尽管AI代理承担了大量的分析和推理工作,但人类始终是最终的决策者。系统的设计应充分考虑人类的认知负荷,提供清晰、简洁而又全面的信息视图,赋能人类进行高效、准确的判断。
- 迭代与学习: 系统的设计应允许通过人类的反馈、历史案例的学习,不断优化代理的推理能力、辩论策略和共识算法,形成一个持续改进的闭环。
图架构设计:核心实体与关系
现在,让我们深入探讨系统的核心——图架构设计。我们将采用图数据库(如Neo4j、ArangoDB或Dgraph等)作为底层存储和查询引擎。图模型能够以节点(Nodes)和关系(Relationships)的形式,直观、高效地表示复杂的实体及其相互作用。
1. 核心节点类型 (Node Types)
以下是构成我们“最高法院”图架构的主要节点类型及其核心属性:
| 节点类型 | 描述 | 核心属性 | 示例 |
|---|---|---|---|
Case (案件) |
代表一个待审理的具体案件或决策请求。 | case_id (唯一ID), title (标题), description (案件描述), status (状态: ‘OPEN’, ‘DEBATING’, ‘VOTING’, ‘HUMAN_REVIEW’, ‘CLOSED’), created_at, closed_at |
case_id: "CASE001", title: "合同违约赔偿案", status: "DEBATING" |
Agent (代理) |
参与辩论和投票的AI代理。 | agent_id (唯一ID), name (名称), role (角色: ‘PROSECUTOR’, ‘DEFENSE’, ‘NEUTRAL’, ‘EXPERT’), model_type (LLM模型类型), expertise (专业领域), reliability_score (可靠性评分) |
agent_id: "A001", name: "辩方律师AI", role: "DEFENSE", model_type: "GPT-4", expertise: "合同法" |
Argument (论点) |
代理在辩论中提出的一个观点、主张或推理。 | argument_id (唯一ID), content (论点内容), type (类型: ‘CLAIM’, ‘EVIDENCE_SUPPORT’, ‘REBUTTAL’, ‘QUESTION’), confidence_score (代理提出的置信度), generated_at |
argument_id: "ARG005", content: "被告未能按时交付货物,构成根本违约。", type: "CLAIM", confidence_score: 0.9 |
Evidence (证据) |
支持或反驳论点的客观事实、数据或文件。 | evidence_id (唯一ID), description (描述), source (来源), content_hash (内容哈希), type (类型: ‘DOCUMENT’, ‘TESTIMONY’, ‘DATA’), retrieved_at |
evidence_id: "EVID002", description: "合同交付条款约定文档", source: "合同文件库", type: "DOCUMENT" |
DebateRound (辩论回合) |
辩论过程中的一个结构化阶段。 | round_id (唯一ID), round_number (回合编号), start_time, end_time, moderator_agent_id (可选,主持代理) |
round_id: "DR003", round_number: 3, start_time: "..." |
Vote (投票) |
代理对案件或特定判决提议的投票。 | vote_id (唯一ID), decision (投票结果: ‘GUILTY’, ‘NOT_GUILTY’, ‘ABSTAIN’, ‘UNCERTAIN’, ‘FOR_PROPOSAL_X’), confidence (投票置信度), voted_at |
vote_id: "V010", decision: "GUILTY", confidence: 0.85 |
Verdict (判决) |
代理提议的或最终的案件裁决。 | verdict_id (唯一ID), summary (判决摘要), outcome (最终结果: ‘GUILTY’, ‘NOT_GUILTY’, ‘SETTLED’, ‘PENDING’), reasoning (核心理由), decided_by (决策者: ‘AGENTS’, ‘HUMAN’) |
verdict_id: "VER001", summary: "被告需赔偿原告...", outcome: "GUILTY", decided_by: "HUMAN" |
HumanReview (人类复审) |
记录人类介入复审的事件。 | review_id (唯一ID), reviewer_id (复审人ID), review_start_time, review_end_time, action (动作: ‘APPROVE’, ‘OVERRIDE’, ‘REQUEST_MORE_INFO’), notes (复审记录) |
review_id: "HR001", reviewer_id: "U123", action: "OVERRIDE", notes: "代理未充分考虑证据B的权重。" |
DecisionLog (决策日志) |
用于审计的通用日志条目,关联到具体事件。 | log_id (唯一ID), event_type (事件类型), timestamp, details (详细信息), actor (执行者: ‘AGENT’, ‘SYSTEM’, ‘HUMAN’) |
log_id: "L001", event_type: "ARGUMENT_PRESENTED", timestamp: "...", details: {"agent_id": "A001", "argument_id": "ARG005"} |
2. 核心关系类型 (Relationship Types)
关系连接节点,形成意义丰富的网络。以下是关键的关系类型:
| 关系类型 | 起始节点 | 终止节点 | 描述 | 核心属性 |
|---|---|---|---|---|
HAS_AGENT |
Case |
Agent |
案件关联的参与代理。 | role (代理在该案件中的角色), assigned_at |
OCCURS_IN |
DebateRound |
Case |
辩论回合属于哪个案件。 | order (回合顺序) |
PARTICIPATES_IN |
Agent |
DebateRound |
代理参与了某个辩论回合。 | start_time, end_time |
PRESENTS_ARGUMENT |
Agent |
Argument |
代理提出了一个论点。 | timestamp, sentiment (论点情绪), impact_score (对案件的影响评估) |
OCCURS_IN_ROUND |
Argument |
DebateRound |
论点在哪个辩论回合提出。 | order_in_round (在回合中的顺序) |
SUPPORTS |
Argument |
Evidence |
论点由某个证据支持。 | strength (支持强度) |
CONTRADICTS |
Argument |
Evidence |
论点与某个证据相矛盾。 | strength (矛盾强度) |
REFERENCES_CASE_EVIDENCE |
Argument |
Evidence |
论点直接引用了案件中的某个证据。 | |
RELATES_TO |
Argument |
Argument |
一个论点与另一个论点相关(如反驳、补充、疑问)。 | relation_type (‘REBUTS’, ‘SUPPORTS’, ‘ELABORATES’, ‘ASKS_ABOUT’), confidence |
ARGUMENT_FOR |
Argument |
Case |
论点支持案件的某种结果。 | direction (‘PRO’, ‘CON’) |
CASTS_VOTE |
Agent |
Vote |
代理进行了投票。 | timestamp |
VOTES_ON_VERDICT_PROPOSAL |
Vote |
Verdict |
投票是针对某个判决提议。 | |
PROPOSES_VERDICT |
Agent |
Verdict |
代理提出了一个判决提议。 | timestamp |
RESULT_OF |
Verdict |
Case |
判决是某个案件的结果。 | |
TRIGGERS_REVIEW |
Case |
HumanReview |
案件触发了人类复审。 | reason (触发原因: ‘NO_CONSENSUS’, ‘HIGH_STAKES’) |
REVIEWS_VERDICT |
HumanReview |
Verdict |
人类复审对某个判决提议进行审阅。 | |
APPROVES |
HumanReview |
Verdict |
人类复审批准了判决。 | |
OVERRULES |
HumanReview |
Verdict |
人类复审推翻了判决。 | |
GENERATED_BY_AGENT |
DecisionLog |
Agent |
日志由哪个代理生成。 | |
CONCERNS_ARGUMENT |
DecisionLog |
Argument |
日志与哪个论点相关。 | |
CONCERNS_CASE |
DecisionLog |
Case |
日志与哪个案件相关。 |
这种图结构不仅存储了数据,更重要的是,它构建了一个动态的知识图谱,清晰地描绘了整个决策过程的逻辑流和实体间的相互作用。任何时刻,我们都可以通过图查询,重建任意代理的思考路径,理解共识的形成过程,或追溯人类终审的依据。
多代理辩论机制
多代理辩论是“最高法院”的核心动态环节。它模拟了真实法庭的辩论过程,通过不同视角的AI代理之间的交互,逐步揭示案件的真相,并形成更为全面的认知。
1. 代理角色与配置
为了模拟真实的法庭环境,我们可以为代理设置不同的角色,每个角色拥有特定的目标和行为模式。
PROSECUTOR(控方代理): 目标是证明某一特定结果(如被告有罪)。会积极寻找支持控方观点的证据和论点,并反驳辩方观点。DEFENSE(辩方代理): 目标是反驳控方观点,证明另一结果(如被告无罪)。会寻找支持辩方的证据和论点,并质疑控方论据。NEUTRAL_OBSERVER(中立观察者代理): 不带预设立场,主要职责是评估论点的逻辑严谨性、证据的相关性和可靠性,指出推理漏洞,并可能在辩论后期提出中立的总结或问题。EXPERT_WITNESS(专家证人代理): 在特定领域拥有深厚知识,只在被请求时提供专业意见或解释复杂技术问题。
这些角色通过其在图中的HAS_AGENT关系上的role属性体现。
2. 辩论流程
辩论流程通常包括以下阶段:
-
初始化 (Initialization):
Case被创建并加载,包括所有初始证据和背景信息。- 根据案件类型和复杂性,
SupremeCourtOrchestrator分配1个或多个PROSECUTOR代理、1个或多个DEFENSE代理、以及可选的NEUTRAL_OBSERVER和EXPERT_WITNESS代理。这些代理通过HAS_AGENT关系连接到Case节点。 - 创建一个
DebateRound节点,表示第一个回合。
-
论点生成 (Argument Generation):
- 在第一回合,
PROSECUTOR和DEFENSE代理会基于案件的初始证据,生成各自的开场论点。 - 每个代理会调用其内部的LLM模型,结合其角色目标和现有信息,产出一段文本作为论点内容。
- 系统将这些论点作为
Argument节点添加到图中,并通过PRESENTS_ARGUMENT关系连接到相应的Agent,并通过OCCURS_IN_ROUND连接到DebateRound。同时,论点会通过ARGUMENT_FOR关系连接到Case节点,并注明其direction(PRO/CON)。
- 在第一回合,
-
交互式辩论回合 (Interactive Debate Rounds):
- 辩论以回合制进行,每个回合代理轮流发言。
- 代理分析: 在其发言回合,代理会首先检索当前
Case中所有的Argument节点及其RELATES_TO关系,理解当前的辩论状态。 - 生成响应: 代理根据其角色目标,对现有论点进行分析。它可能:
REBUTS(反驳) 对方的论点。SUPPORTS(支持) 己方或中立方的论点。ELABORATES(阐述) 己方论点,提供更多细节或证据。ASKS_ABOUT(提问) 对方论点中的模糊之处。PRESENTS_NEW_EVIDENCE(提出新证据) 并基于此提出新论点。
- 更新图: 代理生成的响应被创建为新的
Argument节点,并通过RELATES_TO关系连接到它所响应的论点。例如,一个反驳论点会与被反驳的论点建立RELATES_TO关系,relation_type为 ‘REBUTS’。 - 论点评估: (可选)
NEUTRAL_OBSERVER代理可以在每个论点提出后,对其逻辑强度、相关性进行初步评估,并将评估结果作为属性(如impact_score)记录在Argument节点上,或作为新的DecisionLog节点。 - 回合数可以是预设的,也可以是动态的,直到论点饱和或达到一定复杂性阈值。
3. 代码示例:代理类和辩论交互
我们以Python为例,展示代理的基本结构和如何与图数据库交互来发表论点。这里我们假设有一个 GraphDB 封装类来处理与图数据库的CRUD操作。
import uuid
import datetime
from typing import List, Dict, Any, Optional
# 假设的LLM接口
class LLMService:
def generate_response(self, prompt: str, context: List[str]) -> str:
# 实际这里会调用OpenAI, Anthropic, etc. API
# 为了演示,这里只是一个简单的模拟
print(f"LLM generating response for prompt: {prompt}")
if "反驳" in prompt:
return f"反驳了上一轮的观点:{context[-1]}。我认为..."
elif "支持" in prompt:
return f"支持了上一轮的观点:{context[-1]}。我补充说..."
else:
return f"针对当前辩论,我提出新的观点:..."
# 假设的图数据库交互层
class GraphDB:
def __init__(self, db_config: Dict):
# 实际中这里会初始化Neo4j驱动等
print("GraphDB initialized.")
def add_node(self, label: str, properties: Dict) -> str:
node_id = str(uuid.uuid4())
# 模拟添加到数据库
print(f"Adding node: {label} with properties {properties}, ID: {node_id}")
return node_id
def add_relationship(self, start_node_id: str, end_node_id: str, rel_type: str, properties: Dict = None):
# 模拟添加关系
print(f"Adding relationship: {start_node_id} -[:{rel_type}]-> {end_node_id} with properties {properties}")
def get_case_arguments(self, case_id: str) -> List[Dict]:
# 模拟查询特定案件的所有论点及其关系
print(f"Retrieving arguments for case: {case_id}")
# 实际查询会涉及复杂的Cypher/AQL查询
# 返回一个包含 Argument 节点和 RELATES_TO 关系信息的列表
return [
{"argument_id": "arg_init_1", "content": "原告遭受了实际损失。", "agent_id": "agent_p1", "type": "CLAIM", "direction": "PRO"},
{"argument_id": "arg_init_2", "content": "被告的行为并未直接导致损失。", "agent_id": "agent_d1", "type": "CLAIM", "direction": "CON"},
# 更多论点及其关系...
]
def get_case_evidence(self, case_id: str) -> List[Dict]:
print(f"Retrieving evidence for case: {case_id}")
return [{"evidence_id": "evid_doc1", "description": "合同复印件"}]
# 基础代理类
class BaseAgent:
def __init__(self, agent_id: str, name: str, role: str, llm_service: LLMService, graph_db: GraphDB):
self.agent_id = agent_id
self.name = name
self.role = role
self.llm = llm_service
self.graph_db = graph_db
self.case_id: Optional[str] = None
def assign_to_case(self, case_id: str):
self.case_id = case_id
self.graph_db.add_relationship(case_id, self.agent_id, "HAS_AGENT", {"role": self.role, "assigned_at": datetime.datetime.now().isoformat()})
print(f"Agent {self.name} assigned to case {case_id} with role {self.role}.")
def _get_current_debate_context(self) -> List[str]:
# 从图数据库获取当前案件的所有论点,构建LLM的上下文
arguments = self.graph_db.get_case_arguments(self.case_id)
context = [arg['content'] for arg in arguments]
return context
def present_argument(self, debate_round_id: str, prompt_template: str, direction: str, related_argument_id: Optional[str] = None, relation_type: Optional[str] = None) -> str:
context = self._get_current_debate_context()
llm_input = f"{prompt_template} 基于当前的辩论和我的角色({self.role}),请提出我的论点。"
argument_content = self.llm.generate_response(llm_input, context)
# 将论点添加到图数据库
argument_id = self.graph_db.add_node("Argument", {
"argument_id": str(uuid.uuid4()),
"content": argument_content,
"type": "CLAIM", # 简化,实际可能根据LLM输出判断
"confidence_score": 0.0, # 初始值,可由LLM评估或后续更新
"generated_at": datetime.datetime.now().isoformat(),
"agent_id": self.agent_id # 记录是哪个代理提出的
})
self.graph_db.add_relationship(self.agent_id, argument_id, "PRESENTS_ARGUMENT", {"timestamp": datetime.datetime.now().isoformat()})
self.graph_db.add_relationship(argument_id, debate_round_id, "OCCURS_IN_ROUND", {"order_in_round": 0}) # 简化顺序
self.graph_db.add_relationship(argument_id, self.case_id, "ARGUMENT_FOR", {"direction": direction})
if related_argument_id and relation_type:
self.graph_db.add_relationship(argument_id, related_argument_id, "RELATES_TO", {"relation_type": relation_type})
print(f"Agent {self.name} presented argument: '{argument_content}' (ID: {argument_id})")
return argument_id
# 控方代理
class ProsecutorAgent(BaseAgent):
def __init__(self, agent_id: str, name: str, llm_service: LLMService, graph_db: GraphDB):
super().__init__(agent_id, name, "PROSECUTOR", llm_service, graph_db)
def make_opening_statement(self, debate_round_id: str) -> str:
prompt = "作为控方,请提出你的开场陈述,证明被告有罪。"
return self.present_argument(debate_round_id, prompt, "PRO")
def rebut_defense_argument(self, debate_round_id: str, defense_argument_id: str) -> str:
prompt = f"作为控方,请反驳ID为 {defense_argument_id} 的辩方论点。"
return self.present_argument(debate_round_id, prompt, "PRO", defense_argument_id, "REBUTS")
# 辩方代理
class DefenseAgent(BaseAgent):
def __init__(self, agent_id: str, name: str, llm_service: LLMService, graph_db: GraphDB):
super().__init__(agent_id, agent_id, "DEFENSE", llm_service, graph_db)
def make_opening_statement(self, debate_round_id: str) -> str:
prompt = "作为辩方,请提出你的开场陈述,证明被告无罪。"
return self.present_argument(debate_round_id, prompt, "CON")
def rebut_prosecutor_argument(self, debate_round_id: str, prosecutor_argument_id: str) -> str:
prompt = f"作为辩方,请反驳ID为 {prosecutor_argument_id} 的控方论点。"
return self.present_argument(debate_round_id, prompt, "CON", prosecutor_argument_id, "REBUTS")
# 辩论协调器
class SupremeCourtOrchestrator:
def __init__(self, graph_db: GraphDB, llm_service: LLMService):
self.graph_db = graph_db
self.llm_service = llm_service
self.agents: Dict[str, BaseAgent] = {}
self.current_case_id: Optional[str] = None
self.current_debate_round_id: Optional[str] = None
self.round_counter = 0
def add_agent(self, agent: BaseAgent):
self.agents[agent.agent_id] = agent
def start_case(self, title: str, description: str, agents_to_assign: List[str]) -> str:
self.current_case_id = self.graph_db.add_node("Case", {
"case_id": str(uuid.uuid4()),
"title": title,
"description": description,
"status": "OPEN",
"created_at": datetime.datetime.now().isoformat()
})
print(f"Case '{title}' started with ID: {self.current_case_id}")
for agent_id in agents_to_assign:
if agent_id in self.agents:
self.agents[agent_id].assign_to_case(self.current_case_id)
else:
print(f"Warning: Agent {agent_id} not found.")
self.round_counter = 0
self._start_new_debate_round()
return self.current_case_id
def _start_new_debate_round(self):
self.round_counter += 1
self.current_debate_round_id = self.graph_db.add_node("DebateRound", {
"round_id": str(uuid.uuid4()),
"round_number": self.round_counter,
"start_time": datetime.datetime.now().isoformat()
})
self.graph_db.add_relationship(self.current_debate_round_id, self.current_case_id, "OCCURS_IN", {"order": self.round_counter})
print(f"Debate Round {self.round_counter} started (ID: {self.current_debate_round_id}).")
def run_debate_round(self, max_arguments_per_agent: int = 1):
if not self.current_case_id or not self.current_debate_round_id:
print("No active case or debate round. Please start a case first.")
return
print(f"n--- Running Debate Round {self.round_counter} ---")
# 获取当前案件的所有论点,用于代理构建上下文
current_arguments = self.graph_db.get_case_arguments(self.current_case_id)
# 简单模拟轮流发言
agents_in_case = [agent for agent in self.agents.values() if agent.case_id == self.current_case_id]
for agent in agents_in_case:
print(f"nAgent {agent.name} ({agent.role}) turn:")
if isinstance(agent, ProsecutorAgent):
# 控方尝试反驳最新的辩方论点
defense_arguments = [arg for arg in current_arguments if arg.get("agent_id") == agent.agent_id and arg.get("direction") == "CON"] # 假设agent_id在argument节点中
if defense_arguments:
latest_defense_arg = defense_arguments[-1] # 简化:取最后一个
agent.rebut_defense_argument(self.current_debate_round_id, latest_defense_arg["argument_id"])
else:
agent.make_opening_statement(self.current_debate_round_id) # 首次发言或无反驳目标时
elif isinstance(agent, DefenseAgent):
# 辩方尝试反驳最新的控方论点
prosecutor_arguments = [arg for arg in current_arguments if arg.get("agent_id") == agent.agent_id and arg.get("direction") == "PRO"]
if prosecutor_arguments:
latest_prosecutor_arg = prosecutor_arguments[-1]
agent.rebut_prosecutor_argument(self.current_debate_round_id, latest_prosecutor_arg["argument_id"])
else:
agent.make_opening_statement(self.current_debate_round_id)
# 其他角色代理的逻辑
# 结束当前回合
self.graph_db.add_node("DecisionLog", {
"log_id": str(uuid.uuid4()),
"event_type": "ROUND_ENDED",
"timestamp": datetime.datetime.now().isoformat(),
"details": {"case_id": self.current_case_id, "round_id": self.current_debate_round_id}
})
print(f"--- Debate Round {self.round_counter} Ended ---")
# 准备下一回合
self._start_new_debate_round()
# 示例运行
if __name__ == "__main__":
llm_service = LLMService()
graph_db = GraphDB({}) # 实际会传入数据库连接配置
orchestrator = SupremeCourtOrchestrator(graph_db, llm_service)
# 创建代理
prosecutor_agent = ProsecutorAgent("agent_p1", "控方律师甲", llm_service, graph_db)
defense_agent = DefenseAgent("agent_d1", "辩方律师乙", llm_service, graph_db)
orchestrator.add_agent(prosecutor_agent)
orchestrator.add_agent(defense_agent)
# 启动案件
case_id = orchestrator.start_case("合同违约赔偿案", "原告主张被告未按时交付货物,要求赔偿。", ["agent_p1", "agent_d1"])
# 运行几个辩论回合
for i in range(3):
orchestrator.run_debate_round()
# 实际中会根据辩论状态决定是否继续下一回合
上述代码展示了辩论的基本框架。在实际应用中,代理的智能体部分会更复杂,例如:
- 证据检索 (Evidence Retrieval): 代理在生成论点前,会从证据库中检索相关证据,这些证据也会作为
Evidence节点添加到图中。 - 论点评估 (Argument Evaluation): 代理不仅生成论点,还会评估其他代理论点的强度、相关性和漏洞,这可能涉及更复杂的LLM推理或专用评估模型。评估结果可以作为
RELATES_TO关系的属性(如confidence)或Argument节点的属性(如impact_score)。 - 策略选择 (Strategy Selection): 代理会根据辩论的进展动态调整策略,例如何时提出关键证据,何时进行总结。
共识投票系统
当辩论进入尾声,或者在预设的辩论回合结束后,系统将触发共识投票阶段。这一阶段的目标是让所有参与的代理基于辩论中积累的论点和证据,形成一个初步的、由AI驱动的判决。
1. 投票触发机制
- 回合阈值: 达到预设的辩论回合数后自动触发。
- 论点饱和: 当新的论点生成量显著下降,且现有论点之间的
RELATES_TO关系不再频繁更新时,表明辩论趋于饱和。 - 代理请求: 某个(或多个)代理认为辩论充分,可以发起投票请求。
- 人工干预: 管理员可以手动触发投票。
2. 投票选项与过程
- 投票选项: 针对案件的性质,投票选项可以是预定义的(如“有罪/无罪”、“同意/不同意某个提议”),也可以是代理在辩论过程中提出的具体
Verdict提议。- 例如,如果案件是关于合同违约,代理可能会投票“支持原告赔偿”、“支持被告无责”或“需要进一步调解”。
- 投票记录: 每个代理会根据其内部对辩论结果的评估,投下一票。这一票被记录为一个
Vote节点,并通过CASTS_VOTE关系连接到Agent节点,并通过VOTES_ON_VERDICT_PROPOSAL关系连接到其投票的Verdict提议(如果存在)。Vote节点会包含decision(投票结果)和confidence(代理对其投票的置信度)属性。置信度是衡量代理对其决策确定性的一个重要指标,可以由LLM在生成投票时提供。
3. 共识算法
仅仅统计票数是远远不够的。我们需要更复杂的算法来评估共识的质量和强度。
- 简单多数 (Simple Majority): 最直接的方式,得票最多的选项获胜。
- 加权投票 (Weighted Voting):
- 基于角色权重: 某些角色的代理(如
NEUTRAL_OBSERVER或EXPERT_WITNESS)可能拥有更高的权重。 - 基于可靠性评分: 代理的历史表现、在过去案件中的准确性可以用来计算
reliability_score,作为投票权重。 - 基于论点强度: 代理的投票可以根据其支持的论点链的整体强度进行加权。如果一个代理的投票由一系列强有力的、未被反驳的论点支持,其投票权重可以更高。
- 基于角色权重: 某些角色的代理(如
- 阈值共识 (Threshold Consensus): 并非简单多数,而是要求达到某个百分比(例如,75%)的票数才算达成共识。如果未达到,则可能触发进一步的辩论或直接进入人类终审。
- 迭代投票 (Iterative Voting): 如果第一次投票未能达成共识,系统可以要求代理重新审视辩论,特别是那些未被充分讨论的论点或争议焦点,然后进行新一轮投票。
- 动态权重与影响力 (Dynamic Weighting and Influence):
- 如果一个代理在辩论中能够成功地“说服”其他代理改变立场(通过
RELATES_TO关系中的relation_type: 'CONVINCES'或agent_id改变其后续论点或投票方向来推断),其影响力权重可以暂时增加。 - 对关键论点(如被多次引用、未被有效反驳的论点)的投票,可以获得更高的权重。
- 如果一个代理在辩论中能够成功地“说服”其他代理改变立场(通过
4. 代码示例:投票和共识计算
# 假设的GraphDB类已定义
# 假设的BaseAgent及其子类已定义
class ConsensusVotingSystem:
def __init__(self, graph_db: GraphDB):
self.graph_db = graph_db
def trigger_voting(self, case_id: str, agents: List[BaseAgent]) -> Dict[str, Any]:
print(f"n--- Triggering Voting for Case {case_id} ---")
# 1. 代理投票
all_votes = []
for agent in agents:
if agent.case_id == case_id:
# 代理根据当前案件的论点和证据,生成投票决策
# 实际这里会调用LLM进行推理,生成决策和置信度
# 简化:随机生成决策和置信度
decision = "GUILTY" if agent.role == "PROSECUTOR" else "NOT_GUILTY"
confidence = 0.7 + (0.3 * (hash(agent.agent_id) % 100) / 100) # 模拟置信度
vote_id = self.graph_db.add_node("Vote", {
"vote_id": str(uuid.uuid4()),
"decision": decision,
"confidence": confidence,
"voted_at": datetime.datetime.now().isoformat(),
"agent_id": agent.agent_id # 记录投票代理
})
self.graph_db.add_relationship(agent.agent_id, vote_id, "CASTS_VOTE")
# 假设有一个默认的Verdict提案,所有代理都对它投票
default_verdict_proposal_id = self.graph_db.add_node("Verdict", {
"verdict_id": str(uuid.uuid4()),
"summary": f"Proposed verdict for case {case_id}",
"outcome": "PENDING",
"reasoning": "Initial proposal",
"decided_by": "AGENTS_PROPOSAL"
})
self.graph_db.add_relationship(vote_id, default_verdict_proposal_id, "VOTES_ON_VERDICT_PROPOSAL")
self.graph_db.add_relationship(default_verdict_proposal_id, case_id, "RESULT_OF")
all_votes.append({"agent_id": agent.agent_id, "decision": decision, "confidence": confidence, "role": agent.role})
print(f"Agent {agent.name} ({agent.role}) voted: {decision} with confidence {confidence:.2f}")
# 2. 计算共识
consensus_result = self._calculate_consensus(all_votes)
print(f"nConsensus Result: {consensus_result}")
# 更新案件状态
self.graph_db.add_relationship(case_id, default_verdict_proposal_id, "PROPOSED_VERDICT_CANDIDATE") # 标记为候选判决
self.graph_db.add_node("DecisionLog", {
"log_id": str(uuid.uuid4()),
"event_type": "VOTING_ENDED",
"timestamp": datetime.datetime.now().isoformat(),
"details": {"case_id": case_id, "consensus": consensus_result}
})
return consensus_result
def _calculate_consensus(self, votes: List[Dict]) -> Dict[str, Any]:
# 我们可以实现不同的共识算法
# 这里演示一个简单的加权投票和阈值共识
vote_counts: Dict[str, float] = {}
total_weight = 0.0
# 定义角色权重
role_weights = {"PROSECUTOR": 1.0, "DEFENSE": 1.0, "NEUTRAL": 1.5, "EXPERT": 1.2}
for vote in votes:
decision = vote['decision']
confidence = vote['confidence']
role = vote['role']
# 权重 = 角色权重 * 代理置信度 (可以根据实际情况调整公式)
weight = role_weights.get(role, 1.0) * confidence
vote_counts[decision] = vote_counts.get(decision, 0.0) + weight
total_weight += weight
if total_weight == 0:
return {"status": "NO_VOTES", "winning_decision": None, "score": 0.0, "threshold_met": False}
# 计算百分比
weighted_percentages = {dec: (count / total_weight) * 100 for dec, count in vote_counts.items()}
# 找出得票最高的决策
winning_decision = max(weighted_percentages, key=weighted_percentages.get) if weighted_percentages else None
winning_score = weighted_percentages.get(winning_decision, 0.0) if winning_decision else 0.0
# 设定共识阈值,例如70%
consensus_threshold = 70.0
threshold_met = winning_score >= consensus_threshold
return {
"status": "CONSENSUS_REACHED" if threshold_met else "NO_CONSENSUS",
"winning_decision": winning_decision,
"winning_score": winning_score,
"threshold_met": threshold_met,
"details": weighted_percentages
}
# 示例运行 (接上文 Orchestrator)
if __name__ == "__main__":
llm_service = LLMService()
graph_db = GraphDB({})
orchestrator = SupremeCourtOrchestrator(graph_db, llm_service)
prosecutor_agent = ProsecutorAgent("agent_p1", "控方律师甲", llm_service, graph_db)
defense_agent = DefenseAgent("agent_d1", "辩方律师乙", llm_service, graph_db)
# 增加一个中立观察者代理
neutral_agent = BaseAgent("agent_n1", "中立观察员", "NEUTRAL", llm_service, graph_db)
orchestrator.add_agent(prosecutor_agent)
orchestrator.add_agent(defense_agent)
orchestrator.add_agent(neutral_agent)
case_id = orchestrator.start_case("合同违约赔偿案", "原告主张被告未按时交付货物,要求赔偿。", ["agent_p1", "agent_d1", "agent_n1"])
for i in range(3):
orchestrator.run_debate_round()
# 触发投票
voting_system = ConsensusVotingSystem(graph_db)
all_agents_in_case = [orchestrator.agents["agent_p1"], orchestrator.agents["agent_d1"], orchestrator.agents["agent_n1"]] # 实际应从orchestrator获取
consensus_result = voting_system.trigger_voting(case_id, all_agents_in_case)
# 根据共识结果决定是否进入人类终审
if not consensus_result["threshold_met"]:
print(f"nConsensus not reached. Triggering human review for case {case_id}.")
# 实际会调用人类终审模块
人类终审集成
人类终审是整个“最高法院”架构的最终防线和信任保障。它确保在AI决策可能存在缺陷、争议或涉及高度伦理敏感性时,人类的智慧和价值观能够发挥决定性作用。
1. 触发机制
人类终审的触发可以是多方面、灵活的:
- 共识未达标: 如前所述,当AI代理未能达成预设的共识阈值时,自动将案件提交人类复审。
- 高风险案件: 对于预先标记为“高风险”或“高敏感度”的案件类型,无论AI代理是否达成共识,都必须强制进行人类终审。
- 代理请求: 某些代理(例如,
NEUTRAL_OBSERVER)被赋予权限,可以在辩论或投票过程中,发现重大疑虑时,主动请求人类介入。 - 人工干预: 系统管理员或特定权限用户可以随时手动将任何案件提交人类复审。
当人类复审被触发时,系统会创建一个 HumanReview 节点,并通过 TRIGGERS_REVIEW 关系连接到 Case 节点,并记录触发原因。
2. 复审界面与信息呈现
为人类复审者提供一个高效、直观的界面至关重要。这个界面需要能够将复杂的图数据以易于理解的方式呈现出来。
- 案件总览: 简洁呈现案件的背景、核心问题和现有状态。
- 辩论时间线: 以时间顺序展示所有
DebateRound,每个回合下的Argument及其PRESENTS_ARGUMENT代理。 - 论点网络图: 可视化
Argument节点及其RELATES_TO(反驳、支持、阐述) 关系。这能让人类复审者迅速识别关键论点、争议焦点和论证链。- 可以高亮显示那些置信度低、被多次反驳、或未被有效支持的论点。
- 证据溯源: 当点击某个
Argument时,能够迅速追溯其SUPPORTS或CONTRADICTS的Evidence节点,并查看证据的详细内容和来源。 - 代理视图: 展示每个
Agent的角色、其提出的所有论点、以及其最终投票和置信度。 - 投票结果与共识分析: 清晰展示各代理的投票分布、加权共识得分,以及未能达成共识的原因(例如,不同决策之间的分数差距过小)。
- 交互式探索: 允许人类复审者在图可视化界面中进行筛选、缩放、点击,深入探究感兴趣的节点和关系,例如,查看某个代理的所有发言,或某个论点被反驳的所有路径。
3. 人类决策与反馈闭环
人类复审者在充分理解案件后,可以做出以下决策:
- 批准 (Approve) 代理判决: 如果人类认同AI代理达成的共识判决,则批准。系统会更新
Verdict节点,decided_by设为 ‘HUMAN’,并通过APPROVES关系连接到HumanReview。 - 推翻 (Override) 代理判决: 如果人类认为AI判决存在重大缺陷或不公,可以推翻它,并给出新的裁决。系统会更新
Verdict节点,decided_by设为 ‘HUMAN’,outcome更改为人类指定结果,并通过OVERRULES关系连接到HumanReview。 - 请求进一步辩论/证据 (Request Further Debate/Evidence): 如果人类认为现有信息不足,可以要求系统重新启动部分辩论流程,或指令代理寻找更多证据。这会更新
Case状态,并可能创建一个新的DebateRound。
审计性: 无论人类做出何种决策,其决定、理由 (notes 属性在 HumanReview 节点中) 和时间戳都将完整地记录在图中,与AI代理的决策过程一同构成完整的审计链。
反馈闭环: 人类复审的决策,特别是推翻代理判决的情况,是系统学习和改进的宝贵数据。
- 这些案例可以用于重新训练或微调LLM代理,使其在未来的相似案件中做出更准确的判断。
- 可以用于优化共识算法,使其更能识别潜在的争议点。
- 可以用于调整代理的辩论策略,例如,某个代理总是被人类推翻其判决,可能意味着其推理能力或角色策略存在问题。
4. 代码示例:人类复审触发和决策记录
# 假设GraphDB、BaseAgent、ConsensusVotingSystem已定义
class HumanReviewSystem:
def __init__(self, graph_db: GraphDB):
self.graph_db = graph_db
def trigger_human_review(self, case_id: str, reason: str, reviewer_id: str = "SYSTEM") -> str:
review_id = self.graph_db.add_node("HumanReview", {
"review_id": str(uuid.uuid4()),
"reviewer_id": reviewer_id,
"review_start_time": datetime.datetime.now().isoformat(),
"action": "PENDING",
"notes": f"Review triggered due to: {reason}"
})
self.graph_db.add_relationship(case_id, review_id, "TRIGGERS_REVIEW", {"reason": reason})
print(f"n--- Human Review Triggered for Case {case_id} (Review ID: {review_id}) ---")
return review_id
def record_human_verdict(self, review_id: str, verdict_id: str, action: str, human_outcome: Optional[str] = None, notes: str = ""):
# 更新 HumanReview 节点
self.graph_db.add_node("HumanReview", {"review_id": review_id, "action": action, "notes": notes}) # 假设能更新现有节点
if action == "APPROVE":
self.graph_db.add_relationship(review_id, verdict_id, "APPROVES")
# 更新 Verdict 节点为最终结果
self.graph_db.add_node("Verdict", {"verdict_id": verdict_id, "decided_by": "HUMAN"})
print(f"Human reviewer {review_id} APPROVED verdict {verdict_id}. Notes: {notes}")
elif action == "OVERRIDE" and human_outcome:
self.graph_db.add_relationship(review_id, verdict_id, "OVERRULES")
# 创建或更新 Verdict 节点为人类指定结果
self.graph_db.add_node("Verdict", {"verdict_id": verdict_id, "outcome": human_outcome, "decided_by": "HUMAN", "reasoning": notes})
print(f"Human reviewer {review_id} OVERRODE verdict {verdict_id} with outcome {human_outcome}. Notes: {notes}")
elif action == "REQUEST_MORE_INFO":
print(f"Human reviewer {review_id} REQUESTED MORE INFO for case. Notes: {notes}")
# 实际会触发案件状态回到DEBATING,并可能通知代理进行补充辩论
self.graph_db.add_node("DecisionLog", {
"log_id": str(uuid.uuid4()),
"event_type": f"HUMAN_REVIEW_{action.upper()}",
"timestamp": datetime.datetime.now().isoformat(),
"details": {"review_id": review_id, "verdict_id": verdict_id, "action": action, "notes": notes}
})
# 示例运行 (接上文 Orchestrator 和 VotingSystem)
if __name__ == "__main__":
llm_service = LLMService()
graph_db = GraphDB({})
orchestrator = SupremeCourtOrchestrator(graph_db, llm_service)
prosecutor_agent = ProsecutorAgent("agent_p1", "控方律师甲", llm_service, graph_db)
defense_agent = DefenseAgent("agent_d1", "辩方律师乙", llm_service, graph_db)
neutral_agent = BaseAgent("agent_n1", "中立观察员", "NEUTRAL", llm_service, graph_db)
orchestrator.add_agent(prosecutor_agent)
orchestrator.add_agent(defense_agent)
orchestrator.add_agent(neutral_agent)
case_id = orchestrator.start_case("合同违约赔偿案", "原告主张被告未按时交付货物,要求赔偿。", ["agent_p1", "agent_d1", "agent_n1"])
for i in range(3):
orchestrator.run_debate_round()
voting_system = ConsensusVotingSystem(graph_db)
all_agents_in_case = [orchestrator.agents["agent_p1"], orchestrator.agents["agent_d1"], orchestrator.agents["agent_n1"]]
consensus_result = voting_system.trigger_voting(case_id, all_agents_in_case)
# 假设共识未达成,触发人类复审
human_review_system = HumanReviewSystem(graph_db)
if not consensus_result["threshold_met"]:
review_id = human_review_system.trigger_human_review(case_id, "Consensus not met")
# 模拟人类复审并做出决策
# 假设这里人类看到了一个不合理的AI判决,决定推翻
# 实际的 verdict_id 需要从 voting_system 获取,这里简化为默认提案
# 获取最新的Verdict节点作为人类复审的目标
# 实际查询:MATCH (c:Case)-[:PROPOSED_VERDICT_CANDIDATE]->(v:Verdict) WHERE c.case_id = $case_id RETURN v LIMIT 1
# 简化:假设 default_verdict_proposal_id 就是我们上面创建的那个
latest_verdict_id_for_review = "some_verdict_id_from_voting_system"
# For demonstration, let's assume we know the ID of the last proposed verdict.
# In a real system, you'd query the graph to find the active verdict proposal.
# For the current simple example, let's just make one up or grab the one from the voting system's internal state if available.
# For this specific example's run, the voting system's 'default_verdict_proposal_id' isn't easily accessible here.
# So we'll simulate finding it or linking to a new one for human decision.
# A robust system would have the voting system return the actual verdict node ID it operated on.
# Let's create a placeholder for now and assume the human system would retrieve the correct one.
placeholder_verdict_id = graph_db.add_node("Verdict", {
"verdict_id": str(uuid.uuid4()), "summary": "AI proposed verdict", "outcome": consensus_result["winning_decision"], "decided_by": "AGENTS"
})
graph_db.add_relationship(placeholder_verdict_id, case_id, "RESULT_OF") # Link to case
human_review_system.record_human_verdict(
review_id,
placeholder_verdict_id,
"OVERRIDE",
"NOT_GUILTY",
"代理未能充分考虑证据X,该证据表明被告行为非直接导致损失。"
)
else:
print(f"nConsensus reached: {consensus_result['winning_decision']} with score {consensus_result['winning_score']:.2f}%. Case closed by agents.")
# 实际会更新案件状态为CLOSED,并记录最终判决
技术实现栈与数据流
为了将上述设计理念付诸实践,我们需要一个稳健的技术栈和清晰的数据流。
1. 技术栈
-
图数据库 (Graph Database):
- Neo4j: 业界领先的图数据库,支持Cypher查询语言,拥有成熟的生态系统和可视化工具。适合需要复杂路径查询和实时图分析的场景。
- ArangoDB: 多模型数据库,除了图模型,还支持文档和键值对,提供AQL查询语言。在需要兼顾多种数据模型的场景下表现优秀。
- Dgraph: 分布式、高可用的GraphQL原生图数据库。
- 考虑因素: 数据规模、查询复杂度、高可用性要求、团队熟悉度。
-
后端服务 (Backend Services):
- Python (FastAPI / Django / Flask): 作为主要的编程语言,提供强大的AI库和丰富的Web框架。
- FastAPI: 异步非阻塞,高性能,适合构建API服务。
- Django / Flask: 适用于更复杂的业务逻辑和管理后台。
- 作用: 协调Agent、与图数据库交互、处理人机交互请求、管理案件生命周期。
- Python (FastAPI / Django / Flask): 作为主要的编程语言,提供强大的AI库和丰富的Web框架。
-
代理框架与LLM集成 (Agent Framework & LLM Integration):
- LangChain / LlamaIndex: 提供结构化的方式来构建LLM应用,包括代理链、工具集成和RAG(检索增强生成)功能。
- LLM API (OpenAI API, Anthropic Claude, Google Gemini): 提供强大的基础语言模型能力,用于代理的论点生成、推理、评估。
- 本地部署LLM (Llama.cpp, Ollama): 对于隐私敏感或需要离线部署的场景。
- 作用: 封装LLM调用,管理代理的记忆、工具使用和推理逻辑。
-
前端界面 (Frontend UI – Optional but Recommended):
- React / Vue.js: 构建复杂、交互性强的人类复审界面。
- D3.js / Cytoscape.js / Vis.js: 用于可视化图结构,提供直观的辩论过程展示。
- 作用: 提供人类复审者所需的案件总览、辩论可视化、证据查阅和决策输入界面。
2. 数据流 (Data Flow)
整个系统的数据流可以概括为以下步骤:
-
案件摄入 (Case Ingestion):
- 人类用户或外部系统提交一个新案件 (
Case节点)。 - 案件的初始描述和相关证据 (
Evidence节点) 被存储到图数据库中。 - 技术: Backend API接收请求 -> GraphDB存储。
- 人类用户或外部系统提交一个新案件 (
-
代理分配与初始化 (Agent Assignment & Initialization):
- 后端服务根据案件类型和配置,从代理池中选择并分配
Agent节点。 HAS_AGENT关系建立。- 技术: Backend Orchestrator逻辑 -> GraphDB更新。
- 后端服务根据案件类型和配置,从代理池中选择并分配
-
多代理辩论 (Multi-Agent Debate):
- Orchestrator 启动
DebateRound。 Agent循环:Agent从 GraphDB 查询当前案件的Argument和Evidence,作为 LLM 的上下文。Agent调用 LLM 生成新的Argument内容。Agent将新Argument及其与现有Argument、Evidence之间的RELATES_TO、SUPPORTS等关系写入 GraphDB。
- 技术: Backend Orchestrator调度 -> Agent LLM调用 -> GraphDB更新。
- Orchestrator 启动
-
共识投票 (Consensus Voting):
- 当辩论满足触发条件时,Orchestrator 指示
Agent进行投票。 - 每个
Agent基于 GraphDB 中积累的Argument和Evidence形成Vote,并计算confidence。 Vote节点和CASTS_VOTE关系被写入 GraphDB。- ConsensusVotingSystem 计算加权共识,并将结果记录为
DecisionLog。 - 技术: Backend Orchestrator -> Agent LLM调用 -> GraphDB更新 -> Backend Consensus Calculation。
- 当辩论满足触发条件时,Orchestrator 指示
-
共识评估与人类终审触发 (Consensus Evaluation & Human Review Trigger):
- Orchestrator 评估共识结果。
- 如果共识未达标或案件为高风险,触发
HumanReview节点,并通过TRIGGERS_REVIEW关系连接到Case。 - 技术: Backend Orchestrator逻辑 -> GraphDB更新。
-
人类终审 (Human Final Review):
- 人类复审者通过前端 UI 访问案件。
- UI 从 GraphDB 查询案件的完整辩论图谱(
Case、Agent、Argument、Evidence、Vote、DebateRound及其所有关系)。 - 人类复审者在 UI 上进行决策(批准/推翻/请求更多信息)。
- 人类决策 (
HumanReview节点的action和notes) 更新到 GraphDB,并最终确定Verdict。 - 技术: Frontend UI -> Backend API -> GraphDB查询/更新。
-
反馈与学习 (Feedback & Learning):
- 人类终审结果,特别是推翻决策的案例,被标记为训练数据。
- 这些数据可用于离线或在线微调
Agent的 LLM 模型,或调整其推理策略。 - 技术: Backend Analytics -> ML Training Pipeline。
通过这种清晰的数据流和模块化的技术栈,我们构建了一个既能发挥AI高效分析能力,又能确保人类价值观最终主导的智能决策系统。
代码实现细节
在之前的章节中,我们已经提供了基础的Python代码片段。现在,我们将进一步深入,展示一些更具体的实现细节,特别是如何通过一个抽象层与图数据库进行交互。
1. Graph Database Interaction Layer
为了使上层业务逻辑与具体的图数据库实现解耦,我们通常会创建一个抽象层。这里以Python为例,模拟与Neo4j的交互(实际会使用neo4j驱动)。
import uuid
import datetime
from typing import Dict, List, Any, Optional
class GraphDBClient:
"""
一个用于抽象图数据库操作的客户端。
实际应用中,这里会封装Neo4j、ArangoDB等具体数据库的API调用。
"""
def __init__(self, uri, user, password):
# 实际这里会初始化数据库驱动,例如Neo4j的GraphDatabase.driver
print(f"Connecting to GraphDB at {uri}...")
self._uri = uri
self._user = user
self._password = password
print("GraphDB client initialized (mock).")
def _execute_query(self, query: str, parameters: Dict = None) -> List[Dict]:
"""
模拟执行Cypher查询。
在真实环境中,这里会调用数据库驱动的session.run()方法。
"""
print(f"nExecuting Cypher Query:n{query}nParameters: {parameters}")
# 模拟数据返回
if "MATCH (n:Case)" in query and "RETURN n.case_id" in query:
return [{"case_id": "CASE001_mock"}] # 模拟返回一个case
if "MATCH (a:Argument)" in query and "RETURN a.content" in query:
return [{"content": "Mock argument content."}]
return []
def create_node(self, label: str, properties: Dict) -> str:
"""创建一个新节点,并返回其唯一ID (这里我们使用uuid)。"""
if 'id' not in properties:
properties['id'] = str(uuid.uuid4()) # 确保每个节点有一个唯一ID
# 将properties转换为Cypher可用的字符串
props_str = ", ".join([f"{k}: ${k}" for k in properties.keys()])
query = f"CREATE (n:{label} {{{props_str}}}) RETURN n.id AS id"
result = self._execute_query(query, properties)
return result[0]['id'] if result else None
def update_node_properties(self, label: str, node_id: str, properties: Dict):
"""更新节点属性。"""
props_str = ", ".join([f"n.{k} = ${k}" for k in properties.keys()])
query = f"MATCH (n:{label} {{id: $node_id}}) SET {props_str}"
self._execute_query(query, {"node_id": node_id, **properties})
def create_relationship(self, start_node_id: str, start_label: str, end_node_id: str, end_label: str, rel_type: str, properties: Dict = None):
"""创建两个节点之间的关系。"""
props_str = ""
if properties:
props_str = ", ".join([f"{k}: ${k}" for k in properties.keys()])
props_str = f" {{{props_str}}}"
query = (
f"MATCH (a:{start_label} {{id: $start_node_id}}), (b:{end_label} {{id: $end_node_id}}) "
f"CREATE (a)-[r:{rel_type}{props_str}]->(b)"
)
self._execute_query(query, {"start_node_id": start_node_id, "end_node_id": end_node_id, **(properties if properties else {})})
def get_case_arguments_with_relations(self, case_id: str) -> List[Dict]:
"""
获取一个案件的所有论点及其关键关系,用于构建代理的上下文。
返回的数据结构会包含论点内容、提出者、以及它与其他论点的关系。
"""
query = """
MATCH (c:Case {id: $case_id})<-[:ARGUMENT_FOR]-(arg:Argument)
OPTIONAL MATCH (arg)<-[:PRESENTS_ARGUMENT]-(agent:Agent)
OPTIONAL MATCH (arg)-[r:RELATES_TO]->(targetArg:Argument)
RETURN
arg.id AS argument_id,
arg.content AS content,
arg.type AS type,
arg.confidence_score AS confidence_score,
agent.id AS agent_id,
agent.name AS agent_name,
agent.role AS agent_role,
COLLECT({
target_argument_id: targetArg.id,
relation_type: r.relation_type,
relation_confidence: r.confidence
}) AS relations
ORDER BY arg.generated_at
"""
return self._execute_query(query, {"case_id": case_id})
def get_votes_for_case(self, case_id: str) -> List[Dict]:
"""获取一个案件的所有投票。"""
query = """
MATCH (c:Case {id: $case_id})<-[:RESULT_OF]-(v:Verdict)<-[:VOTES_ON_VERDICT_PROPOSAL]-(vote:Vote)<-[:CASTS_VOTE]-(agent:Agent)
RETURN
agent.id AS agent_id,
agent.name AS agent_name,
agent.role AS agent_role,
vote.decision AS decision,
vote.confidence AS confidence,
vote.voted_at AS voted_at
"""
return self._execute_query(query, {"case_id": case_id})
def get_latest_verdict_proposal_id(self, case_id: str) -> Optional[str]:
"""获取案件最新的判决提案ID。"""
query = """
MATCH (c:Case {id: $case_id})-[r:PROPOSED_VERDICT_CANDIDATE]->(v:Verdict)
RETURN v.id AS verdict_id
ORDER BY r.timestamp DESC LIMIT 1
"""
result = self._execute_query(query, {"case_id": case_id})
return result[0]['verdict_id'] if result else None
# 更新之前的GraphDB类,使其使用GraphDBClient
class GraphDB(GraphDBClient):
def __init__(self, db_config: Dict):
super().__init__(db_config.get("uri", "bolt://localhost:7687"),
db_config.get("user", "neo4j"),
db_config.get("password", "password"))
# 重写或适配原有的add_node/add_relationship方法以使用create_node/create_relationship
def add_node(self, label: str, properties: Dict) -> str:
return self.create_node(label, properties)
def add_relationship(self, start_node_id: str, end_node_id: str, rel_type: str, properties: Dict = None):
# 需要知道起始和终止节点的标签,这里简化处理,实际需要从节点ID反查或传入
# 为了演示,我们暂时假设能从传入的id推断label或直接传入label
# 更严谨的做法是在创建节点时返回一个包含id和label的元组
# 或者在GraphDBClient中实现一个方法来根据ID获取节点信息
# 简化版:我们假设 start_node_id 和 end_node_id 已经包含了足够的上下文
# 实际中,可以这样做:
# start_node_label = self._get_node_label_by_id(start_node_id)
# end_node_label = self._get_node_label_by_id(end_node_id)
# self.create_relationship(start_node_id, start_node_label, end_node_id, end_node_label, rel_type, properties)
# 暂时使用一个简单的模拟,实际应该传入node的label
# 假设start_node_id和end_node_id是uuid,并且在创建时已经指定了label
# 这里的示例为了避免过度复杂化,直接用传入的label参数,而这个参数在业务代码中需要显式提供
# 为了让之前的Agent和Orchestrator代码兼容,我们修改一下add_relationship的签名
# 或者在GraphDBClient中提供一个更通用的创建关系方法,不依赖label
print(f"Adding relationship: {start_node_id} -[:{rel_type}]-> {end_node_id} with properties {properties}")
# 这里只是模拟打印,实际会调用GraphDBClient.create_relationship,但需要节点标签
# 暂时跳过label的获取,直接调用模拟的_execute_query
self._execute_query(f"MATCH (a {{id: $start_node_id}}), (b {{id: $end_node_id}}) CREATE (a)-[r:{rel_type} $properties]->(b)",
{"start_node_id": start_node_id, "end_node_id": end_node_id, "properties": properties})
重要说明: GraphDB 类的 add_relationship 方法在实际实现中需要知道起始和终止节点的标签 (label)。在我们的模拟中,为了简化,我暂时跳过了从 ID 反查标签的复杂逻辑,直接调用了 _execute_query。在真实的 Neo4j 驱动中,session.run() 可以直接通过 MATCH (a {id: ...}) 来找到节点,而无需明确指定标签,但这在创建关系时不是最佳实践,因为它可能性能较低。更