main
fengruixiang 2026-05-15 12:46:31 +08:00
parent 3851474aa3
commit 09f5be0180
8 changed files with 343 additions and 50 deletions

View File

@ -1,2 +1,3 @@
人与助手之间的语言交互默认为简体中文。 人与助手之间的语言交互默认为简体中文。
python 要使用 uv run 运行。 python 要使用 uv run 运行。
不要随意创建说明文档(比如 .md),除非用户有要求。

View File

@ -0,0 +1,41 @@
plaintiff:
- MO YUK PING
defendant:
- HONG KONG SPECIAL ADMINISTRATIVE REGION
jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location:
- 香港特別行政區
- Hong Kong Special Administrative Region
case_reason: >-
Applicant Mo Yuk Ping seeks leave to appeal against convictions for conspiracy to defraud and conspiracy
to pervert the course of public justice from CACC No. 26 of 2006.
case_object:
- leave to appeal
- conviction for conspiracy to defraud
- conviction for conspiracy to pervert the course of public justice
judgment_result:
- charge: Application for leave to appeal against conviction for conspiracy to pervert the course of public
justice under Charge 3 (liability issue)
result: Dismissed. Court of Appeal was right in refusing to certify points 2 to 5, no basis for leave
to appeal on 'substantial and grave injustice' ground, applicant does not have leave to appeal against
this conviction.
- charge: Application for leave to appeal against conviction for conspiracy to defraud under Charge 2
(liability issue)
result: Partially allowed. Time extended and leave to appeal granted, but limited to the certified point
on whether elements of conspiracy to defraud are sufficiently precise to satisfy constitutional requirement
of legal certainty. Court considered it appropriate to grant leave for pursuing this certified point.
judgment_summary: >-
申请人莫玉萍就串谋诈骗罪及串谋妨碍司法公正罪定罪申请上诉许可。上诉法院已就串谋诈骗罪构成要件是否符合法律确定性的宪法要求发出证明书。终审法院审查证据采纳规则、法官指示必要性及上诉法院对原审法官裁断的解读等争议点。法院认为上诉法院正确拒绝就第2至5点发出证明书,该等争议在案情脉络下不成立或属明显法律原则,不符合「重大严重不公」标准。最终批准就已发证明书的法律确定性问题延长时间并准予上诉许可,但驳回就妨碍司法公正罪定罪的上诉许可申请。
involved_entities:
- entity_name: Chief Justice Li
reason: Member of the Appeal Committee in this case, responsible for determining the application for
leave to appeal.
- entity_name: Mr Justice Bokhary PJ
reason: Presiding judge in this case, member of the Appeal Committee, authored the determination.
- entity_name: Mr Justice Chan PJ
reason: Member of the Appeal Committee in this case, responsible for determining the application for
leave to appeal.
- entity_name: Mo Yuk Ping
reason: Applicant in this criminal appeal case, convicted of conspiracy to defraud and conspiracy to
pervert the course of public justice.

View File

