147 lines
6.4 KiB
Python
147 lines
6.4 KiB
Python
from typing import List
|
||
|
||
from agent.models import ProgramMeta
|
||
|
||
|
||
class PromptBuilder:
|
||
"""将解析后的程序元数据和规则组装成 API prompt。"""
|
||
|
||
def build(self, meta: ProgramMeta, rules_text: str,
|
||
group_descriptions: List[str], group_count: int) -> str:
|
||
"""构建完整的 API prompt。"""
|
||
parts = []
|
||
|
||
parts.append(self._build_basic_info(meta))
|
||
parts.append(self._build_process_detail(meta))
|
||
parts.append(self._build_input_structures(meta))
|
||
parts.append(self._build_output_records(meta))
|
||
parts.append(rules_text)
|
||
parts.append(self._build_output_format(meta))
|
||
parts.append(self._build_generation_instruction(
|
||
group_descriptions, group_count, meta.input_type
|
||
))
|
||
|
||
return '\n\n'.join(parts)
|
||
|
||
def _build_basic_info(self, meta: ProgramMeta) -> str:
|
||
lines = [
|
||
f"## プログラム基本情報",
|
||
f"- システム名: {meta.system_name}",
|
||
f"- プログラムID: {meta.program_id}",
|
||
f"- プログラム名: {meta.program_name}",
|
||
f"- PGMパターン: {meta.pgm_pattern}",
|
||
f"- 入力タイプ: {self._input_type_label(meta.input_type)}",
|
||
]
|
||
if meta.summary_lines:
|
||
lines.append(f"- 機能概要: {' '.join(meta.summary_lines)}")
|
||
return '\n'.join(lines)
|
||
|
||
def _build_process_detail(self, meta: ProgramMeta) -> str:
|
||
return f"## 処理詳細\n\n```\n{meta.process_detail}\n```"
|
||
|
||
def _build_input_structures(self, meta: ProgramMeta) -> str:
|
||
parts = ["## 入力構造"]
|
||
|
||
input_files = [f for f in meta.files if 'I' in f.io]
|
||
for f in input_files:
|
||
parts.append(f"### ファイル {f.identifier} (DD名: {f.dd_name}, COPY: {f.copy_group}, 媒体: {f.medium})")
|
||
fields = meta.copy_fields.get(f.identifier, [])
|
||
if fields:
|
||
parts.append("| 項目名 | PIC | バイト数 |")
|
||
parts.append("|--------|-----|----------|")
|
||
for cf in fields:
|
||
parts.append(f"| {cf.name} | {cf.pic_type} | {cf.pic_bytes} |")
|
||
else:
|
||
parts.append("(構造情報なし)")
|
||
|
||
db_inputs = [f for f in meta.files if 'I' in f.io and f.medium == 'DB']
|
||
if db_inputs or meta.db_tables:
|
||
parts.append("### DBテーブル構造")
|
||
for table_name, table in meta.db_tables.items():
|
||
parts.append(f"**表名: {table_name}**")
|
||
pk_str = ', '.join(table.pk_columns)
|
||
parts.append(f"主キー: {pk_str}")
|
||
parts.append("| 項目名 | 英字名 | タイプ | 最大長 | KEY |")
|
||
parts.append("|--------|--------|--------|--------|-----|")
|
||
for col in table.columns:
|
||
key_mark = '✓' if col.is_pk else ''
|
||
parts.append(f"| {col.name_jp} | {col.name_en} | {col.type} | {col.max_len} | {key_mark} |")
|
||
|
||
return '\n\n'.join(parts)
|
||
|
||
def _build_output_records(self, meta: ProgramMeta) -> str:
|
||
return f"## 出力レコード定義\n\n```\n{meta.output_records}\n```"
|
||
|
||
def _build_output_format(self, meta: ProgramMeta) -> str:
|
||
lines = [
|
||
"## 出力形式",
|
||
"",
|
||
"### JSON形式(ファイル入力の場合)",
|
||
"",
|
||
"JSON構造:",
|
||
"```json",
|
||
"{",
|
||
' "program": "{プログラムID}",',
|
||
' "records": [',
|
||
' { "input": { "FD名": { "項目名": "値", ... } } },',
|
||
' ...',
|
||
' ]',
|
||
"}",
|
||
"```",
|
||
"",
|
||
"### 項目値のルール(PIC → JSON値)",
|
||
"| PIC | JSON内表示 | 例 |",
|
||
"|-----|-----------|-----|",
|
||
"| PIC X(n) | 左詰め + スペース埋め | `\"A0000001\"` |",
|
||
"| PIC 9(n) | 右詰め + 先行ゼロ | `\"00000101\"` |",
|
||
"| PIC S9(n) | 符号 + 右詰め + 先行ゼロ | `\"+0000101\"` |",
|
||
"| PIC S9(n)V9(m) | 符号 + 右詰め + 小数点含む | `\"+001234567\"` |",
|
||
"| PIC S9(n) COMP | 通常の10進数文字列 | `\"300\"` |",
|
||
"| PIC S9(n) COMP-3 | 通常の10進数文字列 | `\"1234\"` |",
|
||
"| PIC 9(n) COMP-3 | 通常の10進数文字列 | `\"1234\"` |",
|
||
"| FILLER(純粋予約) | グループIDとレコード番号を含むパターン | `\"D000000...001\"` |",
|
||
"| FILLER(業務予約) | 全スペース(PIC X) または 全ゼロ(PIC 9) | |",
|
||
"",
|
||
"### SQL形式(DB入力の場合)",
|
||
"各グループ1つのSQLファイル: `{program}_g{groupId}.sql`",
|
||
"",
|
||
"SQL例:",
|
||
"```sql",
|
||
"-- Group: 1",
|
||
"INSERT INTO TABLE_NAME (COL1, COL2) VALUES",
|
||
"('val1', 'val2');",
|
||
"```",
|
||
"",
|
||
"### データ生成の注意",
|
||
"- 項目名に意味がある場合(DATE→日付、NAME→氏名)、実際の形式に合った値を生成すること",
|
||
"- 隣接するレコード間で、同じ項目に異なる値を設定すること",
|
||
]
|
||
return '\n'.join(lines)
|
||
|
||
def _build_generation_instruction(self, descriptions: List[str],
|
||
count: int, input_type: str) -> str:
|
||
lines = [
|
||
"## 生成指示",
|
||
f"生成するグループ数: {count}",
|
||
"各グループの内容:",
|
||
]
|
||
for i, desc in enumerate(descriptions, 1):
|
||
lines.append(f" - g{i}: {desc}")
|
||
|
||
if input_type == 'file':
|
||
lines.append("出力: JSONファイル(1グループ=1JSONファイル)")
|
||
elif input_type == 'db':
|
||
lines.append("出力: SQL INSERTファイル(1グループ=1SQLファイル)")
|
||
else:
|
||
lines.append("出力: JSONファイル + SQL INSERTファイル")
|
||
|
||
lines.append("")
|
||
lines.append("応答形式: JSONで返してください。最上位キー \"groups\" を使い、各グループを \"g1\", \"g2\" ... とします。")
|
||
lines.append("例: {\"groups\": {\"g1\": {\"program\": \"XXX\", \"records\": [...]}, \"g2\": {...}}}")
|
||
return '\n'.join(lines)
|
||
|
||
@staticmethod
|
||
def _input_type_label(t: str) -> str:
|
||
labels = {'file': 'ファイル', 'db': 'DB', 'mixed': '混合(ファイル+DB)'}
|
||
return labels.get(t, t)
|