@ -0,0 +1,48 @@
plaintiff:
- MO YUK PING
defendant:
- HONG KONG SPECIAL ADMINISTRATIVE REGION
jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location:
- 香港特別行政區
- Hong Kong Special Administrative Region
case_reason: >-
Applicant seeks leave to appeal to the Court of Final Appeal against her convictions for conspiracy
to defraud and perverting the course of justice (CACC 26/2006).
case_object:
- criminal conviction
- constitutional right to legal certainty
- liberty
judgment_result:
- charge: Application for leave to appeal against conviction for conspiracy to defraud (Charge 2) (liability
issue)
result: Allowed. The Court granted leave to appeal limited to the certified point regarding whether
the elements of the offence of conspiracy to defraud are sufficiently precise to satisfy the constitutional
requirement of legal certainty.
- charge: Application for leave to appeal against conviction for conspiracy to pervert the course of public
justice (Charge 3) (liability issue)
result: Dismissed. The Court refused to certify the applicant's additional points (2 to 5) and found
no basis for leave to appeal on the 'substantial and grave injustice' ground, agreeing with the Court
of Appeal's reasons for refusal.
judgment_summary: >-
Applicant Mo Yuk Ping sought leave to appeal convictions for conspiracy to defraud and perverting justice.
The core issue was whether the fraud charge met constitutional requirements for legal certainty. The
Court accepted the certified point on legal certainty as a matter of great importance. However, it rejected
other points, finding the lower court's evidence assessment and factual findings were sound. Leave to
appeal was granted only for the certified point on Charge 2.
involved_entities:
- entity_name: Mo Yuk Ping
reason: The Applicant in this case, seeking leave to appeal against convictions for conspiracy to defraud
and conspiracy to pervert the course of public justice.
- entity_name: Hong Kong Special Administrative Region
reason: The Respondent in this case, representing the prosecution.
- entity_name: Chief Justice Li
reason: Member of the Appeal Committee presiding over the application for leave to appeal.
- entity_name: Mr Justice Bokhary PJ
reason: Member of the Appeal Committee and the judge who delivered the determination in this case.
- entity_name: Mr Justice Chan PJ
reason: Member of the Appeal Committee presiding over the application for leave to appeal.
- entity_name: Court of Appeal
reason: The lower court that certified one point of law but refused to certify others, whose judgment
is the subject of this leave application.

View File

@ -0,0 +1,41 @@
plaintiff:
- MO YUK PING
defendant:
- HONG KONG SPECIAL ADMINISTRATIVE REGION
jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location:
- 香港特別行政區
- Hong Kong Special Administrative Region
case_reason: >-
Plaintiff, a security guard, claims personal injury damages from defendant for assault at the mall in
July 2023.
case_object:
- personal injury damages
- medical expenses
- distress damages
judgment_result:
- charge: Conspiracy to defraud charge sufficiency (liability issue)
result: Allowed. Court granted leave to appeal on the certified point regarding whether the elements
of the offence of conspiracy to defraud are sufficiently precise to satisfy the constitutional requirement
of legal certainty. Court held the elements were not sufficiently precise and required further clarification.
reasons:
- The Court of Appeal certified the applicant's first point as raising a constitutional issue regarding
legal certainty
- The higher court found the elements of the offence were insufficiently defined
- The appeal was limited to this specific legal certainty issue
judgment_summary: >-
MO YUK PING sued the HKSAR for personal injury damages from a 2023 mall assault. Core issues centered
on liability and compensation for medical expenses and distress. The Court of Final Appeal certified
that the conspiracy to defraud charge's elements lacked legal certainty, allowing appeal on this point.
Other claims were dismissed as non-justiciable. Judgment granted leave to appeal on the legal certainty
issue, rejecting other points as moot or lacking merit.
involved_entities:
- entity_name: Chief Justice Li
reason: Presiding judge in this case, served as member of Appeal Committee responsible for adjudication.
- entity_name: Mr Justice Bokhary PJ
reason: Presiding judge in this case, served as member of Appeal Committee responsible for adjudication.
- entity_name: Mr Justice Chan PJ
reason: Presiding judge in this case, served as member of Appeal Committee responsible for adjudication.
- entity_name: HONG KONG SPECIAL ADMINISTRATIVE REGION
reason: Respondent in this case, representing the government entity involved in the legal proceedings.

View File

@ -1,33 +1,33 @@
plaintiff: plaintiff:
- HKSAR - HKSAR
defendant: defendant:
- MAK KWONG YIU (麥光耀) - MAK KWONG YIU (麥光耀) (D1)
- CHAN LAI YEE (陳麗兒) - CHAN LAI YEE (陳麗兒) (D2)
- WONG SHUK ON (黃淑安) - WONG SHUK ON (黃淑安) (D3)
- LEE YICK MING (李易明) - LEE YICK MING (李易明) (D4)
jurisdiction_code: HKCFA jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location: case_location:
- 香港特別行政區 - 香港特別行政區
- Hong Kong Special Administrative Region - Hong Kong Special Administrative Region
case_reason: >- case_reason: >-
HKSAR appeals against the Court of Appeal's decision (CACC No. 239 of 2021) to overturn convictions HKSAR appeals against the Court of Appeal's decision restoring the convictions of defendants in a conspiracy
of defendants for conspiracy to conceal CISL's role as placing agent in a connected transaction. charge related to concealing CISLs role as placing agent for CFHL, challenging the Court's
case_object: case_object:
- conviction - convictions
- right to have conviction upheld
- connected transaction determination
judgment_result: judgment_result:
- charge: Conspiracy to conceal CISL's role as placing agent (liability issue) - charge: Conspiracy charges (liability issue)
result: Upheld. Appeal allowed, convictions restored. Court found concealment of CISL's agency constituted result: Allowed. The Court agreed that the defendants' scheme involved concealing CISL's role as the
unlawful connected transaction under Listing Rules, and directors' conflict of interest was central actual placing agent, which was a connected transaction under the Listing Rules.
to conspiracy charges. - charge: Interposition of Gransing being a mere disguise (quantum issue)
result: Partially allowed. While the Court agreed that the interposition of Gransing was an arrangement
to conceal CISL's role, it did not find that this alone was sufficient to establish the conspiracy
charges.
judgment_summary: >- judgment_summary: >-
HKSAR appeals against the Court of Appeal's overturning of convictions for conspiracy to conceal CISL's The case involves HKSAR appealing against convictions for conspiracy charges related to concealing CISL's
role in a connected transaction. Core issues: upholding convictions and determining connected transaction role in a placing agent transaction. The Court upheld that the scheme involved concealing CISLs role,
status. Court rejected narrow 'sham' test from Snook v London, citing Adams v The Queen, finding concealment but partially allowed the appeal by rejecting the sole reliance on interposition as sufficient evidence
of CISL's agency constituted unlawful connected transaction under Listing Rules. Convictions upheld, of conspiracy. Key laws and precedents cited include Adams v The Queen and Mo Yuk Ping v HKSAR.
appeal allowed, with directors' conflicts central to charges. Hong Kong Stock Ex
involved_entities: involved_entities:
- entity_name: Mr Justice Ribeiro PJ - entity_name: Mr Justice Ribeiro PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication. reason: Presiding judge in this case, responsible for fact-finding and adjudication.
@ -39,8 +39,13 @@ involved_entities:
reason: Presiding judge in this case, responsible for fact-finding and adjudication. reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Sir William Young NPJ - entity_name: Sir William Young NPJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication. reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Diplock LJ - entity_name: HKSAR (Appellant)
reason: Served as judge in Snook v London and West Riding Investments Ltd, articulated test for determining reason: Government department involved as the appellant in the case.
sham transactions. - entity_name: MAK KWONG YIU (麥光耀) (D1)
- entity_name: Hong Kong Stock Exchange reason: Respondent in the case.
reason: Institution involved in regulatory framework for connected transactions. - entity_name: CHAN LAI YEE (陳麗兒) (D2)
reason: Respondent in the case.
- entity_name: WONG SHUK ON (黃淑安) (D3)
reason: Respondent in the case.
- entity_name: LEE YICK MING (李易明) (D4)
reason: Respondent in the case.

View File

@ -0,0 +1,51 @@
plaintiff:
- HKSAR
defendant:
- MAK KWONG YIU (麥光耀) (D1)
- CHAN LAI YEE (陳麗兒) (D2)
- WONG SHUK ON (黃淑安) (D3)
- LEE YICK MING (李易明) (D4)
jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location:
- 香港特別行政區
- Hong Kong Special Administrative Region
case_reason: >-
HKSAR appeals against the Court of Appeal's decision restoring the convictions of defendants in a conspiracy
charge related to concealing CISLs role as placing agent for CFHL, challenging the Court's
case_object:
- convictions
judgment_result:
- charge: Conspiracy charges (liability issue)
result: Allowed. The Court agreed that the defendants' scheme involved concealing CISL's role as the
actual placing agent, which was a connected transaction under the Listing Rules.
- charge: Interposition of Gransing being a mere disguise (quantum issue)
result: Partially allowed. While the Court agreed that the interposition of Gransing was an arrangement
to conceal CISL's role, it did not find that this alone was sufficient to establish the conspiracy
charges.
judgment_summary: >-
The case involves HKSAR appealing against convictions for conspiracy charges related to concealing CISL's
role in a placing agent transaction. The Court upheld that the scheme involved concealing CISLs role,
but partially allowed the appeal by rejecting the sole reliance on interposition as sufficient evidence
of conspiracy. Key laws and precedents cited include Adams v The Queen and Mo Yuk Ping v HKSAR.
involved_entities:
- entity_name: Mr Justice Ribeiro PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Fok PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Lam PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Stock NPJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Sir William Young NPJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: HKSAR (Appellant)
reason: Government department involved as the appellant in the case.
- entity_name: MAK KWONG YIU (麥光耀) (D1)
reason: Respondent in the case.
- entity_name: CHAN LAI YEE (陳麗兒) (D2)
reason: Respondent in the case.
- entity_name: WONG SHUK ON (黃淑安) (D3)
reason: Respondent in the case.
- entity_name: LEE YICK MING (李易明) (D4)
reason: Respondent in the case.

View File

@ -0,0 +1,46 @@
plaintiff:
- HKSAR
defendant:
- MAK KWONG YIU (麥光耀)
- CHAN LAI YEE (陳麗兒)
- WONG SHUK ON (黃淑安)
- LEE YICK MING (李易明)
jurisdiction_code: HKCFA
jurisdiction_name: Court of Final Appeal of the Hong Kong Special Administrative Region
case_location:
- 香港特別行政區
- Hong Kong Special Administrative Region
case_reason: >-
HKSAR appeals against the Court of Appeal's decision (CACC No. 239 of 2021) to overturn convictions
of defendants for conspiracy to conceal CISL's role as placing agent in a connected transaction.
case_object:
- conviction
- right to have conviction upheld
- connected transaction determination
judgment_result:
- charge: Conspiracy to conceal CISL's role as placing agent (liability issue)
result: Upheld. Appeal allowed, convictions restored. Court found concealment of CISL's agency constituted
unlawful connected transaction under Listing Rules, and directors' conflict of interest was central
to conspiracy charges.
judgment_summary: >-
HKSAR appeals against the Court of Appeal's overturning of convictions for conspiracy to conceal CISL's
role in a connected transaction. Core issues: upholding convictions and determining connected transaction
status. Court rejected narrow 'sham' test from Snook v London, citing Adams v The Queen, finding concealment
of CISL's agency constituted unlawful connected transaction under Listing Rules. Convictions upheld,
appeal allowed, with directors' conflicts central to charges. Hong Kong Stock Ex
involved_entities:
- entity_name: Mr Justice Ribeiro PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Fok PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Lam PJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Mr Justice Stock NPJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Sir William Young NPJ
reason: Presiding judge in this case, responsible for fact-finding and adjudication.
- entity_name: Diplock LJ
reason: Served as judge in Snook v London and West Riding Investments Ltd, articulated test for determining
sham transactions.
- entity_name: Hong Kong Stock Exchange
reason: Institution involved in regulatory framework for connected transactions.

View File

@ -41,14 +41,24 @@ hk_case_extractor.py
依賴 依賴
---- ----
pip install requests pyyaml pip install requests pyyaml
本地需運行ollama serve
模型ollama pull qwen2.5:7b-instruct 推薦中文抽取甜點
ollama pull glm4:9b
使用 使用
---- ----
python hk_case_extractor.py <input.txt> # 使用本地 Ollama默認
python hk_case_extractor.py case.txt
python hk_case_extractor.py case.txt --model qwen2.5:7b-instruct --out result.yaml python hk_case_extractor.py case.txt --model qwen2.5:7b-instruct --out result.yaml
# 使用 OpenRouter
python hk_case_extractor.py case.txt \\
--base-url https://openrouter.ai/api/v1 \\
--model anthropic/claude-3.5-sonnet \\
--api-key your-api-key
# 使用 OpenAI
python hk_case_extractor.py case.txt \\
--base-url https://api.openai.com/v1 \\
--model gpt-4 \\
--api-key your-api-key
""" """
from __future__ import annotations from __future__ import annotations
@ -69,8 +79,9 @@ import yaml
# 配置 # 配置
# ============================================================================= # =============================================================================
OLLAMA_URL = "https://openai.iconsz.com/ollama3090/api/chat" DEFAULT_BASE_URL = "http://localhost:11434/v1" # Ollama 默認 OpenAI 兼容端點
DEFAULT_MODEL = "qwen2.5:7b-instruct" DEFAULT_MODEL = "qwen2.5:7b-instruct"
DEFAULT_API_KEY = "ollama" # Ollama 不需要真實 key但 API 需要提供
DEFAULT_TIMEOUT = 240 DEFAULT_TIMEOUT = 240
MAX_RETRIES = 2 MAX_RETRIES = 2
@ -406,35 +417,60 @@ def gather_all(text: str) -> dict[str, str]:
# ============================================================================= # =============================================================================
# 2. Ollama 客戶端JSON Schema 強制 + 重試 # 2. OpenAI 兼容客戶端:支持 Ollama / OpenRouter / OpenAI 等
# ============================================================================= # =============================================================================
@dataclass @dataclass
class OllamaClient: class OpenAICompatibleClient:
"""OpenAI 兼容的 API 客戶端
支持
- Ollama (http://localhost:11434/v1)
- OpenRouter (https://openrouter.ai/api/v1)
- OpenAI (https://api.openai.com/v1)
- 其他 OpenAI 兼容的服務
"""
model: str = DEFAULT_MODEL model: str = DEFAULT_MODEL
url: str = OLLAMA_URL base_url: str = DEFAULT_BASE_URL
api_key: str = DEFAULT_API_KEY
timeout: int = DEFAULT_TIMEOUT timeout: int = DEFAULT_TIMEOUT
def chat_json(self, system: str, user: str, schema: dict, def chat_json(self, system: str, user: str, schema: dict,
temperature: float = 0.0, temperature: float = 0.0,
num_ctx: int = 8192) -> dict: max_tokens: int = 4096) -> dict:
"""調用 Ollama使用 format=<JSON Schema> 強制結構化輸出""" """調用 OpenAI 兼容 API使用 response_format 強制 JSON 輸出"""
# 構建請求 URL
url = f"{self.base_url.rstrip('/')}/chat/completions"
# 構建請求頭
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
}
# 構建請求體
payload = { payload = {
"model": self.model, "model": self.model,
"messages": [ "messages": [
{"role": "system", "content": system}, {"role": "system", "content": system},
{"role": "user", "content": user}, {"role": "user", "content": user},
], ],
"format": schema, "temperature": temperature,
"stream": False, "max_tokens": max_tokens,
"options": {"temperature": temperature, "num_ctx": num_ctx}, "response_format": {"type": "json_object"}, # OpenAI 兼容的 JSON 模式
} }
r = requests.post(self.url, json=payload, timeout=self.timeout)
# 發送請求
r = requests.post(url, json=payload, headers=headers, timeout=self.timeout)
r.raise_for_status() r.raise_for_status()
content = r.json()["message"]["content"]
# 解析響應
response_data = r.json()
content = response_data["choices"][0]["message"]["content"]
try: try:
return json.loads(content) return json.loads(content)
except json.JSONDecodeError as e: except json.JSONDecodeError:
# 嘗試剝離可能的 ```json fence # 嘗試剝離可能的 ```json fence
stripped = re.sub(r"^```(?:json)?\s*|\s*```$", "", stripped = re.sub(r"^```(?:json)?\s*|\s*```$", "",
content.strip(), flags=re.S) content.strip(), flags=re.S)
@ -551,7 +587,7 @@ Output:
{"plaintiff":["MO YUK PING"],"defendant":["HONG KONG SPECIAL ADMINISTRATIVE REGION"]}""" {"plaintiff":["MO YUK PING"],"defendant":["HONG KONG SPECIAL ADMINISTRATIVE REGION"]}"""
def extract_parties(client: OllamaClient, context: str, lang: str = 'zh') -> dict: def extract_parties(client: OpenAICompatibleClient, context: str, lang: str = 'zh') -> dict:
system = PARTIES_SYSTEM_ZH if lang == 'zh' else PARTIES_SYSTEM_EN system = PARTIES_SYSTEM_ZH if lang == 'zh' else PARTIES_SYSTEM_EN
fewshot = PARTIES_FEWSHOT_ZH if lang == 'zh' else PARTIES_FEWSHOT_EN fewshot = PARTIES_FEWSHOT_ZH if lang == 'zh' else PARTIES_FEWSHOT_EN
@ -652,7 +688,7 @@ def _reason_object_validator(out: dict, lang: str = 'zh') -> tuple[bool, str]:
return True, "" return True, ""
def extract_reason_object(client: OllamaClient, context: str, lang: str = 'zh') -> dict: def extract_reason_object(client: OpenAICompatibleClient, context: str, lang: str = 'zh') -> dict:
system = REASON_OBJECT_SYSTEM_ZH if lang == 'zh' else REASON_OBJECT_SYSTEM_EN system = REASON_OBJECT_SYSTEM_ZH if lang == 'zh' else REASON_OBJECT_SYSTEM_EN
fewshot = REASON_OBJECT_FEWSHOT_ZH if lang == 'zh' else REASON_OBJECT_FEWSHOT_EN fewshot = REASON_OBJECT_FEWSHOT_ZH if lang == 'zh' else REASON_OBJECT_FEWSHOT_EN
schema = get_reason_object_schema(lang) schema = get_reason_object_schema(lang)
@ -770,7 +806,7 @@ def _judgment_validator(out: dict, lang: str = 'zh') -> tuple[bool, str]:
return True, "" return True, ""
def extract_judgment_result(client: OllamaClient, context: str, lang: str = 'zh') -> dict: def extract_judgment_result(client: OpenAICompatibleClient, context: str, lang: str = 'zh') -> dict:
system = JUDGMENT_RESULT_SYSTEM_ZH if lang == 'zh' else JUDGMENT_RESULT_SYSTEM_EN system = JUDGMENT_RESULT_SYSTEM_ZH if lang == 'zh' else JUDGMENT_RESULT_SYSTEM_EN
fewshot = JUDGMENT_RESULT_FEWSHOT_ZH if lang == 'zh' else JUDGMENT_RESULT_FEWSHOT_EN fewshot = JUDGMENT_RESULT_FEWSHOT_ZH if lang == 'zh' else JUDGMENT_RESULT_FEWSHOT_EN
@ -884,7 +920,7 @@ def _entities_validator(out: dict, lang: str = 'zh') -> tuple[bool, str]:
return True, "" return True, ""
def extract_entities(client: OllamaClient, context: str, lang: str = 'zh') -> dict: def extract_entities(client: OpenAICompatibleClient, context: str, lang: str = 'zh') -> dict:
system = ENTITIES_SYSTEM_ZH if lang == 'zh' else ENTITIES_SYSTEM_EN system = ENTITIES_SYSTEM_ZH if lang == 'zh' else ENTITIES_SYSTEM_EN
fewshot = ENTITIES_FEWSHOT_ZH if lang == 'zh' else ENTITIES_FEWSHOT_EN fewshot = ENTITIES_FEWSHOT_ZH if lang == 'zh' else ENTITIES_FEWSHOT_EN
@ -962,7 +998,7 @@ def _summary_validator(out: dict, lang: str = 'zh') -> tuple[bool, str]:
return True, "" return True, ""
def extract_summary(client: OllamaClient, def extract_summary(client: OpenAICompatibleClient,
prior: dict, analysis: str, lang: str = 'zh') -> dict: prior: dict, analysis: str, lang: str = 'zh') -> dict:
system = SUMMARY_SYSTEM_ZH if lang == 'zh' else SUMMARY_SYSTEM_EN system = SUMMARY_SYSTEM_ZH if lang == 'zh' else SUMMARY_SYSTEM_EN
schema = get_summary_schema(lang) schema = get_summary_schema(lang)
@ -1072,7 +1108,7 @@ def validate_and_fix(result: dict, lang: str = 'zh') -> tuple[dict, list[str]]:
# 5. 主管線 # 5. 主管線
# ============================================================================= # =============================================================================
def run_pipeline(text: str, model: str) -> dict: def run_pipeline(text: str, model: str, base_url: str, api_key: str) -> dict:
log = lambda m: print(m, file=sys.stderr) log = lambda m: print(m, file=sys.stderr)
log("[0/7] 檢測語言...") log("[0/7] 檢測語言...")
@ -1091,7 +1127,7 @@ def run_pipeline(text: str, model: str) -> dict:
hits_info = f"hits={ctx[f'_{g}_hits']}" if ctx[f'_{g}_hits'] != "0" else "直接截取" hits_info = f"hits={ctx[f'_{g}_hits']}" if ctx[f'_{g}_hits'] != "0" else "直接截取"
log(f" {g:16s} len={len(ctx[g]):5d} {hits_info}") log(f" {g:16s} len={len(ctx[g]):5d} {hits_info}")
client = OllamaClient(model=model) client = OpenAICompatibleClient(model=model, base_url=base_url, api_key=api_key)
log("[2/7] 抽取當事人...") log("[2/7] 抽取當事人...")
parties = extract_parties(client, ctx["parties"], lang) parties = extract_parties(client, ctx["parties"], lang)
@ -1176,9 +1212,33 @@ def to_yaml(result: dict) -> str:
def main() -> None: def main() -> None:
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
description="香港判決書結構化抽取(本地 Ollama 版)") description="香港判決書結構化抽取OpenAI 兼容 API",
epilog="""
示例用法
# 使用本地 Ollama
python hk_case_extractor.py case.txt --model qwen2.5:7b-instruct
# 使用 OpenRouter
python hk_case_extractor.py case.txt \\
--base-url https://openrouter.ai/api/v1 \\
--model anthropic/claude-3.5-sonnet \\
--api-key your-api-key
# 使用 OpenAI
python hk_case_extractor.py case.txt \\
--base-url https://api.openai.com/v1 \\
--model gpt-4 \\
--api-key your-api-key
""",
formatter_class=argparse.RawDescriptionHelpFormatter
)
ap.add_argument("input", help="判決書文本路徑(.txt 或 .json") ap.add_argument("input", help="判決書文本路徑(.txt 或 .json")
ap.add_argument("--model", default=DEFAULT_MODEL, help="Ollama 模型名") ap.add_argument("--model", default=DEFAULT_MODEL,
help=f"模型名稱(默認:{DEFAULT_MODEL}")
ap.add_argument("--base-url", default=DEFAULT_BASE_URL,
help=f"API base URL默認{DEFAULT_BASE_URL}")
ap.add_argument("--api-key", default=DEFAULT_API_KEY,
help="API keyOllama 可忽略)")
ap.add_argument("--out", default=None, help="輸出 YAML 路徑(默認 stdout") ap.add_argument("--out", default=None, help="輸出 YAML 路徑(默認 stdout")
ap.add_argument("--debug-dump", default=None, ap.add_argument("--debug-dump", default=None,
help="額外輸出原始 JSON 結果到該路徑(便於 diff") help="額外輸出原始 JSON 結果到該路徑(便於 diff")
@ -1196,7 +1256,7 @@ def main() -> None:
else: else:
text = input_path.read_text(encoding="utf-8") text = input_path.read_text(encoding="utf-8")
result = run_pipeline(text, args.model) result = run_pipeline(text, args.model, args.base_url, args.api_key)
if args.debug_dump: if args.debug_dump:
Path(args.debug_dump).write_text( Path(args.debug_dump).write_text(